张量流中的名称范围和变量范围有什么区别?

What's the difference of name scope and a variable scope in tensorflow?

这些函数有什么区别?

tf.variable_op_scope(values, name, default_name, initializer=None)

Returns a context manager for defining an op that creates variables. This context manager validates that the given values are from the same graph, ensures that that graph is the default graph, and pushes a name scope and a variable scope.


tf.op_scope(values, name, default_name=None)

Returns a context manager for use when defining a Python op. This context manager validates that the given values are from the same graph, ensures that that graph is the default graph, and pushes a name scope.


tf.name_scope(name)

Wrapper for Graph.name_scope() using the default graph. See Graph.name_scope() for more details.


tf.variable_scope(name_or_scope, reuse=None, initializer=None)

Returns a context for variable scope. Variable scope allows to create new variables and to share already created ones while providing checks to not create or share by accident. For details, see the Variable Scope How To, here we present only a few basic examples.

您可以将它们视为两组:variable_op_scopeop_scope 将一组变量作为输入,旨在创建操作。区别在于它们如何影响变量的创建 tf.get_variable:

def mysum(a,b,name=None):
    with tf.op_scope([a,b],name,"mysum") as scope:
        v = tf.get_variable("v", 1)
        v2 = tf.Variable([0], name="v2")
        assert v.name == "v:0", v.name
        assert v2.name == "mysum/v2:0", v2.name
        return tf.add(a,b)

def mysum2(a,b,name=None):
    with tf.variable_op_scope([a,b],name,"mysum2") as scope:
        v = tf.get_variable("v", 1)
        v2 = tf.Variable([0], name="v2")
        assert v.name == "mysum2/v:0", v.name
        assert v2.name == "mysum2/v2:0", v2.name
        return tf.add(a,b)

with tf.Graph().as_default():
    op = mysum(tf.Variable(1), tf.Variable(2))
    op2 = mysum2(tf.Variable(1), tf.Variable(2))
    assert op.name == 'mysum/Add:0', op.name
    assert op2.name == 'mysum2/Add:0', op2.name

注意两个例子中的变量名v

tf.name_scopetf.variable_scope 相同:

with tf.Graph().as_default():
    with tf.name_scope("name_scope") as scope:
        v = tf.get_variable("v", [1])
        op = tf.add(v, v)
        v2 = tf.Variable([0], name="v2")
        assert v.name == "v:0", v.name
        assert op.name == "name_scope/Add:0", op.name
        assert v2.name == "name_scope/v2:0", v2.name

with tf.Graph().as_default():
    with tf.variable_scope("name_scope") as scope:
        v = tf.get_variable("v", [1])
        op = tf.add(v, v)
        v2 = tf.Variable([0], name="v2")
        assert v.name == "name_scope/v:0", v.name
        assert op.name == "name_scope/Add:0", op.name
        assert v2.name == "name_scope/v2:0", v2.name

您可以在 tutorial 中阅读有关变量范围的更多信息。 Stack Overflow 上有一个类似的问题 asked before

命名空间是一种以分层方式组织变量和运算符名称的方法(例如"scopeA/scopeB/scopeC/op1")

  • tf.name_scope 在默认图中为运算符创建命名空间。
  • tf.variable_scope 在默认图中为变量和运算符创建命名空间。

  • tf.op_scopetf.name_scope 相同,但用于创建指定变量的图表。

  • tf.variable_op_scopetf.variable_scope 相同,但用于创建指定变量的图表。

指向上述来源的链接有助于消除此文档问题的歧义。

This example 显示所有类型的范围都为变量和运算符定义名称空间,但有以下区别:

  1. tf.variable_op_scopetf.variable_scope 定义的范围与 tf.get_variable 兼容(忽略其他两个范围)
  2. tf.op_scopetf.variable_op_scope 只是 select 指定变量列表中的一个图表,用于创建范围。除了他们的行为等于 tf.name_scopetf.variable_scope 相应
  3. tf.variable_scopevariable_op_scope 添加指定的或默认的初始值设定项。

让我们首先简要介绍一下变量共享。它是 TensorFlow 中的一种机制,允许共享在代码的不同部分访问的变量,而无需传递对变量的引用。

方法 tf.get_variable can be used with the name of the variable as the argument to either create a new variable with such name or retrieve the one that was created before. This is different from using the tf.Variable 每次调用都会创建一个新变量的构造函数(如果已经存在具有此类名称的变量,则可能会向变量名称添加后缀)。

为了变量共享机制,引入了一种独立类型的作用域(variable scope)

因此,我们最终得到两种不同类型的作用域:

这两个范围对所有操作以及使用 tf.Variable 创建的变量具有相同的效果,即范围将作为前缀添加到操作或变量名称中。

但是,名称范围被 tf.get_variable 忽略。我们可以在下面的例子中看到:

with tf.name_scope("my_scope"):
    v1 = tf.get_variable("var1", [1], dtype=tf.float32)
    v2 = tf.Variable(1, name="var2", dtype=tf.float32)
    a = tf.add(v1, v2)

print(v1.name)  # var1:0
print(v2.name)  # my_scope/var2:0
print(a.name)   # my_scope/Add:0

在作用域中放置使用 tf.get_variable 访问的变量的唯一方法是使用变量作用域,如下例所示:

with tf.variable_scope("my_scope"):
    v1 = tf.get_variable("var1", [1], dtype=tf.float32)
    v2 = tf.Variable(1, name="var2", dtype=tf.float32)
    a = tf.add(v1, v2)

print(v1.name)  # my_scope/var1:0
print(v2.name)  # my_scope/var2:0
print(a.name)   # my_scope/Add:0

这使我们能够轻松地在程序的不同部分共享变量,即使在不同的名称范围内也是如此:

with tf.name_scope("foo"):
    with tf.variable_scope("var_scope"):
        v = tf.get_variable("var", [1])
with tf.name_scope("bar"):
    with tf.variable_scope("var_scope", reuse=True):
        v1 = tf.get_variable("var", [1])
assert v1 == v
print(v.name)   # var_scope/var:0
print(v1.name)  # var_scope/var:0

更新

从r0.11版本开始,op_scopevariable_op_scope都是deprecated并被name_scopevariable_scope取代。

至于APIr0.11,op_scopevariable_op_scope都是deprecatedname_scopevariable_scope 可以嵌套:

with tf.name_scope('ns'):
    with tf.variable_scope('vs'): #scope creation
        v1 = tf.get_variable("v1",[1.0])   #v1.name = 'vs/v1:0'
        v2 = tf.Variable([2.0],name = 'v2')  #v2.name= 'ns/vs/v2:0'
        v3 = v1 + v2       #v3.name = 'ns/vs/add:0'

来自tensorflow文档本页的最后一部分:Names of ops in tf.variable_scope()

[...] when we do with tf.variable_scope("name"), this implicitly opens a tf.name_scope("name"). For example:

with tf.variable_scope("foo"):
  x = 1.0 + tf.get_variable("v", [1])
assert x.op.name == "foo/add"

Name scopes can be opened in addition to a variable scope, and then they will only affect the names of the ops, but not of variables.

with tf.variable_scope("foo"):
    with tf.name_scope("bar"):
        v = tf.get_variable("v", [1])
        x = 1.0 + v
assert v.name == "foo/v:0"
assert x.op.name == "foo/bar/add"

When opening a variable scope using a captured object instead of a string, we do not alter the current name scope for ops.

两个 variable_op_scope and op_scope 现已弃用,根本不应使用。

关于其他两个,在我尝试通过创建一个简单的示例来可视化所有内容之前,我也无法理解 variable_scope and name_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 

如果您打开 TensorBoard,您会看到类似的图案(如您所见,bscope_name 矩形之外):


这里给你答案:

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

关于 Sharing variables 的一个很好的文档告诉你

tf.variable_scope(): Manages namespaces for names passed to tf.get_variable().

同一文档提供了有关变量作用域如何工作以及何时有用的更多详细信息。

让我们简单点:只需使用 tf.variable_scopeQuoting a TF developer,:

Currently, we recommend everyone to use variable_scope and not use name_scope except for internal code and libraries.

除了 variable_scope 的功能基本上扩展了 name_scope 的功能之外,它们的共同表现可能会让您感到惊讶:

with tf.name_scope('foo'):
  with tf.variable_scope('bar'):
    x = tf.get_variable('x', shape=())
    x2 = tf.square(x**2, name='x2')
print(x.name)
# bar/x:0
print(x2.name)
# foo/bar/x2:0

此行为有它的用处并证明了两个作用域的共存——但除非您知道自己在做什么,否则坚持使用 variable_scope 只会避免您因此而感到头疼。

Tensorflow 2.0 Compatible Answer: Andrzej PronobisSalvador Dali的解释很详细[=12相关的Functions =].

上面讨论的作用域函数中,截至目前(2020 年 2 月 17 日)处于活动状态的是 variable_scopename_scope .

为了社区的利益,为我们上面讨论的那些函数指定 2.0 兼容调用。

函数在1.x:

tf.variable_scope

tf.name_scope

2.x中的各自函数:

tf.compat.v1.variable_scope

tf.name_scopetf.compat.v2.name_scope 如果从 1.x to 2.x 迁移)

有关从 1.x 迁移到 2.x 的更多信息,请参阅此 Migration Guide