深度学习中的梯度爆炸 梯度消失

梯度消失

随着网络深度的加深,梯度消失问题会愈加明显。无论cnn,rnn

什么形式会爆炸

f_{t+1}

累加的形式不消失
$f{t+1} = w * f{t} + w f_{t-1} + …$

CNN

。设输入数据为 $x$ ,对 $x$ 的卷积操作就可以看做是 $Wx+b$

我们设第一层卷积的参数为$W_1, b_1$ ,第二层卷积的参数是$W_2, b_2$,依次类推。又设激活函数为$f$ ,每一层卷积在经过激活函数前的值为$a_i$,经过激活函数后的值为$f_i$。

按照上面的表示,在CNN中,输入$x$ ,第一层的输出就是$f_1=f[W_1x+b_1]$,第二层的输出就是$f_2= f[W_2f[W_1x+b_1]+b_2]$ ,第三层的输出就是$f_3= f[W_3f[W_2f[W_1x+b_1]+b_2]+b_3]$。设最终损失为$L$ ,我们来尝试从第三层开始,用BP算法推导一下损失对参数$W_1$的偏导数,看看会发生什么。

为了简洁起见,略过求导过程,最后的结果为
$\frac{\partial{L}}{\partial{W_1}}=\frac{\partial{L}}{\partial{f_3}}\frac{\partial{f_3}}{\partial{a_3}}W_3\frac{\partial{f_2}}{\partial{a_2}}W_2\frac{\partial{f_1}}{\partial{a_1}}\frac{\partial{a_1}}{\partial{W_1}}$。
我们常常说原始神经网络的梯度消失问题,这里的$\frac{\partial{f_3}}{\partial{a_3}}$、$\frac{\partial{f_2}}{\partial{a_2}}$
就是梯度消失的“罪魁祸首”
例如sigmoid的函数,它的导数的取值范围是(0, 0.25],也就是说对于导数中的每一个元素,我们都有$0<\frac{\partial{f_3}}{\partial{a_3}}\le0.25$,$0<\frac{\partial{f_2}}{\partial{a_2}}\le0.25$,小于1的数乘在一起,必然是越乘越小的。这才仅仅是3层,如果10层的话, 根据$0.25^{10}\approx0.000000954$,第10层的误差相对第一层卷积的参数 $W_1$的梯度将是一个非常小的值,这就是所谓的“梯度消失”。

ReLU函数的改进就是它使得 $\frac{\partial{f_3}}{\partial{a_3}}\in{0,1}$ , $\frac{\partial{f_2}}{\partial{a_2}}\in{0,1}$ , $\frac{\partial{f_1}}{\partial{a_1}}\in{0,1}$ ,这样的话只要一条路径上的导数都是1,无论神经网络是多少层,这一部分的乘积都始终为1,因此深层的梯度也可以传递到浅层中。

那为什么同样的方法在RNN中不奏效呢?其实这一点Hinton在它的IRNN论文里面(arxiv:[1504.00941] A Simple Way to Initialize Recurrent Networks of Rectified Linear Units)是很明确的提到的:

也就是说在RNN中直接把激活函数换成ReLU会导致非常大的输出值。为了讲清楚这一点,我们先用同上面相似的符号把原始的RNN表示出来:

在这个表示中,RNN每个阶段的输入是 $x_i$,和CNN每一层使用独立的参数$W_i$不同,原始的
RNN在每个阶段都共享一个参数 $W$。如果我们假设从某一层开始输入 $x_i$ 和偏置 $b_i$ 都为0,
那么最后得到的输出就是 $f[W…[Wf[Wf[Wf_i]]]]$ ,这在某种程度上相当于对参数矩阵 $W$ 作连乘,
很显然,只要 $W$ 有一个大于1的特征值,在经过若干次连乘后都会导致结果是一个数值非常庞大的矩阵。

另外一方面,将激活函数换成ReLU也不能解决梯度在长程上传递的问题。同样考虑 $f_3$ 对 $W$ 的导数。在CNN中,每一层的参数 $W_1,W_2,W_3……$ 是互相独立的,然而RNN中 W 参与了每个时间段的运算,因此 $f_3$ 对 $W$ 导数更复杂,写出来是

$\frac{\partial{f_3}}{\partial{W_1}}=\frac{\partial{f_3}}{\partial{a_3}}f_2+\frac{\partial{f_3}}{\partial{a_3}}W\frac{\partial{f_2}}{\partial{a_2}}f_1+\frac{\partial{f_3}}{\partial{a_3}}W\frac{\partial{f_2}}{\partial{a_2}}W\frac{\partial{f_1}}{\partial{a_1}}\frac{\partial{a_1}}{\partial{W_1}}$ 。我们可以看下最后 $\frac{\partial{f_3}}{\partial{a_3}}W\frac{\partial{f_2}}{\partial{a_2}}W\frac{\partial{f_1}}{\partial{a_1}}\frac{\partial{a_1}}{\partial{W_1}}$
这部分,使用ReLU后,当梯度可以传递时,有
$\frac{\partial{f_3}}{\partial{a_3}}=\frac{\partial{f_2}}{\partial{a_2}}=\frac{\partial{f_3}}{\partial{a_1}}=1$ ,但这个式子中还是会有两个 $W$ 的连乘。在更长程上,就会有更多 $W$ 的连乘。对于CNN来说,这个部分是 $W_1,W_2,W_3…..$ 进行连乘,一方面它们都是稀疏矩阵,另一方面 $W_1,W_2,W_3….$ 互不相同,很大程度上都能抵消掉梯度爆炸的影响。

最后,IRNN在RNN上使用了ReLU,取得了比较好的结果,其中的原因在于,它对 $W$ 和 $b_i$ 取了比较特殊的初值: $W=I$ , $b_i=0$ 。
这样在梯度的式子 $\frac{\partial{f_3}}{\partial{a_3}}W\frac{\partial{f_2}}{\partial{a_2}}W\frac{\partial{f_1}}{\partial{a_1}}\frac{\partial{a_1}}{\partial{W_1}}$ 中W尽管会连乘,但是会类似于单位矩阵 $I$ 的连乘,不会引起太明显的梯度数值变化。另外一方面,也不会引起非常大的输出值

RNN

LSTM

梯度爆炸 策略

梯度爆炸问题其实不是什么麻烦。

因为现在大家都会做某种形式的Gradient clipping(也就是限定一下梯度绝对值的上限,超过就截断)来避免梯度爆炸。
觉得Gradient clipping很糙?其实一点都不糙,因为用SGD训练深度模型数学上本身就已经糙的不能再糙了。觉得LSTM不需要这种东西?No。如果查查主流工具包,或者看看比较实际的LSTM应用论文,应该都至少这么做了。

SGD为什么糙?只有GD才不糙?一切近似算法都糙?

saizheng: relu确实容易explode,除非加大很tricky的clipping,因为clipping多了,优化就做不好了
为什么??

saizheng: 普通rnn做超长memory一般就用quoc le的那个trick,identity init,我猜你看过那个paper,但是那个东西的tradeoff就是损失了短时间段内fit复杂nonlinearty的能力

参考