最近参加面试时被问到了神经网络优化方面的问题,由于平时没有好好总结,导致直接拉胯。这篇文章对当前神经网络训练中的常见优化方法进行了比较全面的总结,文章的大部分内容均来自邱锡鹏老师的《神经网络与深度学习》[1] ,部分地方加入了自己的理解。整篇文章的思维导图如下:
神经网络模型在实际训练中存在着以下两类问题:
本文将从「网络优化」和「网络正则化」两个方面来介绍一些经验方法,这些方法可以帮助神经网络在表示能力、复杂度、学习效率和泛化能力之间找到较好的平衡。
网络优化是指寻找一个神经网络模型来使得「结构风险最小化」(即包含正则化的经验风险最小化)的过程。如之前所述,优化的困难主要体现在三个方面,下面将针对非凸优化问题进行简要的介绍。
在低维空间中,非凸优化的主要难点是如何选择初始化参数和逃离局部最优点。而对深度神经网络来说,其参数学习是非常高维空间中的非凸优化问题。在高维空间中,非凸优化问题的难点不在于如何逃离局部最优点,而是如何逃离「鞍点」。直观来看,鞍点的梯度是 0,但是在一些维度上是最高点,在另一些维度上是最低点,如下图所示。其特征是一阶梯度为 0,但是二阶梯度的 Hessian 矩阵不是半正定矩阵。
基于梯度下降的优化方法会在鞍点附近接近停滞,很难从这些鞍点中逃离。「随机梯度下降」(即每次迭代时只采集一个样本来更新参数)通过在梯度方向上引入随机性,可以有效地逃离鞍点,
另一方面,在非常大的神经网络中,「局部最小解」对应的训练损失通常都非常接近于全局最小解,此外大部分的局部最小解是等价的,它们在测试集上的性能都比较相似。虽然神经网络有一定概率收敛于比较差的局部最小值,但随着网络规模的增加,陷入比较差的局部最小值的概率会大大降低,因此在训练网络时,我们通常没有必要找全局最小值,只需要找到局部最小值即可。
而由于深度神经网络的参数非常多,且有一定的冗余性,使得每个参数对最终损失的影响都比较小,导致损失函数在局部最小解附近通常是一个平坦的区域,称为「平坦最小值」。下图给出了平坦最小值和尖锐最小值的示例。经验表明,平坦最小值通常和模型泛化能力有一定的关系,当一个模型收敛到一个平坦的局部最小值时,其鲁棒性会更好(具备良好的泛化能力),因此理想的局部最小值应该是平坦的。
总的来看,改善神经网络的目标是找到更好的局部最小值和提高优化效率,目前比较有效的经验性改善方法可以分为以下几方面:
其中优化地形指在高维空间中损失函数的曲面形状,好的优化地形通常比较平滑。优化地形可以让梯度变大,有效地避免「梯度消失」问题。原书中列举了三种方法:ReLU 激活函数、残差连接和逐层归一化,本章将重点关注逐层归一化。
目前,深度神经网络的参数学习主要是通过「梯度下降」来寻找一组可以最小化结构风险的参数。在具体实现中,梯度下降法可以分为:批量梯度下降、随机梯度下降和小批量梯度下降。
批量梯度下降基于整个训练数据的梯度,计算资源消耗过大;随机梯度下降基于每个样本的梯度,引入随机噪声以逃离鞍点,但是无法充分利用计算机的并行计算能力。在实际训练中,通常选择两者的折中——「小批量梯度下降法」,每次迭代时随机选取一小部分训练样本来计算梯度并更新参数,这样既可以兼顾随机梯度下降法的优点,也可以提高训练效率。
令
表示一个深度神经网络,
为网络参数,在使用小批量梯度下降进行优化时,每次选择
个训练样本
,第
次迭代时损失函数关于参数
的偏导数为:
其中
为可微分的损失函数,这里忽略了正则化项;
称为「批量大小」。
第
次更新的梯度
定义为:
使用梯度下降来更新参数:
其中
为学习率。每次迭代时参数更新的差值
定义为:
代表每次迭代时参数的实际更新方向,即
。在标准的小批量梯度下降中,
,而由于引入了一些改进方法,实际应用中
和
并不完全一致。
基于上述公式,影响小批量梯度下降的主要因素有:
下面将分别从三个方面介绍神经网络优化中的常用算法,大部分算法对批量或随机梯度下降同样适用。
一般来说,批量大小不影响随机梯度的期望,但是会影响随机梯度的「方差」,批量大小越大,随机梯度的方差越小,引入的噪声也越小,训练也越稳定。学习率通常随着批量大小的增大而相应地增大。一个简单有效的方法是「线性缩放规则」,当批量大小增加
倍时,学习率也增加
倍。线性缩放规则往往在批量大小比较小时适用,当批量大小非常大时,线性缩放会使得训练不稳定。
下图给出了从「迭代」(Iteration)和「回合」(Epoch)的角度,批量大小对损失下降的影响。每一次小批量更新为一次迭代,所有训练集的样本更新一遍为一个回合。从图中可以看出,从迭代角度看,批量大小越大,下降效果越明显,且下降曲线越平滑;而从回合角度看,则是批量大小越小,收敛速度越快(实际上由于学习率不同,对比并不严格)。
此外,批量大小和模型的「泛化能力」也有一定的关系。批量越大,越有可能收敛到尖锐最小值;批量越小,越有可能收敛到平坦最小值。综上所述,批量大小的选择不宜过大或过小,需根据实际需求做出选择,较大的批量可以更准确地估计梯度,而较小的批量可以获得更快的收敛速度。
学习率是神经网络优化时的重要超参数。在梯度下降中,学习率过大则不会收敛,学习率过小则收敛速度太慢。常用的学习率调整方法包括学习率衰减、学习率预热、周期性学习率调整以及一些自适应调整学习率的方法,下面将分别介绍。
从经验上看,学习率在一开始要大一些来保证收敛速度,在收敛到最优点附近要小些来避免来回振荡。这种调整方式被称为「学习率衰减」(Learning Rate Decay),也称为「学习率退火」(Learning Rate Annealing)。
学习率衰减可以按每次迭代进行,也可以按每个回合进行,这里选择衰减方式为按「迭代次数」进行衰减。假设初始化学习率为
,第
次迭代时的学习率为
,常见的衰减方法有以下几种:
「分段常数衰减」。每经过
次迭代将学习率衰减为原来的
倍,其中
和
为根据经验设置的超参数。也被称为「阶梯衰减」。
「逆时衰减」。计算公式如下,其中
为衰减率:
「指数衰减」。计算公式如下,其中
为衰减率:
「自然指数衰减」。计算公式如下,其中
为衰减率:
「余弦衰减」。其中
为总的迭代次数:
下图给出了不同衰减方法的比较:
在小批量梯度下降法中,当批量大小设置的比较大时,通常需要较大的学习率。然而在刚开始训练时,由于参数使随机初始化的,梯度往往也比较大,再加上比较大的初始学习率,会使得训练不稳定。为了提高训练稳定性,可以在最初几轮迭代时,采用比较小的学习率,等梯度下降到一定程度时再恢复到初始的学习率,这种方法称为「学习率预热」(Learning Rate Warmup)。
一种常用的学习率预热方法是「逐渐预热」。假设预热的迭代次数为
,初始学习率为
,在预热过程中,每次更新的学习率为:
当预热过程结束,再选择一种学习率衰减方法来逐渐降低学习率。
为了使得梯度下降能够逃离鞍点或尖锐最小值,一种经验性的方式就是在训练过程中「周期性」地增大学习率。当参数处于尖锐最小值或鞍点附近时,增大学习率有助于逃离该点;当参数处于平坦最小值附近时,增大学习率依然有可能在该平坦最小值的「吸引域」内。虽然周期性地增大学习率可能会使得网络收敛的稳定性变差,但是从长期看有助于找到更好的局部最优解。本节介绍两种常用的周期性调整学习率的方法:「循环学习率」和「带热重启的随机梯度下降」。
「循环学习率」。让学习率在一个区间内周期性地增大和缩小,通常可以使用线性缩放来调整学习率,称为「三角循环学习率」。假设每个循环周期的长度相等都为
(以迭代次数表示),其中前
步为学习率线性增大节点,后
步为学习率线性缩小阶段。在第
次迭代时,对应的循环周期数
为:
其中
表示向下取整。第
次迭代的学习率为:
其中
和
分别为第
个周期中学习率的上界和下界,可以随着
的增大而逐渐降低;
的计算公式为:
可以看出,在一个循环周期内,
的取值从 1 到 0 再到 1,学习率先增大后减小。
「带热重启的随机梯度下降」。用热重启方式来替代学习率衰减的方法,学习率每间隔一定周期后重新初始化为某个预先设定的值,然后逐渐衰减。每次重启后模型参数不是从头开始优化,而是在重启前的参数基础上继续优化。
假设在梯度下降的过程中重启
次,第
次重启在上次重启后开始第
个回合后进行,
称为「重启周期」(注意这里以回合而非迭代为单位)。在每个周期内,利用余弦衰减来降低学习率。对于第
次迭代,假设其位于第
个周期中,则学习率为:
其中
为从上次重启之后的回合数,可以取小数以在一个回合内部进行学习率衰减(即以迭代为单位衰减),重启周期可以随着重启次数逐渐增加。
下图给出了两种周期性学习率调整的示例(假设初始学习率为 1),每个周期中的学习率上界也逐步衰减。
在标准梯度下降中,每个参数在每次迭代时都使用相同的学习率,然而由于每个参数的维度上收敛速度都不相同,因此可以根据不同参数的收敛情况分别设置学习率。下面介绍几种常用的算法。
该算法借鉴了
正则化的思想,在每次迭代时自适应地调整每个参数的学习率。在第
次迭代时,先计算每个参数对应的梯度平方的累计值:
其中
为按元素乘积,
为第
次迭代时的梯度。
AdaGrad 算法的参数更新差值为:
其中
为初始的学习率,
是为了保持数值稳定性而设置的极小常数,一般取值为
到
。
在 AdaGrad 算法中,如果某个参数的偏导数累积比较大,其学习率相对较小;相反,如果其偏导数累积较小,则学习率相对较大。整体来看则是随着迭代次数的增加,学习率逐渐缩小。算法的缺点在于经过一定次数的迭代依然没有找到最优点时,由于这时的学习率已经非常小,很难再继续找到最优点。
RMSprop 算法也是一种自适应学习率的方法,可以在有些情况下避免 AdaGrad 算法中学习率不断单调下降以至于过早衰减的缺点。
该算法首先计算每次迭代梯度
平方的指数衰减移动平均:
其中
为衰减类,一般取值为 0.9。
RMSprop 算法的参数更新差值为:
其中
是初始的学习率,比如 0.001。可以看出,RMSprop 算法和 AdaGrad 算法的区别在于
的计算由累积方式变成了指数衰减平均,在迭代过程中,每个参数的学习率并不是呈衰减趋势,既可以变小也可以变大。
AdaDelta 算法也是对 AdaGrad 算法的一种改进,其与 RMSprop 算法类似,通过梯度平方的指数衰减移动平均来调整学习率,此外,该算法还引入了每次参数更新差值
的平方的指数衰减权移动平均,第
次迭代时其计算公式如下:
其中
为衰减率。AdaDelta 算法的参数更新差值为:
可以看出,AdaDelta 算法将 RMSprop 算法中的初始学习率
改为动态计算的
,在一定程度上平抑了学习率的波动。
在小批量梯度下降中,如果批量大小较小,损失会呈现振荡式的下降。其原因在于每次迭代的梯度估计和整个训练集上的最优梯度并不一致,具有一定的随机性,虽然这种随机性可以帮助逃离鞍点,但是对于优化速度会产生影响。一种有效地缓解梯度估计随机性的方式是通过使用最近一段时间内的「平均梯度」来代替当前时刻的随机梯度来作为参数更新的方向,从而提高「优化速度」。
「动量」(Momentum)是模拟物理中的概念,其表示物体在运动方向上的保持运动的趋势,通过质量和速度的乘积计算。动量法将每次迭代的梯度看成加速度,计算负梯度的「加权移动平均」作为参数的更新方向。第
次迭代时参数的更新公式为:
其中
为动量因子,通常设为 0.9,
为学习率。
通过动量法,每个参数的实际更新差值取决于最近一段时间内的梯度的加权平均值。参数的更新幅度大小受该参数最近一段时间内的梯度方向的一致性影响。一般来说,在「迭代初期」,梯度方向都比较一致,动量法会起到「加速」作用,可以更快地到达最优点;而在「迭代后期」,梯度方向会不一致,在收敛值附近振荡,动量法则会起到「减速」作用,增加稳定性。
「Nesterov 加速梯度」(NAG)是对动量法的一种改进,也被成为 「Nesterov 动量法」。在动量法中,实际的参数更新方向
为上一步的参数更新方向
和当前梯度的反方向
的叠加,我们可以将更新拆解为如下两步:
其中
。而根据上式,似乎将第二步中的梯度更新方向设置为
上的梯度更加合理。基于此思想,合并后的更新方向为:
下图给出了动量法和 NAG 在参数更新时的比较,实验结果表明 NAG 的收敛速度要比动量法更快,波动也小了很多。
Adam 算法可以看作是动量法和 RMSprop 算法的结合,不但使用动量作为参数更新方向,而且可以自适应地调整学习率。其一方面计算梯度
的指数加权平均(和动量法类似),另一方面计算梯度平方
的指数加权平均(和 RMSprop 算法类似):
其中
和
分别为两个移动平均的衰减率,通常取值为
。
我们可以将
和
看作是梯度的均值和未减去均值的方差。在迭代初期, 由于衰减率接近 1,
和
的值会比真实的均值和方差要小,因此需要对计算公式进行「修正」:
Adam 算法的参数更新差值为:
其中学习率
通常设置为 0.001,并且也可以进行衰减,如
。由于 Adam 算法是 RMSProp 算法和动量法的结合,因此一种自然的 Adam 算法的改进是引入 Nestero 加速梯度,称为 「Nadam 算法」。
在深度神经网络中,除了梯度消失外,「梯度爆炸」也是影响学习效率的主要因素。如果在优化过程中梯度突然增大,用大的梯度更新参数反而会导致其远离最优点。为了避免这种情况,当梯度的模大于一定阈值时,就对梯度进行截断,称为「梯度截断」(Gradient Clipping)。一般的截断方式有以下几种:
「按值截断」。在第
次迭代时,梯度为
,给定一个区间
,如果一个参数的梯度小于
时,就将其设定为
,如果大于
时,就将其设为
。公式表达如下:
个人思考:关于按值截断的下界阈值
,如果设置得较大,实际上也可以一定程度地缓解梯度消失,但是在模型接近收敛时,我们希望梯度较小,以便模型收敛至局部最优,所以这种处理可能会影响收敛。因此在实际训练中通常不采取这种方法来处理梯度消失。
「按模截断」。将梯度的模截断到一个给定的截断阈值
。如果
,保持不变,否则令:
截断阈值
是一个超参数,可以设为定值,也可以根据一段时间内的平均梯度来自动调整。实现发现,训练过程对阈值
并不十分敏感,通常设置一个较小值即可。
本章主要介绍了针对小批量梯度下降的优化算法,包括批量大小选择、学习率调整(使优化更稳定)和梯度估计修正(优化训练速度),下表对常用的算法进行了总结:
实际上,我们可以使用下面的公式来统一描述概括这些优化算法:
其中
是第
步的梯度;
是第
步的学习率,可以进行衰减,也可以不变;
是学习率缩放函数,可以取 1 或历史梯度的平方的移动平均(或平均);
是优化后的参数更新方向,可以取当前的梯度
或历史梯度的移动平均。
下图给出了这几种优化方法在 MINST 数据集上收敛性的比较(学习率为 0.001,批量大小为 128)。看上去在前 3000 次迭代中 Adam 和 RMSprop 的收敛性最好,不过收敛过程中波动较大;AdaDelta 意外地连普通 SGD 都不如,怒获倒数第一。
在使用梯度下降进行参数优化时,参数初始值的选取十分关键,关系到网络的优化效率和泛化能力,参数的初始化通常有以下三种:
本节将介绍三类常用的随机初始化方法:基于固定方差的参数初始化、基于方差缩放的参数初始化和正交初始化。
一种最简单的随机初始化方法是从一个固定均值(通常为 0)和方差为
的分布中采样来生成参数的初始值,主要有以下两种:
「高斯分布初始化」。使用一个高斯分布
对每个参数进行随机初始化。
「均匀分布初始化」。在给定的区间
内采用均匀分布来初始化参数。由于均匀分布
对应的方差为:
因此,当
时,
的取值为:
在基于固定方差的随机初始化中,方差大小的设置比较关键。如果设置的太小,会导致神经元的输出过小(信号慢慢消失)以及网络丢失非线性的能力(如 Sigmoid 函数在 0 附近近似线性);如果设置的太大,会导致输入状态过大,对于 Sigmoid 函数来说梯度会接近于 0,引起梯度消失问题的出现。
在实际应用中,基于固定方差的随机初始化方法需要配合「逐层归一化」来使用,以降低固定方差对网络性能和优化效率的影响。
在进行参数初始化时,为了防止信号被过分放大或减弱,以及缓解梯度消失或爆炸问题,我们需要尽可能地保持每个神经元的输入和输出的方差一致,根据神经元的的连接数量自适应地调整初始化分布的方差,这类方法称为「方差缩放」(Variance Scaling)。
假定在一个神经网络中,第
层的一个神经元
,其接收前一层的
个神经元的输出
,如下所示:
其中
为激活函数,这里简单起见将其设为恒等函数
,
为参数,
为第
层神经元个数。
假设
和
的均值都为 0,且相互独立,则
的均值为:
对应的方差为:
即输入信号的方差在经过该神经元后被放大或缩小了
倍,为了保证输入和输出的方差一致,可以将其设为 1,即:
同理,为了在反向传播中,误差信号的方差也不被放大或缩小,需要将
的方差保持为:
将二者结合在一起,同时考虑信号在前向和反向传播中都不被放大或缩小,可以设置:
计算出参数的理想方差后,可以通过上一节的高斯分布或均匀分布来随机初始化参数,这种方法称为 「Xavier 初始化」。如果以高斯分布初始化,则方差取值为
;以均为分布初始化,则
的取值为
。
Xavier 初始化适用于 「Logistic 函数」(就是 Sigmoid)和 「Tanh 函数」,因为二者均可以近似为线性函数(大部分时候神经元的参数和输入的绝对值较小,处于激活函数的线性区间内),注意由于 Logisitc 函数在线性区间的斜率约为 0.25,所以其参数初始化的方差约为
。在实际应用中,通常将方差乘以一个缩放因子
,根据经验进行设定(Transformer 的具体实现时就针对所有参数使用了标准的 Xavier 初始化)。
当第
层神经元使用 「ReLU 激活函数」时,通常有一半的神经元输出为 0,因此其分布的方差也近似为使用恒等函数时的一半。因此,只考虑前向传播(实际应用时也要算上反向传播)时,参数
的理想方差为:
若采用高斯分布来初始化参数
,其方差为
;若采用均匀分布,则
。这种方法也称为 「He 初始化」。下表总结了 Xavier 初始化和 He 初始化的具体设置情况:
以上两种基于方差的初始化方法都是对权重矩阵中的每个参数进行独立采样,由于采样的随机性,采样出来的矩阵依然可能存在梯度消失或爆炸问题。以一个
层的等宽线性网络为例(激活函数为恒等函数):
其中
为神经网络的第
层权重矩阵。在反向传播中,「误差项」
的反向传播公式为
。为例避免梯度消失或爆炸问题,我们希望误差项在反向传播中具有「范数保持性」,即
。
为了实现这一点,在有限的
下我们可以将
初始化为正交矩阵,即
,这种方法称为「正交初始化」。其具体实现过程分为两步:
当在非线性神经网络中应用正交初始化时,通常需要将正交矩阵乘以一个缩放系数
。正交初始化常用于 RNN 中循环边上的权重矩阵上,能有效缓解梯度消失或爆炸问题。
一般而言,样本特征由于来源和度量单位的不同,其「尺度」(Scale)即取值范围往往差异很大。如果一个机器学习算法再缩放全部或部分特征后不影响它的学习和预测,我们就称该算法具有「尺度不变性」(Scale Invariance),如线性分类器就是尺度不变的,而最近邻分类器就是尺度敏感的。对于尺度敏感的模型,需要对样本进行「预处理」,将各个维度的特征转换到相同的区间,并消除不同特征之间的相关性。
对于神经网络来说,理论上其应该具有尺度不变性,可以通过参数的调整来适应不同特征的尺度,但是尺度不同的输入特征会增加训练难度,这主要体现在参数初始化的设置困难(需要满足不同尺度特征的敏感梯度区间)以及梯度下降法的效率上。下图给出了数据归一化对梯度的影响,可以看出尺度不同会导致大多数位置上的梯度方向并不是最优的搜索方向,导致迭代次数的增加。
预处理一般通过「归一化」(Normalization)方法来实现,指将数据特征转换为相同尺度的方法。下面介绍几种在神经网络中常用的归一化方法。
「最小最大值归一化」。通过缩放将每一个特征的取值范围归一到 [0,1] 或 [-1,1] 之间。假设有
个样本
,对于每一维特征
,归一化后的特征为:
「标准化」。该方法也叫做 Z 值归一化,将每一维特征都调整为均值为 0,方差为 1。对于每一维特征
,先计算其均值和方差:
然后,将特征
减去均值,并除以标准差,得到新的特征值
:
如果标准差为 0,说明这一维特征没有任何区分性,可以直接删掉。
「白化」。白化主要用于降低输入数据特征之间的冗余性。输入数据经过白化处理后,特征之间的相关性较低,且所有特征具有相同的方差。白化的一个主要实现方式是「主成分分析」(PCA)。下图给出了标准归一化和 PCA 白化的比较:
「逐层归一化」(Layer-wise Normalization)是将传统机器学习中的数据归一化方法应用到深度神经网络中,对神经网络中「隐藏层」的输入进行归一化,从而使网络更容易训练。其对于训练效率的提高主要体现在以下两个方面:
此外,逐层归一化还可以作为一种隐形的「正则化」方法。因为在训练时,神经网络对一个样本的预测不仅和该样本自身相关,也和同一批次中的其他样本相关。由于在选取批次时具有随机性,因此使得神经网络不会”过拟合“到某个特定样本,从而提高网络的泛化能力。
下面将介绍几种比较常用的逐层归一化方法:批量归一化、层归一化、权重归一化和局部响应归一化。
批量归一化是一种有效的逐层归一化方法,可以对神经网络中任意的中间层进行归一化操作。对于一个深度神经网络,令第
层的净输入为
,神经元的输出为
,即:
其中
为激活函数,
和
为可学习的参数。在实践中,归一化操作一般应用在「仿射变换」
之后,激活函数之前。
由于逐层归一化需要在每一层中间层进行操作,要求效率比较高,因此一般选择使用标准化将净输入
的每一维都归一到标准正态分布:
其中
和
是指当前参数下,
的每一维在整个训练集上的期望和方差。由于优化算法使用的是基于小批量的随机梯度下降,因此在训练过程中通常用当前「小批量」的均值和方差近似估计:
其中
为小批量样本的数量。经过上述变换,净输入的取值会集中到 0 附近,对于 Sigmoid 等激活函数来说,这会减弱神经网络的非线性性质,而对于 ReLU 来说,取值在 0 以下则会导致梯度消失(Dead ReLU)的出现。为了使得归一化不对网络的表示能力和优化效率产生负面影响,可以通过一个附加的「缩放」和「平移」来改变取值区间:
其中
和
分别表示缩放和平移的参数向量。最保守的情况下可以通过逆变换将归一化后的变量还原为原来的值(不过一般不用)。这两个参数也是可以学习的。
综上所述,加入批量归一化之后的单层神经元计算公式如下:
由于批量归一化本身具有平移变换,所以移除了仿射变换中的偏置参数。注意在训练时由于小批量样本的均值
和方差
为净输入
的函数,所以计算梯度时需要考虑其影响。训练完成后,使用整个数据集上的均值和方差替换小批量下的结果,来计算最终的输出。 在实践中,也可以使用训练阶段的均值与方差的「移动平均」来作为测试阶段的估计。
批量归一化是对一个中间层的单个神经元进行归一化操作,要求小批量样本不能太小。而如果一个神经元的净输入的分布在神经网络中是动态变化的,比如循环神经网络(同一节点在不同时刻的分布不同)和 Transformer,那么就无法应用批量归一化操作。
「层归一化」是和批量归一化非常类似的方法,区别在于其是对一个中间层的「所有神经元」进行归一化,对应的均值和方差为:
其中
为第
层神经元的数量。层归一化定义为:
可以看到层归一化和批量归一化一样有设置用于缩放和平移的参数向量。
对于「循环神经网络」来说,假设在时刻
。循环神经网络的隐藏层为
,则其层归一化的更新为:
其中输入
为第
时刻的输入,
和
为网络参数。实践表明,层归一化的 RNN 可以有效地缓解梯度爆炸或消失现象。
总的来说,层归一化和批量归一化的差别在于,对于
个样本的一个小批量集合
,层归一化是对矩阵
的每一列进行归一化,而批量归一化则是对每一行进行归一化。一般而言,批量归一化是一种更好的选择,而当小批量样本数量比较小或网络结构不满足要求时,可以选择层归一化。
权重归一化就是对神经网络的连接权重而非神经元输出进行归一化。具体来说,通过「再参数化」方法,将连接权重分解为「长度」和「方向」两种参数。假设第
层神经元
,则我们将
再参数化为:
其中
表示权重
的第
行(权重维数为
),新引入的参数
为标量,
与
的维数相同(这里指对应的神经元数量,而非批量维数)。由于在神经网络中权重经常是共享的(如 RNN),因此权重归一化的开销可能会比较小。
局部响应归一化是一种受生物神经元中的「侧抑制」现象启发的归一化方法,通常用在基于卷积的图像处理上。假设一个卷积层输出特征映射
为三维张量,其中每个切片矩阵
为一个输出特征映射,
,则局部响应归一化是对「同样位置的邻近的特征映射」进行局部归一化:
其中除法和幂运算都是按元素运算,
为超参,
称为局部归一化的「特征窗口」大小。
局部响应归一化和层归一化都是对同层的神经元进行归一化,区别在于局部响应归一化应用在激活函数之后,只是对邻近的神经元进行局部归一化,且不减去均值。
在神经网络中,超参数对网络性能有着很大的影响。常见的超参数一般可以分为以下三类:
「超参数优化」主要存在两方面的困难:一方面是超参数优化是一个组合优化问题,无法像普通参数那样通过梯度下降方法进行优化;另一方面则是评估一组超参数「配置」的时间代价非常高,导致一些优化方法(如演化算法)在超参数优化中难以应用。
本节将介绍几种简单的超参数配置方法:「网格搜索」、「随机搜索」、「贝叶斯优化」、「动态资源分配」和「神经架构搜索」。
网格搜索是一种通过尝试所有超参数的「组合」来寻址一组合适的超参数配置的方法。如果超参数是连续的,则可以将其离散化,根据超参数自身的特点选择几个经验值。假设共有
个超参数,第
个超参数可以取
个值,则总共的配置组合数量为:
网格搜索根据这些超参数的不同组合分别训练一个模型,然后测试这些模型在「开发集」(验证集)上的性能,选取一组性能最好的配置。
不同的超参数对模型性能的影响有很大差异,而网格搜索可能会在一些不重要的超参数上进行不必要的尝试。一种在实践中比较有效的改进方法是对超参数进行随机组合,然后选取一个性能最好的配置,即「随机搜索」。随机搜索在实践中更容易实现,一般会比网格搜索更加有效。
上述两种搜索都没有利用不同超参数组合之间的相关性,总的来说都比较低效。下面介绍两种「自适应」的超参数优化方法:贝叶斯优化和动态资源分配。
贝叶斯优化是根据当前已经试验的超参数组合,来预测下一个可能带来最大收益的组合。一种比较常用的贝叶斯优化方法是「时序模型优化」,其根据已有的 N 组实验结果
来建模超参数优化的函数
的高斯过程,并计算
的后验分布
。具体流程如下图所示:
为了用尽可能少的样本来修正分布,该方法通过定义一个「收益函数」来判断一个样本是否能够给建模
提供更多的收益。收益越大,其修正的高斯过程会越接近目标函数的真实分布。值得一提的是,贝叶斯优化中高斯过程建模的时间复杂度相对较高,难以处理高维情况,需要一些更高效的方法。
动态资源分配的关键是将有限的资源分配给更有可能带来收益的超参数组合。一种有效的方法是「逐次减半」方法,将超参数优化看作一种非随机的最优臂问题。假设要尝试
组超参数配置,总共可利用的资源预算为
,我们可以通过
轮逐次减半的方法来选取最优的配置,具体流程如下图所示:
上述方法都是在固定的超参数空间中进行最优配置搜索,而神经网络的架构一般还是需要由有经验的专家来进行设计。神经架构搜索是指通过神经网络来自动实现网络架构的设计。一个神经网络的二甲购可以用一个「变长的字符串」进行描述,研究人员利用一个「控制器」来生成另一个子网络的架构描述,控制器可以由一个循环神经网络来实现,通过「强化学习」进行训练,奖励信号为生成的子网络在开发集上的准确率。
机器学习模型的关键是泛化问题,即在样本真实分布上的「期望风险」最小化(即泛化误差)。而训练数据集上的「经验风险」最小化和期望风险并不一致,神经网络的极强拟合能力会导致过拟合的出现。
「正则化」是一类通过限制模型复杂度,从而避免过拟合,提高泛化能力的方法。在传统机器学习中,通常采用在经验风险函数后添加正则项的方法提高泛化能力(即「结构风险」最小化)。而在训练深度神经网络时,特别是参数数量非常大时,普通正则化(如
和
正则化)的效果可能不会很显著,往往还会采用其他的正则化方法。下面将对神经网络中常用的正则化方法进行介绍。
和
正则化是机器学习中最常用的正则化方法,通过约束参数的
和
「范数」来减小模型在训练数据集上的过拟合现象。通过加入
和
正则化,优化问题可以写为:
其中
为损失函数,
为训练样本数量,
为待学习的神经网络,
为其参数,
为范数函数,
的取值通常为
,代表
和
范数,
为正则化系数。上述正则化优化问题也等价于下面的带约束条件的优化问题:
由于
范数在零点不可导,因此经常用下式来近似:
其中
为参数数量,
为一个非常小的常数。下图给出了不同范数约束条件下的最优化问题示例,红线表示函数
,
为函数
的等高线(这里比较抽象,从图中得出直观印象即可)。可以看出,随着范数的增加,最优解会越来越远离坐标轴,而离坐标轴越近意味着最终的参数越稀疏,过拟合的可能性更小(但是也可能产生欠拟合)。
一种折中的正则化方法是同时加入
和
正则化,称为「弹性网络正则化」:
其中
和
分别为两个正则化项的系数。
权重衰减是神经网络中一种有效的正则化方法,在每次参数更新时,引入一个「衰减系数」:
其中
为第
步更新时的梯度,
为学习率,
为权重衰减系数,一般取值较小,如 0.00005。在标准的随机梯度下降中,权重衰减正则化和
正则化的效果相同,但是在较为复杂的优化方法中(如 Adam),两者并不等价。
提前停止是一种对神经网络来说简单有效的正则化方法。在使用梯度下降进行优化时,我们可以通过验证集上的错误来代替期望错误,当验证集上的错误率不再下降,就停止迭代,如下图所示:
然而在实际操作中,验证集上的错误率变化曲线并不一定是上图中的平衡曲线,可能先升高再降低。因此具体的停止标准需要根据实际任务进行优化。
「丢弃法」(即大名鼎鼎的 Dropout)是指在训练一个深度神经网络时,我们可以随机丢弃一部分神经元来避免过拟合,每次选择丢弃的神经元是随机的。最简单的方法是设置一个固定的概率
,对每一个神经元都以概率
来判定要不要保留,对于一个神经层
,我们引入一个掩蔽函数
使得
,掩蔽函数的定义为:
其中
是丢弃掩码,通过以概率为
的「伯努利分布」随机生成。而在测试时,为了缓解训练和测试时网络输出的不一致,需要将神经层的输入
乘以
。一般对于隐藏层的神经元,保留率
效果最好,而对于输入层的神经元,保留率通常设为更接近 1 的数,使得输入变化不会太大,同时又给数据增加了一定的噪声,以提高网络的鲁棒性。下图给出了一个网络应用丢弃法后的示例。
丢弃法一般是针对神经元进行随机丢弃,但是也可以扩展到神经元之间的连接或是整个层。关于丢弃法的合理性,可以通过两个角度进行解释。从「集成学习」的角度来看,每做一次丢弃,相当于从原始的网络中采样得到一个子网络,这些子网络都共享原始网络的参数,最终的网络可以近似看作集成了指数级不同网络的组合模型;从贝叶斯学习的角度看,用
表示要学习的神经网络,假设参数
为随机向量,先验分布为
,则贝叶斯方法的预测为:
其中
为第
次应用丢弃方法后的网络,其参数
为对全部参数
的一次采样。
对于 RNN 来说,不能直接对每个时刻的隐状态进行随机丢弃,这样会损害 RNN 在时间维度上的记忆能力。一种简单的方法是对「非循环连接」进行随机丢失,如下图所示,虚线边表示进行随机丢弃,不同的颜色表示不同的丢弃掩码:
而根据贝叶斯学习的解释,丢弃法是对参数
的采样,每次采样的参数需要在每个时刻保持不变。基于上述思想,一种新的丢弃法是对参数矩阵的每个元素进行随机丢弃,并在所有时刻使用相同的丢弃掩码,这种方法称为变分丢弃法,如下图所示(相同颜色表示使用相同的丢弃掩码):
在数据量有限的情况下,可以通过「数据增强」来增加数据量,提高模型鲁棒性,避免过拟合。目前,数据增强主要用于图像领域,在文本等其他类型数据上还没有广泛的应用。
图像数据的增强主要还是通过算法对图像进行转变,引入噪声等方法来增加数据的多样性。增强的方法主要有以下几种:
除了在样本中加入噪声,我们还可以在「输出标签」中添加噪声来避免模型过拟合,这种正则化方法被称为「标签平滑」。一个样本
的标签可以用 one-hot 向量表示,即:
这种标签可以看作「硬目标」。如果使用 softmax 分类器与交叉损失函数,则正确类和其他类的权重差异会异常大,可能导致过拟合的出现。如果标签中存在错误,则过拟合会更加严重。我们可以引入一个噪声对标签进行平滑,假设样本以
的概率为其他类,则平滑后的标签为:
其中
为标签数量,这种标签可以看作「软目标」。标签平滑可以避免模型的输出过拟合到硬目标上,并且通常不会损害其分类能力。除了上述平滑,还可以根据类别相关性来赋予其他标签不同的概率,如知识蒸馏中的教师网络和学生网络。
以上就是关于神经网络优化和正则化方法的全部介绍。关于优化和正则化的关系,原书中的这段话总结的很到位:
深度神经网络的优化和正则化是既对立又统一的关系。一方面我们希望优化算法能找到一个全局最优解(或较好的局部最优解),另一方面我们又不希望模型优化到最优解,这可能陷入过拟合。优化和正则化的统一目标是期望风险最小化。
近年来涌现出了很多深度神经网络的优化和正则化方法,虽然这些方法往往是「经验性」的,但是在实践中取得了很好的效果。在「优化」方面,训练神经网络时的主要难点是非凸优化和梯度消失问题,提高训练效率的方法通常分为以下三个方面:
在「泛化」方面,目前深度神经网络的泛化能力还没有比较好的理论支持,传统机器学习模型上比较有效的
和
正则化在深度神经网络中的作用也比较有限,一些经验做法(如小批量大小、大的学习率、权重衰减、提前停止、丢弃法、数据增强)往往会更加有效。
[1]
《神经网络与深度学习》2020.6.14: https://nndl.github.io