tf之变量与作用域

生成变量

tensorflow生成变量有两种方式,Variable 和 get_variable

Variable(initial_value=None, trainable=True,
         collections=None, validate_shape=True,
         caching_device=None,name=None,
         expected_shape=None, import_scope=None,
         constraint=None)
get_variable(name, shape
=None, dtype=None, initializer=None, regularizer=None, trainable=True, collections=None, caching_device=None, partitioner=None, validate_shape=True, use_resouce=None, constraint=None)

两者有何区别呢?

### Variable
w_1 = tf.Variable(3, name="w_1")
w_2 = tf.Variable(1, name="w_1")
print(w_1.name)     # w_1:0
print(w_2.name)     # w_1_1:0   系统检测到命名冲突,会自动处理,把w_1变成w_1_1,保证了变量的唯一性

### get_variable
w_1 = tf.get_variable(name="g_1", initializer=1)
# w_2 = tf.get_variable(name="w_1",initializer=2)     # 这句报错,系统检测到命名冲突,会直接报错
# 错误信息
# ValueError: Variable w_1 already exists, disallowed. Did you mean to set reuse=True in VarScope?

Variable 系统检测到命名冲突会自动处理,保证了变量的唯一性,也就是不可复用,

get_variable 检测到命名冲突会直接报错。这使得 get_variable 可以创建共享变量

其他情况两者一般通用。

实际上 get_variable 是获取指定属性(name)的已存在变量,如果指定属性的变量不存在,就新建一个。

但需要特定的语法。

变量作用域

扫描二维码关注公众号,回复: 5544955 查看本文章

先看个例子

def myfunc():
    w = tf.Variable(tf.random_uniform([2,2]), name='data')
    return w

### 直接调用2次,结果不同
w1 = myfunc()
w2 = myfunc()

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(sess.run(w1))
    print(sess.run(w2))     # 结果不相同

上面用Variable创建变量,可以直接调用两次,但是结果不相同,这是显而易见的,符合常规。  (ps:如果用 get_variable 创建,无法直接调用两次。)

但是假如我们需要它相同呢?实际上在训练模型时都需要迭代过程中参数保存一致。

此时就需要共享变量。

tf 用 get_variable 和 variable_scope 来解决共享变量问题。并提供了两种方法。

方法1

with tf.variable_scope('test') as scope:
    w1 = tf.get_variable('data', [2,2])
    # scope.reuse_variables()  or
    tf.get_variable_scope().reuse_variables()
    w2 = tf.get_variable('data')

print(w1 is w2)     # True

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(sess.run(w1))
    print(sess.run(w2))     # 结果相同

可以看到两个变量是一样的。

with tf.variable_scope('test2') as scope2:
    w11 = tf.Variable(tf.random_uniform([2,2]), name='data2')       # 这样无法被共享
    # scope.reuse_variables()
    tf.get_variable_scope().reuse_variables()
    w21 = tf.get_variable('data2')      # 报错

用Variable是不行的。

方法2

with tf.variable_scope('test3') as tt:
    w12 = tf.get_variable('data3', [2,2])

with tf.variable_scope(tt, reuse=True):
    w22 = tf.get_variable('data3')

print(w12 is w22)       # True

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(sess.run(w12))
    print(sess.run(w22))        # 结果相同

这也可以共享变量。

variable_scope 是用来创建变量左右域的,后面会详细介绍。

共享变量的方法总结

1. 需要使用 同时使用 get_variable 和 variable_scope

2. 在同一个 variable_scope 内,不需要指定reuse=True,但需要用scope.reuse_variables() 或者 tf.get_variable_scope().reuse_variables()

3. 在不同的 variable_scope 内, 第一个不需要指定reuse=True,但后面需要指定。

get_variable 机制

get_variable 会判断指定属性的变量是否存在,如果存在,并且该变量空间的reuse=True,那么就共享之前的变量,否则新建一个,

但是如果没有指定reuse=True,会提示命名冲突,

也就是说 get_variable 必须和 variable_scope 配套使用。

实例

with tf.variable_scope('scope1'):
    w1 = tf.Variable(1, name='w1')
    w2 = tf.get_variable(name='w2', initializer=2.)

with tf.variable_scope('scope1', reuse=True):
    w1_p = tf.Variable(1, name='w1')
    w2_p = tf.get_variable(name='w2', initializer=2.)  # initializer=3. 亦可

print('w1', w1)         # w1   <tf.Variable 'scope1/w1:0' shape=() dtype=int32_ref>
print('w1_p', w1_p)     # w1_p <tf.Variable 'scope1_1/w1:0' shape=() dtype=int32_ref>

print('w2', w2)         # w2   <tf.Variable 'scope1/w2:0' shape=() dtype=float32_ref>
print('w2_p', w2_p)     # w2_p <tf.Variable 'scope1/w2:0' shape=() dtype=float32_ref>

print(w1 is w1_p, w2 is w2_p)   # False True

可以看到 Variable 无法共享,get_variable 共享。

变量作用域进阶

多重作用域

作用域中的resuse默认是False,调用函数reuse_variables()可设置为True,
一旦设置为True,就不能返回到False,并且该作用域的子空间reuse都是True。
 如果不想重用变量,那么可以退回到上层作用域,相当于exit当前作用域

with tf.variable_scope("root"):
    # At start, the scope is not reusing.
    assert tf.get_variable_scope().reuse == False
    with tf.variable_scope("foo"):
        # Opened a sub-scope, still not reusing.
        assert tf.get_variable_scope().reuse == False
    with tf.variable_scope("foo", reuse=True):
        # Explicitly opened a reusing scope.
        assert tf.get_variable_scope().reuse == True
        with tf.variable_scope("bar"):
            # Now sub-scope inherits the reuse flag.
            assert tf.get_variable_scope().reuse == True
        # with tf.variable_scope("bar2"):
        #     # Now sub-scope inherits the reuse flag.
        #     assert tf.get_variable_scope().reuse == False       # AssertionError
    # Exited the reusing scope, back to a non-reusing one.
    assert tf.get_variable_scope().reuse == False

可以看到在 bar2 作用域内,reuse==False 报错了,因为这个父域是True。

作用域的调用

作用域名字可以作为参数

with tf.variable_scope("foo") as foo_scope:  # 名字
    v = tf.get_variable("v", [1])
with tf.variable_scope(foo_scope):          # 参数
    w = tf.get_variable("w", [1])
with tf.variable_scope(foo_scope, reuse=True):
    v1 = tf.get_variable("v", [1])
    w1 = tf.get_variable("w", [1])
assert v1 is v
assert w1 is w
with tf.variable_scope('scope1'):
    w1 = tf.Variable(1, name='w1')
    w2 = tf.get_variable(name='w2', initializer=2.)

with tf.variable_scope('scope1', reuse=True):  # 另一种方式
    w1_p = tf.Variable(1, name='w1')
    w2_p = tf.get_variable(name='w2', initializer=2.) 

用as 或者直接用名字都可作为参数。 但是有区别,后面会总结。

作用域跳转

不管作用域如何嵌套,当使用with tf.variable_scope()打开一个已经存在的作用域时,就会跳转到这个作用域。

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:
        print(tf.get_variable_scope().name) # foo
        assert foo_scope2.name == "foo" # Not changed
      with tf.variable_scope('foo') as foo_scope3:
        print(tf.get_variable_scope().name) # bar/baz/foo
        assert foo_scope3.name == "foo" # AssertionError

 

这里可以看到,直接用名字没有跳转,而用as跳转成功。 

多重作用域下的变量

变量都是通过作用域/变量名来标识,作用域可以像文件路径一样嵌套。

# encoding:utf-8
__author__ = 'HP'
import tensorflow as tf

with tf.variable_scope('s1'):
    x1 = tf.get_variable('data1', [3, 4])
    print(x1)                   # <tf.Variable 's1/data1:0' shape=(3, 4) dtype=float32_ref>
    tf.get_variable_scope().reuse_variables()

    with tf.variable_scope('s11'):
        # x2 = tf.get_variable('data1')           # ValueError: Variable s1/s11/data1 does not exist
        # print(x2)
        pass

        with tf.variable_scope('s1'):
            # x3 = tf.get_variable('data1')       # ValueError: Variable s1/s11/s1/data1 does not exist
            # print(x3)
            pass

    with tf.variable_scope('s1', reuse=True):
        x4 = tf.get_variable('data1')       # ValueError: Variable s1/s1/data1 does not exist
        print(x4)
        pass

with tf.variable_scope('s1', reuse=True):
    x5 = tf.get_variable('data1')
    print(x5)                       # <tf.Variable 's1/data1:0' shape=(3, 4) dtype=float32_ref>

 with tf.variable_scope('s2'):
   with tf.variable_scope('s1', reuse=True):
 print(tf.get_variable_scope().name) # s2/s1
   # x6 = tf.get_variable('data1') # Variable s2/s1/data1 does not exist
   # print(x6)

 

可以看到 变量 就像文件一样,这个文件夹内的a文件和另外文件夹内的a文件不是一个文件。

综上得出如下结论:

1. 如果直接用名字,只能在同级作用域下跳转,如上例。 

2. 如果用as, 可以在任何地方跳转到该作用域

  // 可以这么理解:如果直接用名字,是相对路径,相当于是在当前目录下创建了一个该名字的文件夹,

  // 而as是绝对路径,不管在哪调用,都能指定该路径。

命名空间

命名空间,也是一种作用域

name_scope 仅对普通operation 有用,对 get_variable 无效,

variable_scope 不仅对普通operation 有效,也对 get_variable 有效

先上代码

with tf.name_scope('name_test'):
    n1 = tf.constant(1, name='cs1')
    n2 = tf.Variable(tf.zeros([1]), name='v1')
    ww1 = tf.multiply(n2, [1])

    nv1 = tf.get_variable(name='nv1', initializer=1.0)

with tf.variable_scope('v_test'):
    v_n1 = tf.constant(2, name='cs2')
    v_n2 = tf.Variable(tf.zeros([1]), name='v2')
    ww2 = tf.multiply(v_n2, [1])

    v1 = tf.get_variable(name='vv1', initializer=2.0)

### name_scope
print('n1', n1.name)        # n1 name_test/cs1:0
print('n2', n2.name)        # n2 name_test/v1:0
print('nv1', nv1.name)      # nv1 nv1:0                 # 注意和前两个不同,name_scope 对 get_variable 无效

print('ww1', ww1.name)      # ww name_test/Mul:0        # 注意也加上了name_scope

### variable_scope
print('v_n1', v_n1.name)    # v_n1 v_test/cs2:0
print('v_n2', v_n2.name)    # v_n2 v_test/v2:0
print('v1', v1.name)        # v1   v_test/vv1:0         # 注意和前两个相同,name_scope 对 get_variable 有效
print('ww2', ww2.name) # ww2 v_test/Mul:0 # 注意也加上了variable_scope

变量的名称是指针名,变量的name是地址。

共享变量用法进阶

在重复使用(即 非第一次使用)时,设置 reuse=True 来 再次调用 该共享变量作用域(variable_scope)。但是这种方法太繁琐了。

 简单方法

def myfunc():
    with tf.variable_scope('test', reuse=tf.AUTO_REUSE):
        w = tf.get_variable('data', [2, 2])
    return w

for i in range(3):
    print(myfunc())

# <tf.Variable 'test/data:0' shape=(2, 2) dtype=float32_ref>
# <tf.Variable 'test/data:0' shape=(2, 2) dtype=float32_ref>
# <tf.Variable 'test/data:0' shape=(2, 2) dtype=float32_ref>

 相关API

with tf.variable_scope('V1') as variable_scope:
    a1 = tf.get_variable(name='a1', shape=[1], initializer=tf.constant_initializer(1))
    a2 = tf.Variable(tf.random_normal(shape=[2,3], mean=0, stddev=1), name='a2')

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(variable_scope)                            # <tensorflow.python.ops.variable_scope.VariableScope object at 0x000000000D5F67F0>
    print(variable_scope.name)                       # V1
    print(tf.get_variable_scope())                   # <tensorflow.python.ops.variable_scope.VariableScope object at 0x000000000D5F67B8>
    print(tf.get_variable_scope().original_name_scope)      #
    print(tf.get_variable_scope().reuse)             # False
    print(tf.get_variable_scope().name)              #
    print(a1.name)      # V1/a1:0
    print(a2.name)      # V1/a2:0

获取当前环境的作用域  tf.get_variable_scope()

参考资料

https://www.cnblogs.com/MY0213/p/9208503.html

https://blog.csdn.net/lucky7213/article/details/78967306

https://blog.csdn.net/u012436149/article/details/53081454

https://blog.csdn.net/u010867294/article/details/78695487

https://blog.csdn.net/jeryjeryjery/article/details/79242684

http://www.cnblogs.com/Charles-Wan/p/6200446.html

猜你喜欢

转载自www.cnblogs.com/yanshw/p/10532722.html