54 votes

Tensorflow: Comment remplacer ou modifier le dégradé?

Je voudrais remplacer ou modifier le gradient d'une op ou une partie du graphe de la tensorflow. L'idéal serait, si je peux utiliser le dégradé dans le calcul.

À certains égards, c'est le contraire de ce qu' tf.stop_gradient() fait: au lieu de l'ajout d'un calcul qui est ignoré lors du calcul des gradients, je veux un calcul qui est utilisé uniquement lors du calcul des gradients.

Un exemple simple serait quelque chose qui, tout simplement, des échelles de gradients en les multipliant par une constante (mais ne pas multiplier le calcul de l'avant par une constante). Un autre exemple serait quelque chose qui clips les dégradés pour une gamme donnée.

60voto

BlueSun Points 2810

Pour tensorflow 1.7 ou plus récent, regardez edit coup.


D'abord définir votre dégradé personnalisé:

@tf.RegisterGradient("CustomGrad")
def _const_mul_grad(unused_op, grad):
  return 5.0 * grad

Puisque vous voulez ne se passe rien dans la passe en avant, remplacer le gradient d'une pièce d'identité avec votre nouveau dégradé:

g = tf.get_default_graph()
with g.gradient_override_map({"Identity": "CustomGrad"}):
  output = tf.identity(input, name="Identity")

Voici un exemple de travail avec une couche de clips dégradés à l'arrière passe et ne fait rien à l'avant de passer, à l'aide de la même méthode:

import tensorflow as tf

@tf.RegisterGradient("CustomClipGrad")
def _clip_grad(unused_op, grad):
  return tf.clip_by_value(grad, -0.1, 0.1)

input = tf.Variable([3.0], dtype=tf.float32)

g = tf.get_default_graph()
with g.gradient_override_map({"Identity": "CustomClipGrad"}):
  output_clip = tf.identity(input, name="Identity")
grad_clip = tf.gradients(output_clip, input)

# output without gradient clipping in the backwards pass for comparison:
output = tf.identity(input)
grad = tf.gradients(output, input)

with tf.Session() as sess:
  sess.run(tf.global_variables_initializer())
  print("with clipping:", sess.run(grad_clip)[0])
  print("without clipping:", sess.run(grad)[0])

edit pour TensorFlow 1.7

Depuis la 1.7, il y a une nouvelle façon de redéfinir le dégradé avec une syntaxe plus courte. (Il permet également de redéfinir le gradient de plusieurs opérations en même temps, ce qui n'est pas nécessaire pour cette question). Voici quelques exemples à partir de ci-dessus, réécrit pour TensorFlow 1.7:

Couche échelles dégradés à l'arrière pass:

@tf.custom_gradient
def scale_grad_layer(x):
  def grad(dy):
    return 5.0 * dy
  return tf.identity(x), grad

Exemple avec une couche de clips dégradés à l'arrière pass:

import tensorflow as tf

input = tf.Variable([3.0], dtype=tf.float32)

@tf.custom_gradient
def clip_grad_layer(x):
  def grad(dy):
    return tf.clip_by_value(dy, -0.1, 0.1)
  return tf.identity(x), grad

output_clip = clip_grad_layer(input)
grad_clip = tf.gradients(output_clip, input)

# output without gradient clipping in the backwards pass for comparison:
output = tf.identity(input)
grad = tf.gradients(output, input)

with tf.Session() as sess:
  sess.run(tf.global_variables_initializer())
  print("with clipping:", sess.run(grad_clip)[0])
  print("without clipping:", sess.run(grad)[0])

18voto

Bily Points 498

En supposant que le calcul en avant est

 y = f(x)
 

Et vous voulez qu'il se propage comme

 y = b(x)
 

Un simple hack sera:

 y = b(x) + tf.stop_gradient(f(x) - b(x))
 

17voto

xxi Points 1012

utilisez optimizer.compute_gradients ou tf.gradient pour obtenir des dégradés originaux
alors fais ce que tu veux
enfin, utilisez optimizer.apply_gradients

J'ai trouvé un exemple de github

9voto

MaxB Points 689

La façon la plus générale de le faire est d'utiliser https://www.tensorflow.org/api_docs/python/tf/RegisterGradient

Ci-dessous, j'ai implémenté un écrêtage de gradient rétroposé, qui peut être utilisé avec matmul , comme indiqué ici, ou tout autre opération:

 import tensorflow as tf
import numpy as np

# from https://gist.github.com/harpone/3453185b41d8d985356cbe5e57d67342
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)
    g = tf.get_default_graph()
    with g.gradient_override_map({"PyFunc": rnd_name}):
        return tf.py_func(func, inp, Tout, stateful=stateful, name=name)

def clip_grad(x, clip_value, name=None):
    """"
    scales backpropagated gradient so that
    its L2 norm is no more than `clip_value`
    """
    with tf.name_scope(name, "ClipGrad", [x]) as name:
        return py_func(lambda x : x,
                        [x],
                        [tf.float32],
                        name=name,
                        grad=lambda op, g : tf.clip_by_norm(g, clip_value))[0]
 

Exemple d'utilisation:

 with tf.Session() as sess:
    x = tf.constant([[1., 2.], [3., 4.]])
    y = tf.constant([[1., 2.], [3., 4.]])

    print('without clipping')
    z = tf.matmul(x, y)
    print(tf.gradients(tf.reduce_sum(z), x)[0].eval())

    print('with clipping')
    z = tf.matmul(clip_grad(x, 1.0), clip_grad(y, 0.5))
    print(tf.gradients(tf.reduce_sum(z), x)[0].eval())

    print('with clipping between matmuls')
    z = tf.matmul(clip_grad(tf.matmul(x, y), 1.0), y)
    print(tf.gradients(tf.reduce_sum(z), x)[0].eval())
 

Sortie:

 without clipping
[[ 3.  7.]
 [ 3.  7.]]
with clipping
[[ 0.278543   0.6499337]
 [ 0.278543   0.6499337]]
with clipping between matmuls
[[ 1.57841039  3.43536377]
 [ 1.57841039  3.43536377]]
 

1voto

cheersmate Points 1140

Pour le courant TensorFlow r1.13, l'utilisation de tf.custom_gradient.

Décoré de la fonction (arguments d'entrée est une liste x) doit retourner

  • le résultat de la passe en avant, et
  • une fonction qui renvoie une liste de gradients, un pour chaque élément en x.

Voici un exemple avec une variable:

@tf.custom_gradient
def non_differentiable(x):
    f = tf.cast(x > 0, tf.float32)
    def grad(dy):
        return tf.math.maximum(0., 1 - tf.abs(x))
    return f, grad

Et une avec deux:

@tf.custom_gradient
def non_differentiable2(x0, x1):
    f = x0 * tf.cast(x1 > 0, tf.float32)
    def grad(dy):
        df_dx0 = tf.cast(x1 > 0, tf.float32)
        return dy*df_dx0, tf.zeros_like(dy)
    return f, grad

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