Variable用来保存和更新参数,使用Tensor保存实际的数据。 Variable必须显示初始化后才能被使用,可以被保存在磁盘中或从磁盘读取。
相关API:
- Variable:https://www.tensorflow.org/api_guides/python/state_ops
- Saver:https://www.tensorflow.org/api_docs/python/tf/train/Saver
新建
使用下列方式可以新建一个变量
biases = tf.Variable(tf.zeros([200]), name="biases")
新建Variable时需要传入一个Tensor作为它的初始值,这个Tensor一般由其他Op得到,参考常量、随机量Op。
tf.Variable()将在计算图中增加三个Op操作
- 变量Op,用于保存Variable的值
- 初始化Op,实际上是一个tf.assign操作
- 产生初始化的值的Op
tf.Variable()的返回值是一个tf.Variable类型的Python对象。
将变量的新建放在with tf.device(...):区块中可以指定变量的位置,使用tf.train.replica_device_setter可以简化并行处理时的设备分配。
with tf.device("/cpu:0"):
v = tf.Variable(...)
with tf.device("/gpu:0"):
v = tf.Variable(...)
with tf.device("/job:ps/task:7"):
v = tf.Variable(...)
注意变量的更新操作(tf.Variable.assign或tf.train.Optimizer中的变量更新操作等)必须在同一个设备中执行,Incompatible device placement directives will be ignored when creating these operations。
初始化
变量使用之前必须初始化。
可以使用tf.variables_initializer的方式,手动传入所有Variable来完成初始化。
可以在所有的计算图都构造完成后,使用tf.global_variables_initializer()添加一个初始化Op执行所有Variable的的初始化操作,然后在Session初始化后第一步就运行这个初始化Op。
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
...
由于这个初始化Op对所有Variable的初始化是并行执行的,如果某个Variable的初始化值依赖于另一个Variable,可以使用另一个的Variable.initialized_value()获得。
w_twice = tf.Variable(W.initialized_value() * 2.0, name="w_twice")
变量共享
使用 tf.Variable() 定义的时候, 即使name一样, 但TF会自动保存为不同的变量名,并且tf.Variable() 定义的变量名受name scope的影响。
TensorFlow使用variable_scope机制提供在多处需要使用同一个Variable的方法。tf.variable_scope()将隐含的打开一个tf.name_scope(),所以在variable_scope中的所有Op(包括name_scope不能影响的tf.get_variable())都会包含name作为前缀。
var = tf.get_variable(<name>, <shape>, <initializer>): 返回名称为name的变量,如果不存在则新建该变量并返回。在这里,相同的变量名并不会像tf.Variable()一样被自动重命名,而是会返回之前定义的变量。注意共享变量使用initializer函数初始化Variable,tf.Variable()使用Tensor初始化Variable。tf.variable_scope(<scope_name>/variable_scope_object, reuse=None): 返回某个变量域,可以使用字符串或其他变量域对象,在不同的scope中可以使用同样的变量名调用tf.get_variable()函数,它们在计算图中的变量名会变为scope/name。tf.get_variable_scope()获取当前variable_scope。- 要想使用共享变量,需要在进入变量域时使用
tf.variable_scope(<scope_name>, reuse=True),或者在变量域内调用tf.get_variable_scope().reuse_variables()函数。如果没有调用该函数而使用相同的变量,会报错ValueError。如果设置了reuse而变量不存在(不能reuse),也会报ValueError。注意默认情况下,子域将继承父域的reuse变量。
def conv_relu(input, kernel_shape, bias_shape):
weights = tf.get_variable("weights", kernel_shape,
initializer=tf.random_normal_initializer())
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)
def my_image_filter(input_images):
# 在不同的variable_scope中可以使用相同的name
with tf.variable_scope("conv1"):
relu1 = conv_relu(input_images, [5, 5, 32, 32], [32])
with tf.variable_scope("conv2"):
return conv_relu(relu1, [5, 5, 32, 32], [32])
with tf.variable_scope("image_filters") as scope:
# 在同一个variable_scope中共享相同的变量需要reuse_variables()
result1 = my_image_filter(image1)
scope.reuse_variables()
# 也可以用 tf.get_variable_scope().reuse_variables()
result2 = my_image_filter(image2)
子变量域默认将包含父变量域名
with tf.variable_scope("foo") as foo_scope:
assert foo_scope.name == "foo"
with tf.variable_scope("bar"):
# 使用字符串时,在子变量域中的域名包含外层变量域
with tf.variable_scope("baz") as other_scope:
assert other_scope.name == "bar/baz"
# 使用变量域对象进入其他变量域不受子域规则约束
with tf.variable_scope(foo_scope) as foo_scope2:
assert foo_scope2.name == "foo" # Other scope
variable_scope和name_scope也可以嵌套使用,子域将包含父域的名称。
variable_scope可以包含一个默认的initializer函数,供当前域及子域的所有get_variable函数做初始化使用。
with tf.variable_scope("foo", initializer=tf.constant_initializer(0.4)):
v = tf.get_variable("v", [1])
...
变量的存储
使用tf.train.Saver可以为计算图的所有Variable或指定的部分Variable增加save和restore操作。并且tf.train.Saver类提供了保存和读取并恢复Variable的方法。
Saver保存变量的文件被称为Checkpoint文件,可以看作一个二进制的字典,使用tf.Variable.name作为Key,实际Tensor数据作Value。使用tensorflow/tensorflow/python/tools/inspect_checkpoint.py文件的print_tensors_in_checkpoint_file()函数可以查看二进制文件中的值。
保存变量
v1 = tf.Variable(..., name="v1")
v2 = tf.Variable(..., name="v2")
init_op = tf.global_variables_initializer()
saver = tf.train.Saver()
with tf.Session() as sess:
sess.run(init_op)
...
save_path = saver.save(sess, "/tmp/model.ckpt")
print("Model saved in file: %s" % save_path)
恢复变量
v1 = tf.Variable(..., name="v1")
v2 = tf.Variable(..., name="v2")
saver = tf.train.Saver()
with tf.Session() as sess:
# 此时并不需要运行sess.run(init_op)
saver.restore(sess, "/tmp/model.ckpt")
print("Model restored.")
...
存储部分变量 在初始化时通过传入一个变量字典,可以指定Saver存储的变量,并且使用字典的Key作为变量存储的Key。注意如果只恢复了部分变量,其余变量一定要初始化。
saver = tf.train.Saver({"my_v2": v2})