Tensorflow學習筆記: 變數及共享變數
TensorFlow中變數主要用來表示機器學習模型中的引數,變數透過 tf.Variable 類進行操作。tf.Variable 表示張量,透過執行 op 可以改變它的值。與 tf.Tensor 物件不同,tf.Variable 存在於單個 session.run 呼叫的上下文之外。
在內部,tf.Variable 儲存持久張量。具體 op 允許您讀取和修改此張量的值。這些修改在多個 tf.Session 之間是可見的,因此對於一個 tf.Variable,多個工作器可以看到相同的值。
1. tf.Variable 建立變數
tf.Variable的初始化函式如下所示
__init__( initial_value=None, trainable=True, collections=None, validate_shape=True, caching_device=None, name=None, variable_def=None, dtype=None, expected_shape=None, import_scope=None, constraint=None )
其中引數
-
initial_value 表示初始化值,用Tensor表示
-
trainable 表示變數是否被訓練,如果被訓練,將加入到tf.GraphKeys.TRAINABLE_VARIABLES集合中,TensorFlow將計算其梯度的變數
-
collections 表示一個graph collections keys的集合,這個建立的變數將被新增到這些集合中,預設集合是[GraphKeys.GLOBAL_VARIABLES].
-
name: 變數的命名,預設是'Variable'
-
dtype 表示型別
例如我們建立一個變數,並且檢視其name和shape
import tensorflow as tfw1 = tf.Variable(tf.random_normal([784,200], stddev = 0.35), name="weights")b1 = tf.Variable(tf.zeros([200]),name="biases")w2 = tf.Variable(tf.random_normal([784,200], stddev = 0.35), name="weights") # 名稱相同b2 = tf.Variable(tf.zeros([200]),name="biases") # 名稱相同print w1.name, w1.shapeprint b1.name, b1.shapeprint w2.name, w2.shapeprint b2.name, b2.shape************************************輸出**************************************weights:0 (784, 200)biases:0 (200,)weights_1:0 (784, 200)biases_1:0 (200,)
可以看到在命名的時候,如果指定的name重複,那麼w2就會被命名為"name_1:0" 這樣累加下去。
2. 變數集合 collections
預設情況下,每個tf.Variable都放置在以下兩個集合中:*tf.GraphKeys.GLOBAL_VARIABLES- 可以在多個裝置共享的變數,*tf.GraphKeys.TRAINABLE_VARIABLES- TensorFlow 將計算其梯度的變數。
2.1 檢視集合變數列表
要檢視放置在某個集合中的所有變數的列表,可以採用如下方式
import tensorflow as tfw1 = tf.Variable(tf.random_normal([784,200], stddev = 0.35), name="weights")b1 = tf.Variable(tf.zeros([200]),name="biases")w2 = tf.Variable(tf.random_normal([784,200], stddev = 0.35), name="weights") # 名稱相同b2 = tf.Variable(tf.zeros([200]),name="biases") # 名稱相同print tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES)************************************輸出**************************************[<tf.Variable 'weights:0' shape=(784, 200) dtype=float32_ref>, <tf.Variable 'biases:0' shape=(200,) dtype=float32_ref>, <tf.Variable 'weights_1:0' shape=(784, 200) dtype=float32_ref>, <tf.Variable 'biases_1:0' shape=(200,) dtype=float32_ref>]
可以看到輸出結果是所有變數的列表
2.2 建立變數集合
如果您不希望變數被訓練,可以將其新增到 tf.GraphKeys.LOCAL_VARIABLES 集合中。例如,以下程式碼段展示瞭如何將名為 my_local 的變數新增到此集合中:
my_local = tf.get_variable("my_local", shape=(), collections=[tf.GraphKeys.LOCAL_VARIABLES])
或者,您可以指定 trainable=False 為 tf.get_variable 的引數:
my_non_trainable = tf.get_variable("my_non_trainable", shape=(), trainable=False)
我們測試效果如下所示,可以看到b2的trainable=False,那麼輸出collection沒有b2
import tensorflow as tfw1 = tf.Variable(tf.random_normal([784,200], stddev = 0.35), name="weights")b1 = tf.Variable(tf.zeros([200]),name="biases")w2 = tf.Variable(tf.random_normal([784,200], stddev = 0.35), name="weights") # 名稱相同b2 = tf.Variable(tf.zeros([200]),name="biases", trainable=False) # 名稱相同print tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES)************************************輸出**************************************[<tf.Variable 'weights:0' shape=(784, 200) dtype=float32_ref>, <tf.Variable 'biases:0' shape=(200,) dtype=float32_ref>, <tf.Variable 'weights_1:0' shape=(784, 200) dtype=float32_ref>]
您也可以使用自己的集合。集合名稱可為任何字串,且您無需顯式建立集合。建立變數(或任何其他物件)後,要將其新增到集合,請呼叫 tf.add_to_collection。例如,以下程式碼將名為 my_local 的現有變數新增到名為 my_collection_name 的集合中:
tf.add_to_collection("my_collection_name", my_local)
3. 共享變數
我們檢視下面的程式碼,表示一個卷積神經網路,其中包括conv1_weights, conv1_biases, conv2_weights, conv2_biases四個引數,也就是4個變數
def my_image_filter(input_images): conv1_weights = tf.Variable(tf.random_normal([5, 5, 32, 32]), name="conv1_weights") conv1_biases = tf.Variable(tf.zeros([32]), name="conv1_biases") conv1 = tf.nn.conv2d(input_images, conv1_weights, strides=[1, 1, 1, 1], padding='SAME') relu1 = tf.nn.relu(conv1 + conv1_biases) conv2_weights = tf.Variable(tf.random_normal([5, 5, 32, 32]), name="conv2_weights") conv2_biases = tf.Variable(tf.zeros([32]), name="conv2_biases") conv2 = tf.nn.conv2d(relu1, conv2_weights, strides=[1, 1, 1, 1], padding='SAME') return tf.nn.relu(conv2 + conv2_biases)
假設我們利用這個函式對兩張圖片進行相同的操作,也就是呼叫兩次,那麼每次都會建立4個變數,假設我們在函式內對變數進行了最佳化求解,那麼每次都會重新建立變數,這樣就無法複用引數,導致訓練過程無效
# 第一次執行方法建立4個變數result1 = my_image_filter(image1)# 第二次執行再建立4個變數result2 = my_image_filter(image2)ValueError: Variable weight already exists, disallowed. Did you mean to set reuse=True or reuse=tf.AUTO_REUSE in VarScope? Originally defined at:
TensowFlow透過變數範圍(variable scope)和tf.get_variable方法解決了共享變數(引數)的問題。
3.1 tf.variable_scope和tf.get_variable
tf.Variable()方法每次被呼叫都會建立新的變數,這樣就無法解決共享變數的問題,而tf.get_variable結合作用域即可表明我們是想建立新的變數,還是共享變數,變數作用域允許在呼叫隱式建立和使用變數的函式時控制變數重用。作用域還允許您以分層和可理解的方式命名變數。tf.get_variable()的機制跟tf.Variable()有很大不同,如果指定的變數名已經存在(即先前已經用同一個變數名透過get_variable()函式例項化了變數),那麼get_variable()只會返回之前的變數,否則才創造新的變數。我們舉例進行說明。
例如上面的例子中有兩個卷積層,我們先來編寫一個函式建立一個卷積/relu層,這個函式使命的變數名稱是'weights'和'biases'
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)
在真實模型中需要多個卷積層,我們透過變數域來區分不同層的變數,不同的變數域下的變數名車為:scope_name/variable_name, 如下所示,第一個卷積層的變數名稱是'conv1/weights', 'conv1/biases', 第二個卷積層的變數名稱是 'conv2/weights', 'conv2/biases'。
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])
但即便這樣,如果多次呼叫該函式,也會丟擲異常,
result1 = my_image_filter(image1)result2 = my_image_filter(image2)# Raises ValueError(... conv1/weights already exists ...)
因為用get_variable()建立兩個相同名字的變數是會報錯的,預設上它只是檢查變數名,防止重複,如果要變數共享,就需要指定在哪個域名內可以共享變數。
開啟共享變數有兩種方式
方法1
採用scope.reuse_variables()觸發重用變數,如下所示
with tf.variable_scope("model") as scope: output1 = my_image_filter(input1) scope.reuse_variables() output2 = my_image_filter(input2)
方法2
使用reuse=True 建立具有相同名稱的作用域
with tf.variable_scope("model"): output1 = my_image_filter(input1)with tf.variable_scope("model", reuse=True): output2 = my_image_filter(input2)
3.2 理解variable_scope
理解變數域的工作機理非常重要,我們對其進行梳理,當我們呼叫tf.get_variable(name, shape, dtype, initializer)時,這背後到底做了什麼
首先,TensorFlow 會判斷是否要共享變數,也就是判斷 tf.get_variable_scope().reuse 的值,如果結果為 False(即你沒有在變數域內呼叫scope.reuse_variables()),那麼 TensorFlow 認為你是要初始化一個新的變數,緊接著它會判斷這個命名的變數是否存在。如果存在,會丟擲 ValueError 異常,否則,就根據 initializer 初始化變數:
with tf.variable_scope("foo"): v = tf.get_variable("v", [1])assert v.name == "foo/v:0"
而如果 tf.get_variable_scope().reuse == True,那麼 TensorFlow 會執行相反的動作,就是到程式裡面尋找變數名為 scope name + name 的變數,如果變數不存在,會丟擲 ValueError 異常,否則,就返回找到的變數:
with tf.variable_scope("foo"): v = tf.get_variable("v", [1])with tf.variable_scope("foo", reuse=True): v1 = tf.get_variable("v", [1])assert v1 is v
變數域可以多層重疊,例如,下面的變數上有兩層的變數域,那麼變數名是'foo/var/v:0'
with tf.variable_scope("foo"): with tf.variable_scope("bar"): v = tf.get_variable("v", [1]) assert v.name == "foo/bar/v:0"
在同一個變數域中,如果需要呼叫同名變數,那麼需要重用變數即可,例如v1和v兩個變數時相同的,因為變數名都是'foo/v'
with tf.variable_scope("foo"): v = tf.get_variable("v", [1]) tf.get_variable_scope().reuse_variables() v1 = tf.get_variable("v", [1]) assert v1 is v
總結
-
tf.get_variable()預設上它只檢查變數名,如果變數名重複,那麼就會報錯;tf.Variable()每次被呼叫都建立相應的變數,即便變數名重複,也會建立新的變數,因此無法共享變數名。
-
如果scope中開啟共享變數,那麼呼叫tf.get_variable()就會查詢相同變數名的變數,如果有,就直接返回該變數,如果沒有,就建立一個新的變數;
-
如果scope沒有開啟共享變數(預設模式),那麼 呼叫tf.get_variable()發現已有相同變數名的變數,就會報錯,如果沒有,就建立一個新的變數。
-
要重用變數,需要在scope中開啟共享變數,有兩種方法,推薦第一種
【本文轉載自:知乎,作者:於翔宇,原文連結:】
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31542119/viewspace-2213121/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Python學習筆記 - 變數Python筆記變數
- TensorFlow常量、變數和佔位符詳解(學習筆記)變數筆記
- javascript學習筆記,二、變數JavaScript筆記變數
- TensorFlow筆記(2) 常量與變數【僅供自學】筆記變數
- TensorFlow——共享變數的使用方法變數
- [go 學習筆記] 二、變數、常量Go筆記變數
- JavaScript學習筆記2: js書寫語法及變數JavaScript筆記JS變數
- C語言學習筆記之變數C語言筆記變數
- Python 學習筆記-2-1-變數Python筆記變數
- Nginx變數詳解(學習筆記十九)Nginx變數筆記
- ES6學習筆記(一)【變數,字串】筆記變數字串
- TensorFlow變數管理變數
- shell指令碼程式設計學習筆記——變數指令碼程式設計筆記變數
- Solidity語言學習筆記————5、全域性變數Solid筆記變數
- Python學習筆記(2)慎重使用全域性變數Python筆記變數
- Python3學習筆記3,變數、運算子Python筆記變數
- Python學習筆記|Python之內建變數__name__Python筆記變數
- 透過交換指標變數的值改變大小數字的位置-學習筆記指標變數筆記
- Shell 變數學習變數
- Python學習-變數Python變數
- 變數與常量 - Go 學習記錄變數Go
- Linux 學習筆記--環境變數與檔案查詢Linux筆記變數
- ES6學習筆記二(變數結構賦值)筆記變數賦值
- TensorFlow入門 - 變數(Variables)變數
- JavaScript筆記3_變數JavaScript筆記變數
- Shell學習【變數使用】變數
- 自學PHP筆記(四) PHP變數和常量PHP筆記變數
- 自學PHP筆記(四) PHP常量和變數PHP筆記變數
- C#學習筆記---異常捕獲和變數運算子C#筆記變數
- Flutter學習筆記(3)--Dart變數與基本資料型別Flutter筆記Dart變數資料型別
- Python 3 學習筆記之——變數作用域、模組和包Python筆記變數
- shell程式設計學習筆記(二):Shell中變數的使用程式設計筆記變數
- Solidity語言學習筆記————7、單位和全域性變數Solid筆記變數
- Solidity語言學習筆記————6、全域性變數風格指南Solid筆記變數
- Golang併發之共享記憶體變數Golang記憶體變數
- C++筆記:輸入輸出、變數、變數加減乘除C++筆記變數
- Swift筆記之變數講解Swift筆記變數
- 5.3_前端筆記-js變數前端筆記JS變數