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之变量与作用域的主要内容,如果未能解决你的问题,请参考以下文章

js补充之作用域

深入作用域之静态作用域与动态作用域

前端之JS

TensorFlow 变量作用域 变量管理 共享变量

JavaScript之静态作用域与动态作用域 #yyds干货盘点#

JavaScript 作用域 与 作用域链