New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Force tensor evaluation inside while-loop, scan and others. #13616
Comments
@skye Can you comment on this? |
Let me make sure I understand what you're requesting: you would like a tensor computed from variables to have a new updated value every time it's executed in a while loop. This is a reasonable expectation. However, I don't think we'll change this behavior. Right now, only tensors defined inside the loop will be evaluated every loop iteration. All tensors defined outside a loop will be evaluated exactly once. So you would have to write something like: import tensorflow as tf
import numpy as np
def cond(i, _x):
return tf.less(i, 10)
def gen_body(v):
def body(i, x):
x = tf.Print(x, [x], "x: ")
v_assign = v.assign(x + 1)
v_assign = tf.Print(v_assign, [v_assign], "v_assign: ")
with tf.control_dependencies([v_assign]):
sq = tf.square(v) # <---- put 'sq' definition inside loop
sq = tf.Print(sq, [sq], "sq: ")
sq_neg = tf.negative(sq)
sq_neg = tf.Print(sq_neg, [i, sq_neg], message='i and sq_neg:')
return tf.add(i, 1), sq_neg
return body
sess = tf.InteractiveSession()
v = tf.Variable(2)
l = tf.while_loop(cond, gen_body(v), (1, v))
sess.run(tf.global_variables_initializer())
sess.run(l) Note that I moved the Does this help? |
I believe if you want a variable to be evaluated at each value of the loop, you should access it using variable.ref |
or perhaps if you use a ResourceVariable (get_variable(..., use_resource=True)) it may do the right thing. you may need to also set parallel_iterations=1, but probably not. |
This is an optimization that we turned on because you don't want a Variable sitting on a parameter server elsewhere in the datacenter to be accessed at each iteration of an RNN. you want to access it only once and cache it. in this case you have an assign inside the while_loop and converting a Variable to a Tensor doesn't look to see if an assign has been performed just prior to this, to know it must refresh the value. @mrry should we reset the caching identity tensor of a Variable after each assign? |
@skye also, wdyt? |
You're more familiar with these semantics than I am, but changing this might break (or at least regress) existing models, no? |
Not sure. Depends how many users perform a tf.assign/tf.assign_{add,sub} inside a while_loop (including applying an optimiser) and read the new value inside the while loop -- and what behavior they expect; and may be getting silent errors or models not converging because of this issue. If this already just "works" with ResourceVariable, then that's a sign that the Variable semantics are just broken. |
I think this gets tricky because (from the perspective of a
+1. I'd strongly encourage people to switch to ResourceVariable in new code, and it's more compatible with other recent features like |
Thank you for your responses. Here is some clarifications to the task:
edited @skye, so |
I can be wrong, but in example below I never got different values for assign operation In [91]: tf.reset_default_graph()
...: a = tf.get_variable('a', initializer=2, use_resource=True)
...: c = a + 1
...: b = a.assign(a * 10)
...: with tf.control_dependencies([b]):
...: c = c # <<<<<<<<<<<<<<<
...: # here should be `c.eval_tensor()` or `c.read_tensor()`,
...: # which constructs an operation being equivalent
...: # to read_value() for variables.
...: # ***Edited***: tf.identity() doesn't work here!
...: sess = tf.InteractiveSession()
...: sess.run(tf.global_variables_initializer())
...: sess.run([a, b, c])
...
Out[91]: [20, 20, 3] # <<< 3
...
Out[92]: [20, 20, 21] # <<< 21 |
@skye, @mrry, @ebrevdo I'm sorry for bombarding you with multiple messages, here is a proof that assigning inside loop is not a problem: import tensorflow as tf
import numpy as np
def cond(i, _x):
return tf.less(i, 10000)
def gen_body(x):
def body(i, xa_prev):
xa = x.assign(xa_prev + 1)
with tf.control_dependencies([xa]):
with tf.control_dependencies([tf.assert_equal(x, xa)]):
i = tf.add(i, 1)
return i, xa
return body
tf.reset_default_graph()
i = tf.get_variable('i', initializer=0, use_resource=True)
v = tf.get_variable('v', initializer=0, use_resource=True)
sess = tf.InteractiveSession()
loop = tf.while_loop(cond, gen_body(v), [i, v])
sess.run(tf.global_variables_initializer())
sess.run([loop])
Out[203]: [(10000, 10000)] The issue is that there is no way to recompute tensors which depend on it and were constructed outside of the |
I faced the same problem,
Although this problem is trivial, and can be done without using |
@dchatterjee172, I think you have different issue. @ebrevdo what is |
After playing with this for a bit, I reached two conclusions:
Here's code that does what I think you want it to do: def cond(i, _x, _sq):
return i < 5
def gen_body(v):
def body(i, x, sq):
x = tf.Print(x, [x, sq], "x and sq: ")
with tf.control_dependencies([v.assign(x + 1)]):
v_assign = v._ref()
v_assign = tf.Print(v_assign, [v_assign], "v_assign: ")
with tf.control_dependencies([v_assign]):
sq_neg = tf.negative(sq)
sq_neg = tf.Print(sq_neg, [i, sq_neg], message='i and sq_neg:')
return tf.add(i, 1), sq_neg, sq
return body
sess = tf.InteractiveSession()
i = tf.get_variable("i", initializer=0)
v = tf.get_variable("v", initializer=2)
sq = tf.square(v)
l = tf.while_loop(cond, gen_body(v), (i, v, sq))
sess.run(tf.global_variables_initializer())
sess.run((l, v)) @alextp any suggestions on how one would force accessing the updated value of a |
@alextp perhaps a stateful read operator is in order? |
It is possible to force a resource variable read to happen after an assignment, and indeed it will happen. @ebrevdo , why did resource variables not work? Do you have an example with them? (I do not fully understand why we're using variables at all here instead of the regular while loop variable mechanism) |
@alextp, @ebrevdo Hello, your snippet doesn't do what I need, I simplified it a bit below. Let me explain what it does. Here def cond(i, _x_prev, _f_prev):
return i < 3
def gen_body(x, f):
def body(i, _x_prev, _f_prev):
x_assign = x.assign(x + 1)
# with tf.control_dependencies([x.assign(x + 1)]):
# x_assign = x._ref()
with tf.control_dependencies([x_assign]):
f_neg = tf.negative(f)
i = tf.add(i, 1)
i = tf.Print(i, [i], message='>>> Iteration ')
i = tf.Print(i, [x], message='x = ')
i = tf.Print(i, [x_assign], message='x_assign = ')
i = tf.Print(i, [f], message='f = ')
i = tf.Print(i, [f_neg], message='f_neg = ')
return i, x_assign, f_neg
return body
tf.reset_default_graph()
sess = tf.InteractiveSession()
i = tf.get_variable("i", initializer=0)
v = tf.get_variable("v", initializer=0)
func_v = tf.square(v)
l = tf.while_loop(cond, gen_body(v, func_v), (i, v, func_v))
sess.run(tf.global_variables_initializer())
sess.run((l, v)) Output. x incremented successfully from 0 to 3, but f remains non-updated! I need updated f.
In [259]: tf.__version__
Out[259]: '1.2.1' I propose to add a method to the tensor structure which will be equivalent to |
The task is update the variable and the dependant on it function which are defined outside of |
Right. If you want the function to be computed inside the while loop you need to call it inside the while loop. Something like def cond(i, _x_prev, _f_prev):
return i < 3
def gen_body(x, f):
def body(i, _x_prev, _f_prev):
x_assign = x.assign(x + 1)
# with tf.control_dependencies([x.assign(x + 1)]):
# x_assign = x._ref()
with tf.control_dependencies([x_assign]):
f_neg = tf.negative(f(x_assign))
i = tf.add(i, 1)
i = tf.Print(i, [i], message='>>> Iteration ')
i = tf.Print(i, [x], message='x = ')
i = tf.Print(i, [f(x_assign)], message='x_assign = ')
i = tf.Print(i, [f(v)], message='f = ')
i = tf.Print(i, [f_neg], message='f_neg = ')
return i, x_assign, f_neg
return body
tf.reset_default_graph()
sess = tf.InteractiveSession()
i = tf.get_variable("i", initializer=0)
v = tf.get_variable("v", initializer=0)
func_v = lambda v: tf.square(v)
l = tf.while_loop(cond, gen_body(v, func_v), (i, v, func_v(v)))
sess.run(tf.global_variables_initializer())
sess.run((l, v)) When you define func_v as a tensor outside the loop it has a fixed value which will stay fixed forever. If you want it to be recomputed you need to actually recompute it. |
@alextp, that's right. In fact, it was a feature request - to be able to recompute the tensor and introduce property which generates this operation. I'm sorry if it wasn't clear from previous messages That would be super useful in The solution which you proposed doesn't work for me, as I do not have control on input tensors How hard is it to implement re-computation operation for tensor? |
With the current graph semantics it's essentially impossible.
…On Fri, Oct 13, 2017 at 7:51 AM, Artem Artemev ***@***.***> wrote:
@alextp <https://github.com/alextp>, that's right. In fact, it was a
feature request - to be able to recompute the tensor and introduce property
which generates this operation. I'm sorry if it wasn't clear from previous
messages That would be super useful in while_loop-like flow statements.
The solution which you proposed doesn't work for me, as I do have control
on input tensors x and f, and I do not know what's inside f.
How hard is it to implement re-computation operation for tensor?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#13616 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAATxcKI_k6pTejahsF7TvgFWFYvTgyCks5sr3j5gaJpZM4P0qPF>
.
--
- Alex
|
@alextp. oh, that's bad. Okay, If I manage to pass f(x) as python function, hence at each iteration I construct new tensor |
No, the size of the graph is constant even if you call f(x) in each
iteration, because tensorflow's while loop is symbolic and only creates the
body graph once.
…On Fri, Oct 13, 2017 at 8:43 AM, Artem Artemev ***@***.***> wrote:
@alextp <https://github.com/alextp>. oh, that's bad. Okay, I managed to
pass f(x) as python function and at each iteration I construct new tensor y
= f(x), does it mean that the size of the graph will grow linearly with
number of iterations? Can an unwise usage of while_loop lead to the graph
"overflow", taking into account that graph size is limited?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#13616 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAATxXr6HKksKduSmuWFS3-emCehda_tks5sr4UrgaJpZM4P0qPF>
.
--
- Alex
|
@alextp, last question: If I create c++ operation which will accept |
You need to explicitly include this C++ operation in the graph in all the
places where you'd want it to run. There's no way to trigger it running
only when x is modified.
…On Fri, Oct 13, 2017 at 1:35 PM, Artem Artemev ***@***.***> wrote:
@alextp <https://github.com/alextp>, last question: If I create c++
operation, which will accept x variable and f tensor, will I be able to
manipulate execution of f depending on changes in x. In other words, will
I be able to execute f tensor each time when I change x. Thanks!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#13616 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAATxUGGup22k2UCOAs2An65SDeEfn6fks5sr8mYgaJpZM4P0qPF>
.
--
- Alex
|
Thanks all for your responses! |
Reevaluation does not work outside of a loop; a tensor is only evaluated a
single time within a single call to session.run.
Try to have more than one update per call to session.run and you'll see
things breaking.
(your example also relies on the lack of a memory model for legacy tf
variables but this is not relevant)
…On Sat, Nov 3, 2018 at 11:32 AM Jonas Eschle ***@***.***> wrote:
A year later, did anything change on that? @alextp
<https://github.com/alextp>, is it still impossible? I find that it
somehow works and am confused about why it does/does not (I write here as
the previous discussion is basically the same thing as I try to accomplish):
Basically, the re-evaluation works *outside* of a loop, that's confusing
for me
Pseudo-code
x = <Tensor depending on var>
var_assign = tf.assign(var, value)
with tf.control_dependencies([var_assign])
sess.run(x) # prints with the old value, of course
but adding an identity before
var_assign = tf.assign(var, value)
with tf.control_dependencies([var_assign])
x_2 = tf.identity(x)
sess.run(x_2) # prints with the new value!
So in the second case, sess.run reevaluates the whole Tensor X. But this
does not work in a while loop (of course, we cannot have a sess.run). So
why does x_2 get's re-evaluated here but not in a while-loop? I did not
expect this way to work
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#13616 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAATxfWbCkETnuov7i73C5uTctP_vaXXks5ureFJgaJpZM4P0qPF>
.
--
- Alex
|
Ah, I see (so it's actually independent of a loop): one sess.run -> zero (if can be cached) or one evaluation of a tensor. Thanks, @alextp! |
Hello everyone,
I had big plans for
tf.while_loop
until I discovered that it is impossible to re-evaluate tensor inside it. Let's dive into the the issue and potential useful feature:I created
v
variable and tensorsq
which equals tov^2
. In fact, we don't have control over them, they are our input asx
andy
and we know thaty
depends onx
. I would like to assign new value inside TensorFlow loop tox
(equavalent tov
at example) and evaluate freshy
(sq
inside example) at each iteration of the loop. Meanwhile we can do other evaluations insidewhile_loop
, but most important is that I need to updatex
and get updatedy
. Currently, assigning operation doesn't propagate updates down to the dependant nodes and it shouldn't, but when someone calls tensor depending on value which were updated via assign insidewhile_loop
, I suppose the tensor node must detect this change and evaluate new tensor value again.Thanks!
The text was updated successfully, but these errors were encountered: