TensorFlow——计算图管理


一句话说明

  • TensorFlow 1.x 版本中,tf.Graph() 用于创建一个新的计算图,而会话(tf.Session())则用于执行计算图中的操作
  • 本文将分别给出明确创建图(tf.Graph())和不明确创建图创建会话的示例,并说明它们之间的区别

不明确创建图

  • 当不指定图时,TensorFlow 会使用一个全局默认的计算图。以下是一个简单的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 在TensorFlow 2.x中可以通过下面的代码替换 `import tensorflow as tf`,仍可在TensorFlow 2.x环境中使用TensorFlow 1.x
    import tensorflow.compat.v1 as tf
    tf.disable_v2_behavior()

    # 定义操作,使用默认计算图
    a = tf.constant(3.0)
    b = tf.constant(4.0)
    c = tf.add(a, b)

    # 创建会话并执行操作
    with tf.Session() as sess:
    result = sess.run(c)
    print("不使用 tf.Graph() 的结果:", result)
  • 在这个示例中,我们没有显式地创建计算图,TensorFlow 会使用默认的计算图来定义操作 abc。然后创建一个会话并在该会话中运行操作 c,最终得到计算结果


明确创建图(使用tf.Graph()

  • 也可以使用 tf.Graph() 函数,创建一个新的独立计算图,并在这个图中定义操作。以下是示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # 在TensorFlow 2.x中可以通过下面的代码替换 `import tensorflow as tf`,仍可在TensorFlow 2.x环境中使用TensorFlow 1.x
    import tensorflow.compat.v1 as tf
    tf.disable_v2_behavior()

    # 创建一个新的计算图
    graph = tf.Graph()

    # as_default() 只在 with 上下文中生效,退出后默认图会恢复为原来的全局默认图
    with graph.as_default():
    # 在新的计算图中定义操作
    a = tf.constant(5.0)
    b = tf.constant(6.0)
    c = tf.add(a, b)

    # 创建会话并指定使用新的计算图
    with tf.Session(graph=graph) as sess:
    result = sess.run(c)
    print("使用 tf.Graph() 的结果:", result)
  • 在这个示例中,我们做了以下操作:

    • 首先使用 tf.Graph() 创建了一个新的计算图 graph
    • 然后使用 graph.as_default() 上下文管理器,将这个新的计算图设置为默认图,并在其中定义操作 abc
    • 最后创建一个会话,并通过 graph=graph 参数指定该会话使用我们创建的新计算图,在会话中运行操作 c 并得到结果

多图管理(TensorFlow 1.x)

  • TensorFlow 1.x中创建和管理多个计算图的示例:
    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
    import tensorflow as tf

    # 创建第一个计算图
    graph1 = tf.Graph()
    with graph1.as_default():
    # 在图1中定义变量和操作
    a = tf.constant(5, name='a')
    b = tf.constant(3, name='b')
    c = tf.add(a, b, name='sum')

    # 在图1中初始化所有变量
    init1 = tf.global_variables_initializer()

    # 创建第二个计算图
    graph2 = tf.Graph()
    with graph2.as_default():
    # 在图2中定义不同的变量和操作
    x = tf.constant(10, name='x')
    y = tf.constant(4, name='y')
    z = tf.multiply(x, y, name='product')

    # 在图2中初始化所有变量
    init2 = tf.global_variables_initializer()

    # 创建会话执行图1
    with tf.Session(graph=graph1) as sess1:
    sess1.run(init1)
    result1 = sess1.run(c)
    print("图1的结果:", result1) # 输出: 图1的结果: 8

    # 创建会话执行图2
    with tf.Session(graph=graph2) as sess2:
    sess2.run(init2)
    result2 = sess2.run(z)
    print("图2的结果:", result2) # 输出: 图2的结果: 40

多图管理的必要性

  • 隔离性 :不同的图完全隔离,变量和操作不会相互干扰。这在以下场景特别有用:
    • 同时运行多个模型
    • 比较不同模型结构
    • 隔离训练和推理图
  • 资源管理 :每个图有自己的资源集合,可以独立释放,避免内存泄漏
  • 命名空间 :不同图中的操作可以有相同的名称而不会冲突
  • 并行执行 :可以在不同线程中同时运行不同的图(每个线程有自己的会话)
  • 调试 :当出现问题时,可以更容易地定位是哪个图出现了错误

多图管理的最佳实践

  • 使用with graph.as_default():上下文管理器来明确操作属于哪个图
  • 会话(Session)和图(Graph)是一一对应的,需要为每个图创建独立的会话,明确关闭不再需要的会话以释放资源
  • 一般情况可以不用明确创建多个图仅维护一个默认的图即可,更好的做法是在单个图中使用不同的名称作用域(namescope)来组织操作
  • 多线程运行时需要维护不同的图,确保图不会被同时访问(注:不同图的变量命名也可以相同,也就是可以使用相同的代码来定义)
  • TensorFlow 2.x 中,默认启用了即时执行(Eager Execution)模式,不再需要显式地创建计算图和会话

关于图的作用域示例

  • as_default() 只在 with 上下文中生效,退出后默认图会恢复为原来的全局默认图:

    1
    2
    3
    4
    with new_graph.as_default():
    op1 = tf.constant(1) # 属于 new_graph

    op2 = tf.constant(2) # 属于全局默认图(不是 new_graph)
  • 嵌套多个 as_default() 的示例(不建议使用)

    1
    2
    3
    4
    5
    6
    7
    8
    g1 = tf.Graph()
    g2 = tf.Graph()

    with g1.as_default():
    a = tf.constant(1) # 属于 g1

    with g2.as_default():
    b = tf.constant(2) # 属于 g2
  • 通过 tf.get_default_graph() 可以检查当前默认图:

    1
    2
    with new_graph.as_default():
    print(tf.get_default_graph() is new_graph) # 输出 True

新增变量及其初始化

  • 在已经创建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中,所有变量都需要初始化(因为变量定义操作不会分配内存,只有在初始化后才会分配内存),重复初始化变量会将变量重置为初始化值