-
name/variable_scope 的作用
-
充分理解 name / variable_scope
-
TensorFlow 入門筆記
- 當一個神經網絡比較復雜、參數比較多時,就比較需要一個比較好的方式來傳遞和管理這些參數。而Tensorflow提供了通過變量名稱來創建或者獲取變量的機制。通過這個機制,可以在不同的函數中直接通過變量的名稱來使用變量,而不需要將變量通過參數進行傳遞。
-
* name_scope: * 為了更好地管理變量的命名空間而提出的。比如在 tensorboard 中,因為引入了 name_scope, 我們的 Graph 看起來才井然有序。
-
* variable_scope: * 大大大部分情況下,跟 tf.get_variable() 配合使用,實現變量共享的功能。
2.三種方式創建變量: tf.placeholder, tf.Variable, tf.get_variable
從上面的實驗結果來看,這三種方式所定義的變量具有相同的類型。而且只有 tf.get_variable() 創建的變量之間會發生命名沖突。在實際使用中,三種創建變量方式的用途也是分工非常明確的。其中
- tf.placeholder() 占位符。* trainable==False *
- tf.Variable() 一般變量用這種方式定義。 * 可以選擇 trainable 類型 *
- tf.get_variable() 一般都是和 tf.variable_scope() 配合使用,從而實現變量共享的功能。 * 可以選擇 trainable 類型 *
- tf.get_variable的作用不僅在於創建變量,它還可以通過變量名稱獲取變量,但在創建變量時,如果創建失敗(變量已經存在),那么就會報錯。因此,就需要一個上下文管理器tf.variable_scope,可以更好的管理變量,並且可以增加程序的可讀性。
- with tf.variable_scope('foo',reuse=None):參數reuse=True時,通過tf.get_variable只能獲取已經存在的變量名。上下文管理器在嵌套的時候,變量名稱的前面會加上上下文管理器的名稱。
3. 探索 name_scope 和 variable_scope
tf.name_scope() 並不會對 tf.get_variable() 創建的變量有任何影響。
tf.name_scope() 主要是用來管理命名空間的,這樣子讓我們的整個模型更加有條理。而 tf.variable_scope() 的作用是為了實現變量共享,它和 tf.get_variable() 來完成變量共享的功能。
- 首先我們要確立一種 Graph 的思想。在 TensorFlow 中,我們定義一個變量,相當於往 Graph 中添加了一個節點。和普通的 python 函數不一樣,在一般的函數中,我們對輸入進行處理,然后返回一個結果,而函數里邊定義的一些局部變量我們就不管了。但是在 TensorFlow 中,我們在函數里邊創建了一個變量,就是往 Graph 中添加了一個節點。出了這個函數后,這個節點還是存在於 Graph 中的。
-
name_scope
返回的是 string, 而variable_scope
返回的是對象. 這也可以感覺到,variable_scope
能干的事情比name_scope
要多.- name_scope對 get_variable()創建的變量 的名字不會有任何影響,而創建的
op
會被加上前綴. - tf.get_variable_scope() 返回的只是 variable_scope,不管 name_scope. 所以以后我們在使用tf.get_variable_scope().reuse_variables() 時可以無視name_scope
總結簡單來看
1. 使用tf.Variable()
的時候,tf.name_scope()
和tf.variable_scope()
都會給 Variable
和 op
的 name
屬性加上前綴。
2. 使用tf.get_variable()
的時候,tf.name_scope()
就不會給 tf.get_variable()
創建出來的Variable
加前綴。但是 tf.Variable()
創建出來的就會受到 name_scope
的影響.
name_scope可以用來干什么
典型的 TensorFlow 可以有數以千計的節點,如此多而難以一下全部看到,甚至無法使用標准圖表工具來展示。為簡單起見,我們為op/tensor
名划定范圍,並且可視化把該信息用於在圖表中的節點上定義一個層級。默認情況下, 只有頂層節點會顯示。下面這個例子使用tf.name_scope在hidden命名域下定義了三個操作:
import tensorflow as tf with tf.name_scope('hidden') as scope: a = tf.constant(5, name='alpha') W = tf.Variable(tf.random_uniform([1, 2], -1.0, 1.0), name='weights') b = tf.Variable(tf.zeros([1]), name='biases') print a.name print W.name print b.name
結果是得到了下面三個操作名:
hidden/alpha
hidden/weights
hidden/biases
name_scope 是給op_name加前綴, variable_scope是給get_variable()創建的變量的名字加前綴。
tf.variable_scope有時也會處理命名沖突
import tensorflow as tf def test(name=None): with tf.variable_scope(name, default_name="scope") as scope: w = tf.get_variable("w", shape=[2, 10]) test() test() ws = tf.trainable_variables() for w in ws: print(w.name) #scope/w:0 #scope_1/w:0 #可以看出,如果只是使用default_name這個屬性來創建variable_scope #的時候,會處理命名沖突
共享變量
TensorFlow 支持兩種共享變量的方式:
- 顯式傳遞
tf.Variable
對象。 - 在
tf.variable_scope
對象內隱式包裝tf.Variable
對象。
雖然顯式傳遞變量的代碼非常清晰,但有時編寫 TensorFlow 函數(在實現中隱式使用變量)非常方便。tf.layer
中的大多數功能層以及所有 tf.metrics
和部分其他庫工具都使用這種方法。
變量作用域允許您在調用隱式創建和使用變量的函數時控制變量重用。作用域還允許您以分層和可理解的方式命名變量。
例如,假設我們編寫一個函數來創建一個卷積/relu 層:
def conv_relu(input, kernel_shape, bias_shape):
# Create variable named "weights".
weights = tf.get_variable("weights", kernel_shape,
initializer=tf.random_normal_initializer())
# Create variable named "biases".
biases = tf.get_variable("biases", bias_shape,
initializer=tf.constant_initializer(0.0))
conv = tf.nn.conv2d(input, weights,
strides=[1, 1, 1, 1], padding='SAME')
return tf.nn.relu(conv + biases)
此函數使用短名稱 weights
和 biases
,這有利於清晰區分二者。然而,在真實模型中,我們需要很多此類卷積層,而且重復調用此函數將不起作用:
input1 = tf.random_normal([1,10,10,32])
input2 = tf.random_normal([1,20,20,32])
x = conv_relu(input1, kernel_shape=[5, 5, 32, 32], bias_shape=[32])
x = conv_relu(x, kernel_shape=[5, 5, 32, 32], bias_shape = [32]) # This fails.
由於期望的操作不清楚(創建新變量還是重新使用現有變量?),因此 TensorFlow 將會失敗。不過,在不同作用域內調用 conv_relu
可表明我們想要創建新變量:
def my_image_filter(input_images):
with tf.variable_scope("conv1"):
# Variables created here will be named "conv1/weights", "conv1/biases".
relu1 = conv_relu(input_images, [5, 5, 32, 32], [32])
with tf.variable_scope("conv2"):
# Variables created here will be named "conv2/weights", "conv2/biases".
return conv_relu(relu1, [5, 5, 32, 32], [32])
如果您想要共享變量,有兩種方法可供選擇。首先,您可以使用 variable_scope
:reuse=True
創建具有相同名稱的作用域:
with tf.variable_scope("model"):
output1 = my_image_filter(input1)
with tf.variable_scope("model", reuse=True):
output2 = my_image_filter(input2)
您也可以調用 scope.reuse_variables()
以觸發重用:
with tf.variable_scope("model") as scope:
output1 = my_image_filter(input1)
scope.reuse_variables()
output2 = my_image_filter(input2)
由於根據作用域的具體字符串名稱初始化變量作用域可能比較危險,因此也可以根據另一作用域進行初始化:
with tf.variable_scope("model") as scope:
output1 = my_image_filter(input1)
with tf.variable_scope(scope, reuse=True):
output2 = my_image_filter(input2)