在tensorflow中,name作用域和变量作用域的区别是什么?

这些函数之间有什么区别?

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

返回一个上下文管理器,用于定义创建变量的操作。 这个上下文管理器验证给定的值是否来自同一个图,确保该图是默认图,并推入名称作用域和变量作用域


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

返回一个上下文管理器,用于定义Python op。 这个上下文管理器验证给定的值是否来自同一个图,确保该图是默认图,并推入名称作用域。< / p >


tf.name_scope(name)

使用默认图形的Graph.name_scope()包装器。 有关详细信息,请参见Graph.name_scope()


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

返回变量scope的上下文。 变量作用域允许创建新变量并共享已创建的变量,同时提供检查,以防止意外创建或共享。有关详细信息,请参见变量作用域如何,在这里我们只提供几个基本示例。< / p >

107834 次浏览

你可以把它们看作两组: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
你可以在教程中阅读关于变量作用域的更多信息。 一个类似的问题是Stack Overflow上的问之前

命名空间是一种以分层方式组织变量和操作符名称的方法。“scopeA / scopeB / scopeC op1”)

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

这个例子表明所有类型的作用域都为变量和操作符定义了名称空间,区别如下:

  1. tf.variable_op_scopetf.variable_scope定义的作用域与tf.get_variable兼容(它忽略了其他两个作用域)
  2. tf.op_scopetf.variable_op_scope只是从指定变量列表中选择一个图来创建范围。除了它们的行为等于tf.name_scopetf.variable_scope之外
  3. tf.variable_scopevariable_op_scope添加指定的或默认的初始化项。

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

方法tf.get_variable可以使用变量名作为参数来创建一个具有此名称的新变量或检索之前创建的变量。这与使用tf.Variable构造函数不同,后者在每次调用时都会创建一个新变量(如果已经存在这样的变量名,则可能会在变量名后面添加一个后缀)。

正是为了实现变量共享机制,引入了一种单独类型的作用域(变量作用域)。

结果,我们得到了两种不同类型的作用域:

这两个作用域对所有操作以及使用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版本开始,__ABC0和__ABC1都是弃用,并被__ABC2和variable_scope取代。

对于API r0.11, op_scopevariable_op_scope都是弃用name_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文档的最后一节:tf.variable_scope()中的操作名称

[…]当我们执行with tf.variable_scope("name")时,这将隐式地打开tf.name_scope("name").;例如:

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

除了变量作用域之外,还可以打开名称作用域,这样它们只会影响操作的名称,而不会影响变量的名称。

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"

当使用捕获的对象而不是字符串打开变量作用域时,我们不改变操作的当前名称作用域。

variable_op_scopeop_scope现在都已弃用,根本不应该使用。

关于另外两个,在我试图通过创建一个简单的例子来可视化所有内容之前,我也在理解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

如果你打开TensorBoard,你会看到类似的模式(如你所见,bscope_name矩形的外面):


这就是答案:

现在你可以看到tf.variable_scope()为所有变量(不管你如何创建它们)、操作符、常量的名字添加了一个前缀。另一方面,tf.name_scope()会忽略用tf.get_variable()创建的变量,因为它假定你知道你想在哪个范围内使用哪个变量。

共享变量的一个好的文档会告诉你这一点

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

同样的文档提供了变量作用域如何工作以及何时有用的更多细节。

让我们简单点:只使用tf.variable_scope引用TF开发人员的话,:

目前,我们建议每个人都使用variable_scope,而不使用name_scope,除了内部代码和库。

除了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兼容答案: Andrzej PronobisSalvador Dali的解释非常详细地介绍了与Scope相关的函数。

在上面讨论的范围函数中,截至目前(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_scope(如果从1.x to 2.x迁移到tf.compat.v2.name_scope)

有关从1迁移的更多信息。X到2。x,请参考这个迁移向导