1 梯度不稳定
前面层上的梯度是来自于后面层上梯度的乘乘积.当存在过多的层次时,就出现了内在本质上的不稳定场景,如梯度消失和梯度爆炸.
1.1 梯度消失
vanishing gradient
当前面层的梯度比后面层的梯度变化更小, 会引起vanishing gradient.
1.2 梯度爆炸
exploding gradient
当前面层的梯度比后面层的梯度变化更大, 会引起exploding gradient.
2 实例
1个输入层, 3个隐藏层, 1个输出层, 激活函数为\(\sigma = sigmoid()\) 为例:
\[ \begin{align*} a_1 &= \sigma(z_1 = w_1 x_0 + b_1) \\ a_2 &= \sigma(z_2 = w_2 a_1 + b_2) \\ a_3 &= \sigma(z_3 = w_3 a_2 + b_3) \\ a_4 &= \sigma(z_4 = w_4 a_3 + b_4) \\ C &= Cost(a_4, y) \end{align*} \]
给出sigmoid函数以及导函数图:
然后计算梯度:
\[ \begin{align*} \dfrac{\partial{C}}{\partial{b_1}} &= \sigma(z_1)' w_2 \sigma(z_2)' w_2 \sigma(z_3)' w_3 \sigma(z_4)' w_4 \dfrac{\partial{C}}{\partial{a_4}} \\ \dfrac{\partial{C}}{\partial{b_3}} &= \sigma(z_3)' w_3 \sigma(z_4)' w_4 \dfrac{\partial{C}}{\partial{a_4}} \end{align*} \]
从sigmoid的导函数看大于4或小于-4时, 值变得接近于0, 而且导函数的最大值为0.25, 如果权重初始化时值比较小, 上 面的计算公式可以看出, 最后计算出的梯度, 是非常小的, 且越靠前面的层的梯度越小(一直连乘一个小于1的数), 所以 会出现梯度消失的情况. 同样道理, 如果权重值初始化值很大(或者过程中计算出的值大于1), 经过连乘之后会发现前面层的值越来越大, 引起梯 度爆炸.
3 解决办法
使用ReLU,maxout等替代sigmoid.
3.1 clip gradients
- 首先设置一个梯度阈值:clip_gradient
- 在后向传播中求出各参数的梯度,这里我们不直接使用梯度进去参数更新,我们求这些梯度的l2范数
- 然后比较梯度的l2范数||g||与clip_gradient的大小
- 如果前者大,求缩放因子clip_gradient/||g||, 由缩放因子可以看出梯度越大,则缩放因子越小,这样便很好地控制了梯度的范围
- 最后将梯度乘上缩放因子便得到最后所需的梯度
draft:
常见的 gradient clipping 有两种做法
根据参数的 gradient 的值直接进行裁剪
根据若干参数的 gradient 组成的 vector 的 L2 norm 进行裁剪
第一种做法很容易理解,就是先设定一个 gradient 的范围如 (-1, 1), 小于 -1 的 gradient 设为 -1, 大于这个 1 的 gradient 设为 1.
第二种方法则更为常见,先设定一个 clip_norm, 然后在某一次反向传播后,通过各个参数的 gradient 构成一个
vector,计算这个 vector 的 L2 norm(平方和后开根号)记为 LNorm,然后比较 LNorm 和 clip_norm 的值,若 LNorm
<= clip_norm 不做处理,否则计算缩放因子 scale_factor = clip_norm/LNorm ,然后令原来的梯度乘上这个缩放因子
。这样做是为了让 gradient vector 的 L2 norm 小于预设的 clip_norm。