4.8数值稳定性和模型初始化
梯度消失和梯度爆炸
神经网络的梯度
- 考虑一个d层的神经网络:
- 如何计算损失l关于参数Wt的梯度呢?
梯度消失 or 梯度爆炸
因为计算梯度时做了过多的矩阵乘法,累乘会导致幂次的不可控:
- eg1: 梯度爆炸: 1. 5 100 ≈ 4 × 1 0 17 1.5^{100} \approx 4 \times 10^{17} 1.5100≈4×1017
- eg2: 梯度消失: 0. 8 100 ≈ 2 × 1 0 − 10 0.8^{100} \approx 2 \times 10^{-10} 0.8100≈2×10−10
例:MLP(简单起见忽略了偏移b)
-
计算第t层输出结果:
那么,上式计算所得的ft(ht-1)就是第t层输出ht。 -
求导:分子布局-向量对向量求导得到矩阵
-
为啥是对角矩阵diag嘞?考虑一个简单情况W=(5,4,3), ht-1=(x,y,z), 那么Wht-1=(5x, 4y, 3z),对ht-1求导之后就是一个对角阵diag(5,4,3)
-
回到开头,计算第d层对第t层的导数——d-t次矩阵乘法
梯度爆炸
- 梯度爆炸的问题:
- 值超出值域, 变为inf,不可继续计算
- 对学习率过于敏感:
- 如果学习率稍微比较大→每次对权重的更新比较大→更大的梯度→…→梯度爆炸inf
- 如果学习率太小→训练无进展
- 就需要在训练过程中不断地调整学习率
梯度消失
- sigmoid函数是导致梯度消失问题的一个常见的原因,绘图来理解
x = torch.arange(-8.0, 8.0, 0.1, requires_grad=True)
y = torch.sigmoid(x)
y.backward(torch.ones_like(x))
d2l.plot(x.detach().numpy(), [y.detach().numpy(), x.grad.numpy()],
legend=['sigmoid', 'gradient'], figsize=(4.5, 2.5))
很明显,当sigmoid函数的输入很大或是很小时,它的梯度都会消失。
从而,回到之前计算d层对t层的求导:
- 梯度消失的问题:
- 梯度变为0 → 无论如何修改学习率,训练再无进展
- 对深层网络的底部网络更为严重:
- 因为反向传播是从顶开始的,所以会导致只有顶部层的训练效果比较好
- 无法使神经网络更深
让训练更加稳定
- 目标:使得梯度值在合理的范围内,eg:[1e-6,1e3]
- 措施:
- 将乘法变成加法:ResNet, LSTM
- 归一化:梯度归一化;梯度裁剪
- 设置合理的初始化参数&激活函数
设置合理的初始化参数
设置思路
- 让每一层的输出&梯度的均值为0,方差为固定常数:
Xavier初始化方法
以MLP为例推导Xavier方法
-
正向forward过程——使得每一层输出hit的方差恒定:那么就要求 n t − 1 γ t = 1 n_{t-1} \gamma_t = 1 nt−1γt=1⭐
-
反向backward过程——要求梯度的方差恒定
-
也就是说,要同时满足:
{ n t − 1 γ t = 1 n t γ t = 1 \left\{ \begin{aligned} n_{t-1} \gamma_t = 1 \\ n_{t} \gamma_t = 1 \\ \end{aligned} \right. {nt−1γt=1ntγt=1怎么可能呢?要满足这个,那就必须得是输入向量和输出向量是相同维度的了: n t − 1 = n t n_{t-1} = n_{t} nt−1=nt。不现实。 -
所以考虑折中一下:Xavier方法:
使得 γ t ( n t − 1 + n t ) / 2 = 1 \gamma_t(n_{t-1} + n_{t})/2 = 1 γt(nt−1+nt)/2=1
设置合理的激活函数
因此,思路就是找可以近似于x的激活函数!