Tensorflow:如何替换或修改渐变?

Ale*_*x I 49 python neural-network tensorflow

我想在tensorflow中替换或修改op的梯度或图的一部分.如果我可以在计算中使用现有的梯度,那将是理想的.

在某些方面,这与以下内容相反tf.stop_gradient():在计算渐变时,我想要一个仅在计算渐变时使用的计算,而不是添加一个被忽略的计算.

一个简单的例子就是通过将它们与常数相乘(但不会将正向计算乘以常数)来简单地缩放渐变.另一个例子是将渐变剪辑到给定范围的东西.

Blu*_*Sun 52

对于tensorflow 1.7或更新版本,请查看编辑打击.


首先定义自定义渐变:

@tf.RegisterGradient("CustomGrad")
def _const_mul_grad(unused_op, grad):
  return 5.0 * grad
Run Code Online (Sandbox Code Playgroud)

由于您不希望在前向传递中发生任何事情,因此使用新渐变覆盖身份操作的渐变:

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

下面是一个工作示例,其中一个图层使用相同的方法在向后传递中剪切渐变并且在前向传递中不执行任何操作:

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])
Run Code Online (Sandbox Code Playgroud)

编辑TensorFlow 1.7

从1.7开始,有一种新方法可以用更短的语法重新定义渐变.(它还允许同时重新定义多个操作的梯度,这个问题不需要).以下是上面的示例,为TensorFlow 1.7重写:

在后向传递中缩放渐变的图层:

@tf.custom_gradient
def scale_grad_layer(x):
  def grad(dy):
    return 5.0 * dy
  return tf.identity(x), grad
Run Code Online (Sandbox Code Playgroud)

带有在后向传递中剪切渐变的图层的示例:

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])
Run Code Online (Sandbox Code Playgroud)

  • @KevinP 例如,对于裁剪:在恒等操作的向后传递期间,渐变将仅裁剪 1 次。但是链中所有前面的层都会受到影响,因为每一层都使用其下一层的梯度进行反向传播。但是之前的图层它们自己不会再次剪辑。 (3认同)

xxi*_*xxi 16

使用optimizer.compute_gradientstf.gradient获得原始渐变
然后做你想做的任何事情
,使用optimizer.apply_gradients

我从github 找到了一个例子


Bil*_*ily 11

假设正向计算是

y = f(x)
Run Code Online (Sandbox Code Playgroud)

而且你希望它反向传播

y = b(x)
Run Code Online (Sandbox Code Playgroud)

一个简单的黑客将是:

y = b(x) + tf.stop_gradient(f(x) - b(x))
Run Code Online (Sandbox Code Playgroud)


Max*_*axB 9

最常用的方法是使用 https://www.tensorflow.org/api_docs/python/tf/RegisterGradient

下面,我实现了反向传播的渐变裁剪,可以使用matmul,如此处所示,或任何其他操作:

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]
Run Code Online (Sandbox Code Playgroud)

用法示例:

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())
Run Code Online (Sandbox Code Playgroud)

输出:

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]]
Run Code Online (Sandbox Code Playgroud)