最近一直在跟着《TensorFlow:实战google深度学习框架》学习Tensorflow,学到第5.3节变量管理以及5.4节模型持久化的时候忽然发现基础好像学的还是不够扎实,被变量的使用搞得一脸懵逼。书中讲的还是很详细的,我针对一些有疑问的定义进行了实验,这里总结一下。
问题1:tf.Variable中不指定name参数会怎样?
运行以下程序
a=tf.Variable(tf.constant(1.0,shape=[1]))
b=tf.Variable(tf.constant(1.0,shape=[1]))
print(a)
print(b)
输出
<tf.Variable 'Variable:0' shape=(1,) dtype=float32_ref>
<tf.Variable 'Variable_1:0' shape=(1,) dtype=float32_ref>
可见程序中a定义的第一个变量被自动命名为Variable,由于重名而把b定义的第二个变量自动命名为Variable_1
补充:tf.get_variable的name参数是不能重名的
运行以下程序
a = tf.get_variable(name='a1',shape=[1,2])
b = tf.get_variable(name='a1',shape=[3,4])
print(a)
print(b)
报了如下错
ValueError: Variable a1 already exists, disallowed. Did you mean to set reuse=True or reuse=tf.AUTO_REUSE in VarScope?
因此在使用tf.get_variable要注意name参数是不能相同的
问题2:变量名指的究竟是什么?
a = tf.get_variable( 'b' , [1] , initializer = tf.constant_initializer(1.0) )
该语句定义了一个名为“b”的节点,并且书中的变量名指的也应该是‘b’(即tf.Variable中的name参数)
print(a)后输出Tensor("b:0", shape=(2,), dtype=float32),其中“b:0”表示该张量计算节点“b”输出的第一个结果
print(a.name)后输出b:0
注:tf.get_variable与tf.Variable最大不同就是tf.get_variable中name参数是一个必填的参数,而在tf.Variable中是可选的。tf.get_variable就是根据这一参数来创建和命名变量
那么该语句中的a是指什么呢?
运行下面两个程序
code1
a = tf.get_variable(name='a1',shape=[1,2])
a = tf.get_variable(name='a1',shape=[1,2])
print(a)
code2
a=tf.Variable(tf.constant(1.0,shape=[1]))
a=tf.Variable(tf.constant(1.0,shape=[1]))
print(a)
code1的运行结果为
ValueError: Variable a1 already exists, disallowed. Did you mean to set reuse=True or reuse=tf.AUTO_REUSE in VarScope?
code2的运行结果为
<tf.Variable 'Variable_1:0' shape=(1,) dtype=float32_ref>
通过上述两个程序比较,可以看出,每个程序中相同语句执行了两次,因此可以将这个‘a’理解为用来指代程序中操作的结果的标识(也就是python中的变量名指向了一个新的内存地址)(此处有问题可能是混淆了C与python变量名的意义,可以参考https://www.jianshu.com/p/3289be65c76d)
问题3:tf.variable_scope的作用范围?
一开始我对tf.variable_scope的理解有问题,所有对以下代码有个疑惑,就是为什么在scope('layer2')中也可以直接用在scope('layer1')中定义的layer1
#训练时会创建变量,测试时会通过保存的模型加载变量v
def get_weight_variable(shape,regularizer):
weights=tf.get_variable("weights",shape,initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer !=None :
#将张量加入自定义集合losses中
tf.add_to_collection('losses',regularizer(weights))
return weights
#dnn前向传播
def inference(input_tensor,regularizer):
#第一层变量
with tf.variable_scope('layer1'):
weights=get_weight_variable([INPUT_NODE,LAYER1_NODE],regularizer)
biases=tf.get_variable("biases",[LAYER1_NODE],initializer=tf.constant_initializer(0.0))
layer1=tf.nn.relu(tf.matmul(input_tensor,weights)+biases)
#第二层变量
with tf.variable_scope('layer2'):
weights=get_weight_variable([LAYER1_NODE,OUTPUT_NODE],regularizer)
biases=tf.get_variable("biases",[OUTPUT_NODE],initializer=tf.constant_initializer(0.0))
layer2=tf.nn.relu(tf.matmul(layer1,weights)+biases)
因为scope只对name参数中的变量名起作用而不对程序中定义的变量名起作用,程序中定义的变量名仍是起到一个指示的作用。这里tf.variable_scope('XXX')语句是用来在name参数指定的变量名前添加命名空间的,可以将每次执行 with tf.variable_scope('XXX'):理解为打开了一个名为XXX的文件夹(variable_scope的命名空间也可以嵌套,类似绝对地址)
下列两个代码执行结果是一样的
with tf.variable_scope('layer1'):
biases=tf.get_variable("biases1",[1],initializer=tf.constant_initializer(0.0))
with tf.variable_scope('layer2'):
biases=tf.get_variable("biases2",[1],initializer=tf.constant_initializer(0.0))
print(biases.name)
with tf.variable_scope('layer1'):
biases=tf.get_variable("biases1",[1],initializer=tf.constant_initializer(0.0))
with tf.variable_scope('layer2'):
biases=tf.get_variable("biases2",[1],initializer=tf.constant_initializer(0.0))
print(biases.name)
均为
layer2/biases2:0
(还是对python中变量名理解的问题T T)
问题4:保存或加载指定变量时,tf.train.saver列表中提供的变量是哪一个?
如在加载模型时只加载一个变量则应该使用如下代码定义一个Saver类
saver=tf.train.Saver([v1])
那么这个v1到底是name参数还是程序中定义的变量名呢
分别执行以下两段代码(只声明了一个Saver类,没有执行具体操作的代码)
code1
v=tf.Variable(tf.constant(1.0,shape=[1]),name='v1')
saver=tf.train.Saver([v1])
code2
v1=tf.Variable(tf.constant(1.0,shape=[1]),name='v')
saver=tf.train.Saver([v1])
code1报错如下错,而code2可以执行
NameError: name 'v1' is not defined
因此列表中指定的变量应该是在程序中定义的变量,此时可以将它当做普通python程序理解,毕竟只有程序中定义了的变量才能被列表使用
问题5:tf.train.Saver在保存或者加载变量时给变量重命名,字典中的key-value分别对应什么?
如在保存模型时为v1,v2重命名,则应该使用如下代码定义一个Saver类
那这个字典中key,value是什么含义呢?继续做一个实验。
首先执行了
a=tf.Variable(tf.constant(1.0,shape=[1]),name='a1')
saver=tf.train.Saver({"a":a2})
报错
NameError: name 'a2' is not defined
这说明key应该是这个变量在被保存的模型里的名称,value应该是a或者a1,根据问题4我们可以推测value应该是a(在程序中定义了的变量)
接着执行
a=tf.Variable(tf.constant(1.0,shape=[1]),name='a1')
saver=tf.train.Saver({"a2":a})
程序没有报错,说明value应该对应程序中定义了的变量,为了证明使用name参数作为value是错误的,再执行以下程序
a=tf.Variable(tf.constant(1.0,shape=[1]),name='a1')
saver=tf.train.Saver({"a2":a1})
程序报错
NameError: name 'a1' is not defined
综上所述,定义用来重命名的Saver类时,使用的字典的key应该是在被保存的模型中的名称,value是程序中定义的变量名称。在加载重命名时是将名为key的变量重命名并加载到当前程序名为value的变量中;在保存重命名时是将当前程序名为value的变量重命名为key再保存。