数值稳定性和模型初始化
初始化方案的选择在神经网络学习中起着举足轻重的作用, 它对保持数值稳定性至关重要。
此外,这些初始化方案的选择可以与非线性激活函数的选择有趣的结合在一起。
选择哪个函数以及如何初始化参数可以决定优化算法收敛的速度有多快。 糟糕选择可能会导致我们在训练时遇到梯度爆炸或梯度消失。
梯度消失和梯度爆炸
不稳定梯度带来的风险不止在于数值表示; 不稳定梯度也威胁到我们优化算法的稳定性。 我们可能面临一些问题。
要么是梯度爆炸(gradient exploding)问题: 参数更新过大,破坏了模型的稳定收敛;
要么是梯度消失(gradient vanishing)问题: 参数更新过小,在每次更新时几乎不会移动,导致模型无法学习。
梯度消失
曾经sigmoid函数1/(1+exp(−𝑥)) 很流行, 因为它类似于阈值函数。
- 梯度值变为0,对16为浮点数尤为严重
- 训练没有进展,不管如何选择学习率
- 对底部层尤为严重
- 仅仅顶部层训练的较好
- 无法让神经网络更深
由于早期的人工神经网络受到生物神经网络的启发, 神经元要么完全激活要么完全不激活(就像生物神经元)的想法很有吸引力。
然而,它却是导致梯度消失问题的一个常见的原因, 让我们仔细看看sigmoid函数为什么会导致梯度消失。
%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))
当sigmoid函数的输入很大或是很小时,它的梯度都会消失。
此外,当反向传播通过许多层时,除非我们在刚刚好的地方, 这些地方sigmoid函数的输入接近于零,否则整个乘积的梯度可能会消失。 当我们的网络有很多层时,除非我们很小心,否则在某一层可能会切断梯度。
事实上,这个问题曾经困扰着深度网络的训练。
因此,更稳定的ReLU系列函数已经成为从业者的默认选择(虽然在神经科学的角度看起来不太合理)。
梯度爆炸
相反,梯度爆炸可能同样令人烦恼。
- 值超出值域,对16为浮点数尤为严重(6e-5到6e-4)
- 对学习率敏感
- 学习率太大:大参数值导致更大梯度
- 学习率太小:训练无进展
- 可能在训练过程中需要不断调整学习率
生成100个高斯随机矩阵,并将它们与某个初始矩阵相乘。 对于我们选择的尺度(方差𝜎2=1),矩阵乘积发生爆炸。
当这种情况是由于深度网络的初始化所导致时,我们没有机会让梯度下降优化器收敛。
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)
一个矩阵
tensor([[-0.6332, -0.2195, -0.8210, 0.2839],
[-0.3682, 1.2227, 0.7664, 0.2074],
[-0.2526, -0.2356, 1.3696, -0.4856],
[ 0.1025, 0.1475, 0.8112, -0.4159]])
乘以100个矩阵后
tensor([[-1.1608e+22, -2.3321e+22, 2.9323e+22, 3.5501e+22],
[ 1.6864e+21, 3.3881e+21, -4.2602e+21, -5.1576e+21],
[ 8.8276e+21, 1.7735e+22, -2.2300e+22, -2.6997e+22],
[ 9.5740e+21, 1.9235e+22, -2.4186e+22, -2.9281e+22]])
打破对称性
神经网络设计中的另一个问题是其参数化所固有的对称性。
假设我们有一个简单的多层感知机,它有一个隐藏层和两个隐藏单元。
在这种情况下,我们可以对第一层的权重𝐖(1)进行重排列, 并且同样对输出层的权重进行重排列,可以获得相同的函数。
第一个隐藏单元与第二个隐藏单元没有什么特别的区别。 换句话说,我们在每一层的隐藏单元之间具有排列对称性。
假设输出层将上述两个隐藏单元的多层感知机转换为仅一个输出单元。
想象一下,如果我们将隐藏层的所有参数初始化为𝐖(1)=𝑐, 𝑐为常量,会发生什么?
在这种情况下,在前向传播期间,两个隐藏单元采用相同的输入和参数, 产生相同的激活,该激活被送到输出单元。 在反向传播期间,根据参数𝐖(1)对输出单元进行微分, 得到一个梯度,其元素都取相同的值。
因此,在基于梯度的迭代(例如,小批量随机梯度下降)之后, 𝐖(1)的所有元素仍然采用相同的值。 这样的迭代永远不会打破对称性,我们可能永远也无法实现网络的表达能力。 隐藏层的行为就好像只有一个单元。
请注意,虽然小批量随机梯度下降不会打破这种对称性,但暂退法正则化可以。
让训练更加稳定
- 目标:让梯度值在合理范围内,例如[1e-6,1e3]
- 将乘法变加法
- ResNet ,LSTM
- 归一化
- 梯度归一化,梯度裁剪
- 合理的权重初始和激活函数
让每层的方差是一个常数
- 将每层的输出和梯度都看做随机变量
- 让它们的均值和方差保持一致
权重初始化
- 在合理值区间里随机初始化参数
- 训练开始的时候更容易有数值不稳定性
- 远离最优解的地方损失函数表面可能很复杂
- 最优解附近表面会比较平
- 使用N(0,0.01)来初始可能对小网络没问题,但不能保证深度神经网络
参数初始化
解决(或至少减轻)上述问题的一种方法是进行参数初始化, 优化期间的注意和适当的正则化也可以进一步提高稳定性。
默认初始化
我们之前使用正态分布来初始化权重值。如果不指定初始化方法, 框架将使用默认的随机初始化方法,对于中等难度的问题,这种方法通常很有效。
Xavier初始化
详解
检查常用激活函数
小结
- 梯度消失和梯度爆炸是深度网络中常见的问题。在参数初始化时需要非常小心,以确保梯度和参数可以得到很好的控制。
- 需要用启发式的初始化方法来确保初始梯度既不太大也不太小。
- ReLU激活函数缓解了梯度消失问题,这样可以加速收敛。
- 随机初始化是保证在进行优化前打破对称性的关键。
- Xavier初始化表明,对于每一层,输出的方差不受输入数量的影响,任何梯度的方差不受输出数量的影响。