Tensorflow:使用列表在sess.run中进行变量赋值的时间是什么时候?

10

我曾认为变量赋值是在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->assign x->...->udpate_z->assign z”或“update_x->udpate_y->udpate_z->assign a,b,c”?(其中(x,y,z)是(a,b,c)的排列) 此外,如果有一种方法能够实现后者的赋值(在列表中所有操作完成后进行赋值),请告诉我如何实现。

2个回答

16
三个操作 update_aupdate_bupdate_c 在数据流图中没有相互依赖,因此 TensorFlow 可以选择以任何顺序执行它们。(在当前实现中,可能会同时在不同的线程上并行执行这三个操作。)第二个问题是默认情况下变量读取是被缓存的,因此在您的程序中,update_b 中分配的值(即 c+a)可能使用 a 的原始值或更新后的值,这取决于变量何时首次被读取。
如果您想确保操作按特定顺序发生,则可以使用 with tf.control_dependencies([...]): 块来强制执行块内创建的操作在列表中命名的操作之后发生。您可以在 with tf.control_dependencies([...]): 块内使用 tf.Variable.read_value() 来明确变量读取的点。
因此,如果要确保update_aupdate_b之前发生并且update_bupdate_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)? - Sarah
@Sarah,请检查我的答案! - kmario23
2
啊,我明白为什么会让人感到困惑了。当你一次调用 sess.run([x, y, z]) 时,TensorFlow 只会执行那些张量所依赖的操作一次(除非在你的图中有一个 tf.while_loop())。如果一个张量在列表中出现两次(比如你的例子中的 mul),TensorFlow 将只执行一次并返回两个结果副本。要多次运行赋值,你必须要么多次调用 sess.run(),要么使用 tf.while_loop() 在你的图中添加一个循环。 - mrry
感谢@kmario23详细解释我的代码行为。此外,感谢@mrry的解释,我终于理解了sess.run(list)的行为。非常感谢你们! - Sarah
不客气! @mrry 谢谢你更详细地解释了这个问题! - kmario23
显示剩余2条评论

1
根据你的这个例子,
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,它就会寻找定义,找到了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() 在图中添加循环。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接