在较深的网络,如多层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值出现跳动,一直不收敛时,除了设小学习率之外,梯度裁剪也是一个好方法。
然而这也说明,如果你的模型稳定而且会收敛,但是效果不佳时,那这就跟学习率和梯度爆炸没啥关系了。因此,学习率的设定和梯度裁剪的阈值并不能提高模型的准确率。
原文地址:https://www.cnblogs.com/zongfa/p/9737698.html
时间: 2024-10-30 05:37:58