Hexo

凡事预则立,不预则废


  • Home

  • Tags

  • Archives

  • Navigation

  • Search

TensorFlow——variable_scope和name_scope

  • 在 TensorFlow 1.x 中,variable_scope 和 name_scope 都是用于管理命名空间的工具,但它们的用途和行为有所不同,本文将详细介绍二者的区别

主要用途不同

  • variable_scope :主要用于管理变量的命名和共享,特别是在构建复杂的神经网络模型时,确保不同层或不同部分的变量可以正确命名和复用
  • name_scope :主要用于组织图中的操作,使图的结构更加清晰,便于在 TensorBoard 中查看和分析

对 tf.Variable 的影响相同

  • variable_scope :variable_scope 会为 tf.Variable 创建的变量添加前缀:

    1
    2
    3
    4
    5
    import tensorflow as tf

    with tf.variable_scope('var_scope'):
    var3 = tf.Variable(3.0, name='var3')
    print(var3.name) # 输出: var_scope/var3:0
  • name_scope :name_scope 同样会为 tf.Variable 创建的变量添加前缀:

    1
    2
    3
    4
    5
    import tensorflow as tf

    with tf.name_scope('name_scope'):
    var4 = tf.Variable(4.0, name='var4')
    print(var4.name) # 输出: name_scope/var4:0

对 tf.get_variable 的影响不同

  • variable_scope :variable_scope 会影响 tf.get_variable 创建的变量的命名,并且支持变量共享。tf.get_variable 创建的变量名称会带上 variable_scope 的前缀:

    1
    2
    3
    4
    5
    import tensorflow as tf

    with tf.variable_scope('var_scope'):
    var1 = tf.get_variable('var1', shape=[1], initializer=tf.constant_initializer(1.0))
    print(var1.name) # 输出: var_scope/var1:0
  • name_scope :name_scope 不会影响 tf.get_variable 创建的变量的命名。tf.get_variable 创建的变量会忽略 name_scope,直接使用 variable_scope 或默认的命名空间:

    1
    2
    3
    4
    5
    import tensorflow as tf

    with tf.name_scope('name_scope'):
    var2 = tf.get_variable('var2', shape=[1], initializer=tf.constant_initializer(2.0))
    print(var2.name) # 输出: var2:0

variable_scope变量共享功能

  • variable_scope :支持变量共享,通过设置 reuse 参数(如 reuse=True 或 reuse=tf.AUTO_REUSE),可以在不同的作用域中复用相同名称的变量

  • 共享变量功能在构建具有共享参数的神经网络时非常有用,下面是构建神经网络的最佳实践:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import tensorflow as tf

    def my_network(inputs):
    with tf.variable_scope('my_network', reuse=tf.AUTO_REUSE):
    w = tf.get_variable('weights', shape=[1], initializer=tf.constant_initializer(3.0))
    output = inputs * w
    return output

    input1 = tf.constant(1.0)
    input2 = tf.constant(2.0)

    output1 = my_network(input1)
    output2 = my_network(input2)
    # 这里 w 在两个调用中是共享的
  • 注:**name_scope** :不支持变量共享 ,主要用于组织操作(如 tf.add、tf.matmul 等)的命名,方便在 TensorBoard 中可视化


附录:variable_scope vs name_scope更多代码示例

  • 测试代码1:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    def test():
    data = tf.ones(shape=[3,5], dtype=tf.float32)
    with tf.variable_scope("vs_test"):
    x = tf.get_variable("x", initializer=[10])
    y = tf.constant(20)
    z = tf.layers.dense(inputs=data, units=1, name="output")
    a = tf.Variable("a")
    with tf.name_scope("ns_test"):
    x1 = tf.get_variable("x", initializer=[10])
    y1 = tf.constant(20)
    z1 = tf.layers.dense(inputs=data, units=1, name="output")
    a1 = tf.Variable("a")
    test()
    # <tf.Variable 'vs_test/x:0' shape=(1,) dtype=int32_ref>
    # Tensor("vs_test/Const:0", shape=(), dtype=int32)
    # Tensor("vs_test/output/BiasAdd:0", shape=(3, 1), dtype=float32)
    # <tf.Variable 'vs_test/Variable:0' shape=() dtype=string_ref>
    # ==========
    # <tf.Variable 'x:0' shape=(1,) dtype=int32_ref>
    # Tensor("ns_test/Const:0", shape=(), dtype=int32)
    # Tensor("ns_test/output/BiasAdd:0", shape=(3, 1), dtype=float32)
    # <tf.Variable 'ns_test/Variable:0' shape=() dtype=string_ref>
  • 结论1:

    • 对于variable_scope()来说,所有方式获取的变量或layer等调用都会被加上前缀
    • variable_scope()包含reuse参数,对这个scope下的所有变量生效(包括通过layer调用或get_variable获取的变量)
      • reuse = True: 复用之前的同名变量,没有同名变量则抛出异常
      • reuse = False: 创建新变量,有同名变量则抛出异常
      • reuse = tf.AUTO_REUSE: 如果有同名变量,则复用之前的同名变量,否则创建新变量
    • 对于name_scope()来说,通过tf.get_variable和layer获取到的变量不会被加上前缀,上面示例中打印出来的不是变量,而是网络输出值,可以被name_scope来管理
    • name_scope()没有reuse参数
  • 参考链接:https://blog.csdn.net/shenxiaoming77/article/details/79141078

    name_scope: 为了更好地管理变量的命名空间而提出的。比如在 tensorboard 中,因为引入了 name_scope, 我们的 Graph 看起来才井然有序
    variable_scope: 大部分情况下,跟 tf.get_variable() 配合使用,实现变量共享的功能

  • 测试代码2:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    def test():
    data = tf.ones(shape=[3,5], dtype=tf.float32)
    with tf.variable_scope("vs_test"):
    x = tf.get_variable("x", initializer=[10])
    y = tf.constant(20)
    z = tf.layers.dense(inputs=data, units=1, name="output")
    a = tf.Variable(1, name="a")
    with tf.variable_scope("vs_test"):
    x = tf.get_variable("y", initializer=[10])
    # 下面这行会报错ValueError: Variable vs_test/output/kernel already exists
    # z = tf.layers.dense(inputs=data, units=1, name="output")
    a = tf.Variable(1, name="b")
    with tf.name_scope("ns_test"):
    x1 = tf.get_variable("x", initializer=[10])
    y1 = tf.constant(20)
    z1 = tf.layers.dense(inputs=data, units=1, name="output")
    a1 = tf.Variable(1, name="a")
    test()

    print "=====trainable===="
    trainable_var = tf.trainable_variables()
    for v in trainable_var: print v

    print "=====vs_test===="
    main_qnet_var = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='vs_test')
    for v in main_qnet_var: print v

    print "=====ns_test===="
    main_qnet_var = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='ns_test')
    for v in main_qnet_var: print v

    # =====trainable====
    # <tf.Variable 'vs_test/x:0' shape=(1,) dtype=int32_ref>
    # <tf.Variable 'vs_test/output/kernel:0' shape=(5, 1) dtype=float32_ref>
    # <tf.Variable 'vs_test/output/bias:0' shape=(1,) dtype=float32_ref>
    # <tf.Variable 'vs_test/a:0' shape=() dtype=int32_ref>
    # <tf.Variable 'vs_test/y:0' shape=(1,) dtype=int32_ref>
    # <tf.Variable 'vs_test_1/b:0' shape=() dtype=int32_ref>
    # <tf.Variable 'x:0' shape=(1,) dtype=int32_ref>
    # <tf.Variable 'output/kernel:0' shape=(5, 1) dtype=float32_ref>
    # <tf.Variable 'output/bias:0' shape=(1,) dtype=float32_ref>
    # <tf.Variable 'ns_test/a:0' shape=() dtype=int32_ref>
    # =====vs_test====
    # <tf.Variable 'vs_test/x:0' shape=(1,) dtype=int32_ref>
    # <tf.Variable 'vs_test/output/kernel:0' shape=(5, 1) dtype=float32_ref>
    # <tf.Variable 'vs_test/output/bias:0' shape=(1,) dtype=float32_ref>
    # <tf.Variable 'vs_test/a:0' shape=() dtype=int32_ref>
    # <tf.Variable 'vs_test/y:0' shape=(1,) dtype=int32_ref>
    # <tf.Variable 'vs_test_1/b:0' shape=() dtype=int32_ref>
    # =====ns_test====
    # <tf.Variable 'ns_test/a:0' shape=() dtype=int32_ref>
  • 结论2:

    • 在重复定义vs_test后,
      • tf.get_variable获得的变量命名是vs_test开头的
      • tf.Variable获得的变量命名是vs_test_1开头的(变量名自增)

嵌套作用域的reuse继承和覆盖

  • 在多层级 tf.variable_scope 中使用 reuse 参数时,reuse 参数的状态在嵌套的 variable_scope 中会进行继承和覆盖
    • 继承 :子作用域会继承父作用域的 reuse 状态,
    • 覆盖 :子作用域可以通过显式设置 reuse 参数来覆盖继承的状态

子作用域未显式设置 reuse 参数(继承)

  • 当子作用域没有显式设置 reuse 参数时,它会继承父作用域的 reuse 状态

    1
    2
    3
    4
    5
    import tensorflow as tf

    with tf.variable_scope('outer_scope', reuse=True) as outer:
    with tf.variable_scope('inner_scope') as inner:
    print(inner.reuse) # 输出: True
  • 在上述代码中,outer_scope 的 reuse 设置为 True,inner_scope 未显式设置 reuse 参数,所以 inner_scope 继承了 outer_scope 的 reuse 状态,即 True

子作用域显式设置 reuse 参数(覆盖)

  • 若子作用域显式设置了 reuse 参数,那么它会覆盖从父作用域继承的状态

    1
    2
    3
    4
    5
    import tensorflow as tf

    with tf.variable_scope('outer_scope', reuse=True) as outer:
    with tf.variable_scope('inner_scope', reuse=False) as inner:
    print(inner.reuse) # 输出: False
    • 这里,outer_scope 的 reuse 为 True,但 inner_scope 显式将 reuse 设置为 False,所以 inner_scope 的 reuse 状态为 False

reuse=tf.AUTO_REUSE(与True和False一致)

  • reuse=tf.AUTO_REUSE 允许在变量存在时复用,不存在时创建。在多层级作用域中,它同样遵循继承和覆盖规则

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import tensorflow as tf

    def create_or_reuse_variable():
    with tf.variable_scope('outer', reuse=tf.False):
    with tf.variable_scope('inner', reuse=tf.AUTO_REUSE):
    var = tf.get_variable('my_var', shape=[1], initializer=tf.constant_initializer(1.0))
    return var

    # 两次调用
    var1 = create_or_reuse_variable()
    var2 = create_or_reuse_variable()

    print(var1.name) # 输出: outer/inner/my_var:0
    print(var2.name) # 输出: outer/inner/my_var:0
    • 在这个例子中,两次调用 create_or_reuse_variable 函数时,由于使用了 reuse=tf.AUTO_REUSE,第二次调用会复用第一次创建的变量

TensorFlow——变量初始化

  • 注:本文的设置仅针对 TensorFlow 1.x

变量的内存分配与初始值设定

  • 变量创建 :在 TensorFlow 1.x 中,当你定义一个变量(Variable)时,实际上是在图中创建了一个占位符(创建了一个表示该变量的节点),它仅仅定义了变量的形状和数据类型 ,并未为其分配实际的内存空间和设定初始值

    1
    2
    3
    import tensorflow as tf

    my_variable = tf.Variable(initial_value=3.0, dtype=tf.float32)
    • 这里的 my_variable 只是一个图中的节点,它描述了变量的基本信息,但在内存中还没有真正存储值
  • 变量初始化 :在会话中执行初始化操作时(注意,初始化操作需要和变量在同一个图中),TensorFlow 会为这个变量分配内存空间,用于存储其值(注意:如果有多个会话绑定同一个图,变量需要重新初始化)

    • 在 TensorFlow 1.x 里,变量的状态是与会话(Session)相关联的。每个会话都有自己独立的变量副本和状态
    • 多个 Session 绑定同一个 Graph 的特殊场景 :当在一个会话中初始化变量时,只会影响该会话中的变量状态,其他会话中的同一变量仍然处于未初始化状态,如果有多个会话绑定同一个图,为了能在每个会话中正常使用变量,需要分别对每个会话中的变量进行初始化操作
  • 防止未定义行为 :如果不进行初始化就尝试使用变量,会导致未定义行为,因为变量在内存中的值是不确定的。这可能会引发错误或得到意外的计算结果。通过强制要求初始化变量,可以确保在使用变量之前,它们已经被正确地赋予了初始值,从而保证计算的正确性和稳定性


全局变量初始化

  • 在大多数情形下,你需要对所有的变量进行初始化。可以**借助tf.global_variables_initializer()**来实现这一目的。示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import tensorflow as tf

    a = tf.Variable(3, name='a')
    b = tf.Variable(2, name='b')
    c = tf.add(a, b)

    with tf.Session() as sess:
    # 初始化所有全局变量
    init = tf.global_variables_initializer()
    sess.run(init)
    result = sess.run(c)
    print("结果: ", result)
    • 在这个示例中,tf.global_variables_initializer()会初始化所有定义的全局变量。在运行计算图之前,必须先运行这个初始化操作

初始化部分变量

  • 如果你只想初始化部分变量 ,可以**使用tf.variables_initializer()**。示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import tensorflow as tf

    a = tf.Variable(3, name='a')
    b = tf.Variable(2, name='b')
    c = tf.add(a, b)

    with tf.Session() as sess:
    # 初始化部分变量
    init_ab = tf.variables_initializer([a, b])
    sess.run(init_ab)
    result = sess.run(c)
    print("结果: ", result)
    • 在这个示例中,tf.variables_initializer([a, b])仅初始化了变量a和b

初始化单个变量

  • 你还可以使用variable.initializer**来初始化单个变量**。示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import tensorflow as tf

    a = tf.Variable(3, name='a')
    b = tf.Variable(2, name='b')
    c = tf.add(a, b)

    with tf.Session() as sess:
    # 初始化单个变量
    sess.run(a.initializer)
    sess.run(b.initializer)
    result = sess.run(c)
    print("结果: ", result)
    • 在这个示例中,分别对变量a和b进行了初始化

新加入的变量都要初始化

  • 在已经创建session后,依然是可以加入新的变量的,但是需要进行初始化才可以使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    import tensorflow as tf

    var1 = tf.Variable(3, dtype=tf.float32)
    var2 = tf.Variable(5, dtype=tf.float32)
    add_op1 = tf.add(var1, var2)

    sess = tf.Session()

    init_op = tf.global_variables_initializer() # 初始化所有变量
    sess.run(init_op)

    result1 = sess.run(add_op1)
    print("两个变量的和:", result1)

    # 在Session后继续创建变量
    var3 = tf.Variable(7, dtype=tf.float32)
    add_op2 = tf.add(add_op1, var3)

    init_new_var = tf.variables_initializer([var3]) # 初始化新创建的变量
    sess.run(init_new_var)

    result2 = sess.run(add_op2)
    print("三个变量的和:", result2)

    sess.close() # 显式关闭会话

    # 两个变量的和: 8.0
    # 三个变量的和: 15.0

附录:多次初始化变量会发生什么?

  • 在 TensorFlow 1.x 中,对同一个变量进行两次初始化后,从内存占用的角度来说,变量所占用的内存位置通常是一致的,但变量存储的值会被重置为初始值

  • 当你在 TensorFlow 1.x 里:

    • 定义一个变量时,实际上是在图中创建了一个表示该变量的节点
    • 在会话中执行初始化操作时,TensorFlow 会为这个变量分配内存空间,用于存储其值
    • 同一个Session中,再次对该变量执行初始化操作,并不会重新分配内存空间,而是直接在已分配的内存位置上修改存储的值
      • 同一个图中,内存分配操作只会在第一次初始化时发生
  • 示例代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import tensorflow as tf

    var = tf.Variable(10, dtype=tf.float32)
    init = tf.global_variables_initializer()

    with tf.Session() as sess:
    # 第一次初始化变量
    sess.run(init)
    print("第一次初始化后变量的值:", sess.run(var))

    # 修改变量的值
    assign_op = var.assign(20)
    sess.run(assign_op)
    print("修改变量后的值:", sess.run(var))

    # 再次初始化变量
    sess.run(init)
    print("第二次初始化后变量的值:", sess.run(var))

    # 第一次初始化后变量的值: 10.0
    # 修改变量后的值: 20.0
    # 第二次初始化后变量的值: 10.0
    • 定义变量和初始化操作 :定义了变量 var,并创建了初始化所有全局变量的操作 init
    • 第一次初始化 :运行 init 操作,此时 TensorFlow 为变量 var 分配内存空间,并将初始值 10 存储在该内存位置
    • 修改变量的值 :使用 assign 操作将变量 var 的值修改为 20,这会直接在已分配的内存位置上更新存储的值
    • 第二次初始化 :再次运行 init 操作,TensorFlow 不会重新分配内存空间,而是直接将内存中存储的值重置为初始值 10

附录:多个Session执行同一个图

  • 核心注意点 :在 TensorFlow 1.x 中,当一个图被多个 Session 执行时,需要分别对每个 Session 中的变量进行初始化

  • 在 TensorFlow 1.x 里,变量的状态是与会话(Session)相关联的。每个会话都有自己独立的变量副本和状态。当你在一个会话中初始化变量时,只会影响该会话中的变量状态,其他会话中的同一变量仍然处于未初始化状态。因此,为了能在每个会话中正常使用变量,需要分别对每个会话中的变量进行初始化操作

  • 多个Session执行同一个图的示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import tensorflow as tf

    # 创建一个图
    graph = tf.Graph()
    with graph.as_default():
    var = tf.Variable(10, dtype=tf.float32)
    add_op = tf.add(var, 5)
    init_op = tf.global_variables_initializer() # 注意:这个操作必须添加到图中,后续所有Session的初始化都可以调用这个操作

    # 创建第一个会话
    sess1 = tf.Session(graph=graph)
    # 初始化第一个会话中的变量
    sess1.run(init_op)
    result1 = sess1.run(add_op)
    print("第一个会话执行结果:", result1)

    # 创建第二个会话
    sess2 = tf.Session(graph=graph)
    try:
    result2 = sess2.run(add_op) # 尝试在未初始化变量的情况下执行操作
    except tf.errors.FailedPreconditionError:
    print("第二个会话未初始化变量,操作失败")
    # 初始化第二个会话中的变量
    sess2.run(init_op)
    result2 = sess2.run(add_op) # 在第二个会话中执行操作
    print("第二个会话初始化变量后执行结果:", result2)

    # 关闭会话以释放资源
    sess1.close()
    sess2.close()

    # 第一个会话执行结果: 15.0
    # 第二个会话未初始化变量,操作失败
    # 第二个会话初始化变量后执行结果: 15.0
    • 图的定义 :创建了一个图,并在图中定义了一个变量 var 和一个操作 add_op,同时定义初始化操作 init_op
    • 第一个会话 :创建第一个会话 sess1,对其中的变量进行初始化(执行图中的操作 init_op),然后执行操作并打印结果
    • 第二个会话 :创建第二个会话 sess2,尝试在未初始化变量的情况下执行操作,会抛出 FailedPreconditionError 异常;捕获异常后,对第二个会话中的变量进行初始化(执行图中的操作 init_op),再次执行操作并打印结果
1…312313314…352
San Ye

San Ye

Stay Hungry. Stay Foolish.

704 posts
53 tags
© 2026 San Ye
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4