Oui, Il y est!
Crédit:
Il était difficile de trouver de l'information et obtenir ce travail, mais voici un exemple de la copie de l'principes et le code trouvé ici et ici.
Exigences:
Avant de commencer, il y a deux impératifs pour cela être capable de réussir. Vous devez d'abord être capable d'écrire votre activation en fonction sur les tableaux numpy. Deuxième, vous devez être en mesure d'écrire la dérivée de cette fonction soit une fonction dans Tensorflow (plus facile) ou dans le pire des cas, comme une fonction sur les tableaux numpy.
L'écriture de l'Activation de la fonction:
Donc, nous allons prendre l'exemple de cette fonction que nous voulons utiliser une fonction d'activation:
def spiky(x):
r = x % 1
if r <= 0.5:
return r
else:
return 0
Comme ci-dessous:
La première étape est d'en faire une fonction numpy, c'est facile:
import numpy as np
np_spiky = np.vectorize(spiky)
Maintenant, nous devrions écrire ses dérivés.
Gradient de l'Activation:
Dans notre cas, c'est facile, il est 1 si x mod 1 < 0,5 et 0 sinon. Donc:
def d_spiky(x):
r = x % 1
if r <= 0.5:
return 1
else:
return 0
np_d_spiky = np.vectorize(d_spiky)
Maintenant pour la partie la plus difficile de faire un TensorFlow fonction d'elle.
Faire un numpy fct à un tensorflow fct:
Nous allons commencer par faire np_d_spiky dans un tensorflow fonction. Il y a une fonction dans tensorflow tf.py_func(func, inp, Tout, stateful=stateful, name=name)
[doc] qui transforme n'importe quel numpy fonction à un tensorflow fonction, de sorte que nous pouvons l'utiliser:
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
agit sur les listes des tenseurs (et qui renvoie une liste de tenseurs), c'est pourquoi nous avons [x]
(aller et retour, y[0]
). L' stateful
option est à dire tensorflow si la fonction donne toujours le même résultat pour la même entrée (stateful = False) et dans ce cas tensorflow pouvez simplement le tensorflow graphique, c'est notre cas, et sera probablement le cas dans la plupart des situations. Une seule chose à faire attention à ce point, c'est que numpy utilisé float64
mais tensorflow utilise float32
si vous avez besoin de convertir votre fonction à utiliser float32
avant que vous pouvez convertir en un tensorflow fonction autrement tensorflow va se plaindre. C'est pourquoi nous devons faire de l' np_d_spiky_32
première.
Que dire de la Dégradés? Le problème ne faire que ce qui précède est que, même si nous avons maintenant tf_d_spiky
qui est le tensorflow version de np_d_spiky
, nous ne pouvions pas l'utiliser comme une fonction d'activation si nous le voulions, car tensorflow ne sais pas comment calculer les gradients de la fonction.
Hack pour obtenir des Dégradés: Comme expliqué dans les sources mentionnées ci-dessus, il y a un hack pour définir les dégradés d'une fonction à l'aide d' tf.RegisterGradient
[doc] et tf.Graph.gradient_override_map
[doc]. Copier le code de harpone nous pouvons modifier l' tf.py_func
à définir le dégradé dans le même temps:
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)
Maintenant, nous sommes presque fait, la seule chose est que le grad de la fonction, nous devons passer au-dessus py_func fonction doit prendre une forme particulière. Il doit prendre dans une opération, et les anciens dégradés avant l'opération et de propager les gradients en arrière après l'opération.
Gradient de la Fonction: pour notre hérissés de l'activation de la fonction qui est la manière dont nous le faire:
def spikygrad(op, grad):
x = op.inputs[0]
n_gr = tf_d_spiky(x)
return grad * n_gr
La fonction d'activation a une seule entrée, c'est pourquoi, x = op.inputs[0]
. Si l'opération a eu de nombreuses entrées, nous aurions besoin de retourner un tuple, un gradient pour chaque entrée. Par exemple, si l'opération a été a-b
le dégradé à l'égard a
est +1
et, à l'égard b
est -1
afin que nous ayons return +1*grad,-1*grad
. Notez que nous avons besoin de retourner tensorflow fonctions de l'entrée, c'est pourquoi faut - tf_d_spiky
, np_d_spiky
n'aurait pas fonctionné, parce qu'il ne peut pas agir sur tensorflow tenseurs. Sinon, nous aurions pu écrire la dérivée à l'aide de tensorflow fonctions:
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
En combinant tous ensemble: Maintenant que nous avons toutes les pièces, on peut combiner tous ensemble:
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]
Et nous sommes maintenant fait. Et nous pouvons le tester.
Test:
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.]
Succès!