【问题标题】:Tensorflow: When are variable assignments done in sess.run with a list?Tensorflow:何时在 sess.run 中使用列表完成变量赋值?
【发布时间】:2017-05-08 09:47:24
【问题描述】:

我曾认为变量赋值是在给 sess.run 的列表中的所有操作之后完成的,但是下面的代码在不同的执行时返回不同的结果。似乎在列表中随机运行操作,并在列表中的操作运行后分配变量。

a = tf.Variable(0)
b = tf.Variable(1)
c = tf.Variable(1)
update_a = tf.assign(a, b + c)
update_b = tf.assign(b, c + a)
update_c = tf.assign(c, a + b)

with tf.Session() as sess:
  sess.run(initialize_all_variables)
  for i in range(5):
    a_, b_, c_ = sess.run([update_a, update_b, update_c])

我想知道变量赋值的时间。 哪些是正确的:“update_x -> 分配 x -> ... -> udpate_z -> 分配 z”或“update_x -> udpate_y -> udpate_z -> 分配 a、b、c”? (其中 (x, y, z) 是 (a, b, c) 的排列) 另外,如果有实现后一种赋值的方法(列表中所有操作都完成后赋值),请告诉我如何实现。

【问题讨论】:

    标签: python variables tensorflow variable-assignment timing


    【解决方案1】:

    update_aupdate_bupdate_c 这三个操作在数据流图中没有相互依赖关系,因此 TensorFlow 可以选择以任意顺序执行它们。 (在当前的实现中,它们三个可能会在不同的线程上并行执行。)第二个问题是默认情况下会缓存变量的读取,因此在您的程序中分配的值update_b(即c + a) 可以使用 a 的原始值或更新值,具体取决于首次读取变量的时间。

    如果您想确保操作以特定顺序发生,您可以使用with tf.control_dependencies([...]): 块来强制在块中创建的操作在列表中指定的操作之后发生。您可以在 with tf.control_dependencies([...]): 块内使用 tf.Variable.read_value() 来明确读取变量的点。

    因此,如果您想确保update_a 发生在update_bupdate_b 发生在update_c 之前,您可以这样做:

    update_a = tf.assign(a, b + c)
    
    with tf.control_dependencies([update_a]):
      update_b = tf.assign(b, c + a.read_value())
    
    with tf.control_dependencies([update_b]):
      update_c = tf.assign(c, a.read_value() + b.read_value())
    

    【讨论】:

    • 感谢您的回答@mrry。我很欣赏它并了解如何在我的代码中指定操作的顺序。那么,关于你的回答,我还有一个问题。如果列表中的操作有一些相互依赖关系,它们的值是否在所有操作执行后被分配?在这种情况下,默认情况下执行操作的顺序是什么(不使用tf.control_dependencies)。
    • “列表中的操作”是什么意思?通常,只要新值可用,分配操作就可以运行。变量读取操作可以在该步骤开始后立即运行。但是,如果没有明确的控制(或数据)依赖关系,对变量的赋值可能发生在读取同一变量之前或之后,具体取决于操作的调度方式。在这种情况下,默认排序是未定义的。
    • 抱歉有歧义。我的意思是像[op1, op2, op3] 这样的操作列表。我可能有误解。伪代码:v = tf.Variable(0), c = tf.constant(3), add = tf.add(v, c), update = tf.assign(v, add), mul = tf.mul(add, update), sess.run([mul, mul]) 给出的是 [9, 9] 而不是 [9, 36]。所以我认为分配是在列表[mul, mul] 中的所有操作之后完成的,例如 mul -> mul -> assign,而不是 mul -> assign -> mul -> assign。
    • @Sarah 请检查我的答案!
    • 啊,我明白这令人困惑的原因了。当您调用 sess.run([x, y, z]) 一次时,TensorFlow 会执行这些张量仅依赖一次的每个操作(除非您的图中有 tf.while_loop())。如果一个张量在列表中出现两次(例如您的示例中的mul),TensorFlow 将执行一次并返回结果的两个副本。要多次运行分配,您必须多次调用sess.run(),或使用tf.while_loop() 在图表中放置一个循环。
    【解决方案2】:

    根据你的这个例子,

    v = tf.Variable(0)
    c = tf.constant(3)
    add = tf.add(v, c)
    update = tf.assign(v, add)
    mul = tf.mul(add, update)
    
    with tf.Session() as sess:
        sess.run(tf.initialize_all_variables())
        res = sess.run([mul, mul])
        print(res)
    

    输出:[9, 9]

    你得到[9, 9],这实际上是我们要求它做的。可以这样想:

    在运行期间,一旦从列表中取出 mul,它就会查找 this 的定义并找到 tf.mul(add, update)。现在,它需要add 的值,这导致tf.add(v, c)。所以,它插入vc的值,得到add的值为3。

    好的,现在我们需要update 的值,它被定义为tf.assign(v, add)。我们有add(刚才计算为3)和v的值。因此,它会将v 的值更新为3,这也是update 的值。

    现在,addupdate 的值均为 3。因此,mul 中的乘法结果为 9。

    根据我们得到的结果,我认为,对于列表中的下一项(操作),它只返回刚刚计算的值mul。我不确定它是否再次执行这些步骤,或者只是返回它刚刚为mul 计算的相同(缓存?)值,意识到我们有结果或这些操作并行发生(对于列表中的每个元素)。请问@mrry 或@YaroslavBulatov 可以对此部分发表评论吗?


    引用@mrry 的评论:

    当您调用一次 sess.run([x, y, z]) 时,TensorFlow 会执行这些张量所依赖的每个操作仅一次(除非您的图表中有 tf.while_loop())。如果一个张量在列表中出现两次(例如您的示例中的 mul),TensorFlow 将执行一次并返回结果的 两个副本。要多次运行分配,您必须多次调用sess.run(),或使用tf.while_loop() 在图表中放置一个循环。

    【讨论】:

      猜你喜欢
      • 2018-08-05
      • 1970-01-01
      • 1970-01-01
      • 2016-03-17
      • 1970-01-01
      • 2016-09-27
      • 1970-01-01
      • 2017-02-28
      • 1970-01-01
      相关资源
      最近更新 更多