57 votes

Comment faire un personnalisé fonction d'activation avec seulement Python dans Tensorflow?

Supposons que vous avez besoin de faire une fonction d'activation qui n'est pas possible à l'aide de pré-définies tensorflow de construction-blocs, que pouvez-vous faire?

Ainsi, dans Tensorflow il est possible de créer votre propre fonction d'activation. Mais c'est assez compliqué, vous devez l'écrire en C++ et recompiler l'ensemble de tensorflow [1] [2].

Est-il un moyen plus simple?

84voto

patapouf_ai Points 6350

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: Spiky Activation

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-ble 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!

16voto

Mr Tsjolder Points 1630

Pourquoi ne pas simplement utiliser les fonctions qui sont déjà disponibles dans tensorflow construire votre nouvelle fonction?

Pour l' spiky fonction de votre réponse, cela pourrait ressembler à ce qui suit

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))

Je considère ce considérablement beaucoup plus facile (même pas besoin de calculer toutes les dégradés), et, sauf si vous voulez faire vraiment des choses exotiques, j'ai peine à imaginer que tensorflow ne pas fournir les blocs de construction pour la construction très complexe fonctions d'activation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X