1. 程式人生 > >TensorFlow之name_scope/variable_scope

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()吧~

        最後祝大家週末愉快~