TensorFlow之name_scope/variable_scope
引子
前面寫過一篇博文《TensorFlow學習筆記(六)》,其內容主要介紹的就是name_scope/variable_scope的使用,不過並沒有明確地對這二者的使用場景進行區分,所以本文將清晰地給出name_scope/variable_scope的應用場景。
文章比較長,如果時間不夠的話,那就直接看總結啦~
主要用法
我們知道,在TensorFlow中,常常會定義一堆節點,如果我們不能對此進行有效地管理,那麼這些節點很可能會使我們心如亂麻,並且再也不想看這些雜亂的程式碼。
name_scope/variable_scope正是為了更有效地管理節點而產生的兩個操作(op)。
- name_scope:用於對變數設定命名域,從而讓變數按照其關係組成一個個scope,並不會對tf.get_variable()建立的變數名字產生影響;
- variable_scope:絕大部分情形下,與tf.get_variable()配合使用,實現變數共享。
示例
我們下面舉幾個簡單的例子:
a)利用name_scope對tf.Variable建立的變數加上命名域:
with tf.name_scope("scope_1"):
v1 = tf.Variable([1], name="v1", dtype=tf.float32)
v2 = tf.get _variable("v2", [1])
print v1
print v2
此時輸出結果為:
<tf.Variable 'scope_1/v1:0' shape=(1,), dtype=float32_ref>
<tf.Variable 'v2:0' shape=(1,) dtype=float32_ref>
從這裡我們可以得出下列結論:
- name_scope並不會對tf.get_variable()建立的變數新增命名域;
- name_scope並不能用於實現變數共享。
b)結合使用variable_scope與tf.get_variable()實現變數共享:
實驗一
with tf.variable_scope("scope_1"):
v1 = tf.Variable([1], name="v1", dtype=tf.float32)
with tf.variable_scope("scope_1"):
v2 = tf.Variable([1], name="v1", dtype=tf.float32)
此時的輸出結果為:
<tf.Variable 'scope_1/v1:0' shape=(1,), dtype=float32_ref>
<tf.Variable 'scope_1_1/v1:0' shape=(1,), dtype=float32_ref>
這個實驗說明了tf.Variable()遇到重名變數時,將自動重新命名,而不會發生衝突。在這個實驗中,我們是在同一個scope中定義的同名變數,可是為什麼不是修改最內層的變數名呢?也即為什麼輸出結果不是下面這樣呢?
<tf.Variable 'scope_1/v1:0' shape=(1,), dtype=float32_ref>
<tf.Variable 'scope_1/v1_1:0' shape=(1,), dtype=float32_ref>
這是TensorFlow的一種很巧妙的手段,因為tf.Variable()被設計為提供給庫編寫者使用的一個介面,所以我們修改scope的話更加有利。為什麼這麼說?比如,我們執行下面的程式碼:
net = fully_connected(input_tensor, shape)
net = fully_connected(net, shape)
這個時候我們對fully_connected()函式的兩次呼叫均沒有給出scope,那這個時候我們的全連線層中的變數應該如何命名呢?這就與上面的實驗情形類似了,TFLearn的fully_connected()函式是使用tf.Variable()實現的,所以一般情況下,將會使用預設的“FullyConnected”這一scope作為命名域,當我們第二次呼叫fully_connected()函式時,自動改變命名域為“FullyConnected_1”,而不是修改命名域內部的變數名,是不是比直接改最內層的變數名要合理許多?那如果我們改變最內層的變數名,假設我們的函式fn()在scope中定義了“scope/v1, scope/v2”,那麼,我們接下來將重新命名為“scope/v1_1, scope/v2_1”,這樣的話,那不是都在同一個scope中了麼?到時候在TensorBoard中將顯得亂七八糟~
對了,tf.Variable()在解決衝突時,總是重新命名最外層的scope喲~驗證如下:
with tf.variable_scope("scope_top"):
with tf.variable_scope("scope_bot"):
v1 = tf.Variable([1], name="v1", dtype=tf.float32)
with tf.variable_scope("scope_top"):
with tf.variable_scope("scope_bot"):
v2 = tf.Variable([1], name="v1", dtype=tf.float32)
vs=tf.trainable_variables()
for v in vs:
print v
此時,我們的輸出為:
<tf.Variable 'scope_top/scope_bot/v1:0' shape=(1,), dtype=float32_ref>
<tf.Variable 'scope_top_1/scope_bot/v1:0' shape=(1,), dtype=float32_ref>
這樣的話,如果我們呼叫一些基本的Layers來定義自己的Layer,比如說叫做layer_udef,且預設命名域為“Layer_Udef”,那麼重複使用layer_udef時,得到的是“Layer_Udef”、“Layer_Udef_1”…這就很符合我們的預期咯。
實驗二
在實驗一中,我們發現使用tf.Variable()並不能獲取已經定義變數,換句話說,不能達到共享變數的目的,那我們能否用tf.get_variable()函式獲取tf.Variable()定義的變數呢?
with tf.variable_scope("scope_1"):
v1 = tf.Variable([1], name="v1", dtype=tf.float32)
print v1
with tf.variable_scope("scope_1", reuse=True):
v2 = tf.get_variable("v1", [1])
輸出為:
<tf.Variable 'scope_1/v1:0' shape=(1,) dtype=float32_ref>
...
ValueError: Variable scope_1/v1 does not exist, or was not created with tf.get_variable().
相信大家都有疑惑,不是已經有了scope_1/v1變數麼?為什麼說它不存在呢?因為tf.Variable()所定義的變數並不是用於共享的,雖然它對於tf.get_variable()是可見的:
with tf.variable_scope("scope_1"):
v1 = tf.Variable([1], name="v1", dtype=tf.float32)
with tf.variable_scope("scope_1"):
v2 = tf.get_variable("v1", [1])
此時輸出為:
<tf.Variable 'scope_1/v1:0' shape=(1,) dtype=float32_ref>
<tf.Variable 'scope_1/v1_1:0' shape=(1,) dtype=float32_ref>
那到底為什麼tf.Variable()定義的變數不能共享呢?這就涉及到tf.Variable()和tf.get_variable()的設計理念了:個人認為,tf.Variable()主要是為庫的編寫者設計,或者,我們可以用它來編寫自己的需要重複定義的Layers,這樣就不用操心變數之間的衝突了,這一點我們在實驗一中已經說明過了。所以,當我們想要從最底層開始定義自己的層時,使用tf.Variable()吧~
實驗三
說了這麼多,那到底怎樣才能成功地共享變數呢?
with tf.variable_scope("scope_1"):
v1 = tf.get_variable("v1", [1])
with tf.variable_scope("scope_1", reuse=True):
v2 = tf.get_variable("v1", [1])
vs = tf.trainable_variables()
for v in vs:
print v
此時我們的輸出為:
<tf.Variable 'scope_1/v1:0' shape=(1,) dtype=float32_ref>
也就是說,變數“scope_1/v1”被共享咯~
總結
現將本文總結如下:
- name_scope並不會對tf.get_variable()定義的變數的命名產生影響;
- 如果要從底層變數開始定義庫函式的話,使用tf.Variable()是一種較好的選擇;
- tf.Variable()定義的變數並不能被共享;
- 如果想要實現變數共享,那就同時使用variable_scope和tf.get_variable()吧~
最後祝大家週末愉快~