变量作用域(variable_scope)和命名空间作用域(name_scope)有什么区别?

74

变量作用域 variable_scope 和名称作用域 name_scope 有什么区别?变量作用域教程 隐含地讲述了 variable_scope 打开 name_scope. 我还注意到在 name_scope 中创建一个变量会自动使用作用域名称扩展变量名。那么,两者的区别是什么呢?

3个回答

54

在我尝试通过创建一个简单的示例来可视化一切之前,我很难理解variable_scopename_scope之间的区别(它们看起来几乎相同):

import tensorflow as tf
def scoping(fn, scope1, scope2, vals):
    with fn(scope1):
        a = tf.Variable(vals[0], name='a')
        b = tf.get_variable('b', initializer=vals[1])
        c = tf.constant(vals[2], name='c')
        with fn(scope2):
            d = tf.add(a * b, c, name='res')

        print '\n  '.join([scope1, a.name, b.name, c.name, d.name]), '\n'
    return d

d1 = scoping(tf.variable_scope, 'scope_vars', 'res', [1, 2, 3])
d2 = scoping(tf.name_scope,     'scope_name', 'res', [1, 2, 3])

with tf.Session() as sess:
    writer = tf.summary.FileWriter('logs', sess.graph)
    sess.run(tf.global_variables_initializer())
    print sess.run([d1, d2])
    writer.close()

在这里,我创建了一个函数,它创建一些变量和常量,并将它们根据提供的类型分组到作用域中。在这个函数中,我还打印了所有变量的名称。之后,我执行图形以获取结果值的值并保存事件文件以在tensorboard中进行调查。如果您运行此代码,您会得到以下结果:

scope_vars
  scope_vars/a:0
  scope_vars/b:0
  scope_vars/c:0
  scope_vars/res/res:0 

scope_name
  scope_name/a:0
  b:0
  scope_name/c:0
  scope_name/res/res:0 

如果您打开TB(如下图所示,bscope_name矩形框之外),将看到类似的模式:

enter image description here

这就给出了答案:

现在您可以看到,tf.variable_scope()会为所有变量、操作和常量的名称添加前缀(无论您如何创建它们)。另一方面,tf.name_scope()会忽略使用tf.get_variable()创建的变量,因为它假定您知道要使用哪个变量以及在哪个作用域中使用。

共享变量的良好文档告诉您:

tf.variable_scope(): 管理传递给tf.get_variable()的名称的命名空间。

同一份文件提供了更详细的Variable Scope工作方式以及何时使用它的说明。


4
建议简化这个非常有用的示例,删除 vals 参数。 - Mr_and_Mrs_D
2
我还建议将“d”的名称从“res”更改。我假设这与您在scope2中传递的“res”无关。 - Robert Lugg

39
当使用tf.get_variable创建变量时,Tensorflow将开始检查使用相同方法创建的变量名称是否冲突。如果发生冲突,将引发异常。如果你使用tf.name_scope上下文管理器尝试更改变量名称的前缀,这不会防止Tensorflow抛出异常。只有tf.variable_scope上下文管理器才能有效地更改此情况下变量的名称。或者,如果想要重用变量,则应该在第二次创建变量之前调用scope.reuse_variables()。
简而言之,tf.name_scope仅为该作用域中创建的所有张量添加前缀(除了使用tf.get_variable创建的变量),而tf.variable_scope则为使用tf.get_variable创建的变量添加前缀。

5
为什么需要两种作用域机制,你可以再多说一点吗? - Andrzej Pronobis
2
我不知道。也许你应该在 Github 上创建一个问题,要求更好地记录两种机制之间的区别。 - cesarsalgado
4
我可以推测,存在两种创建变量的方式(tf.Variable和tf.get_variable)的原因是通常情况下您不想共享一个变量。如果您想要共享变量,则需要使用tf.get_variable创建一个变量,并使用tf.variable_scope更改作用域,以明确说明您正在处理可共享的变量。如果在这种情况下可以使用tf.name_scope,那么可能会降低代码的可读性。 - cesarsalgado
variable_scope是匿名作用域,而name_scope是命名作用域吗?如果是这样,将'name_scope'重命名为'named_variable_scope'是否有意义? - ariejdl

2

tf.variable_scopetf.name_scope的演进,用于处理Variable的重用。正如您所注意到的,它比tf.name_scope做得更多,因此没有真正的理由使用tf.name_scope:毫不奇怪,一个TF开发者建议只使用tf.variable_scope

我理解为什么还保留了tf.name_scope是因为这两者的行为存在微妙的不兼容性,这使得tf.variable_scope无法作为tf.name_scope的替代品。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接