Skip to content

Can't declare tf.Variable in @tf.function decorated function #26812

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

Closed
galeone opened this issue Mar 17, 2019 · 12 comments
Closed

Can't declare tf.Variable in @tf.function decorated function #26812

galeone opened this issue Mar 17, 2019 · 12 comments
Assignees
Labels
comp:ops OPs related issues TF 2.0 Issues relating to TensorFlow 2.0 type:bug Bug

Comments

@galeone
Copy link

galeone commented Mar 17, 2019

System information

  • OS Platform and Distribution (e.g., Linux Ubuntu 16.04):Archlinux
  • TensorFlow installed from (source or binary): binary
  • TensorFlow version (use command below): 2.0.0-dev20190317
  • Python version: 3.6

Describe the current behavior

A function that correctly works in eager execution can't be decorated with @tf.function if declares a tf.Variable in the function body.

The error message, reported below, is misleading since it talks about a non-first invocation when the function is invoked only once.

ValueError: tf.function-decorated function tried to create variables on non-first call.

Describe the expected behavior

Calling a function decorated with the @tf.function should produce the same output as the same function without the decoration.

Code to reproduce the issue

import tensorflow as tf

import tensorflow as tf

@tf.function
def f():
    a = tf.constant([[10, 10], [11., 1.]])
    x = tf.constant([[1., 0.], [0., 1.]])
    b = tf.Variable(12.)
    y = tf.matmul(a, x) + b
    return y

print(f())
@zakizhou
Copy link

zakizhou commented Mar 18, 2019

see this

and rewrite your code to

import tensorflow as tf

b = None


@tf.function
def f():
    a = tf.constant([[10, 10], [11., 1.]])
    x = tf.constant([[1., 0.], [0., 1.]])
    global b
    if b is None:
        b = tf.Variable(12.)
    y = tf.matmul(a, x) + b
    return y

print(f())

or

class F(object):
    def __init__(self):
        self.b = None

    @tf.function
    def __call__(self):
        a = tf.constant([[10, 10], [11., 1.]])
        x = tf.constant([[1., 0.], [0., 1.]])
        if self.b is None:
            self.b = tf.Variable(12.)
        y = tf.matmul(a, x) + self.b
        return y


f = F()
print(f())

@galeone
Copy link
Author

galeone commented Mar 18, 2019

HI @zakizhou ! I'm aware that defining a function that declares a variable inside creates a status and it has to be handled differently.

As you suggested an alternative I to wrap the function in a class and make the variable a private attribute, or declaring the variable as global and check if it is None.

However, as stated in the RFC:

State (like tf.Variable objects) are only created the first time the function f is called.

Thus, when I invoke f() for the first time, I should have the graph definition with a tf.Variable object created and executed once. And it should work according to what is written in the RFC.

Instead, what happens is that even though I call the function only once, and thus this is the first call, it seems like tf.function is evaluating the function twice (like calling f(), f()), making what is stated in the RFC false.

@jvishnuvardhan jvishnuvardhan self-assigned this Mar 19, 2019
@jvishnuvardhan jvishnuvardhan added TF 2.0 Issues relating to TensorFlow 2.0 comp:ops OPs related issues type:bug Bug labels Mar 19, 2019
@jvishnuvardhan jvishnuvardhan added the stat:awaiting tensorflower Status - Awaiting response from tensorflower label Mar 19, 2019
@alextp
Copy link
Contributor

alextp commented Mar 19, 2019

tf.function may evaluate your python function more than once.

What the RFC states instead is that you are allowed to create variables as long as variable creation only happens the first time your python function is evaluated.

@alextp alextp closed this as completed Mar 19, 2019
@alextp
Copy link
Contributor

alextp commented Mar 19, 2019

And the reason for this is that if you write python code which unconditionally creates variables then I can't tell whether you mean to create a new variable every time the python function is called (eager behavior) or reuse the existing variables (what happens in graph tf 1.x) so an error felt safer.

Sorry, something went wrong.

@jvishnuvardhan jvishnuvardhan removed the stat:awaiting tensorflower Status - Awaiting response from tensorflower label Mar 19, 2019
@galeone
Copy link
Author

galeone commented Mar 20, 2019

Thank you for the explanation @alextp - now everything is clearer. The RFC just describes the first call, but there is no guarantee that tf.function won't call the function more than once while converting it as a graph and for this reason, the developer should take care of handling the variables creation; thus If I need to reuse or not a variable it makes no difference, I have to take care of its status manually.

Sorry, something went wrong.

@alextp
Copy link
Contributor

alextp commented Mar 20, 2019 via email

Sorry, something went wrong.

@ericpts
Copy link

ericpts commented Mar 22, 2019

I have an exact issue like this, but with using different optimizers:

@tf.function
def apply_gradients_once(optimizer, grads, vars):
    optimizer.apply_gradients(zip(
        grads, vars))


def apply_gradients(self, use_fast, grads_per_model):
    for i in range(self.nmodels):
        grads = grads_per_model[i]
        vars = self.model[i].get_trainable_variables()

        if use_fast[i]:
            optimizer = self.fast_optimizer
            apply_gradients_once(optimizer, grads, vars)
        else:
            optimizer = self.slow_optimizer
            apply_gradients_once(optimizer, grads, vars)

On a first epoch, I have use_fast = [True, False], and then on a second one I have
use_fast = [False, True].

With this, I have a huge error trace on the apply_gradients_once call, ending with ValueError: tf.function-decorated function tried to create variables on non-first call..

From my understanding, since the function is always called with different arguments, and there are no external dependencies, this error should not be happening.

@alextp
Copy link
Contributor

alextp commented Mar 25, 2019

@ericpts this is an interesting use case I hadn't thought of.

As a workaround I recommend you use two instances of tf.function here.

Let's open another issue to discuss this?

@alextp
Copy link
Contributor

alextp commented Mar 25, 2019 via email

@praveen-14
Copy link

praveen-14 commented Nov 22, 2019

Hello, Is there a workaround for a situation like this. Here I need to create several w, b variables using add_layer method. But I get the variable reused error.

def add_layer(input, c_kdimension, c_kstrides):
w = tf.get_variable(name="w", shape=c_kdimension, initializer = tf.contrib.layers.xavier_initializer()
b = tf.get_variable(name="b", shape=c_kdimension[-1], initializer=tf.contrib.layers.xavier_initializer())
return tf.nn.conv2d(input, w, strides=c_kstrides, padding="SAME")

@tf.function
def inference(input):
model = Model()
layer_1 = add_layer(input, c_kdimension=[3, 3, 1, 32], c_kstrides=[1, 1, 1, 1])
layer_2 = add_layer(layer_1, c_kdimension=[3, 3, 32, 32], c_kstrides=[1, 1, 1, 1])
return layer2

@cis-apoorv
Copy link

Hello @galeone ,
As you are using TensorFlow 2.0 you must add
tf.config.experimental_run_functions_eagerly(True)
after import and then run the code.

@jonas-eschle
Copy link
Contributor

Hello @galeone ,
As you are using TensorFlow 2.0 you must add
tf.config.experimental_run_functions_eagerly(True)
after import and then run the code.

Sorry, this is wrong, do not use it. This disables the graph construction and runs everything eagerly, like numpy. This can be wanted for debugging or in certain cases, but it is surely not the workaround.

@praveen-14 read again above about creating them as class variables or in the global scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comp:ops OPs related issues TF 2.0 Issues relating to TensorFlow 2.0 type:bug Bug
Projects
None yet
Development

No branches or pull requests

8 participants