TensorFlow教程——梯度爆炸与梯度裁剪
在较深的网络,如多层CNN或者非常长的RNN,由于求导的链式法则,有可能会出现梯度消失(Gradient Vanishing)或梯度爆炸(Gradient Exploding )的问题。(这部分知识后面补充)
原理
问题:为什么梯度爆炸会造成训练时不稳定而且不收敛?
梯度爆炸,其实就是偏导数很大的意思。回想我们使用梯度下降方法更新参数:
损失函数的值沿着梯度的方向呈下降趋势,然而,如果梯度(偏导数)很大话,就会出现函数值跳来跳去,收敛不到最值的情况,如图:
当然出现这种情况,其中一种解决方法是,将学习率 α α 设小一点,如0.0001。
这里介绍梯度裁剪(Gradient Clipping)的方法,对梯度进行裁剪,论文提出对梯度的L2范数进行裁剪,也就是所有参数偏导数的平方和再开方。
让 g1=∂J(w)∂w1 g 1 = ∂ J ( w ) ∂ w 1 , g2=∂J(w)∂w2 g 2 = ∂ J ( w ) ∂ w 2 ,设定裁剪阈值为 c c ,。
当 ∥g∥2 ∥ g ∥ 2 大于 c c 时:
当 ∥g∥2 ∥ g ∥ 2 小于等于 c c 时:不变。
其中, c∥g∥2 c ∥ g ∥ 2 是一个标量,大家有没有觉得这个跟学习率 α α 很类似?
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值出现跳动,一直不收敛时,除了设小学习率之外,梯度裁剪也是一个好方法。
然而这也说明,如果你的模型稳定而且会收敛,但是效果不佳时,那这就跟学习率和梯度爆炸没啥关系了。因此,学习率的设定和梯度裁剪的阈值并不能提高模型的准确率。
更多推荐
所有评论(0)