学了比较长一段时间的tf了,一直没有搞懂tf中的variable_scope的用法。感觉有些知识点很零碎,这次看了一本书(质量比想象中的要好很多啊),整体的回顾一下tf。
1. tf变量管理
tf提供了通过变量名称来创建或者获取一个变量的机制。通过这个机制,在不同的函数中可以直接通过变量的名字来使用变量,而不需要将变量通过参数的形式到处传递(确实是一个痛点)。tf中通过变量名称获取变量的机制主要是通过tf.get_variable
和tf.variable_scope
函数实现的。
除了tf.Variable
函数,tf还提供了tf.get_variable
函数来创建或者获取变量。当tf.get_variable
用于创建变量时,它和tf.Variable
的功能是基本等价的:
# 下面这两个定义是等价的
v = tf.get_variable("v", shape=[1], initializer=tf.constant_initializer(1.0))
v = tf.Variable(tf.constant(1.0, shape=[1]), name="v")
两者最大的区别在于指定变量名称的参数。对于tf.Variable
函数,变量名称是一个可选的参数。但是对于tf.get_variable
函数,变量名称是一个必填的参数。
2. variable_scope用法
- 下面给出了一段代码说明如何通过
tf.variable_scope
函数来控制tf.get_variable
函数获取己经创建过的变量:
# 在名字为foo的命名空间内创建名字为v的变量
with tf.variable_scope("foo"):
v = tf.get_variable("v", [1], initializer=tf.constant_initializer(1.0))
# 因为在命名空间foo中已经存在名字为v的变量,所以以下代码会报错:
# with tf.variable_scope("foo"):
# v = tf.get_variable("v")
# 在生成上下文管理器时,将参数reuse设置为True,这样get_variable可以直接获取已经声明的变量
with tf.variable_scope("foo", reuse=True):
v1 = tf.get_variable("v")
print(v==v1) # 输出True
# 下面一句会报错:
# u = tf.get_variable("u", [1], initializer=tf.constant_initializer(2.0))
# reuse为True时,只能获取已经创建的变量,除非改成tf.ATUO_REUSE
with tf.variable_scope("foo", reuse=tf.AUTO_REUSE):
u = tf.get_variable("u", [1], initializer=tf.constant_initializer([2.0]))
tf.variable_scope
函数是可以嵌套的:
with tf.variable_scope("root"):
print(tf.get_variable_scope().reuse) # 输出False
with tf.variable_scope("foo", reuse=True):
print(tf.get_variable_scope().reuse) # 输出True
with tf.variable_scope("bar"): # 输出True(不指定的话会和上一层保持一致)
print(tf.get_variable_scope().reuse)
print(tf.get_variable_scope().reuse) # 输出False(回到了最外层)
- 使用
tf.variable_scope
管理变量名称:
v1 = tf.get_variable("v", [1])
print(v1.name) # 输出v:0
with tf.variable_scope("foo"):
v2 = tf.get_variable("v", [1])
print(v2.name) # 输出foo/v:0
with tf.variable_scope("foo"):
with tf.variable_scope("bar"):
v3 = tf.get_variable("v", [1])
print(v3.name) # 输出foo/bar/v:0
with tf.variable_scope("foo", reuse=True):
v5 = tf.get_variable("bar/v")
print(v5==v3) # 输出True
v6 = tf.get_variable("v")
print(v6==v2) # 输出True
with tf.variable_scope("rick"):
v = tf.get_variable("v", [1])
print(v.name) # 输出rick/v:0
print(v==v1) # 输出False
总结一下:
- 如果已经创建过一个name为"foo"的variable_scope,再次使用
with tf.variable_scope("foo")
时,不能够用get_variable
获取到"foo"中的同名变量。 - 对于1,如果是
with tf.variable_scope("foo", reuse=True)
的话,可以获取到同名变量,但是无法创建新的变量,除非将reuse设置为tf.AUTO_REUSE
。 - 如果variable_scope的name和之前都不同的话,且reuse=False,那么可以用任意名称创建变量(如"foo"和"rick"的对比)。
- variable_scope就像一个维护变量的空间,reuse在variable_scope创建时一般是False,变量使用时再指定为True。
3. 具体实例
以一个mnist的小程序为例:
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
INPUT_NODE = 784
OUTPUT_NODE = 10
LAYER1_NODE = 500
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.8
LEARNING_RATE_DECAY = 0.99
REGULARIZATION_RATE = 0.0001
TRAINING_STEPS = 10000
MOVING_AVERAGE_DECAY = 0.99
# 使用variable_scope管理变量
def inference(input_tensor, reuse=False):
with tf.variable_scope("layer1", reuse=reuse):
weights = tf.get_variable("weights", [INPUT_NODE, LAYER1_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1))
biases = tf.get_variable("biases", [LAYER1_NODE], initializer=tf.constant_initializer(0.1))
layer1 = tf.nn.relu(tf.matmul(input_tensor, weights) + biases)
with tf.variable_scope("layer2", reuse=reuse):
weights = tf.get_variable("weights", [LAYER1_NODE, OUTPUT_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1))
biases = tf.get_variable("biases", [OUTPUT_NODE], initializer=tf.constant_initializer(0.1))
layer2 = tf.matmul(layer1, weights) + biases
return layer2
def train(mnist):
x = tf.placeholder(tf.float32, [None, INPUT_NODE], name="x-input")
y_ = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name="y-input")
y = inference(x)
global_step = tf.Variable(0, trainable=False)
# 计算损失函数
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
cross_entropy_mean = tf.reduce_mean(cross_entropy)
# 注意这一段,weights1和weights2都在inference中创建,但是L2正则化需要用到这个变量。
# 使用variable_scope就能很方便的获取到这两个变量:
with tf.variable_scope("", reuse=True):
weights1 = tf.get_variable("layer1/weights")
weights2 = tf.get_variable("layer2/weights")
regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
regularization = regularizer(weights1) + regularizer(weights2)
loss = cross_entropy_mean + regularization
learning_rate = tf.train.exponential_decay(LEARNING_RATE_BASE,
global_step,
mnist.train.num_examples/BATCH_SIZE,
LEARNING_RATE_DECAY)
train_op = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
with tf.Session() as sess:
tf.global_variables_initializer().run()
validate_feed = {x: mnist.validation.images, y_: mnist.validation.labels}
test_feed = {x: mnist.test.images, y_: mnist.test.labels}
for i in range(TRAINING_STEPS):
if (i+1) % 1000 == 0:
validate_acc = sess.run(accuracy, feed_dict=validate_feed)
print("After %d training steps, validation accuracy using average model is %f" % (i+1, validate_acc))
xs, ys = mnist.train.next_batch(BATCH_SIZE)
sess.run(train_op, feed_dict={x:xs, y_:ys})
test_acc = sess.run(accuracy, feed_dict=test_feed)
print("After %d training steps, validation accuracy using average model is %f" % (TRAINING_STEPS, test_acc))
def main(argv=None):
mnist = input_data.read_data_sets("./mnist", one_hot=True)
train(mnist)
if __name__ == "__main__":
main()
参考资料
- TensorFlow:实战Google深度学习框架(第2版)