TensorFlow的梯度裁剪

Posted zongfa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TensorFlow的梯度裁剪相关的知识,希望对你有一定的参考价值。

在较深的网络,如多层CNN或者非常长的RNN,由于求导的链式法则,有可能会出现梯度消失(Gradient Vanishing)或梯度爆炸(Gradient Exploding )的问题。

原理

问题:为什么梯度爆炸会造成训练时不稳定而且不收敛?
梯度爆炸,其实就是偏导数很大的意思。回想我们使用梯度下降方法更新参数:

技术分享图片

损失函数的值沿着梯度的方向呈下降趋势,然而,如果梯度(偏导数)很大话,就会出现函数值跳来跳去,收敛不到最值的情况,如图:

技术分享图片

当然出现这种情况,其中一种解决方法是,将学习率αα设小一点,如0.0001。

这里介绍梯度裁剪(Gradient Clipping)的方法,对梯度进行裁剪,论文提出对梯度的L2范数进行裁剪,也就是所有参数偏导数的平方和再开方。

技术分享图片

TensorFlow代码

方法一:

optimizer = tf.train.AdamOptimizer(learning_rate=0.001, beta1=0.5)
grads = optimizer.compute_gradients(loss)
for i, (g, v) in enumerate(grads):
    if g is not None:
        grads[i] = (tf.clip_by_norm(g, 5), v)  # 阈值这里设为5
train_op = optimizer.apply_gradients(grads)

其中
optimizer.compute_gradients()返回的是正常计算的梯度,是一个包含(gradient, variable)的列表。

tf.clip_by_norm(t, clip_norm)返回裁剪过的梯度,维度跟t一样。

不过这里需要注意的是,这里范数的计算不是根据全局的梯度,而是一部分的。

方法二:

optimizer = tf.train.AdamOptimizer(learning_rate=0.001, beta1=0.5)
grads, variables = zip(*optimizer.compute_gradients(loss))
grads, global_norm = tf.clip_by_global_norm(grads, 5)
train_op = optimizer.apply_gradients(zip(grads, variables))

这里是计算全局范数,这才是标准的。不过缺点就是会慢一点,因为需要全部梯度计算完之后才能进行裁剪。

总结

当你训练模型出现Loss值出现跳动,一直不收敛时,除了设小学习率之外,梯度裁剪也是一个好方法。

然而这也说明,如果你的模型稳定而且会收敛,但是效果不佳时,那这就跟学习率和梯度爆炸没啥关系了。因此,学习率的设定和梯度裁剪的阈值并不能提高模型的准确率。

 



以上是关于TensorFlow的梯度裁剪的主要内容,如果未能解决你的问题,请参考以下文章

Tensorflow Adam Multigpu 梯度

TensorFlow2 手把手教你避开梯度消失和梯度爆炸

如何在 Tensorflow 中实现反转梯度?

Tensorflow--梯度下降解决线性回归

在 keras(tensorflow 后端)中计算梯度时出错

Tensorflow:不提供梯度的自定义损失函数