tf.name_scope()和tf.variable_scope()解析

本文爲轉載,原博客地址:https://blog.csdn.net/gqixf/article/details/80191918

摘要: tf.name_scope()和tf.variable_scope()是兩個作用域,一般與兩個創建/調用變量的函數tf.variable() 和tf.get_variable()搭配使用。它們搭配在一起的兩個常見用途:1)變量共享,2)tensorboard畫流程圖時爲了可視化封裝變量,這兩種用途有特定的搭配方式,掌握好就可以用了。如果您想打破砂鍋問到底,想要探尋更多的知識,瞭解它們的用法和區別,本文中則闡述了更多的詳情。

一. name_scope 和 variable_scope的用途
用途1: 共享變量
TensorFlow (TF) 中,name_scope 和 variable_scope 主要是因爲 變量共享 的需求。爲什麼要共享變量?舉個簡單的例子:例如,當我們研究生成對抗網絡GAN的時候,判別器的任務是如果接收到的是生成器生成的圖像,判別器就嘗試優化自己的網絡結構來使自己輸出0,如果接收到的是來自真實數據的圖像,那麼就嘗試優化自己的網絡結構來使自己輸出1。也就是說,生成圖像和真實圖像經過判別器的時候,要共享同一套變量,所以TensorFlow引入了變量共享機制。

變量共享主要涉及兩個函數:

tf.get_variable(<name>, <shape>, <initializer>)
tf.variable_scope(<scope_name>)
即就是必須要在tf.variable_scope的作用域下使用tf.get_variable()函數。這裏用tf.get_variable( ) 而不用tf.Variable( ),是因爲前者擁有一個變量檢查機制,會檢測已經存在的變量是否設置爲共享變量,如果已經存在的變量沒有設置爲共享變量,TensorFlow 運行到第二個擁有相同名字的變量的時候,就會報錯(詳見本文的RNN應用例子章節)。

兩個創建變量的方式。如果使用tf.Variable() 的話每次都會新建變量。但是大多數時候我們是希望重用一些變量,所以就用到了get_variable(),它會去搜索變量名,有就直接用,沒有再新建。
名字域。既然用到變量名了,就涉及到了名字域的概念。通過不同的域來區別變量名,畢竟讓我們給所有變量都取不同名字還是很辛苦。
這就是爲什麼會有scope 的概念。name_scope 作用於操作,variable_scope 可以通過設置reuse 標誌以及初始化方式來影響域下的變量。
“共享變量” 的應用場景:RNN應用例子
在tf.variable_scope的作用域下,通過get_variable()使用已經創建的變量,實現了變量的共享。在 train RNN 和 test RNN 的時候, RNN 的 time_steps 會有不同的取值, 這將會影響到整個 RNN 的結構, 所以導致在 test 的時候, 不能單純地使用 train 時建立的那個 RNN. 但是 train RNN 和 test RNN 又必須是有同樣的 weights biases 的參數. 所以, 這時, 就是使用 reuse variable 的好時機.

例子:首先定義train 和 test 的不同參數.

class TrainConfig:
batch_size = 20
time_steps = 20
input_size = 10
output_size = 2
cell_size = 11
learning_rate = 0.01

class TestConfig(TrainConfig):
time_steps = 1

train_config = TrainConfig()
test_config = TestConfig()
然後讓 train_rnn 和 test_rnn 在同一個 tf.variable_scope('rnn') 之下.

並且定義 scope.reuse_variables(), 使我們能把 train_rnn 的所有 weights, biases 參數全部綁定到 test_rnn 中.

這樣, 不管兩者的 time_steps 有多不同, 結構有多不同, train_rnn W, b 參數更新成什麼樣, test_rnn 的參數也更新成什麼樣.

with tf.variable_scope('rnn') as scope:
sess = tf.Session()
train_rnn = RNN(train_config)
scope.reuse_variables()
test_rnn = RNN(test_config)
sess.run(tf.global_variables_initializer())
RNN 例子的完整代碼可以看這裏.

因爲想要達到變量共享的效果, 就要在 tf.variable_scope()的作用域下使用 tf.get_variable() 這種方式產生和提取變量. 不像 tf.Variable() 每次都會產生新的變量, tf.get_variable() 如果遇到了已經存在名字的變量時, 它會單純的提取這個同樣名字的變量,如果不存在名字的變量再創建.

但在重複使用的時候, 一定要在代碼中強調 scope.reuse_variables(),如下面代碼所示,否則系統將會報錯, 以爲你是不小心重複使用到了一個變量.

with tf.variable_scope("a_variable_scope") as scope:
initializer = tf.constant_initializer(value=3)
var3 = tf.get_variable(name='var3', shape=[1], dtype=tf.float32, initializer=initializer)
scope.reuse_variables()
var3_reuse = tf.get_variable(name='var3',) # var3_reuse
var4 = tf.Variable(name='var4', initial_value=[4], dtype=tf.float32)
var4_reuse = tf.Variable(name='var4', initial_value=[4], dtype=tf.float32) # var4_reuse

with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var3.name) # a_variable_scope/var3:0
print(sess.run(var3)) # [ 3.]
print(var3_reuse.name) # a_variable_scope/var3:0
print(sess.run(var3_reuse)) # [ 3.]
print(var4.name) # a_variable_scope/var4:0
print(sess.run(var4)) # [ 4.]
print(var4_reuse.name) # a_variable_scope/var4_1:0
print(sess.run(var4_reuse)) # [ 4.]

不強調reuse_variables()而報錯的例子:

如果把 tf.Variable 改成 tf.get_variable,直接調用兩次,就會報錯:

result1 = my_image_filter(image1)
result2 = my_image_filter(image2)

Raises ValueError(... conv1/weights already exists ...)

爲了解決這個問題,TF提出了 tf.variable_scope 函數:它的主要作用是,在一個 variable_scope 作用域下內共享一些變量。實現方式可以有如下幾種用法:

1)

with tf.variable_scope("image_filters") as scope:
result1 = my_image_filter(image1)
scope.reuse_variables() # or

tf.get_variable_scope().reuse_variables()

result2 = my_image_filter(image2)

2)

with tf.variable_scope("image_filters1") as scope1:
result1 = my_image_filter(image1)
with tf.variable_scope(scope1, reuse = True)
result2 = my_image_filter(image2)
注意最好不要採用的方式:設置 reuse 標識爲 False,然後在需要的時候設置 reuse 標識爲 True。

可視化用途:畫tensorboard流程圖時封裝
當然對我們而言name_scope還有個更直觀的用處就是:在tensorboard 裏可視化的時候用name_scope進行封裝後會更清晰. 通常情況下,tf.variable_scope 和 tf.name_scope 配合,能畫出非常漂亮的tensorboard流程圖,但是他們兩個之間又有着細微的差別,那就是 name_scope 只能管住操作 Ops 的名字,而管不住變量 Variables 的名字,看下例:

with tf.variable_scope("foo"):
with tf.name_scope("bar"):
v = tf.get_variable("v", [1])
x = 1.0 + v
assert v.name == "foo/v:0"
assert x.op.name == "foo/bar/add"
參考:https://www.zhihu.com/question/54513728/answer/177901159

Tensorflow變量共享,參考:http://www.cnblogs.com/Charles-Wan/p/6200446.html

參考:http://sentiment-mining.blogspot.com/2016/12/tensorflow-name-scope-variable-scope.html

參考:https://morvanzhou.github.io/tutorials/machine-learning/tensorflow/5-12-scope/

二. TensorFlow中name scope和variable scope區別
爲了在代碼的任何部分可以使用某一個已經創建的變量,TF引入了變量共享機制共享變量,而不是傳一個變量的引用。

I. TF中創建變量的方式有兩種:tf.get_variable()和tf.Variable()
tf.get_variable(“vname”)方法,在創建變量時,如果這個變量vname已經存在,直接使用這個變量,如果不存在,則重新創建;
tf.Variable()在創建變量時,一律創建新的變量,如果這個變量已存在,則後綴會增加0、1、2等數字編號予以區別。
例子:在 tf.name_scope() 的框架下使用這兩種方式, 結果如下.

import tensorflow as tf

with tf.name_scope("a_name_scope"):
initializer = tf.constant_initializer(value=1)
var1 = tf.get_variable(name='var1', shape=[1], dtype=tf.float32, initializer=initializer)
var2 = tf.Variable(name='var2', initial_value=[2], dtype=tf.float32)
var21 = tf.Variable(name='var2', initial_value=[2.1], dtype=tf.float32)
var22 = tf.Variable(name='var2', initial_value=[2.2], dtype=tf.float32)

with tf.Session() as sess:
sess.run(tf.initialize_all_variables())
print(var1.name) # var1:0
print(sess.run(var1)) # [ 1.]
print(var2.name) # a_name_scope/var2:0
print(sess.run(var2)) # [ 2.]
print(var21.name) # a_name_scope/var2_1:0
print(sess.run(var21)) # [ 2.0999999]
print(var22.name) # a_name_scope/var2_2:0
print(sess.run(var22)) # [ 2.20000005]
可以看出使用 tf.Variable() 定義變量的時候, 雖然var2, var21, var22的 name 一樣, 但是爲了不重複變量名, Tensorflow 爲它們做了附加的區分,因爲輸出的變量名是不一樣的. 所以, 本質上tensorflow令 var2, var21, var22 成爲不一樣的變量.

這樣做的目的是,搭配上不同的作用域類型,可以實現變量共享機制。

II. TF中有兩種作用域類型
命名域 (name scope),通過tf.name_scope 或 tf.op_scope創建;
變量域 (variable scope),通過tf.variable_scope 或 tf.variable_op_scope創建;
這兩種作用域,對於使用tf.Variable()方式創建的變量,具有相同的效果,都會在變量名稱前面,加上域名稱。
對於通過tf.get_variable()方式創建的變量,只有variable scope名稱會加到變量名稱前面,而name scope不會作爲前綴。例如 print(v1.name) # var1:0
例子:

with tf.name_scope("my_name_scope"):


 
  1. v1 = tf.get_variable("var1", [1], dtype=tf.float32)

  2.  
  3. v2 = tf.Variable(1, name="var2", dtype=tf.float32)

  4.  
  5. a = tf.add(v1, v2)

print(v1.name) # var1:0

print(v2.name) # my_name_scope/var2:0

print(a.name) # my_name_scope/Add:0

小結:name_scope不會作爲tf.get_variable變量的前綴,但是會作爲tf.Variable的前綴。

例子:

with tf.variable_scope("my_variable_scope"):


 
  1. v1 = tf.get_variable("var1", [1], dtype=tf.float32)

  2.  
  3. v2 = tf.Variable(1, name="var2", dtype=tf.float32)

  4.  
  5. a = tf.add(v1, v2)

print(v1.name) # my_variable_scope/var1:0

print(v2.name) # my_variable_scope/var2:0

print(a.name) # my_variable_scope/Add:0

小結:在variable_scope的作用域下,tf.get_variable()和tf.Variable()都加了scope_name前綴。因此,在tf.variable_scope的作用域下,通過get_variable()可以使用已經創建的變量,實現了變量的共享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章