tf之变量与作用域
Posted yanshw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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)的已存在变量,如果指定属性的变量不存在,就新建一个。
但需要特定的语法。
变量作用域
先看个例子
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
以上是关于tf之变量与作用域的主要内容,如果未能解决你的问题,请参考以下文章