I'm trying to set custom gradients using tf.py_func
and tf.RegisterGradient
. Specifically, I'm trying to take a gradient of an eigen value w.r.t its Laplacian. I got the basic thing working, where my python
function returns one value, which is the eigen value. But for the gradient to work, I also need to return the eigen vector. But trying to return 2 values results in pyfunc_1 returns 2 values, but expects to see 1 values
. How can I solve this error?
Here's the full code of my custom gradient.
import numpy as np
import networkx as nx
from scipy import sparse
import tensorflow as tf
from tensorflow.python.framework import ops
# python function to calculate the second eigen value
def calc_second_eigval(X):
G = nx.from_numpy_matrix(X)
degree_dict = nx.degree(G)
degree_list = [x[1] for x in degree_dict]
lap_matrix = sparse.diags(degree_list, 0)-nx.adjacency_matrix(G)
eigval, eigvec = sparse.linalg.eigsh(lap_matrix, 2, sigma=0, which='LM')
return float(eigval[0]), eigvec[:,0]
# define custom py_func which takes also a grad op as argument:
def py_func(func, inp, Tout, stateful=True, name=None, grad=None):
# Need to generate a unique name to avoid duplicates:
rnd_name = 'PyFuncGrad' + str(np.random.randint(0, 1E+8))
tf.RegisterGradient(rnd_name)(grad) # see _MySquareGrad for grad example
g = tf.get_default_graph()
with g.gradient_override_map({"PyFunc": rnd_name}):
return tf.py_func(func, inp, Tout, stateful=stateful, name=name)
# define custom second_eigval function for tensorflow
def custom_second_eigval(x, name=None):
with ops.op_scope([x], name, "SecondEigValGrad") as name:
eigval = py_func(calc_second_eigval,
[x],
[tf.float64],
name=name,
grad=_SecondEigValGrad) # <-- here's the call to the gradient
return eigval[0]
# actual gradient:
def _SecondEigValGrad(op, grad):
# TODO: this should involve eigen vectors
x = op.inputs[0]
return grad * 20 * x
X = tf.Variable(tf.random_normal([200,200],dtype=tf.float64))
second_eigval = custom_second_eigval(X)
optimizer = tf.train.AdamOptimizer(0.01)
update = tf.contrib.slim.learning.create_train_op(second_eigval, optimizer,summarize_gradients=True)
with tf.Session() as sess:
tf.initialize_all_variables().run()
print(update.eval())
You can return multiple values from a function in Python. To do so, return a data structure that contains multiple values, like a list containing the number of miles to run each week. Data structures in Python are used to store collections of data, which can be returned from functions.
To return multiple values from a function in Python, return a tuple of values. A tuple is a group of comma-separated values. You can create a tuple with or without parenthesis. To access/store the multiple values returned by a function, use tuple destructuring.
In Python, you can return multiple values by simply return them separated by commas. In Python, comma-separated values are considered tuples without parentheses, except where required by syntax. For this reason, the function in the above example returns a tuple with each value as an element.
Yes, returning multiple values (i.e., a tuple) is definitely pythonic. As others have pointed out, there are plenty of examples in the Python standard library, as well as in well-respected Python projects. Two additional comments: Returning multiple values is sometimes very, very useful.
Your Tout
must be (tf.float64,tf.float64)
instead of [tf.float64]
eigval = py_func(calc_second_eigval,
[x],
(tf.float64,tf.float64),
name=name,
grad=_SecondEigValGrad)
Here is an working demo
import tensorflow as tf
# Function in python
def dummy(x):
return [x,x]
print(dummy([1.0,2.0]))
tf_fun = tf.py_func(dummy,[[1.0,2.0]],(tf.float32,tf.float32))
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(tf_fun))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With