Tensorflow:如何替换或修改渐变?
neural-network
python
tensorflow
6
0

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

在某些方面,这与tf.stop_gradient()的工作相反:我不希望添加一个在计算梯度时会被忽略的计算,而是要一个仅在计算梯度时使用的计算。

一个简单的例子就是简单地通过将梯度乘以常数来缩放比例(但不将正向计算乘以常数)。另一个例子是将梯度裁剪到给定范围的东西。

参考资料:
Stack Overflow
收藏
评论
共 4 个回答
高赞 时间 活跃

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

我从github找到了一个例子

收藏
评论

最通用的方法是使用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]

用法示例:

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

输出:

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]]
收藏
评论

假设正向计算为

y = f(x)

而您希望它像

y = b(x)

一个简单的技巧将是:

y = b(x) + tf.stop_gradient(f(x) - b(x))
收藏
评论

对于TensorFlow 1.7和TensorFlow 2.0,请看编辑打击。


首先定义您的自定义渐变:

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

由于您不希望前进过程中发生任何事情,因此请使用新的梯度来替代身份操作的梯度:

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

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

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

编辑TensorFlow 1.7和TensorFlow 2.0

从1.7开始,有一种新的方法可以使用较短的语法重新定义渐变,这也适用于Tensorflow 2.0。它还允许同时重新定义多个操作的梯度。以下是上面为TensorFlow 1.7和TensorFlow 2.0重写的示例:

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

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

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

@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
收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号