在使用TensorFlow[1.x版本]搭建模型过程中,tf.variable_scope和tf.name_scope,tf.Variable和tf.get_variable是两组重要但是容易混淆的用法。其主要作用是解决部分模型中需要的变量共享。

variable_scope和name_scope都是作用域,目的就是说明变量的作用范围和区别具有相同变量名的变量。

tf.Variable和tf.get_variable都是创建变量,但是结合variable_scope和name_scope会产生不同的变量共享效果。

对于tf.get_variable来说,tf.name_scope对其无效,也就是说tf认为当你使用tf.get_variable时,你只归属于tf.variable_scope来管理共享与否。

对于使用tf.Variable来说,tf.name_scope和tf.variable_scope功能一样,都是给变量加前缀,相当于分类管理,模块化。

tf.get_variable和tf.variable_scope配合可以实现变量共享

下面的例子,

import tensorflow as tf

with tf.name_scope('name_scope_1'):
    var1 = tf.get_variable(name='var1'dtype=tf.int8)
    var2 = tf.Variable(name='var2', dtype=tf.float32)
    var3 = tf.Variable(name='var2', dtype=tf.float32)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(var1.name, sess.run(var1))
    print(var2.name, sess.run(var2))
    print(var3.name, sess.run(var3))

# 输出结果:
# var1:0 [-0.30036557] 可以看到前面不含有指定的'name_scope_x',证明name_scope对get_variable不起作用
# name_scope_1/var2:0 [ 2.]
# name_scope_1/var2_1:0 [ 2.]  可以看到变量名自行变成了'var2_1',避免了和'var2'冲突,而且有了前置scope

############################################

with tf.variable_scope('variable_scope_2') as scope:
    var1 = tf.get_variable(name='var1', dtype=tf.int8)
    scope.reuse_variables()  # 设置共享变量
    var1_reuse = tf.get_variable(name='var1')
    var2 = tf.Variable(initial_value=[2], name='var2', dtype=tf.int8)
    var2_reuse = tf.Variable(initial_value=[2], name='var2', dtype=tf.int8)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(var1.name, sess.run(var1))
    print(var1_reuse.name, sess.run(var1_reuse))
    print(var2.name, sess.run(var2))
    print(var2_reuse.name, sess.run(var2_reuse))
# 输出结果:
# variable_scope_2/var1:0 [1]
# variable_scope_2/var1:0 [1] 变量var1_reuse重复使用了var1
# variable_scope_2/var2:0 [ 2.]
# variable_scope_2/var2_1:0 [ 2.] 变量var2_1是虽然初始设置了var2为变量名,但是自动变成了var2_1作为区分。

下面的例子进一步说明了他们的用法。

#下面两个例子效果相同

#Example 1
with tf.name_scope('name_scope_1') as scp1:
    with tf.variable_scope('var_scope_2') as scp2:
            a = tf.Variable('a')
            b = tf.get_variable('b')

#Example 2
with tf.name_scope('name_scope_1') as scp1:
    with tf.name_scope('var_scope_2') as scp2:
        a = tf.Variable('a')

with tf.variable_scope('var_scope_2') as scp2:
    b = tf.get_variable('b')

同时,共享变量本质上有两个要求,第一该变量存在,第二该变量设置为可共享

比如下面两个例子都会报错

#Example 1
with tf.variable_scope('scp1', reuse=True) as scp:
    a = tf.get_varialbe('a') #报错,因为虽然设置了reuse=True,但是该变量不存在。

#Example 2
with tf.variable_scope('scp2', reuse=False) as scp:
    a = tf.get_varialbe('a')
    a = tf.get_varialbe('a') #报错,因为设置了reuse=False,使用get_variable无法获得相同名字的变量

设置reuse=tf.AUTO_REUSE可以同时达到两个要求,如果变量不存在就创建并设置为可共享。

总结

  1. 一般情况下,tf.variable_scopetf.get_variable和搭配使用,达到变量共享的效果。

  2. tf.Variable可以单独使用,也可以搭配tf.name_scope使用,给变量分类命名,模块化。

  3. tf.get_variabletf.name_scope搭配使用,无任何效果。

  4. tf.Variabletf.variable_scope搭配使用虽然也可以达到前缀命名效果,但是不建议如此使用,应该使用tf.name_scope