Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tensorflow 2.0: minimize a simple function

    import tensorflow as tf
    import numpy as np

    x = tf.Variable(2, name='x', trainable=True, dtype=tf.float32)
    with tf.GradientTape() as t:
        t.watch(x)
        log_x = tf.math.log(x)
        y = tf.math.square(log_x)

    opt = tf.optimizers.Adam(0.5)
    # train = opt.minimize(lambda: y, var_list=[x]) # FAILS

    @tf.function
    def f(x):
        log_x = tf.math.log(x)
        y = tf.math.square(log_x)
        return y

    yy = f(x)
    train = opt.minimize(lambda: yy, var_list=[x]) # ALSO FAILS

Yields ValueError:

No gradients provided for any variable: ['x:0'].

This looks like the examples they partially give. I'm not sure if this is a bug with eager or 2.0 or something I am doing wrong.

UPDATE:

Pasted an embellished version of the solution below as there were some issues and interesting notes.

    import numpy as np
    import tensorflow as tf

    x = tf.Variable(3, name='x', trainable=True, dtype=tf.float32)
    with tf.GradientTape(persistent=True) as t:
        # log_x = tf.math.log(x)
        # y = tf.math.square(log_x)
        y = (x - 1) ** 2

    opt = tf.optimizers.Adam(learning_rate=0.001)

    def get_gradient_wrong(x0):
        # this does not work, it does not actually update the value of x
        x.assign(x0)
        return t.gradient(y, [x])

    def get_gradient(x0):
        # this works
        x.assign(x0)
        with tf.GradientTape(persistent=True) as t:
            y = (x - 1) ** 2
        return t.gradient(y, [x])

    #### Option 1
    def a(x0, tol=1e-8, max_iter=10000):
        # does not appear to work properly
        x.assign(x0)
        err = np.Inf # step error (banach), not actual erro
        i = 0
        while err > tol:
            x0 = x.numpy()
            # IMPORTANT: WITHOUT THIS INSIDE THE LOOP THE GRADIENTS DO NOT UPDATE
            with tf.GradientTape(persistent=True) as t:
                y = (x - 1) ** 2
            gradients = t.gradient(y, [x])
            l = opt.apply_gradients(zip(gradients, [x]))
            err = np.abs(x.numpy() - x0)
            print(err, x.numpy(), gradients[0].numpy())
            i += 1
            if i > max_iter:
                print(f'stopping at max_iter={max_iter}')
                return x.numpy()
        print(f'stopping at err={err}<{tol}')
        return x.numpy()

    #### Option 2
    def b(x0, tol=1e-8, max_iter=10000):
        x.assign(x0)
        # To use minimize you have to define your loss computation as a funcction
        def compute_loss():
            log_x = tf.math.log(x)
            y = tf.math.square(log_x)
            return y
        err = np.Inf # step error (banach), not actual erro
        i = 0
        while err > tol:
            x0 = x.numpy()
            train = opt.minimize(compute_loss, var_list=[x])
            err = np.abs(x.numpy() - x0)
            print(err, x.numpy())
            i += 1
            if i > max_iter:
                print(f'stopping at max_iter={max_iter}')
                return x.numpy()
        print(f'stopping at err={err}<{tol}')
        return x.numpy()
like image 709
mathtick Avatar asked Apr 06 '19 19:04

mathtick


2 Answers

You're doing something wrong. You have two options:

Use the tape to compute the gradients

In that case, you have to use the optimizer only to apply the update rule.

import tensorflow as tf

x = tf.Variable(2, name='x', trainable=True, dtype=tf.float32)
with tf.GradientTape() as t:
    # no need to watch a variable:
    # trainable variables are always watched
    log_x = tf.math.log(x)
    y = tf.math.square(log_x)

#### Option 1

# Is the tape that computes the gradients!
trainable_variables = [x]
gradients = t.gradient(y, trainable_variables)
# The optimize applies the update, using the variables
# and the optimizer update rule
opt.apply_gradients(zip(gradients, trainable_variables))

Define the loss as a function

In this case, you can use the optimizer .minimize method, that will create the tape to compute the gradient + update the parameters for you

#### Option 2
# To use minimize you have to define your loss computation as a funcction
def compute_loss():
    log_x = tf.math.log(x)
    y = tf.math.square(log_x)
    return y
train = opt.minimize(compute_loss, var_list=trainable_variables)
like image 157
nessuno Avatar answered Nov 04 '22 01:11

nessuno


I've up-voted the accepted solution above as well, but I still needed some time to get an end to end solution running, so let me share that with you as well, the code is solving some simple mathematical puzzle:

f(x)=x-(6/7)*x-1/7
g(x)=f(f(f(f(x))))
Find x such that g(x) == 0

!pip install setuptools --upgrade
!pip install -q tensorflow==2.0.0-beta1

import tensorflow as tf
import numpy as np
tf.__version__ #=> '2.0.0-beta1'

   


@tf.function
def f(x):
    return x-(6/7)*x-1/7

print(tf.autograph.to_code(step.python_function))

x = tf.Variable(0, trainable=True, dtype=tf.float64)
y = tf.constant([0], dtype=tf.float64)

@tf.function
def g(x):
    return f(f(f(f(x))))

print(tf.autograph.to_code(compute.python_function))

# Create a list of variables which needs to be adjusted during the training process, in this simple case it is only x
variables = [x]
    
# Instantiate a Gradient Decent Optimizer variant, it this case learning rate and specific type of optimizer doesn't matter too much
optimizer = tf.optimizers.Adam(0.5)
    
# We need to somehow specify the error between the actual value of the evaluated function in contrast to the target (which is zero)
loss_object = tf.keras.losses.MeanAbsoluteError()
    
# Since we are not running inside a TensorFlow execution graph anymore we need some means of keeping state of the gradient during training
# so a persistent GradientTape is your friend and the way to go in TensorFlow 2.0
with tf.GradientTape(persistent=True) as tape:
        
    #Let's train for some iterations
    for i in range(1000):
            
        # given the actual value of X (which we now continueously adjust in order to find the root of the equation)
        y_pred = g(x)
            
        # At this point we are actually setting the whole equation to zero. Since X is variable, the goal is to find an X which satisfies the condition
        # (that the whole equations becomes zero). We are doing this by defining a loss which becomes zero if y_pred approximates y. Or in other words,
        # since y is zero, the loss becomes zero if y_pred approximates zero.
        loss = loss_object(y,y_pred)
            
        # Now the magic happens. Loss basically represents the error surface and is only dependent on X. So now let's compute the first derivative and
        # see in which direction we need to adjust X in order to minimize the error and getting a value (output of the nested equations) closer to zero
        grads = tape.gradient(loss, variables)
            
        # Once we've found this magic number magically, let's update the value of X based on this magic number in order to perform better on the next
        # iteration
        optimizer.apply_gradients(zip(grads, variables))
           
        # And now it's pretty cool, we can just print the current error (loss) and the actual value of X in each iteration. At the end of the training,
        # we've found the optima wich a loss / error close to zero and a value of X close to 400 where 400 is the correct solution.
        # Small deviations from the true solutions stem from numeric errors
        print('Loss: {}, X: {}'.format(loss.numpy(), x.numpy()))

like image 27
Romeo Kienzler Avatar answered Nov 04 '22 02:11

Romeo Kienzler