数值稳定性
神经网络的梯度
-
考虑如下有
d
层的神经网络 -
计算损失
ℓ
关于参数 Wt 的梯度
数值稳定性的常见问题
- 梯度爆炸:1.5100 约等于 4 x 1017
- 梯度消失:0.8100约等于 2 x 10-10
例子:MLP
加入如下MLP(为了简单省略了偏移)
梯度爆炸
使用ReLU作为激活函数
如果d-t
很大,值将会很大
梯度爆炸的问题
- 值超出值域(infinity)
- 对于16位浮点数尤为严重(数值区间6e-5到6e4)
- 对学习率敏感
- 如果学习率太大 -> 大参数值 -> 更大的梯度
- 如果学习率太小 -> 训练无进展
- 我们可能需要在训练过程不断调整学习率
梯度消失
使用sigmoid作为激活函数
梯度消失的问题
-
梯度值变成
0
对
16
位浮点数尤为严重 -
训练没有进展
不管如何选择学习率
-
对底部层尤为严重
仅仅顶部层训练的较好
无法让神经网络更深
总结
- 当数值过大或者过小时会导致数值问题
- 常发生在深度模型中,因为其会对
n
个数累乘
模型初始化和激活函数
如何让训练更加稳定
-
目标:让梯度值在合理的范围内
例如
[1e-6, 1e3]
-
将乘法变加法
ResNet
,LSTM
-
归一化
梯度归一化,梯度裁剪
-
合理的权重初始和激活函数
让每层的方差是一个常数
-
将每层的输出和梯度都看做随机变量
-
让它们的均值和方差都保持一致
权重初始化
-
在合理值区间里随机初始参数
-
训练开始的时候更容易有数值不稳定
- 远离最优的地方损失函数表面可能很复杂
- 最优解附近表面会比较平
-
使用
𝒩(0, 0.01)
来初始可能对小网络没问题,但不能保证深度神经网络
例子:MLP
正向方差
反向均值和方差
跟正向情况类似
Xavier初始
假设线性的激活函数
反向
检查常用激活函数
-
使用泰勒展开
-
调整sigmoid:
4 x sigmoid(x)-2
总结
合理的权重初始值和激活函数的选取可以提升数值稳定性
代码实现
数值稳定性和模型初始化
梯度消失
%matplotlib inline
import torch
from d2l import torch as d2l
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))
结果:
梯度爆炸
M = torch.normal(0, 1, size=(4, 4))
print('一个矩阵 \n', M)
for i in range(100):
M = torch.mm(M, torch.normal(0, 1, size=(4, 4)))
print('乘以100个矩阵后\n', M)
结果: