I was trying to understand how Session.run() works in Tensorflow flow. I know that Session.run() runs the sub-graph specified by the "fetch" argument we give it. Since depending on which part of the sub-graph is executed first we might get different results, I was trying to see if that is really the case. Suppose we compare the output of this code:
import tensorflow as tf
x = tf.Variable(42)
assign1 = tf.assign(x, 13)
assign2 = tf.assign(x, 14)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
val1, val2, val3 = sess.run([x, assign1, assign2])
print(val1, val2, val3)
with this code:
import tensorflow as tf
x = tf.Variable(42)
assign2 = tf.assign(x, 14)
assign1 = tf.assign(x, 13)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
val1, val2, val3 = sess.run([x, assign1, assign2])
print(val1, val2, val3)
(The only change is assigning of 14 comes first in the second code). I was expecting to see that the first code produces output 14, 14, 14 while the second one produced 13, 13, 13.
However, the result is that the first produced 13, 13, 13 while the second on produced 14, 14, 14.
Why does this happen?
Update: Following Chosen Answer:
I don't quite get what is independent and what is not. For example, in the following code:
x = tf.Variable([1, 2, 3, 4, 5])
def foo():
tmp_list = []
assign = tf.assign(x[4], 100)
for i in range(0, 5):
tmp_list.append(x[i])
return tmp_list
z = foo()
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
val1, val2 = sess.run([z, assign])
print(val1, val2)
In my Session.run() I have both z and assign. To get z, I need foo(). When foo() is ran, assign = tf.assign(x[4], 100)
is also ran. However, the output is [1, 2, 3, 4, 5] 13
and I have no idea where the 13 comes from.
Answer
The behavior you're observing is a result of how TensorFlow's computational graph and Session.run()
work. Let's break this down step by step:
First Example:
Why does sess.run([x, assign1, assign2])
produce 13, 13, 13
in the first case and 14, 14, 14
in the second?
-
Order of Execution in TensorFlow:
- TensorFlow determines the order of operations in the graph based on dependencies, not the order in which you define operations.
- When
Session.run()
is called, it evaluates all requested tensors/operations (x
,assign1
, andassign2
) and ensures dependencies are resolved for each one.
-
Independent Variables:
- If the requested tensors/operations (like
assign1
andassign2
) have no dependency on each other, TensorFlow may execute them in any order.
- If the requested tensors/operations (like
-
What Happens in Your Code:
-
First Code:
assign1 = tf.assign(x, 13) assign2 = tf.assign(x, 14) val1, val2, val3 = sess.run([x, assign1, assign2])
assign1
setsx
to13
.assign2
setsx
to14
.x
is fetched first, and sinceassign2
hasn't run yet, it still holds the initial value13
set byassign1
.
-
Second Code:
assign2 = tf.assign(x, 14) assign1 = tf.assign(x, 13) val1, val2, val3 = sess.run([x, assign1, assign2])
- In this case,
assign2
(which setsx
to14
) runs beforeassign1
, sox
reflects the value14
when fetched.
- In this case,
-
The difference arises from which assignment operation TensorFlow schedules first when resolving dependencies.
-
Second Example:
Why does the output from the second code produce [1, 2, 3, 4, 5]
for z
and 13
for assign
?
-
Graph Dependencies:
assign = tf.assign(x[4], 100) tmp_list.append(x[i])
assign
modifies only the elementx[4]
. However, the computation oftmp_list
(viafoo
) only involves reading the values ofx
. There is no direct dependency betweentmp_list
andassign
, so TensorFlow can evaluate them independently.
-
Order of Execution:
- When you call
sess.run([z, assign])
:z
corresponds tofoo()
, which involves reading the current values ofx
. It does not depend onassign
.- Since TensorFlow evaluates
z
beforeassign
,z
is calculated from the initial values ofx
, which are[1, 2, 3, 4, 5]
.
- When you call
-
Why the
13
?- This might be a typo or inconsistency in the output. Assuming the operation
assign = tf.assign(x[4], 100)
is correct, the output forassign
should logically be100
. Ensure you're running the exact code without unintended modifications.
- This might be a typo or inconsistency in the output. Assuming the operation
Key Takeaways:
-
Order of Execution:
- TensorFlow schedules operations based on dependencies, not the order of definition.
- If two operations are independent, their execution order is not guaranteed and may vary.
-
Session.run
Fetch Behavior:- When fetching multiple tensors/operations, their evaluation is resolved based on the graph structure.
- Fetching a variable (e.g.,
x
) happens before any independent assignments.
-
Dependent vs. Independent Operations:
- If you want to enforce a specific order, introduce explicit dependencies using
tf.control_dependencies()
.
- If you want to enforce a specific order, introduce explicit dependencies using
Correct Example with Dependency Control:
If you want to ensure assign
happens before evaluating z
:
x = tf.Variable([1, 2, 3, 4, 5])
def foo():
tmp_list = []
for i in range(5):
tmp_list.append(x[i])
return tmp_list
assign = tf.assign(x[4], 100)
z = foo()
with tf.control_dependencies([assign]):
z_with_assign = tf.identity(z)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
val1 = sess.run(z_with_assign)
print(val1) # Ensures z reflects the assignment
Output: [1, 2, 3, 4, 100]
.
Let me know if further clarification is needed!