TensorFlow變量(Variables)是在程序中表示共享、持久狀態的最佳方式。
通過tf.Variable
類操作變量。tf.Variable
表示值可以被操作改變的張量。跟tf.Tensor
對象不一樣,tf.Variable
存在於單個session.run
調用的上下文之外。
在內部,atf.Variable
存儲一個持久張量,允許讀取和修改張量的值。這些修改是跨tf.Session
可見的,所以不同的工作節點可以看到相同的值tf.Variable
。
創建一個變量
創建變量的最好方法是調用tf.get_variable
功能。此功能要求您指定變量的名稱。此名稱將被其他副本用於訪問相同變量,以及在檢查點和導出模型時命名此變量的值。tf.get_variable
還允許您重複使用先前創建的同名變量,從而可以輕鬆定義重複使用圖層的模型。
用tf.get_variable
來創建一個變量,隻需提供名稱和形狀
my_variable = tf.get_variable("my_variable", [1, 2, 3])
這將創建一個名為”my_variable”的變量,它是一個帶有形狀[1, 2, 3]
的3維張量。這個變量默認的dtype
是tf.float32
,其初始值將通過tf.glorot_uniform_initializer
隨機化。
您可以選擇指定dtype
和初始化程序tf.get_variable
。例如:
my_int_variable = tf.get_variable("my_int_variable", [1, 2, 3], dtype=tf.int32,
initializer=tf.zeros_initializer)
TensorFlow提供了許多方便的初始化程序。或者,您可以初始化一個tf.Variable
為具有tf.Tensor
的值。例如:
other_variable = tf.get_variable("other_variable", dtype=tf.int32,
initializer=tf.constant([23, 42]))
請注意,當初始化器是一個tf.Tensor
,你不應該指定變量的形狀,因為用來初始化的張量的形狀將被複用。
變量集合
因為TensorFlow程序中不連接的部分之間可能需要創建變量,所以用單一的方法來訪問所有的變量是有用的。出於這個原因,TensorFlow提供 collections(集合),它們被命名為張量或其他對象的列表,例如tf.Variable
實例。
默認情況下每個tf.Variable
被放置在以下兩個集合中:*tf.GraphKeys.GLOBAL_VARIABLES
—可以在多個設備上共享的變量,*tf.GraphKeys.TRAINABLE_VARIABLES
— TensorFlow將計算梯度的變量。
如果你不想要一個變量是可訓練的,把它添加到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)
您也可以使用自己的集合。任何字符串都是有效的集合名稱,並且不需要顯式創建集合。在創建變量之後向變量添加一個變量(或任何其他對象),調用tf.add_to_collection
。例如,下麵的代碼添加一個名為my_local
的現有變量到一個名為my_collection_name
的集合:
tf.add_to_collection("my_collection_name", my_local)
為了檢索你已經放置在集合中的所有變量(或其他對象)的列表,你可以使用:
tf.get_collection("my_collection_name")
設備放置
就像任何其他TensorFlow操作一樣,您可以在特定設備上放置變量。例如,下麵的代碼片段創建一個名為v
的變量並將其放置在第二個GPU設備上:
with tf.device("/device:GPU:1"):
v = tf.get_variable("v", [1])
變量在分布式設置中處於正確的設備中尤為重要。例如,不小心將變量放在worker而不是參數服務器上,可能會嚴重減慢訓練速度,或者在最壞的情況下,讓每個worker都進行各自獨立的每個變量的複製。為此我們提供tf.train.replica_device_setter
,它可以自動將變量放在參數服務器中。例如:
cluster_spec = {
"ps": ["ps0:2222", "ps1:2222"],
"worker": ["worker0:2222", "worker1:2222", "worker2:2222"]}
with tf.device(tf.train.replica_device_setter(cluster=cluster_spec)):
v = tf.get_variable("v", shape=[20, 20]) # this variable is placed
# in the parameter server
# by the replica_device_setter
初始化變量
在你使用一個變量之前,它必須被初始化。如果您在low-level TensorFlow API中進行編程(即,您正在顯式創建自己的圖形和會話),則必須明確地初始化變量。大多數high-level框架如tf.contrib.slim
,tf.estimator.Estimator
和Keras
在訓練模型之前自動初始化變量。
顯式初始化在其他方麵很有用,因為它允許您從檢查點重新加載模型時不會重新運行潛在的昂貴初始化程序,並且在分布式設置中共享隨機初始化變量時保證確定性。
要在訓練開始之前一次初始化所有可訓練變量,請調用tf.global_variables_initializer()
。這個函數返回一個負責初始化tf.GraphKeys.GLOBAL_VARIABLES
集合中所有變量的操作。運行此操作會初始化所有變量。例如:
session.run(tf.global_variables_initializer())
# Now all variables are initialized.
如果你確實需要自己初始化某個變量,你可以運行變量的初始化操作。例如:
session.run(my_variable.initializer)
你也可以問哪些變量還沒有被初始化。例如,以下代碼打印所有尚未初始化的變量的名稱:
print(session.run(tf.report_uninitialized_variables()))
請注意,默認情況下tf.global_variables_initializer
沒有指定變量初始化的順序。因此,如果變量的初始值取決於另一個變量的值,那麽很可能會出現錯誤。任何時候,如果在並非所有變量都被初始化的情況下使用變量的值(例如,如果在初始化另一個變量時使用變量的值),最好使用variable.initialized_value()
代替variable
:
v = tf.get_variable("v", shape=(), initializer=tf.zeros_initializer())
w = tf.get_variable("w", initializer=v.initialized_value() + 1)
使用變量
要在一個TensorFlow圖中使用tf.Variable
的值,簡單地把它當作一個普通的tf.Tensor
對待:
v = tf.get_variable("v", shape=(), initializer=tf.zeros_initializer())
w = v + 1 # w is a tf.Tensor which is computed based on the value of v.
# Any time a variable is used in an expression it gets automatically
# converted to a tf.Tensor representing its value.
要為變量賦值,請使用這些方法assign
,assign_add
以及tf.Variable
類中的其他方法。方法使用示例:
v = tf.get_variable("v", shape=(), initializer=tf.zeros_initializer())
assignment = v.assign_add(1)
tf.global_variables_initializer().run()
assignment.run()
大多數TensorFlow優化器都有專門的操作,根據某種梯度下降類算法有效地更新變量的值。見tf.train.Optimizer
了解如何使用優化器。
因為變量是可變的,所以知道在任何時間點使用什麽版本的變量值是有用的。在某個事件發生後強製變量值的re-read,可以使用tf.Variable.read_value
。例如:
v = tf.get_variable("v", shape=(), initializer=tf.zeros_initializer())
assignment = v.assign_add(1)
with tf.control_dependencies([assignment]):
w = v.read_value() # w is guaranteed to reflect v's value after the
# assign_add operation.
共享變量
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])
如果你想要共享變量,你有兩個選擇。首先,您可以使用相同的名稱創建一個範圍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)