首页 文章

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

提问于
浏览
207

这些功能之间有什么区别?

tf.variable_op_scope(values,name,default_name,initializer = None)返回用于定义创建变量的op的上下文管理器 . 此上下文管理器验证给定值是否来自同一图,确保该图是默认图,并推送名称范围和变量范围 .


tf.op_scope(values,name,default_name = None)返回在定义Python操作时使用的上下文管理器 . 此上下文管理器验证给定值是否来自同一图,确保该图是默认图,并推送名称范围 .


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


tf.variable_scope(name_or_scope,reuse = None,initializer = None)返回变量范围的上下文 . 变量范围允许创建新变量并共享已创建的变量,同时提供不会意外创建或共享的检查 . 有关详细信息,请参阅变量范围操作方法,此处我们仅提供一些基本示例 .

7 回答

  • 9

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

    目前,我们建议大家使用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 只会因为这种不兼容而避免一些麻烦 .

  • 6

    至于API r0.11, op_scopevariable_op_scope 都是deprecated . 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'
    
  • 41

    您可以将它们视为两个组: 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 .

  • 40

    让我们从变量共享的简短介绍开始 . 它是 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
    

    更新

    As of version r0.11, op_scope and variable_op_scope are both deprecated and replaced by name_scope and variable_scope.

  • 7

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

    • tf.name_scope为默认图形中的运算符创建名称空间 .

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

    • tf.op_scopetf.name_scope 相同,但是对于创建指定变量的图形 .

    • tf.variable_op_scopetf.variable_scope 相同,但是对于创建指定变量的图形 .

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

    This example表明所有类型的作用域都为变量和运算符定义了名称空间,但有以下区别:

    tf.variable_op_scopetf.variable_scope 定义的

    • 范围与 tf.get_variable 兼容(它忽略了另外两个范围)

    • tf.op_scopetf.variable_op_scope 只需从指定变量列表中选择一个图形即可创建范围 . 除此之外,他们的行为相应于 tf.name_scopetf.variable_scope

    • tf.variable_scopevariable_op_scope 添加指定或默认初始值设定项 .

  • 2

    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 矩形之外):


    This gives you the answer

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

    关于Sharing variables的好文档告诉你

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

    相同的文档提供了有关变量范围如何工作以及何时有用的更多详细信息 .

  • 291

    从tensorflow文档的本页的最后一节开始:Names of ops in tf.variable_scope()

    [...]当我们使用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"
    

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

相关问题