python - una - fijar ejes matplotlib
Tensorflow: ¿Cómo escribir op con gradiente en python? (2)
Me gustaría escribir una operación TensorFlow en Python, pero me gustaría que fuera diferenciable (para poder calcular un gradiente).
Esta pregunta pregunta cómo escribir una operación en python, y la respuesta sugiere usar py_func (que no tiene gradiente): Tensorflow: Escribir una operación en Python
La documentación de TF describe cómo agregar una operación comenzando solo desde el código C ++: https://www.tensorflow.org/versions/r0.10/how_tos/adding_an_op/index.html
En mi caso, estoy creando prototipos, así que no me importa si se ejecuta en la GPU, y no me importa que sea utilizable desde otra cosa que no sea la API TF Python.
Aquí hay un ejemplo de agregar gradiente a un
py_func
específico
here
Aquí está la discussion problema
Sí, como se menciona en la respuesta de @ Yaroslav, es posible y la clave son los enlaces a los que hace referencia: here y here . Quiero dar más detalles sobre esta respuesta dando un ejemplo concreto.
Operación de módulo: Implementemos la operación de módulo de elemento en flujo de tensor (ya existe pero su gradiente no está definido, pero por ejemplo lo implementaremos desde cero).
Función Numpy: el primer paso es definir la operación que queremos para las matrices numpy. La operación del módulo en cuanto a elementos ya está implementada en numpy, por lo que es fácil:
import numpy as np
def np_mod(x,y):
return (x % y).astype(np.float32)
La razón del
.astype(np.float32)
se debe a que, por defecto, el tensorflow toma los tipos float32 y si le da float64 (el predeterminado numpy) se quejará.
Función de gradiente: a
continuación, debemos definir la función de gradiente para nuestra operación para cada entrada de la operación como función de tensor de flujo.
La función debe tomar una forma muy específica.
Debe tomar la representación de flujo de tensor de la operación de operación y el gradiente del gradiente de salida y decir cómo propagar los gradientes.
En nuestro caso, los gradientes de la operación
mod
son fáciles, la derivada es 1 con respecto al primer argumento y
con respecto al segundo (casi en todas partes, e infinito en un número finito de puntos, pero ignoremos eso, vea
https://math.stackexchange.com/questions/1849280/derivative-of-remainder-function-wrt-denominator
para detalles).
Entonces tenemos
def modgrad(op, grad):
x = op.inputs[0] # the first argument (normally you need those to calculate the gradient, like the gradient of x^2 is 2x. )
y = op.inputs[1] # the second argument
return grad * 1, grad * tf.neg(tf.floordiv(x, y)) #the propagated gradient with respect to the first and second argument respectively
La función grad necesita devolver una n-tupla donde n es el número de argumentos de la operación. Tenga en cuenta que necesitamos devolver las funciones de tensorflow de la entrada.
Realización de una función TF con gradientes:
como se explica en las fuentes mencionadas anteriormente, existe un truco para definir gradientes de una función utilizando
tf.RegisterGradient
[doc]
y
tf.Graph.gradient_override_map
[doc]
.
Al copiar el código de
here
podemos modificar la función
tf.py_func
para que defina el gradiente al mismo tiempo:
import tensorflow as tf
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)
La opción con
stateful
es decirle al tensorflow si la función siempre da la misma salida para la misma entrada (stateful = False) en cuyo caso el tensorflow puede simplemente el gráfico de tensorflow, este es nuestro caso y probablemente será el caso en la mayoría de las situaciones.
Combinándolo todo: ahora que tenemos todas las piezas, podemos combinarlas todas juntas:
from tensorflow.python.framework import ops
def tf_mod(x,y, name=None):
with ops.op_scope([x,y], name, "mod") as name:
z = py_func(np_mod,
[x,y],
[tf.float32],
name=name,
grad=modgrad) # <-- here''s the call to the gradient
return z[0]
tf.py_func
actúa en listas de tensores (y devuelve una lista de tensores), es por eso que tenemos
[x,y]
(y devuelve
z[0]
).
Y ahora hemos terminado.
Y podemos probarlo.
Prueba:
with tf.Session() as sess:
x = tf.constant([0.3,0.7,1.2,1.7])
y = tf.constant([0.2,0.5,1.0,2.9])
z = tf_mod(x,y)
gr = tf.gradients(z, [x,y])
tf.initialize_all_variables().run()
print(x.eval(), y.eval(),z.eval(), gr[0].eval(), gr[1].eval())
[0.30000001 0.69999999 1.20000005 1.70000005] [0.2 0.5 1. 2.9000001] [0.10000001 0.19999999 0.20000005 1.70000005] [1. 1. 1. 1.] [-1. -1. -1. 0.]
¡Éxito!