Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can cond support TF ops with side effects?

Tags:

tensorflow

The (source code) documentation for tf.cond is unclear on whether the functions to be performed when the predicate is evaluated can have side effects or not. I've done some tests but I'm getting conflicting results. For example the code below does not work:

import tensorflow as tf
from tensorflow.python.ops import control_flow_ops

pred = tf.placeholder(tf.bool, [])
count = tf.Variable(0)
adder = count.assign_add(1)
subtractor = count.assign_sub(2)

my_op = control_flow_ops.cond(pred, lambda: adder, lambda: subtractor)

sess = tf.InteractiveSession()
tf.initialize_all_variables().run()

my_op.eval(feed_dict={pred: True})
count.eval() # returns -1

my_op.eval(feed_dict={pred: False})
count.eval() # returns -2

I.e. no matter what value the predicate evaluates to, both functions are getting run, and so the net result is a subtraction of 1. On the other hand, this code snippet does work, where the only difference is that I add new ops to the graph every time my_op is called:

pred = tf.placeholder(tf.bool, [])
count = tf.Variable(0)

my_op = control_flow_ops.cond(pred, lambda:count.assign_add(1), lambda:count.assign_sub(2))

sess = tf.InteractiveSession()
tf.initialize_all_variables().run()

my_op.eval(feed_dict={pred: False})
count.eval() # returns -2

my_op.eval(feed_dict={pred: True})
count.eval() # returns -1

Not sure why creating new ops every time works while the other case doesn't, but I'd obviously rather not be adding nodes as the graph will eventually become too big.

like image 453
Mohammed AlQuraishi Avatar asked Jan 21 '16 18:01

Mohammed AlQuraishi


1 Answers

The second case works because you have added the ops within the cond: this causes them to conditionally execute.

The first case it is analogous to saying:

adder = (count += 1)
subtractor = (count -= 2)
if (cond) { adder } else { subtractor }

Since adder and subtractor are outside the conditional, they are always executed.

The second case is more like saying

if (cond) { adder = (count += 1) } else { subtractor = (count -= 2) }

which in this case does what you expected.

We realize that the interaction between side effects and (somewhat) lazy evaluation is confusing, and we have a medium-term goal to make things more uniform. But the important thing to understand for now is that we do not do true lazy evaluation: the conditional acquires a dependency on every quantity defined outside the conditional that is used within either branch.

like image 67
Michael Isard Avatar answered Nov 12 '22 12:11

Michael Isard