1. 共享变量用途
在构建模型时, 需要使用 tf.Variable 来创建一个变量 (也可以理解成节点). 当两个模型一起训练时, 一个模型需要使用其他模型创建的变量, 比如, 对抗网络中的生成器和判别器. 如果使用 tf.Variable, 将会生成一个新的变量, 而我们需要使用原来的那个变量. 这时就是通过引入 get_Variable 方法, 实现共享变量来解决这个问题. 这种方法可以使用多套网络模型来训练一套权重.
2. 使用 get_Variable 获取变量
get_Variable 一般会配合 Variable_scope 一起使用, 以实现共享变量. Variable_scope 的含义是变量作用域. 在某一作用域中的变量可以被设置成共享的方式, 被其他网络模型使用.
get_Variable 函数的定义如下:
tf.get_Variable(<name>, <shape>, <initializer>)
在 TensorFlow 里, 使用 get_Variable 时候生成的变量是以指定的 name 属性为唯一标识, 并不是定义的变量名称. 使用时一般是通过 name 属性定位到具体变量, 并将其共享到其他的模型中.
- import tensorflow as tf
- import numpy as np
- var1 = tf.Variable(1.0, name='first_var')
- print("var1:", var1.name)
- var1 = tf.Variable(2.0, name='first_var')
- print('var1:', var1.name)
- var2 = tf.Variable(3.0)
- print('var2:', var2.name)
- var2 = tf.Variable(4.0)
- print('var1:', var2.name)
- with tf.Session() as sess:
- sess.run(tf.global_variables_initializer())
- print("var1=", var1.eval())
- print("var2=", var2.eval())
- print()
在上述的代码中,, 可以看到内存中有两个 var1, 并且他们的 name 是不一样的, 对于图来说, 后面的 var1 是生效的. 当 Variable 定义没有指定名字时, 系统会自动的加上一个名字 Variable:0
3.get_Variable 用法演示
- import tensorflow as tf
- import numpy as np
- get_var1 = tf.get_variable('firat_var_1', [1], initializer=tf.constant_initializer(2))
- print("var1:", get_var1.name)
- get_var1 = tf.get_variable('firat_var_2', [1], initializer=tf.constant_initializer(3))
- print("var1:", get_var1.name)
- with tf.Session() as sess:
- sess.run(tf.global_variables_initializer())
- print("var1=", get_var1.name)
- print("var1=", get_var1.eval())
使用不同的 name 定义变量, 当使用相同的 name 时, 会抛出异常, 变量名可以相同, 但是 name 是不能相同的.
如果要使用相同的 name 的话, 我们需要使用 variable_scope 将他们隔开, 看如下代码:
- import tensorflow as tf
- with tf.variable_scope('test_1'):
- var1 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
- with tf.variable_scope('test_2'):
- var1 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
- print("var1:", var1.name)
- print("var1:", var1.name)
根据程序的运行结果, 我们可以发现变量的名字加上了作用域的名称, 这样使得我们能够在不同的作用域下面定义 name 相同的变量, 同时, scope 还支持嵌套定义,
- with tf.variable_scope('test_0'):
- with tf.variable_scope('test_1'):
- var1 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
- with tf.variable_scope('test_2'):
- var1 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
- print("var1:", var1.name)
- print("var1:", var1.name)
4. 共享作用域
使用作用域中的参数 reuse 可以实现共享变量功能
在 variable_scope 里面有一个 reuse=True 属性, 表示使用已经定义过的变量, 这时, get_variable 将不会在创建新的变量, 而是去图中 get_variable 所创建的变量中找与 name 相同的变量.
- import tensorflow as tf
- with tf.variable_scope('test_0'):
- var1 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
- with tf.variable_scope('test_2'):
- var2 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
- with tf.variable_scope('test_0', reuse=True):
- var3 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
- with tf.variable_scope('test_2'):
- var4 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
- print("var1:", var1.name)
- print("var2:", var2.name)
- print("var3:", var3.name)
- print("var4:", var4.name)
在上述的输出结果中, 我们可以看到, var1 和 var3 的名字一样, var2 和 var4 的名字一样, 则表明他们是同一个变量, 如此就实现了变量的共享. 在实际应用中, 可以将 1,2 和 3,4 分别放在不同的模型进行训练, 但是他们会作用于同一个模型的学习参数上.
使用 anaconda 的 spyder 工具运行时, 代码只能运行一次, 第二次运行将会报错. 可以退出当前的 kernel, 再重新进入一下, 因为 tf.get_varibale 在创建变量时, 会去检查图中是否已经创建过该变量, 如果创建过且不是共享的方式, 则会报错.
因而可以使用 tf.reset_default_graph(), 将图里面的变量清空, 就可以解决这个问题.
5. 初始化共享变量
variable_scope 和 get_variable 都具有初始化的功能. 在初始化时, 如果没有对当前变量初始化, 则 TensorFlow 会默认使用作用域的初始化, 并且作用域的初始化方法也有继承功能.
- import tensorflow as tf
- with tf.variable_scope('test_0', initializer=tf.constant_initializer(0.15)):
- var1 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
- with tf.variable_scope('test_2'):
- var2 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
- var3 = tf.get_variable('first_var_2', shape=[2], initializer=tf.constant_initializer(0.315))
- with tf.Session() as sess:
- sess.run(tf.global_variables_initializer())
- print("var1:", var1.eval())
- print("var2:", var2.eval())
- print("var3:", var3.eval())
当变量没有进行初始化时, 会继承它的域的初始化方式, 域也会继承它的上一级的域的初始化方式. 在多模型训练时, 常常可以对模型中的张量进行分区, 同时, 同一进行初始化. 在变量共享方面, 可以使用 tf.AUTO_REUSE 来为 reuse 属性赋值. tf.AUTO_REUSE 可以实现第一次调用 variable_scope 时, 传入 reuse 的值为 false, 再次调用时, reuse 的值为 True.
6. 作用域与操作符的受限范围
variable_scope 还可以使用 with variable_scope as xxxscope 的方式定义作用域, 当使用这种方式时, 将不会在受到外层的 scope 所限制.
- import tensorflow as tf
- with tf.variable_scope('test2', initializer=tf.constant_initializer(1.5)) as sp:
- var1 = tf.get_variable('var1', [2], dtype=tf.float32)
- print(sp.name)
- print(var1.name)
- with tf.variable_scope('test1', dtype=tf.float32, initializer=tf.constant_initializer(5.5)):
- var2 = tf.get_variable('var1', [2], dtype=tf.float32)
- with tf.variable_scope(sp) as sp1:
- var3 = tf.get_variable('var2', [2], dtype=tf.float32)
- print("var2:", var2.name)
- print("var3:", var3.name)
- with tf.Session() as sess:
- sess.run(tf.global_variables_initializer())
- print("var2:", var2.eval())
- print("var2:", var3.eval())
通过 with tf.variable_scope(sp) as sp1 我们可知其没有收到外层的作用域所限制, 初始化的操作时, 它的值不是外层作用域的初始化值, 而是指定的作用域的初始化的值.
对于操作符而言, 不仅收到 tf.name_scope 限制还收到 tf.variable_scope 限制.
- import tensorflow as tf
- import numpy as np
- with tf.variable_scope('scope'):
- with tf.name_scope('op'):
- v = tf.get_variable('var1', [1])
- x = v + 2.0
- print("v:", v.name)
- print('x', x.name)
根据结果, 我们可知, 通过添加 tf.name_scope('op'): 作用域时, 变量的命名并没有收到限制, 只是改变了 op 的命名, 通过 tf.name_scope(''): 还可以返回到顶层的作用域中.
- import tensorflow as tf
- import numpy as np
- with tf.variable_scope('scope'):
- var1 = tf.get_variable("v", [1])
- with tf.variable_scope('scope_1'):
- var2 = tf.get_variable("v", [1])
- with tf.name_scope(''):
- var3 = tf.get_variable('var1', [1])
- x = var3 + 2.0
- print("var1", var1.name)
- print('var2', var2.name)
- print('var3', var3.name)
- print('x', x.name)
通过将通过 tf.name_scope('') 设置为空, 对于变量名是没有影响, 但是可以看到 x 的命名, 它已经变成了最外层的命名了.
来源: https://www.cnblogs.com/baby-lily/p/10934131.html