una tutorial redes neuronales neuronal libreria implementacion español python tensorflow neural-network deep-learning activation-function

tutorial - ¿Cómo hacer una función de activación personalizada con solo Python en Tensorflow?



tensorflow tutorial español pdf (2)

Supongamos que necesita realizar una función de activación que no es posible utilizando solo bloques de construcción de tensorflow predefinidos, ¿qué puede hacer?

Entonces, en Tensorflow es posible hacer su propia función de activación. Pero es bastante complicado, tienes que escribirlo en C ++ y recompilar todo el flujo de tensor [1] [2] .

¿Hay alguna forma más simple?


¿Por qué no simplemente usar las funciones que ya están disponibles en tensorflow para construir su nueva función?

Para la función de spiky en su respuesta , esto podría verse de la siguiente manera

def spiky(x): r = tf.floormod(x, tf.constant(1)) cond = tf.less_equal(r, tf.constant(0.5)) return tf.where(cond, r, tf.constant(0))

Consideraría esto mucho más fácil (ni siquiera es necesario calcular gradientes) y, a menos que desee hacer cosas realmente exóticas, apenas puedo imaginar que el flujo de tensor no proporcione los bloques de construcción para construir funciones de activación altamente complejas.


¡Sí hay!

Crédito: fue difícil encontrar la información y hacer que funcionara, pero aquí hay un ejemplo que copia de los principios y el código que se encuentran here y here .

Requisitos: antes de comenzar, hay dos requisitos para que esto pueda tener éxito. Primero debe poder escribir su activación como una función en matrices numpy. En segundo lugar, debe poder escribir la derivada de esa función como una función en Tensorflow (más fácil) o, en el peor de los casos, como una función en matrices numpy.

Función de activación de escritura:

Así que tomemos por ejemplo esta función que nos gustaría usar una función de activación:

def spiky(x): r = x % 1 if r <= 0.5: return r else: return 0

Que se ve de la siguiente manera:

El primer paso es convertirlo en una función numpy, esto es fácil:

import numpy as np np_spiky = np.vectorize(spiky)

Ahora deberíamos escribir su derivada.

Gradiente de activación: en nuestro caso es fácil, es 1 si x mod 1 <0.5 y 0 en caso contrario. Entonces:

def d_spiky(x): r = x % 1 if r <= 0.5: return 1 else: return 0 np_d_spiky = np.vectorize(d_spiky)

Ahora, para la parte difícil de hacer que TensorFlow funcione fuera de él.

Hacer un fct numpy a un fct tensorflow: Comenzaremos haciendo np_d_spiky en una función tensorflow. Hay una función en tensorflow tf.py_func(func, inp, Tout, stateful=stateful, name=name) [doc] que transforma cualquier función numpy en una función de tensorflow, por lo que podemos usarla:

import tensorflow as tf from tensorflow.python.framework import ops np_d_spiky_32 = lambda x: np_d_spiky(x).astype(np.float32) def tf_d_spiky(x,name=None): with tf.name_scope(name, "d_spiky", [x]) as name: y = tf.py_func(np_d_spiky_32, [x], [tf.float32], name=name, stateful=False) return y[0]

tf.py_func actúa en listas de tensores (y devuelve una lista de tensores), es por eso que tenemos [x] (y devolvemos y[0] ). 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. Una cosa a tener en cuenta en este punto es que numpy usó float64 pero tensorflow usa float32 por lo que debe convertir su función para usar float32 antes de poder convertirla en una función de tensorflow, de lo contrario, tensorflow se quejará. Es por eso que necesitamos hacer np_d_spiky_32 primero.

¿Qué pasa con los gradientes? El problema con solo hacer lo anterior es que a pesar de que ahora tenemos tf_d_spiky que es la versión de np_d_spiky de np_d_spiky , no podríamos usarlo como una función de activación si quisiéramos porque el tensorflow no sabe cómo calcular los gradientes de ese función.

Hack para obtener degradados: como se explica en las fuentes mencionadas anteriormente, hay un hack para definir gradientes de una función usando 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:

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)

Ahora casi hemos terminado, lo único es que la función grad que necesitamos pasar a la función py_func anterior necesita tomar una forma especial. Necesita tomar una operación y los gradientes anteriores antes de la operación y propagar los gradientes hacia atrás después de la operación.

Función de gradiente: Entonces, para nuestra función de activación puntiaguda, así es como lo haríamos:

def spikygrad(op, grad): x = op.inputs[0] n_gr = tf_d_spiky(x) return grad * n_gr

La función de activación tiene una sola entrada, por eso x = op.inputs[0] . Si la operación tuviera muchas entradas, tendríamos que devolver una tupla, un gradiente para cada entrada. Por ejemplo, si la operación fuera ab el gradiente con respecto a a es +1 y con respecto a b es -1 por lo que tendríamos return +1*grad,-1*grad . Tenga en cuenta que necesitamos devolver las funciones de tensorflow de la entrada, es por eso que necesitar tf_d_spiky , np_d_spiky no habría funcionado porque no puede actuar sobre los tensores de tensorflow. Alternativamente, podríamos haber escrito la derivada usando las funciones de tensorflow:

def spikygrad2(op, grad): x = op.inputs[0] r = tf.mod(x,1) n_gr = tf.to_float(tf.less_equal(r, 0.5)) return grad * n_gr

Combinándolo todo: ahora que tenemos todas las piezas, podemos combinarlas todas juntas:

np_spiky_32 = lambda x: np_spiky(x).astype(np.float32) def tf_spiky(x, name=None): with tf.name_scope(name, "spiky", [x]) as name: y = py_func(np_spiky_32, [x], [tf.float32], name=name, grad=spikygrad) # <-- here''s the call to the gradient return y[0]

Y ahora hemos terminado. Y podemos probarlo.

Prueba:

with tf.Session() as sess: x = tf.constant([0.2,0.7,1.2,1.7]) y = tf_spiky(x) tf.initialize_all_variables().run() print(x.eval(), y.eval(), tf.gradients(y, [x])[0].eval())

[0.2 0.69999999 1.20000005 1.70000005] [0.2 0. 0.20000005 0.] [1. 0. 1. 0.]

¡Éxito!