本文由网络上一些回答和博文汇总而成。
要将这个问题解释清楚,得结合tensorflow中创建变量的两种方式tf.get_variable()和tf.Variable()一起说明。
在tf.name_scope下:
要共享变量,需要使用tf.variable_scope()
来看一个例子:
with tf.name_scope('name_sp1') as scp1:
with tf.variable_scope('var_scp2') as scp2:
with tf.name_scope('name_scp3') as scp3:
a = tf.Variable('a')
b = tf.get_variable('b')
等同于:
with tf.name_scope('name_sp1') as scp1:
with tf.name_scope('name_sp2') as scp2:
with tf.name_scope('name_scp3') as scp3:
a = tf.Variable('a')
with tf.variable_scope('var_scp2') as scp2:
b = tf.get_variable('b')
要注意的是:
with tf.variable_scope('scp', reuse=True) as scp:
a = tf.get_varialbe('a') #报错
和
with tf.variable_scope('scp', reuse=False) as scp:
a = tf.get_varialbe('a')
a = tf.get_varialbe('a') #报错
都会报错,因为reuse=True是,get_variable会强制共享,如果不存在,报错;reuse=Flase时,会强制创造,如果已经存在,也会报错。
如果想实现“有则共享,无则新建”的方式,可以:
with tf.variable_scope('scp', reuse=tf.AUTO_REUSE) as scp:
a = tf.get_variable('a') #无,创造
a = tf.get_variable('a') #有,共享
当你写一个建图模块时(e.g. LSTM_block() ),你不知道用户是否会共享此模块,因此你可以只用tf.variable_scope来分组模块内变量,用tf.get_variable来为共享提供可能,而不能用tf.Variable。
举个例子:我们想构建一个NewAutoEncoder, 包含了一个encoder和2个decoder, 这2个decoder是共享的。
首先,新建一个Dense层:
def Dense(x, x_dim, y_dim, name, reuse=None):
with tf.variable_scope(name, reuse=reuse):
w = tf.get_variable('weight', [x_dim, y_dim])
b = tf.get_variable('bias', [y_dim])
y = tf.add(tf.matmul(x, w), b)
return y
def Encoder(x, name):
with tf.variable_scope(name, reuse=None):
x = tf.nn.relu(Dense(x, 784, 1000, 'layer1', reuse=False))
x = tf.nn.relu(Dense(x, 1000, 1000, 'layer2', reuse=False))
x = Dense(x, 1000, 10, 'layer3', reuse=False)
return x
def Decoder(x, name, reuse=None):
with tf.variable_scope(name, reuse=reuse):
x = tf.nn.relu(Dense(x, 10, 1000, 'layer1', reuse=False))
x = tf.nn.relu(Dense(x, 1000, 1000, 'layer2', reuse=False))
x = tf.nn.sigmoid(Dense(x, 1000, 784, 'layer3', reuse=False))
return x
def build_network(x):
batchsz = 32
x_ph = tf.placeholder(tf.float32, [batchsz, 784], name='input')
z_ph = tf.placeholder(tf.float32, [1, 10], name='z')
x = Encoder(x_ph, 'Encoder')
x_hat = Decoder(x, 'Decoder1', reuse=None)
x_hat2 = Decoder(z_ph, 'Decoder2', reuse=True)
# ...
解读:所有的模块都要使用tf.variable_scope带name参数封装,如Dense(), Encoder(), Decoder()。对于明确不会共享的模块,如本例中的Encoder, reuse参数可以不提供。
注意:当多个tf_variable_scope嵌套时,如果中间某层开启了reuse=True, 则内层自动全部共享,即使内层设置了reuse=False。而且,一旦使用tf.get_variable_scope().reuse_variables()打开了当前域共享,就不能关闭了!
总结: 1. `tf.variable_scope`和`tf.get_variable`必须要搭配使用(全局scope除外),为share提供支持。
2. `tf.Variable`可以单独使用,也可以搭配`tf.name_scope`使用,给变量分类命名,模块化。
3. `tf.Variable`和`tf.variable_scope`搭配使用不伦不类,不是设计者的初衷
作用域中的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
# Exited the reusing scope, back to a non-reusing one.
assert tf.get_variable_scope().reuse == False
一个作用域可以作为另一个新的作用域的参数,如:
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()打开一个已经存在的作用域时,就会跳转到这个作用域。
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:
assert foo_scope2.name == "foo" # Not changed.