TensorFlow:怎么替换或者修改梯度?

问题:

我想要去替换或修改 TensorFlow 中一个 op 或 部分计算图的梯度,该怎么整?

在某些情况下,这和 tf.stop_gradient() 是相对的:我们想要的不是“添加一个 op,然后在计算梯度的过程中忽略该 op”,我们想要的是“只在梯度计算时起作用(I want a calculation which is only used when calculating gradients)”

一个简单的例子如下:

在不改变前向传播的情况下,通过给梯度乘以一个常量来缩放梯度。

另一个例子如下:

在不改变前向传播的过程下,将梯度裁剪到给定的范围。

版本1
首先定义你自己的梯度计算函数,并注册到 tensorflow

@tf.RegisterGradient("CustomGrad") # 注册到tensorflow
def _const_mul_grad(unused_op, grad):
  return 5.0 * grad

因为我们不想改变前向传播过程,因此下面使用上面注册的函数替换原始的梯度计算函数(即用 “CustomGrad” 覆盖 “Identity”)

g = tf.get_default_graph()
with g.gradient_override_map({"Identity": "CustomGrad"}): # 覆盖该 op 的梯度计算函数
  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])

版本2
在 TensorFlow 1.7 版本后,有一种定义梯度的快捷方式(也可以用来同时重定义多个op的梯度)

定义一个层,该层在保持正向传播不变的情况下,缩放梯度:

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

下面是一个反向传播过程中梯度裁剪的例子:

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("input:", sess.run(input))
  print("output_clipping:", sess.run(output_clip))
  print("output_no_clipping:", sess.run(output))
  print("with clipping:", sess.run(grad_clip)[0])
  print("without clipping:", sess.run(grad)[0])

使用 optimizer.compute_gradients 或 tf.gradient 来获得原始梯度,然后进行任何你需要的变换,最后使用 optimizer.apply_gradients

回答3

回答人:Bily
使用梯度截断,构造函数

假设前向传播过程如下:

y = f(x)

反向传播过程如下:

y = b(x)

一个简单的修改梯度的方法如下:

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

实例:

def clip_gradient(x, l=1.0):
	positive_path = tf.stop_gradient(x * tf.cast(1 - l, tf.float32))
	negative_path = x * tf.cast(l, tf.float32)
	return positive_path + negative_path

需要注意的是,l也可以使用平滑,并不是一个常数,而是由0变为1,即

[公式]

其中, [公式] 是一个超参数,文章中设为10; [公式] 随着训练的进行由0变为1,表示当前的训练步数/总的训练步数。上面的式子意味着一开始时, [公式] ,领域分类损失不会回传到编码器网络中,只有领域分类器得到训练;随着训练的进行, [公式] 逐渐增加,编码器得到训练,并开始逐步生成可以混淆领域分类器的特征。

部分参考:
https://blog.csdn.net/u014061630/article/details/81369787

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值