Following on from an earlier question, it seems tf.group
is indeed ignoring dependencies. Here's a simple stand-alone example (I have run it on Python 2.7 with TensorFlow 1.1):
import tensorflow as tf
from tensorflow.python.ops import control_flow_ops
xs = [tf.constant(x) for x in range(10)]
xs = [tf.Print(x, [x]) for x in xs]
dependency = None
dxs = []
for x in xs:
if dependency is None:
dependency = x
else:
dependency = control_flow_ops.with_dependencies([dependency], x)
dxs.append(dependency)
print_all_op = tf.group(*dxs)
with tf.Session() as session:
session.run(print_all_op)
Expected output:
2017-05-29 15:11:53.961221: I tensorflow/core/kernels/logging_ops.cc:79] [0]
2017-05-29 15:11:53.961236: I tensorflow/core/kernels/logging_ops.cc:79] [1]
2017-05-29 15:11:53.961255: I tensorflow/core/kernels/logging_ops.cc:79] [2]
2017-05-29 15:11:53.961237: I tensorflow/core/kernels/logging_ops.cc:79] [3]
2017-05-29 15:11:53.961262: I tensorflow/core/kernels/logging_ops.cc:79] [4]
2017-05-29 15:11:53.961263: I tensorflow/core/kernels/logging_ops.cc:79] [5]
2017-05-29 15:11:53.961268: I tensorflow/core/kernels/logging_ops.cc:79] [6]
2017-05-29 15:11:53.961272: I tensorflow/core/kernels/logging_ops.cc:79] [7]
2017-05-29 15:11:53.961274: I tensorflow/core/kernels/logging_ops.cc:79] [8]
2017-05-29 15:11:53.961221: I tensorflow/core/kernels/logging_ops.cc:79] [9]
Actual output (different each time the code is run):
2017-05-29 15:16:26.279655: I tensorflow/core/kernels/logging_ops.cc:79] [0]
2017-05-29 15:16:26.279655: I tensorflow/core/kernels/logging_ops.cc:79] [9]
2017-05-29 15:16:26.279697: I tensorflow/core/kernels/logging_ops.cc:79] [3]
2017-05-29 15:16:26.279660: I tensorflow/core/kernels/logging_ops.cc:79] [1]
2017-05-29 15:16:26.279711: I tensorflow/core/kernels/logging_ops.cc:79] [8]
2017-05-29 15:16:26.279713: I tensorflow/core/kernels/logging_ops.cc:79] [4]
2017-05-29 15:16:26.279723: I tensorflow/core/kernels/logging_ops.cc:79] [5]
2017-05-29 15:16:26.279663: I tensorflow/core/kernels/logging_ops.cc:79] [2]
2017-05-29 15:16:26.279724: I tensorflow/core/kernels/logging_ops.cc:79] [7]
2017-05-29 15:16:26.279728: I tensorflow/core/kernels/logging_ops.cc:79] [6]
There's nothing in the tf.group
documentation to indicate why dependencies are ignored.
Is there an alternative to tf.group
that does consider dependencies?
Switching to use tf.control_dependencies
instead of tensorflow.python.ops.control_flow_ops.with_dependencies
doesn't help:
import tensorflow as tf
xs = [tf.constant(x) for x in range(10)]
xs = [tf.Print(x, [x]) for x in xs]
dependency = None
dxs = []
for x in xs:
if dependency is None:
dependency = x
else:
with tf.control_dependencies([dependency]):
dependency = x
dxs.append(dependency)
print_all_op = tf.group(*dxs)
with tf.Session() as session:
session.run(print_all_op)
Answer
The issue you're encountering stems from how tf.group
behaves in TensorFlow. tf.group
creates an operation that groups a list of operations to be executed at the same time, but it does not enforce dependencies between them. As you've observed, dependencies are ignored because tf.group
doesn't respect the order in which operations are added to the group.
In TensorFlow, the control_dependencies
context manager, which is used to enforce the execution order of operations, doesn’t work directly inside tf.group
. This is because tf.group
doesn’t manage dependencies; it's only for grouping multiple operations together.
To achieve the desired sequential behavior (i.e., ensuring that each tf.Print
operation happens after its predecessor), you should use a combination of tf.control_dependencies
directly in the loop or outside of the tf.group
context. Here's how you can fix it:
Updated Solution
You can directly control the flow of dependencies using tf.control_dependencies
in a way that ensures each operation is executed after the previous one.
import tensorflow as tf
xs = [tf.constant(x) for x in range(10)]
xs = [tf.Print(x, [x]) for x in xs]
dependency = None
dxs = []
# Create dependencies using tf.control_dependencies in a proper sequence
for x in xs:
if dependency is None:
dependency = x
else:
# Ensure each operation depends on the previous one
with tf.control_dependencies([dependency]):
dependency = x
dxs.append(dependency)
# The final print operation (for all x) is not grouped here.
with tf.control_dependencies(dxs):
print_all_op = tf.no_op() # Placeholder for the operation
# Run the session
with tf.Session() as session:
session.run(print_all_op)
Key Modifications:
-
Control Dependencies: Instead of using
tf.group
, you should usetf.control_dependencies
directly to enforce sequential execution. You build up the list of dependencies withtf.control_dependencies([dependency])
for each step. -
tf.no_op()
: After all dependencies are defined, you can usetf.no_op()
as the operation that effectively runs after all dependent operations have been executed. This will ensure that the operations are run sequentially.
Explanation:
- Control Flow in TensorFlow:
tf.control_dependencies
ensures that operations are executed in the specified order. By wrapping eachdependency
operation intf.control_dependencies
, you're ensuring that each operation waits for the previous one to complete before starting. - Why
tf.group
doesn't work as expected:tf.group
runs operations in parallel, without respect to the dependencies between them. So when you want ordered execution, you must manage dependencies explicitly.
Output:
When you run this updated version, you should see the print statements in sequential order, as expected.
0
1
2
3
4
5
6
7
8
9
This approach ensures that the operations respect the execution order and should behave as expected, even when using multiple dependencies.