TensorFlow之name_scope/variable_scope

引子

        前面写过一篇博文《TensorFlow学习笔记(六)》,其内容主要介绍的就是name_scope/variable_scope的使用,不过并没有明确地对这二者的使用场景进行区分,所以本文将清晰地给出name_scope/variable_scope的应用场景。

        文章比较长,如果时间不够的话,那就直接看总结啦~

主要用法

        我们知道,在TensorFlow中,常常会定义一堆节点,如果我们不能对此进行有效地管理,那么这些节点很可能会使我们心如乱麻,并且再也不想看这些杂乱的代码。

        name_scope/variable_scope正是为了更有效地管理节点而产生的两个操作(op)。

  • name_scope:用于对变量设置命名域,从而让变量按照其关系组成一个个scope,并不会对tf.get_variable()创建的变量名字产生影响;
  • variable_scope:绝大部分情形下,与tf.get_variable()配合使用,实现变量共享。

示例

        我们下面举几个简单的例子:

a)利用name_scope对tf.Variable创建的变量加上命名域:

with tf.name_scope("scope_1"):
    v1 = tf.Variable([1], name="v1", dtype=tf.float32)
    v2 = tf.get_variable("v2", [1])
print v1
print v2

        此时输出结果为:

<tf.Variable 'scope_1/v1:0' shape=(1,), dtype=float32_ref>
<tf.Variable 'v2:0' shape=(1,) dtype=float32_ref>

        从这里我们可以得出下列结论:

  • name_scope并不会对tf.get_variable()创建的变量添加命名域;
  • name_scope并不能用于实现变量共享。

b)结合使用variable_scope与tf.get_variable()实现变量共享:

实验一
with tf.variable_scope("scope_1"):
    v1 = tf.Variable([1], name="v1", dtype=tf.float32)
with tf.variable_scope("scope_1"):
    v2 = tf.Variable([1], name="v1", dtype=tf.float32)

        此时的输出结果为:

<tf.Variable 'scope_1/v1:0' shape=(1,), dtype=float32_ref>
<tf.Variable 'scope_1_1/v1:0' shape=(1,), dtype=float32_ref>

        这个实验说明了tf.Variable()遇到重名变量时,将自动重命名,而不会发生冲突。在这个实验中,我们是在同一个scope中定义的同名变量,可是为什么不是修改最内层的变量名呢?也即为什么输出结果不是下面这样呢?

<tf.Variable 'scope_1/v1:0' shape=(1,), dtype=float32_ref>
<tf.Variable 'scope_1/v1_1:0' shape=(1,), dtype=float32_ref>

        这是TensorFlow的一种很巧妙的手段,因为tf.Variable()被设计为提供给库编写者使用的一个接口,所以我们修改scope的话更加有利。为什么这么说?比如,我们运行下面的代码:

net = fully_connected(input_tensor, shape)
net = fully_connected(net, shape)

        这个时候我们对fully_connected()函数的两次调用均没有给出scope,那这个时候我们的全连接层中的变量应该如何命名呢?这就与上面的实验情形类似了,TFLearn的fully_connected()函数是使用tf.Variable()实现的,所以一般情况下,将会使用默认的“FullyConnected”这一scope作为命名域,当我们第二次调用fully_connected()函数时,自动改变命名域为“FullyConnected_1”,而不是修改命名域内部的变量名,是不是比直接改最内层的变量名要合理许多?那如果我们改变最内层的变量名,假设我们的函数fn()在scope中定义了“scope/v1, scope/v2”,那么,我们接下来将重命名为“scope/v1_1, scope/v2_1”,这样的话,那不是都在同一个scope中了么?到时候在TensorBoard中将显得乱七八糟~

        对了,tf.Variable()在解决冲突时,总是重命名最外层的scope哟~验证如下:

with tf.variable_scope("scope_top"):
    with tf.variable_scope("scope_bot"):
        v1 = tf.Variable([1], name="v1", dtype=tf.float32)
with tf.variable_scope("scope_top"):
    with tf.variable_scope("scope_bot"):
        v2 = tf.Variable([1], name="v1", dtype=tf.float32)  
vs=tf.trainable_variables()
for v in vs:
    print v

        此时,我们的输出为:

<tf.Variable 'scope_top/scope_bot/v1:0' shape=(1,), dtype=float32_ref>
<tf.Variable 'scope_top_1/scope_bot/v1:0' shape=(1,), dtype=float32_ref>

        这样的话,如果我们调用一些基本的Layers来定义自己的Layer,比如说叫做layer_udef,且默认命名域为“Layer_Udef”,那么重复使用layer_udef时,得到的是“Layer_Udef”、“Layer_Udef_1”…这就很符合我们的预期咯。

实验二

        在实验一中,我们发现使用tf.Variable()并不能获取已经定义变量,换句话说,不能达到共享变量的目的,那我们能否用tf.get_variable()函数获取tf.Variable()定义的变量呢

with tf.variable_scope("scope_1"):
    v1 = tf.Variable([1], name="v1", dtype=tf.float32)
print v1
with tf.variable_scope("scope_1", reuse=True):
    v2 = tf.get_variable("v1", [1])

        输出为:

<tf.Variable 'scope_1/v1:0' shape=(1,) dtype=float32_ref>
...
ValueError: Variable scope_1/v1 does not exist, or was not created with tf.get_variable().

        相信大家都有疑惑,不是已经有了scope_1/v1变量么?为什么说它不存在呢?因为tf.Variable()所定义的变量并不是用于共享的,虽然它对于tf.get_variable()是可见的:

with tf.variable_scope("scope_1"):
    v1 = tf.Variable([1], name="v1", dtype=tf.float32)
with tf.variable_scope("scope_1"):
    v2 = tf.get_variable("v1", [1])

        此时输出为:

<tf.Variable 'scope_1/v1:0' shape=(1,) dtype=float32_ref>
<tf.Variable 'scope_1/v1_1:0' shape=(1,) dtype=float32_ref>

        那到底为什么tf.Variable()定义的变量不能共享呢?这就涉及到tf.Variable()和tf.get_variable()的设计理念了:个人认为,tf.Variable()主要是为库的编写者设计,或者,我们可以用它来编写自己的需要重复定义的Layers,这样就不用操心变量之间的冲突了,这一点我们在实验一中已经说明过了。所以,当我们想要从最底层开始定义自己的层时,使用tf.Variable()吧~

实验三

        说了这么多,那到底怎样才能成功地共享变量呢?

with tf.variable_scope("scope_1"):
    v1 = tf.get_variable("v1", [1])
with tf.variable_scope("scope_1", reuse=True):
    v2 = tf.get_variable("v1", [1])
vs = tf.trainable_variables()
for v in vs:
    print v

        此时我们的输出为:

<tf.Variable 'scope_1/v1:0' shape=(1,) dtype=float32_ref>

        也就是说,变量“scope_1/v1”被共享咯~

总结

        现将本文总结如下:

  • name_scope并不会对tf.get_variable()定义的变量的命名产生影响;
  • 如果要从底层变量开始定义库函数的话,使用tf.Variable()是一种较好的选择;
  • tf.Variable()定义的变量并不能被共享;
  • 如果想要实现变量共享,那就同时使用variable_scope和tf.get_variable()吧~

        最后祝大家周末愉快~

 
 
 
 
 
 
 
 

猜你喜欢

转载自blog.csdn.net/u013745804/article/details/80428618
今日推荐