目前,深度神经网络的参数学习主要是通过梯度下降法来寻找一组可以最小化结构风险的参数。在具体实现中,梯度下降法可以分为:批量梯度下降、随机梯度下降以及小批量梯度下降三种形式。根据不同的数据量和参数量,可以选择一种具体的实现形式。这里介绍一些在训练神经网络时常用的优化算法,这些优化算法大体上可以分为两类:1)调整学习率,使得优化更稳定;2)梯度估计修正,优化训练速度。
0 问题引入——小批量梯度下降(Mini-batch Gradient Descent)
在训练深度神经网络时,训练数据的规模通常都比较大。如果在梯度下降时,每次迭代都要计算整个训练数据上的梯度,这就需要比较多的计算资源。另外大规模训练集中的数据通常会非常冗余,也没有必要在整个训练集上计算梯度。因此,在训练深度神经网络时,经常使用小批量梯度下降法(Mini-Batch Gradient Descent)。
令 (f(oldsymbol{x} ; omega)) 表示一个深度神经网络,(omega) 为网络参数,在使用小批量梯度下降进行优化时,每次选取 (K) 个训练样本 (delta_t = {(oldsymbol{x}^{(k)}, oldsymbol{y}^{(k)})}_{k=1}^K) 。第 (t) 次迭代(Iteration) 时损失函数关于参数 (omega) 的偏导数为
其中 (mathcal{L}(cdot)) 为可微分的损失函数,(K) 称为批量大小(Batch Size)。
第 (t) 次更新的梯度 (g_t) 定义为
使用梯度下降来更新参数
其中 (alpha > 0) 为学习率。
从上面的公式来看,影响梯度下降法的主要因素有:
- 批量大小 (K)
- 学习率 (alpha)
- 梯度估计
因此,为了更有效地训练深度神经网络,在标准的小批量梯度下降法的基础上,我们会使用一些改进方法以加快优化速度,比如如何选择批量大小、如何调整学习率以及如何修正梯度估计。本文分别从这三个方面来介绍在神经网络优化中常用的算法。这些改进的优化算法也同样可以应用在批量或随机梯度下降法上。
1 批量大小选择
在小批量梯度下降法中,批量大小(Batch Size)对网络优化的影响非常大。一般而言,批量大小不影响随机梯度的期望,但是会影响随机梯度的方差。批量大小越大,随机梯度的方差越小,引入的噪声也越小,训练也越稳定,因此可以设置较大的学习率。而批量大小较小时,需要设置较小的学习率,否则模型会不收敛。学习率通常要随着批量大小的增大而相应地增大。一个简单有效的方法是线性缩放规则(Linear Scaling Rule)[Goyal et al., 2017]:当批量大小增加 (m) 倍时,学习率也增加 (m) 倍。线性缩放规则往往在批量大小比较小时适用,当批量大小非常大时,线性缩放会使得训练不稳定。
下图给出了从回合(Epoch)和 迭代(Iteration)的角度,批量大小对损失下降的影响。每一次小批量更新为一次迭代,所有训练集的样本更新一遍为一个回合。两者的关系为
值得注意的是,图 7.3 中的 4 种批量大小对应的学习率设置不同,因此并不是严格对比。
从图 7.3a 可以看出,批量大小越大,下降效果越明显,并且下降曲线越平滑。但从图 7.3b 可以看出,如果按整个数据集上的回合(Epoch)数来看,则是批量样本数越小,适当小的批量大小会导致更快的收敛。
如果训练集比较小(N <= 2000)时,通常使用 Batch Gradient Descent;当训练集比较大时,通常的 Mini-Batch size 设置为 64,128, 256,512 等,而且要确保 size 满足 CPU/GPU 内存。Mini-Batch size 可以作为一个参数,找到一个让梯度下降优化算法最高效的数值。
Gradient Descent 对比
如果 Mini-Batch size = m,则为 Batch Gradient Descent
如果 Mini-Batch size = 1,则为 Stochastic Gradient Descent
上图为 Batch gradient descent 和 Mini-batch gradient descent 的损失函数与迭代次数的关系。Batch gradient descent 每次迭代都是用全量样本,所以损失函数是单调下降的,而 Mini-batch gradient descent 每次迭代为部分样本,因此其损失函数呈震荡状态下降。
图中蓝色线条为 Batch gradient descent 寻找最优点路径;紫色曲线为 Stochastic Gradient Descent 寻找最优点路径,Stochastic Gradient Descent 会有许多噪声,因此永远不会收敛,而是在最小值附近波动。
2 学习率调整
学习率是神经网络优化时的重要超参数。在梯度下降法中,学习率 (alpha) 的取值非常关键,如果过大就不会收敛,如果过小则收敛速度太慢。常用的学习率调整方法包括学习率衰减、学习率预热、周期性学习率调整以及一些自适应调整学习率的方法,比如 AdaGrad、RMSprop、AdaDelta 等。自适应学习率方法可以针对每个参数设置不同的学习率。
2.1 学习率衰减(Learning Rate Decay)
从经验上看,学习率在一开始要保持大些来保证收敛速度,在收敛到最优点附近时要小些以避免来回振荡。比较简单的学习率调整可以通过学习率衰减 (Learning Rate Decay)的方式来实现,也称为学习率退火(Learning Rate Annealing)。
学习率衰减是按每次迭代(Iteration)进行, 也可以按每 (m) 次迭代或每个回合(Epoch)进行。衰减率通常和总迭代次数相关。不失一般性,这里的衰减方式设置为按迭代次数进行衰减。
假设初始化学习率为 (alpha_0),在第 (t) 次迭代时的学习率 (alpha_t), 常见的衰减方法有以下几种:
分段常数衰减(Piecewise Constant Decay)
即每经过 (T_1, T_2, cdots, T_m) 次迭代将学习率衰减为原来的 (eta_1, eta_2, cdots, eta_m) 倍,其中 (T_m) 和 (eta_m lt 1) 为根据经验设置的超参数。分段常数衰减也称为阶梯衰减(Step Decay)。
逆时衰减(Inverse Time Decay)
其中 (eta) 为衰减率。
指数衰减(Exponential Decay)
其中 (eta lt 1) 为衰减率。
自然指数衰减(Natural Exponential Decay)
其中 (eta) 为衰减率。
余弦衰减(Cosine Decay)
其中 (T) 为总的迭代次数。
下图给出了不同衰减方法的示例(假设初始学习率为 1)。
2.2 AdaGrad 算法(Adaptive Gradient Algorithm)
在小批量随机梯度下降法中,目标函数自变量(权重)的每一个元素在相同时间步都使用同一个学习率来自我迭代。在下面介绍的 “动量法” 一节里我们看到,当 (w_1) 和 (w_2) 的梯度值有较大差别时,需要选择足够小的学习率使得自变量在梯度值较大的维度上不发散。但这样会导致自变量在梯度值较小的维度上迭代过慢。动量法依赖指数加权移动平均(在下面有详细介绍)使得自变量的更新方向更加一致,从而降低发散的可能。本节我们介绍 AdaGrad 算法,它根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率,从而避免统一的学习率难以适应所有维度的问题。
AdaGrad 算法会使用一个小批量随机梯度 (g_t) 按元素的平方累加到变量 (G_t) 。在时间步 0,AdaGrad 将 (G_0) 中每个元素初始化为 0。在时间步 (t) ,首先将小批量随机梯度 (g_t) 按元素平方后累加到变量 (G_t):
其中 (odot) 是按元素相乘,(eta) 是学习率,(epsilon) 是为了维持数值稳定性而添加的常数,一般取值 (e^{-7}) 到 (e^{-10})。这些按元素运算使得目标函数自变量中每个元素都分别拥有自己的学习率。
需要强调的是,小批量随机梯度按元素平方的累加变量 (G_t) 出现在学习率的分母项中。因此,如果目标函数有关自变量(权重)中某个元素的偏导数一直都较大,那么该元素的学习率将下降较快;反之,如果目标函数有关自变量中某个元素的偏导数一直都较小,那么该元素的学习率将下降较慢。然而,由于 (G_t) 一直在累加按元素平方的梯度,自变量中每个元素的学习率在迭代过程中一直在降低(或不变)。所以,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad 算法在迭代后期由于学习率过小,可能较难找到一个有用的解。
2.3 RMSprop 算法(Root Mean Square Prop Algorithm)
在 “AdaGrad算法” 一节中提到,因为调整学习率时分母上的变量 (G_t) 一直在累加按元素平方的小批量随机梯度,所以目标函数自变量每个元素的学习率在迭代过程中一直在降低(或不变)。 因此,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad 算法在迭代后期由于学习率过小,可能较难找到一个有用的解。为了解决这一问题,RMSProp 算法对 AdaGrad 算法做了一点小小的修改。
不同于 AdaGrad 算法里状态变量 (G_t) 是截至时间步 (t) 所有小批量随机梯度 (g_t) 按元素平方和,RMSProp 算法将这些梯度按元素平方做指数加权移动平均。具体来说,给定超参数 (0 leq eta lt 1) ,(epsilon = 10^{-8}) 是一个不错的选择, RMSProp 算法在时间步 (t > 0) 计算
因为 RMSProp 算法的状态变量 (G_t) 是对平方项 (g_t odot gt) 的指数加权移动平均,所以可以看作最近 (1/(1 − eta)) 个时间步的小批量随机梯度平方项的加权平均。如此一来,自变量每个元素的学习率在迭代过程中就不再一直降低(或不变)。
2.4 AdaDelta 算法
除了 RMSProp 算法以外,另一个常用优化算法 AdaDelta 算法也针对 AdaGrad 算法在迭代后期可能较难找到有用解的问题做了改进。有意思的是,AdaDelta 算法没有学习率这一超参数。
AdaDelta 算法也像 RMSProp 算法一样,使用了小批量随机梯度 (g_t) 按元素平方的指数加权移动平均变量 (G_t)。在时间步 0,它的所有元素被初始化为 0。给定超参数 (0 leq eta lt 1)(对应RMSProp算法中的 (eta)),在时间步 (t > 0),同 RMSProp 算法一样计算
与 RMSProp 算法不同的是,AdaDelta 算法还维护一个额外的状态变量 (Delta X_t),其元素同样在时间步 0 时被初始化为 0。我们使用 (Delta X_{t-1}) 来计算自变量(权重)的变化量:
其中 (epsilon) 是为了维持数值稳定性而添加的常数,如 (10^{−8}) 。接着更新自变量:
最后,我们使用 (Delta X_t) 来记录自变量变化量 (g_t') 按元素平方的指数加权移动平均:
可以看到,如不考虑 (epsilon) 的影响,AdaDelta 算法与 RMSProp 算法的不同之处在于使用 (sqrt{Delta X_{t-1}}) 来替代超参数 (eta)。
3 梯度估计修正
3.1 指数加权平均数(Exponentially Weighted Averages)
3.1.1 指数加权平均数
有几个复杂的优化算法,它们比梯度下降法速度要快,但是,要理解这些算法,就需要用到指数加权平均,在统计上也叫作指数加权移动平均,本节对这个概念进行介绍。
以伦敦的温度为例,上图中的散点图标记的是从 1月1日开始全年的温度,也就是说大概夏季温度转暖,冬季降温。
上图中的数据看起来有些杂乱,如果要计算趋势的话,也就是温度的局部平均值,或者说移动平均值,可以用如下方法:
利用 (v_1 = 0.9v_0 + 0.1 heta_1) 计算得出第一天的移动平均温度值,依次类推,就得到上图中的红色曲线。如果把上面的公式 (v_t = 0.9v_{t-1}+ 0.1 heta_t) 中的 0.9 变成 (eta) ,则 0.1 可以写为 (1 - eta) ,公式即变为
由于以后要考虑的原因,在计算时可以认为 (v_t) 大概是近 (frac{1}{1-eta}) 日的移动平均温度,如果 (eta) 是 0.9,则计算 10 天的平均值,也就是下图中红色曲线。
当 (eta) 为一个接近 1 的值时,比如 0.98,计算 (frac{1}{1-0.98} = 50) ,则粗略计算一下过去 50 天的温度,下图中的绿色曲线。当值 (eta) 比较大时,得到的曲线会平坦一些,波动也小,缺点是曲线右移,变化缓慢。
当 (eta) 为一个极端值,比如 0.5 ,根据公式 (frac{1}{1-eta}) 就是平均了 2 天的温度,下图中黄色曲线。近平均了 2 天的温度,数据太少,得到的曲线有更多的噪声,有可能会出现异常值,但是这个曲线能够更快适应温度变化。
3.1.2 理解指数加权平均数
在这里进一步分析,理解如何计算出每日温度的平均值,并且理解指数加权平均数的本质。
使 (eta = 0.9),则有:
由此我们可以得到:
这些系数 (0.1, 0.1 imes 0.9, 0.1 imes (0.9)^2, cdots),相加起来为 1 或者逼近 1,我们称之为偏差修正。
计算 (v_{100}) 需要 ( heta_i, i = 100, 99, 98, ..., 2, 1),也就是 100, 99, 98, ..., 2, 1 日的数据((v_0 = 0)),然后乘以相应的权重(在图中看是一个指数衰减函数,这应该就是指数加权名字的由来)。
上图中是每日的数据 ( heta_t) ,下图是跟 ( heta_t) 对应的权重 (0.1 imes (0.9)^{(100 - t)})
最后也许你会问,到底需要平均多少天的温度。实际上 ((0.9)^{10}) 大约为 0.349,这大约是 (frac{1}{e}),(e) 是自然算法的基础之一。也就是说 10 天后,曲线的高度下降到 (frac{1}{3}),相当于在峰值的 (frac{1}{e})。(我理解的是,作者认为 10 天之后的数据对最终结果的加权不到 (frac{1}{3}),可以认为是用了 10 天的数据进行的加权平均。)
因此当 (eta = 0.9) 的时候,我们说仿佛你在计算一个指数加权平均数,只关注了过去 10 天的温度,因为 10 天后,权重下降到不到当日权重的三分之一。相反,如果,那么 0.98 需要多少次方才能达到这么小的数值?((0.98)^{50}) 大约等于 (frac{1}{e}),所以前 50 天这个数值比 (frac{1}{e}) 大,数值会快速衰减,所以本质上这是一个下降幅度很大的函数,你可以看作平均了 50 天的温度。
指数加权平均数公式的好处之一在于,它占用极少内存,电脑内存中只占用一行数字而已,然后把最新数据代入公式,不断覆盖就可以了。当然它并不是最好的, 也不是最精准的计算平均数的方法。如果你要计算移动窗,你直接算出过去 10 天的总和, 过去 50 天的总和,除以 10 和 50 就好,如此往往会得到更好的估测。但缺点是,如果保存所有最近的温度数据和过去 10 天的总和,必须占用更多的内存,执行更加复杂,计算成本也更加高昂。
3.1.3 指数加权平均的偏差修正(Bias Correction)
学过了如何计算指数加权平均数,有一个技术名词叫做偏差修正,可以让平均数运算更加准确,来看看它是怎么运行的。
这个(红色)曲线对应 (eta) 的值为 0.9,这个(绿色)曲线对应的 (eta= 0.98) ,如果你执行写在这里的公式,在 (eta) 等于 0.98 的时候,得到的其实并不是绿色曲线,而是紫色曲线,你可以注意到紫色曲线的起点较低,我们来看看是怎么计算出来的。
计算移动平均数的时候,初始化 (v_0 = 0) ,(v_1 = 0.98v_0 + 0.02 heta_1) ,但是 (v_0 = 0),所以 (v_1 = 0.02 heta_1) ,所以如果第一天温度是 40 华氏度,那么 (v_1 = 0.02 heta_1 = 0.02 × 40 = 8),因此得到的值会小很多,所以第一天温度的估测不准。
(v_2 = 0.98v_1 + 0.02 heta_2) ,如果代入 (v_1),然后相乘,所以 (v_2 = 0.98 × 0.02 heta_1 + 0.02 heta_2 = 0.0196 heta_1+ 0.02 heta_2) ,假设 ( heta_1) 和 ( heta_2) 都是正数,计算后 (v_2) 要远小于 ( heta_1) 和 ( heta_2),所以 (v_2) 不能很好估测出这一年前两天的温度。
有个办法可以修正这一估测,让估测变得更好,更准确,特别是在估测初期,也就是不用 (v_t) ,而是用 (frac{v_t}{1-eta^t}) ,(t) 就是现在的天数。
修正后,公式分两步:
举个具体例子,当 (t = 2) 时,(1 − eta^t = 1 − (0.98)^2 = 0.0396) ,因此对第二天温度的估测变成了 (frac{v_2}{0.0396} = frac{0.0196 heta_1+0.02 heta_2}{0.0396}),也就是 ( heta_1) 和 ( heta_2) 的加权平均数,并去除了偏差。你会发现随着 (t) 增加,(eta_t) 接近于 0,所以当 (t) 很大的时候,偏差修正几乎没有作用, 因此当 (t) 较大的时候,紫线基本和绿线重合了。
除了调整学习率之外,还可以进行梯度估计(Gradient Estimation)的修正。从上面图 1 中看出,在随机(小批量)梯度下降法中,如果每次选取样本数量比较小,损失会呈现振荡的方式下降。也就是说,随机梯度下降方法中每次迭代的梯度估计和整个训练集上的最优梯度并不一致,具有一定的随机性(导致速度收敛速度慢)。一种有效地缓解梯度估计随机性的方式是通过使用最近一段时间内的平均梯度来代替当前时刻的随机梯度来作为参数更新的方向,从而提高优化速度(当然,增加批量大小也是缓解随机性的一种方式)。
3.2 动量梯度下降(Gradient Descent with Momentum)
动量(Momentum)是模拟物理中的概念。一个物体的动量指的是该物体在它运动方向上保持运动的趋势,是该物体的质量和速度的乘积。动量法(Momentum Method)是用之前积累动量来替代真正的梯度。
算法一:在吴恩达的讲解课程当中,动量梯度下降的算法为
算法二:在李沐的书中,设时间步 (t) 的权重向量为 (w_t) ,学习率为 (eta_t) 。 在时间步 0,动量法创建速度变量 (v_0 = 0) ,在时间步 (t > 0) ,动量法对每次迭代的步骤作如下修改:
其中,动量超参数 (eta) 满足 (0 leq eta lt 1) 。当 (eta = 0) 时,动量法等价于小批量随机梯度下降,(eta) 通常取值为 0.9。
我们对动量法的速度变量做变形:
由指数加权移动平均的形式可得,速度变量 (v_t) 实际上对序列 ({eta_{t-i} g_{t-i}/(1 − eta) : i = 0, . . . , 1/(1 − eta) − 1}) 做了指数加权移动平均。换句话说,相比于小批量随机梯度下降,动量法在每个时间步的权重向量更新量近似于将前者对应的最近 (1/(1 − eta)) 个时间步的更新量做了指数加权移动平均后再除以 (1 − eta)。所以,在动量法中,自变量在各个方向上的移动幅度不仅取决于当前梯度,还取决于过去的各个梯度在各个方向上是否一致。
其实吴恩达和李沐的讲法中的区别是:
1)算法二中将学习率 (eta_t)(算法一的对应为 (alpha))移到第一个公式当中了;
2)算法二在时间步 (t) 的梯度乘以 (frac{1}{1-eta}),这就是最大的区别了,而且从别的参考书中来看,基本都是采用算法二中的定义。
左图是小批量随机梯度下降法的结果,右图为动量梯度下降法的结果,可以看到,右图更快的逼近最优解。
3.3 Adam 算法(Adaptive Moment Estimation Algorithm)
Adam 算法可以看作动量法和 RMSprop 算法的结合,不但使用动量作为参数更新方向,而且可以自适应调整学习率。
Adam 算法使用了动量变量 (v_t) 和 RMSProp 算法中小批量随机梯度按元素平方的指数加权移动平均变量 (G_t),并在时间步 0 将它们中每个元素初始化为 0。给定超参数 (0 leq eta_1 lt 1)(算法作者建议设 为 0.9),时间步 (t) 的动量变量 (v_t) 即小批量随机梯度 (g_t) 的指数加权移动平均:
和 RMSProp 算法中一样,给定超参数 (0 leq eta_2 lt 1)(算法作者建议设为 0.999),将小批量随机梯度按元素平方后的项 (g_t odot g_t) 做指数加权移动平均得到 (G_t):
由于我们将 (v_0) 和 (G_0) 中的元素都初始化为 0,在时间步 (t) 我们得到 (v_t = (1 − eta_1) sum_{i=1}^{t}eta_1^{t-i}g_i) 。将过去各时间步小批量随机梯度的权值相加,得到 ((1-eta_1)sum_{i=1}^{t}eta_1^{t-i} = 1 - eta_1^t) 。需要注意的是,当 (t) 较小时,过去各时间步小批量随机梯度权值之和会较小。例如,当 (eta_1 = 0.9) 时,(v_1 = 0.1g_1) 。为了消除这样的影响,对于任意时间步 (t),我们可以将 (v_t) 再除以 (1 − eta_1^t),从而使过去各时间步小批量随机梯度权值之和为 1。这也叫作偏差修正。在 Adam 算法中,我们对变量 (v_t) 和 (G_t) 均作偏差修正:
接下来,Adam 算法使用以上偏差修正后的变量 (hat{v_t}) 和 (hat{G_t}) ,将模型参数中每个元素的学习率通过按元素运算重新调整:
其中 (eta) 是学习率,(epsilon) 是为了维持数值稳定性而添加的常数,如 (10^{−8}) 。和 AdaGrad 算法、RMSProp 算法以及 AdaDelta 算法一样,目标函数自变量中每个元素都分别拥有自己的学习率。最后,使用 (g_t') 迭代自变量
超参数选择(吴恩达)
- (alpha):需要通过训练进行调整
- (eta_1):0.9
- (eta_2):0.99,这两个都是作者推荐
- (epsilon):(10^{-8}),几乎没有人去调整这三个参数
3.4 梯度截断(Gradient Clipping)
在深度神经网络或循环神经网络中,除了梯度消失之外,梯度爆炸也是影响学习效率的主要因素。当梯度的模大于一定阈值时,对梯度进行截断,称为梯度截断。截断的方式一般有以下两种:
按值截断:在第 (t) 次迭代时,梯度为 (g_t),给定一个区间 ([a, b]),如果一个参数的梯度小于 (a) 时,就将其设为 (a);如果大于 (b) 时,就将其设为 (b)。
按模截断:将梯度的模截断到一个给定的截断阈值 (b)。如果 (||g_t||^2 leq b),保持 (g_t) 不变,如果 (||g_t||^2 gt b),令
截断阈值 (b) 是一个超参数,也可以根据一段时间内的平均梯度来自动调整。实验发现,训练过程对阈值 (b) 并不敏感,通常一个小的阈值就可以得到很好的结果。
4 优化算法总结
神经网络常用优化方法汇总
下图给出了这几种优化方法在 MNIST 数据集上收敛性的比较(学习率为 0.001,批量大小为 128)。
参考资料
- 《神经网络与深度学习》邱锡鹏
- 《改善深度神经网络》吴恩达
- 《动手学深度学习》李沐