在本文中,评估了权值初始化的许多方法和当前的最佳实践
零初始化
将权值初始化为零是不行的。那我为什么在这里提到它呢?要理解权值初始化的需要,我们需要理解为什么将权值初始化为零是无效的。
让我们考虑一个类似于上面所示的简单网络。每个输入只是一个标量X₁,X₂X₃。和每个神经元的权重是W₁和W₂。每次权重更新如下:
Out₁ = X₁*W₁ X₂*W₁ X₃*W₁
Out₂ = X₁*W₂ X₂*W₂ X₃*W₂
正如你所看到的现在,如果权重矩阵W = [W₁W₂]被初始化为零,然后out1和out2都完全一样。
即使我们对两者都添加一个非零的随机偏差项,权值也会更新为非零,但它们仍将保持相同,因此隐藏单元的两个神经元都在计算相同的东西。换句话说,它们是对称的。
这是非常不可取的,因为这是浪费计算。这就是零初始化无法工作的原因。
随机初始化
现在我们知道权重必须是不同的,下一个想法是随机初始化这些权重。随机初始化比零初始化好得多,但是这些随机数可以是任意数吗?
假设你使用的是s型非线性。sigmoid函数如下所示。
我们可以看到,对于大到6的值,sigmoid的值几乎是1,对于小到-6的值,sigmoid的值为0。这意味着如果我们的权值矩阵被初始化为过大或过小的值,所有有用的信息都会在sigmoid函数中丢失。
如果我们使用ReLu非线性,这就不那么重要了,但是在将权重初始化为大值或小值时还有其他问题。有更好的方法来初始化权重。
Xavier初始化
Xavier初始化是由Xavier Glorot和Yoshua Bengio在2010年提出的。本文的主要目标是初始化权重,使激活的平均值为零,标准偏差为1。考虑如下所示计算的函数。
Z = WX b
这里W是权值矩阵,X是来自前一层的输入,b是偏差。Z是一个层计算的输出,也称为激活。我们希望Z的均值是0,标准差是1。(从技术上讲,Z是ReLu等非线性激活后的结果)
为什么均值为0,标准差为1这么重要?
考虑一个有100层的深度神经网络。在每一步,权重矩阵乘以来自前一层的激活。如果每一层的激活大于1,当它们被重复乘以100次时,它们就会不断变大,爆炸到无穷大。类似地,如果激活值小于1,它们将消失为零。这叫做渐变爆炸和渐变消失问题。我们可以从下图中看到这一点。甚至比1稍大一点的值也会爆炸成非常大的数字,而比1稍小一点的值也会消失为零。
为了避免梯度和激活的爆炸和消失,我们希望激活的平均值为0,标准偏差为1。我们可以通过仔细选择权重来实现这一点。
在本文发布期间,权值的最佳实践是从均匀分布[-1,1]中随机选取,然后除以输入维数的平方根。事实证明,这不是一个好主意,梯度消失了,如果可能的话,训练也非常缓慢。
这个问题通过Xavier的初始化得到了解决,Xavier的初始化建议我们从一个均匀分布中随机初始化权重,如下图所示。
Xavier 初始化的 Uniform分布
现在,Xavier的初始化是通过从标准正态分布中选择权重来完成的,每个元素都要除以输入维度大小的平方根。在PyTorch中,代码如下所示。
代码语言:javascript复制torch.randn(n_inp, n_out)*math.sqrt(1/n_inp)
Xavier的初始化工作相当好,对于对称非线性,如sigmoid和Tanh。然而,对于目前最常用的非线性函数ReLu,它的工作效果并不理想。
Kaiming 初始化
2015年,何凯明等人撰写了一篇名为《Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification》的论文,在论文中,他们介绍了现在广为人知的Kaiming 初始化。
但是为什么我们需要Kaiming 初始话呢?关于ReLu非线性的Xavier Init有什么问题?
从上图中可以看出,ReLu对所有X<0给出了0,对所有X>给出了Y=X。ReLu并没有很好地定义0,但大多数程序都将其赋值为接近于0的近似,比如machine epsilon。
左:均值0,标准差1的正态分布。右:通过ReLu后的正态分布。
在上面我们可以看到2个散点图,左边是ReLu之前的数据,右边是ReLu之后的数据。从图像中可以清楚地看到,在ReLu之后,方差几乎是一半,平均值略高。这改变了激活,方差减少了一半,所以我们需要将方差加倍才能得到Xavier Init的原始效果。因此,我们将权重乘以一个额外的值√2。所以在PyTorch中,Kaiming 初始化如下所示。
代码语言:javascript复制torch.randn(n_inp, n_out)*math.sqrt(2/n_inp)
如果你仍然对上面的公式不太理解,那么记住: 方差=标准差的平方 方差翻倍所以需要乘以√2
Fixup初始化
Fixup是Zhang等人在2019年提出的初始化。根据他们的观察,Kaiming 的初始化和其他标准的初始化不能很好地用于有残差分支的网络(又叫残差网络)。他们发现,标准初始化的残差网络只有在BatchNorm下才能正常工作。
让我们看看为什么Kaiming Init不能在残差网络上更好的工作。考虑如下图所示的跳过连接。X2 = f(X1) X3 = f(X2) X1。我们知道Kaiming init选择的权重使得每一层后的激活有0均值和1方差。我们知道X1的方差是1 X2的方差是1。但是Kaiming init不考虑跳过连接。因此,根据总方差定律,X3的方差翻倍。由残差分支添加的额外方差在Kaiming Init中没有考虑在内。因此残差网络不能很好地与标准初始化一起工作,除非它们有BatchNorm。没有BatchNorm,输出方差会随深度呈指数爆炸式增长。
Var[Xₗ₊₁] ≈ 2Var[Xₗ ]
残差网络中的跳过连接
该论文的作者提出了一个重要的观察结果,即SGD更新每个残差分支的权重会在高度相关的方向上更新网络输出。这意味着,如果所有残差分支权重都由X更新,则网络输出也将在相同的权重更新方向上按比例地更改为X。
作者将所需的网络输出变化定义为Θ(η)。如我们所知,平均每个残差分支对输出更新的贡献均等。如果我们称残差分支的数量为L,则每个残差分支平均应将输出改变Θ(η/ L),以实现总变化。输出上的Θ(η)。
接下来,作者展示如何初始化m层的残差分支,以便SGD更新将输出更改Θ(η/ L)。作者表明,可以通过以下方式重新调整这些权重层的标准初始化:
权重比例因子
作者还讨论了偏差和乘数的效用。他们发现在每次卷积,添加一个初始值为0的偏置层,线性层和逐元素激活导致训练显着改善之前。他们还发现,在每个残差分支上添加一个乘法标度有助于通过标准化来模仿网络的权重范数动态。
所有的解决方案汇总如下
LSUV初始化
Mishkin等人在2016年的一篇论文《All you need is a good Init》中介绍了LSUV。LSUV Init是一种数据驱动的方法,它具有最小的计算量和非常低的计算开销。初始化是一个2部分的过程,首先初始化标准正交矩阵的权值(与高斯噪声相反,它只是近似正交)。下一部分是迭代一个小批处理并缩放权重,以便激活的方差为1。作者断言,在大范围内,小批量大小对方差的影响可以忽略不计。
在论文中,作者列出了以下初始化步骤。
- 使用单位方差将权重初始化为高斯噪声。
- 使用SVD或QR将它们分解为正交坐标。
- 使用第一个微型批处理在网络中进行迭代,并在每次迭代比例时权重以使输出方差接近1。重复直到输出方差为1或发生最大迭代。
论文中,作者提出比例因子为√Var(BL),其中BL —它的输出Blob
作者还提出了最大迭代次数的值,以防止无限循环。但是,在他们的实验中,他们发现在1–5次迭代中实现了单位方差。
可以将LSUV Init看作是正交初始化和BatchNorm的组合,它仅在第一个迷你批处理中执行。作者在实验中表明,与完整的BatchNorm相比,该方法在计算上非常高效。
转移学习
转移学习是一种在我们的新模型中使用已经训练有素的模型进行权重的方法,该模型已经针对相似的任务进行了训练。这些权重已经学习了很多有用的信息,我们可以针对我们的特定目标进行微调!我们有一个了不起的模型,没有初始化的麻烦。
每次使用来自另一个模型的预训练权重都是最好的方法。唯一需要我们自己初始化权值的情况是,我们在一个从未有人训练过的网络上工作。在大多数实际情况下,情况并非如此,所以一般情况下,我们使用一个与训练的模型作为我们训练的开始是一个很好的习惯。
作者 Akash Shastri
deephub翻译组