0

I am trying to write some custom TensorFlow functions in python (using tf.py_func) where I want to calculate both the results and the gradients in python. I'm using the gradient_override_map trick (for example from from https://gist.github.com/harpone/3453185b41d8d985356cbe5e57d67342 and How to make a custom activation function with only Python in Tensorflow?).

However, while the function in the forward direction gets a numpy array as an input, the function for the gradient gets Tensors. This is a problem, depending on when the function gets called, because there may not be a default session, and/or there may not be a feed_dict with all the required values yet (for example, in a tf.train optimizer).

How do I do a py_func where both the forward and backward functions get (and return) numpy arrays?

Sample code:

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

def sin_func(x):
    return np.sin(x)

def sin_grad_func(op, grad):
    x = op.inputs[0].eval()
    grad = grad.eval() # <--- this is what I'd like to avoid
    output_grad = np.cos(x) * grad
    return tf.convert_to_tensor(output_grad)

def py_func(func, inp, Tout, stateful=True, name=None, grad_func=None):
    grad_name = 'PyFuncGrad_' + str(np.random.randint(0, 1E+8))
    tf.RegisterGradient(grad_name)(grad_func)
    g = tf.get_default_graph()
    with g.gradient_override_map({"PyFunc": grad_name}):
        return tf.py_func(func, inp, Tout, stateful=stateful, name=name)

with tf.Session() as sess:
    np_x = np.linspace(0, np.pi, num=1000, dtype=np.float32)
    x = tf.constant(np_x)
    y = py_func(sin_func,
                [x],
                [tf.float32],
                name='np_sin',
                grad_func=sin_grad_func)
    y = y[0]
    gr = tf.gradients(y, [x])
    tf.global_variables_initializer().run()
    plt.plot(y.eval())
    plt.plot(gr[0].eval())
Community
  • 1
  • 1
Alex I
  • 19,689
  • 9
  • 86
  • 158

1 Answers1

1

If you want to include arbitrary Python code in your gradient function, the easiest solution is to create another tf.py_func() inside sin_grad_func():

def sin_grad_func_impl(x, grad):
    return np.cos(x) * grad

def sin_grad_func(op, grad):
    return tf.py_func(sin_grad_func_impl, [x, grad], grad.dtype)
mrry
  • 125,488
  • 26
  • 399
  • 400