Variables(变量)
变量
TensorFlow 变量
是表示程序操作的共享持久状态的最佳方式。
变量是通过tf.Variable
类操纵的。 tf.Variable
代表张量,通过运行op可以改变它的值。与tf.Tensor
对象不同, tf.Variable
存在于单个session.run
调用的上下文之外。
在内部,tf.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]
。默认情况下,这个变量将具有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提供集合
,这些集合
被命名为张量或其他对象的列表,例如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_variablede
参数:
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])
变量在分布式设置中处于正确的设备中尤其重要。例如,不小心将变量放在工作者而不是参数服务器上,可能会严重减慢训练速度,或者在最坏的情况下,让每个工作人员愉快地进行各自独立的每个变量的复制。出于这个原因,我们提供了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
初始化变量
在你使用变量之前,它必须被初始化。如果您在低级别的TensorFlow API中进行编程(即,您正在显式创建自己的图形和会话),则必须明确地初始化变量。最高层次的框架,例如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)
使用变量
要使用tf.Variable
TensorFlow图中的值,只需将其视为普通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
如何使用优化器的说明。
因为变量是可变的,所以知道在任何时间点使用的变量值是什么版本是有用的。为了在事情发生后强制重新读取变量的值,可以使用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
在tf.variable_scope
对象中隐式地包装对象。
虽然显式传递变量的代码非常清晰,但编写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)