TensorFlow 变量是表示程序处理的共享,持久状态的最佳方法。
变量通过tf.Variable
类进行操作。A tf.Variable
表示可以通过运行op来更改其值的张量。与tf.Tensor
对象不同 ,a tf.Variable
存在于单个session.run
调用的上下文之外 。
在内部,tf.Variable
存储一个持续张量。具体操作允许您读取和修改此张量的值。这些修改在多个tf.Session
s 中可见,因此多个工作人员可以看到相同的值 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提供了许多方便的初始化器。或者,您可以初始化a tf.Variable
以具有值a tf.Tensor
。例如:
other_variable = tf.get_variable("other_variable", dtype=tf.int32, initializer=tf.constant([23, 42]))
请注意,当初始化程序为a时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_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("/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图中的a 值,只需将其视为正常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, 1, 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)