理解 Tensorflow 控制依赖

Understanding Tensorflow control dependencies

我正在努力更好地掌握 TensorFlow。我遇到了控制依赖的概念。我了解我们指定的操作顺序在执行期间与 Tensorflow 并不真正相关。为了优化执行速度,TensorFlow 自行决定计算节点的顺序。 但是我们可以使用 tf.control_dependencies 自定义执行顺序。 我无法理解该功能的用例。任何人都可以指导我使用某些资源(文档除外)或解释此功能的工作原理吗? 一个例子:

tf.reset_default_graph()
x = tf.Variable(5)
y=tf.Variable(3)
assign = tf.assign(x,x+y)
z = x+assign
with tf.Session() as sess:
   sess.run(tf.global_variables_initializer())
   with tf.control_dependencies([assign]):
        z_out = sess.run(z)

print(z_out)

代码的输出是 8。所以我推断,由于 z=x+y,assign 节点还没有被评估(对吧?)。但是这不就意味着tensorflow的结果可能是错误的吗?这意味着我们需要在每次操作期间创建新节点,以强制 TensorFlow 计算导致结果的所有节点。但是如果训练一个有 10000 个步骤的神经网络,如果每个步骤创建一组新的 1000 weights/parameters space 复杂性不会爆炸吗?

在您发布的代码段中,tf.control_dependencies 没有任何效果。该函数创建一个上下文,其中 新操作 创建时具有对给定操作的控制依赖性,但在您的代码中,上下文中没有新操作,只是对先前存在的操作进行评估。

在大多数情况下,TensorFlow 中的控制流是 "obvious",因为只有一种方法可以正确进行计算。但是,当涉及有状态对象(即变量)时,有些情况可能会产生歧义。考虑以下示例:

import tensorflow as tf

v1 = tf.Variable(0)
v2 = tf.Variable(0)
upd1 = tf.assign(v1, v2 + 1)
upd2 = tf.assign(v2, v1 + 1)
init = tf.global_variables_initializer()

v1v2都是初始化为0然后更新的变量。但是,每个都在更新中使用另一个变量的值。在一个常规的 Python 程序中,事情会按顺序 运行,所以 upd1 会先 运行(所以 v1 会是 1)和 upd2 之后(因此 v2 将是 2,因为 v11)。但是 TensorFlow 不记录创建操作的顺序,只记录它们的依赖关系。所以也可能发生 upd2 运行s 在 upd1 之前(所以 v1 会是 2v2 会是 1 ) 或者两个更新值(v2 + 1v1 + 1)都在赋值之前计算(因此 v1v2 最后都会是 1)。确实,如果我运行它几次:

for i in range(10):
    with tf.Session() as sess:
        sess.run(init)
        sess.run([upd1, upd2])
        print(*sess.run([v1, v2]))

我并不总是得到相同的结果(我个人得到 1 12 1,尽管技术上 1 2 也是可能的)。例如,如果您想在 v1 更新后计算 v2 的新值,您可以执行以下操作:

import tensorflow as tf

v1 = tf.Variable(0)
v2 = tf.Variable(0)
upd1 = tf.assign(v1, v2 + 1)
upd2 = tf.assign(v2, upd1 + 1)
init = tf.global_variables_initializer()

这里新值v2是用upd1计算出来的,保证是更新后变量的值。所以这里 upd2 对赋值有隐式依赖,所以事情会按预期工作。

但是,如果您希望始终使用未更新的变量值计算 v1v2 的新值(也就是说,始终以 v1v1v21)?在这种情况下,您可以使用 tf.control_dependencies:

import tensorflow as tf

v1 = tf.Variable(0)
v2 = tf.Variable(0)
new_v1 = v2 + 1
new_v2 = v1 + 1
with tf.control_dependencies([new_v1, new_v2]):
    upd1 = tf.assign(v1, new_v1)
    upd2 = tf.assign(v2, new_v2)
init = tf.global_variables_initializer()

此处,在计算出 v1v2 的新值之前不会发生赋值操作,因此在这两种情况下它们的最终值将始终为 1