最近在看tf.slim实现的inception_v1的代码,发现一个函数tf.variable_scope()不太清楚,查询到网上多对比name和variable,特意记录一下。

tf.name_scope()、tf.variable_scope()函数解析(最清晰的解释)_变量名

1 tf.name_scope(‘scope_name’)

tf.name_scope 主要结合 tf.Variable() 来使用,方便参数命名管理。

import tensorflow as tf

# 也就是说,它的主要目的是为了更加方便地管理参数命名。
# 与 tf.Variable() 结合使用。简化了命名
with tf.name_scope('conv1') as scope:
weights1 = tf.Variable([1.0, 2.0], name='weights')
bias1 = tf.Variable([0.3], name='bias')

# 下面是在另外一个命名空间来定义变量的
with tf.name_scope('conv2') as scope:
weights2 = tf.Variable([4.0, 2.0], name='weights')
bias2 = tf.Variable([0.33], name='bias')

# 所以,实际上weights1 和 weights2 这两个引用名指向了不同的空间,不会冲突
print(weights1.name)
print(weights2.name)
> conv1/weights:0
> conv2/weights:0
import tensorflow as tf

# 注意,这里的 with 和 python 中其他的 with 是不一样的
# 执行完 with 里边的语句之后,这个 conv1/ 和 conv2/ 空间还是在内存中的
# 这时候如果再次执行上面的代码就会再生成其他命名空间
with tf.name_scope('conv1') as scope:
weights1 = tf.Variable([1.0, 2.0], name='weights')
bias1 = tf.Variable([0.3], name='bias')

with tf.name_scope('conv2') as scope:
weights2 = tf.Variable([4.0, 2.0], name='weights')
bias2 = tf.Variable([0.33], name='bias')

print(weights1.name)
print(weights2.name)
> conv1_1/weights:0
> conv2_1/weights:0


为什么变量名加了后缀将在第三节说明。

2 tf.variable_scope(‘scope_name’)

tf.variable_scope() 主要结合 tf.get_variable() 来使用,实现 变量共享。

# 这里是正确的打开方式~~~可以看出,name 参数才是对象的唯一标识
import tensorflow as tf

with tf.variable_scope('v_scope') as scope1:
Weights1 = tf.get_variable('Weights', shape=[2,3])
bias1 = tf.get_variable('bias', shape=[3])

# 下面来共享上面已经定义好的变量
# note: 在下面的 scope 中的变量必须已经定义过了,才能设置 reuse=True,否则会报错
with tf.variable_scope('v_scope', reuse=True) as scope2:
Weights2 = tf.get_variable('Weights')

print(Weights1.name)
print(Weights2.name)
# 可以看到这两个引用名称指向的是同一个内存对象
> v_scope/Weights:0
> v_scope/Weights:0


tf.variable_scope() 也可以结合 tf.variable() 一块使用。

import tensorflow as tf

# 注意, bias1 的定义方式
with tf.variable_scope('v_scope') as scope1:
Weights1 = tf.get_variable('Weights', shape=[2,3])
# bias1 = tf.Variable([0.52], name='bias')

# 下面来共享上面已经定义好的变量
# note: 在下面的 scope 中的 get_variable()变量必须已经定义过了,
# 才能设置 reuse=True,否则会报错
with tf.variable_scope('v_scope', reuse=True) as scope2:
Weights2 = tf.get_variable('Weights')
bias2 = tf.Variable([0.52], name='bias')

print(Weights1.name)
print(Weights2.name)
#print(bias1.name)
print(bias2.name)
> v_scope/Weights:0
> v_scope/Weights:0
> v_scope_1/bias:0


如果 reuse=True 的scope中的变量没有已经定义,会报错!!

import tensorflow as tf

# 注意, bias1 的定义方式
with tf.variable_scope('v_scope') as scope1:
Weights1 = tf.get_variable('Weights', shape=[2,3])
bias1 = tf.Variable([0.52], name='bias')

print(Weights1.name)
print(bias1.name)

# 下面来共享上面已经定义好的变量
# note: 在下面的 scope 中的get_variable()变量必须已经定义过了,
# 才能设置 reuse=True,否则会报错
with tf.variable_scope('v_scope', reuse=True) as scope2:
Weights2 = tf.get_variable('Weights')
bias2 = tf.get_variable('bias', [1]) # 'bias'

print(Weights2.name)
print(bias2.name)

# 这样子的话就会报错
# Variable v_scope/bias does not exist, or was not created with tf.get_variable()


3 name_scope和variable_scope

tf.get_variable()遇到重命名的变量创建且变量名没有设置成共享变量(所谓的共享是指在同一参数空间下的共享,参数空间名称不一样就不能共享了)时,就会报错 ;相对应的,变量名称这个参数是必填参数,tf.get_variable()会根据这个参数去创建或者获取变量。如果这个变量name已经存在,直接使用这个变量;如果不存在,则重新创建。

tf.Variable()每次调用都会产生一个新的变量,它会自动检测命名冲突并自行处理,如果这个变量已存在,则后缀会增加0、1、2等数字编号予以区别。

import tensorflow as tf

with tf.name_scope("a_name_scope"):
initializer = tf.constant_initializer(value=1)
var1 = tf.get_variable(name='var1', shape=[1],
dtype=tf.float32,initializer=initializer)
var2 = tf.Variable(name='var2', initial_value=[2], dtype=tf.float32)
var21 = tf.Variable(name='var2', initial_value=[2.1], dtype=tf.float32)
var22 = tf.Variable(name='var2', initial_value=[2.2], dtype=tf.float32)

with tf.Session() as sess:
sess.run(tf.initialize_all_variables())
print(var1.name)
print(sess.run(var1))
print(var2.name)
print(sess.run(var2))
print(var21.name)
print(sess.run(var21))
print(var22.name)
print(sess.run(var22))
> var1:0
> [1.]
> a_name_scope/var2:0
> [2.]
> a_name_scope/var2_1:0
> [2.1]
> a_name_scope/var2_2:0
> [2.2]


可以看出使用 tf.Variable() 定义变量的时候, 虽然var2, var21, var22的 name 一样, 但是为了不重复变量名, Tensorflow 为它们做了附加的区分,因为输出的变量名是不一样的. 所以, 本质上tensorflow令 var2, var21, var22 成为不一样的变量。

变量作用域也支持嵌套定义。tf.get_variable得到的结果带有前缀,方便知道是哪一个嵌套下的变量。

with tf.variable_scope("foo"):
with tf.variable_scope("bar"):
v = tf.get_variable("v",[1])
assert v.name == "foo/bar/v:0"