神经网络-优化器篇-从梯度下降到Adam方法
1、最开始的梯度下降算法1.1 批梯度下降算法(Batch Gradient Descent)
BGD 采用整个训练集的数据来计算 cost function 对参数的梯度,公式:
\theta = \theta - \eta \cdot(θ)\cdot J(\theta)
其中η是学习率,(θ)是损失函数J(\theta) 的梯度。
优点:对于凸函数可以收敛到全局极小值,对于非凸函数可以收敛到局部极小值。
缺点:由于这种方法是在一次更新中,就对整个数据集计算梯度,所以计算起来非常慢,遇到很大量的数据集也会非常棘手,而且不能投入新数据实时更新模型。
1.2 随机梯度下降算法(Stochastic Gradient Descent)
和 BGD 的一次用所有数据计算梯度相比,SGD 每次更新时对每个样本进行梯度更新,对于很大的数据集来说,可能会有相似的样本,这样 BGD 在计算梯度时会出现冗余,而SGD 一次只进行一个样本更新,就没有冗余,而且比较快,并且可以新增样本。
SGD参数更新公式如下,其中 \alpha 是学习率, g_{t} 是当前参数的梯度,其中 x_{(i)} 和 y_{(i)} 为训练样本。
w_{t+1} = w_t - \eta_t=w_t - \alpha \cdot g_t(x_{(i)},y_{(i)})优缺点:
优点:随机梯度下降是通过每个样本来迭代更新一次,如果样本量很大的情况,那么可能只用其中部分的样本,就已经将wt迭代到最优解了,对比上面的批量梯度下降,迭代一次需要用到十几万训练样本,一次迭代不可能最优,如果迭代10次的话就需要遍历训练样本10次。
缺点:SGD的噪音较BGD要多,使得SGD并不是每次迭代都向着整体最优化方向。所以虽然训练速度快,但是准确度下降,并不是全局最优。虽然包含一定的随机性,但是从期望上来看,它是等于正确的导数的。总结:
[*]SGD 因为更新比较频繁,会造成 cost function 有严重的震荡。
[*]BGD 可以收敛到局部极小值,当然 SGD 的震荡可能会跳到更好的局部极小值处。
[*]当我们稍微减小 learning rate,SGD 和 BGD 的收敛性是一样的。(?)
1.3 小批量梯度下降算法(Mini-Batch Gradient Descent)
MBGD 每一次利用一小批样本,即 n 个样本进行计算(n 一般取值在 50~256),这样它
[*]可以降低参数更新时的方差,收敛更稳定。
[*]可以充分地利用深度学习库中高度优化的矩阵操作来进行更有效的梯度计算。
总结:
[*] Mini-batch gradient descent 不能保证很好的收敛性,learning rate 如果选择的太小,收敛速度会很慢,如果太大,loss function 就会在极小值处不停地震荡甚至偏离。对于非凸函数,还要避免陷于局部极小值处,或者鞍点处,因为鞍点周围的error是一样的,所有维度的梯度都接近于0,SGD 很容易被困在这里。(会在鞍点或者局部最小点震荡跳动,因为在此点处,如果是训练集全集带入即BGD,则优化会停止不动,如果是mini-batch或者SGD,每次找到的梯度都是不同的,就会发生震荡,来回跳动。)
[*]SGD对所有参数更新时应用同样的 learning rate,如果我们的数据是稀疏的,我们更希望对出现频率低的特征进行大一点的更新。LR会随着更新的次数逐渐变小。
2、解决梯度震荡问题 -
2.1 Momentum动量更新方法
SGD 在山谷的情况下容易被困住, 山谷就是曲面的一个方向比另一个方向更陡,这时 SGD 会发生震荡而迟迟不能接近极小值,所以对更新梯度引入阻力概念,加速SGD,并抑制震荡(也就是在SGD基础上引入了一阶动量),公式:
m_t = \beta_1 \cdot m_{t-1} + (1-\beta_1)\cdot g_tSGD-M参数更新公式如下,其中 \alpha 是学习率, g_{t} 是当前参数的梯度
w_{t+1} = w_t -\alpha \cdot m_t = w_t -\alpha \cdot(\beta_1 \cdot m_{t-1} + (1-\beta_1)\cdot g_t)当我们将一个小球从山上滚下来时,没有阻力的话,它的动量会越来越大,但是如果遇到了阻力,速度就会变小。
加入的这一项,可以使得梯度方向不变的维度上速度变快,梯度方向有所改变的维度上的更新速度变慢,这样就可以加快收敛并减小震荡。这就意味着下降方向主要是此前累积的下降方向,并略微偏向当前时刻的下降方向。
超参数设定值:一般 \beta 取值 0.9 左右。
[*]\beta=0.98时,曲线更为平滑和滞后,\beta = 0.5时,曲线会有更多的噪点
[*]\beta的取值越大,相当于求取平均利用的天数越多,曲线自然会越平滑且越滞后,\beta被称为 偏差修正参数
缺点:
这种情况相当于小球从山上滚下来时是在盲目地沿着坡滚,如果它能具备一些先知,例如快要上坡时,就知道需要减速了的话,适应性会更好。
目前为止,我们可以做到,<b>在更新梯度时顺应 loss function 的梯度来调整速度,并且对 SGD 进行加速。我们还希望可以根据参数的重要性而对不同的参数进行不同程度的更新。
2.2 Nesterov Accelerated Gradient
SGD 还有一个问题是困在局部最优的沟壑里面震荡。想象一下你走到一个盆地,四周都是略高的小山,你觉得没有下坡的方向,那就只能待在这里了。可是如果你爬上高地,就会发现外面的世界还很广阔。因此,我们不能停留在当前位置去观察未来的方向,而要向前一步、多看一步、看远一些。 NAG全称Nesterov Accelerated Gradient,是在SGD、SGD-M的基础上的进一步改进,改进点在于算历史梯度的时候。我们知道在时刻 t 的主要下降方向是由累积动量决定的,自己的梯度方向说了也不算,那与其看当前梯度方向,不如先看看如果跟着累积动量走了一步,那个时候再怎么走。因此,NAG在算目标函数关于当前参数的梯度时,不计算当前位置的梯度方向,而是计算如果按照累积动量走了一步,那个时候的下降方向
公式如下:向前走一步后的下降梯度
g_t=\nabla f(w_t-\alpha \cdot m_{t-1} )
m_{t} = \beta \cdot m_{t-1} + (1-\beta)*g_{t} NAG参数更新公式如下,其中 \alpha 是学习率, g_{t} 是当前参数的梯度
w_{t+1} =w_t - \alpha \cdot m_t = w_t - \alpha \cdot (\beta \cdot m_{t-1} + (1-\beta)*g_{t}) = w_t - \alpha \cdot (\beta \cdot m_{t-1} + (1-\beta)*(\nabla f(w_t-\alpha \cdot m_{t-1} ))) 特点:有利于跳出当前局部最优的沟壑,寻找新的最优值,但是收敛速度慢。
3、不同参数进行不同程度的更新 - 逐参数适应学习率方法
3.1 Adagrad -二阶动量 - (自适应学习率算法)
SGD系列的都没有用到二阶动量。二阶动量的出现,才意味着“自适应学习率”优化算法时代的到来。SGD及其变种以同样的学习率更新每个参数,但深度神经网络往往包含大量的参数,这些参数并不是总会用得到(想想大规模的embedding)。对于经常更新的参数,我们已经积累了大量关于它的知识,不希望被单个样本影响太大,希望学习速率慢一些;对于偶尔更新的参数,我们了解的信息太少,希望能从每个偶然出现的样本身上多学一些,即学习速率大一些。
怎么样去度量历史更新频率呢? 那就是二阶动量——该维度上,记录到目前为止所有梯度值的平方和: V_{t} 之前累计梯度信息,此时m_{t} = g_{t} , V_t = \sum_{\tau=1}^{t} g_\tau^2
我们再回顾一下当前时刻的下降梯度:
\eta_t = \alpha \cdot m_t / \sqrt{V_t}
AdaGrad参数更新公式如下,其中 \alpha 是学习率, g_{t} 是当前参数的梯度 w_{t+1} =w_t - \alpha \cdot m_t / \sqrt{V_t}=w_t - \alpha \cdot m_t / \sqrt{\sum_{\tau=1}^{t} g_\tau^2}
可以看出,此时实质上的学习率由\alpha 变成了\alpha / \sqrt{V_t} 。 一般为了避免分母为0,会在分母上加一个小的平滑项。因此\sqrt{V_t} 是恒大于0的,而且参数更新越频繁,二阶动量越大,学习率就越小。
优点: (1)在稀疏数据场景下表现非常好 (2)此前的SGD及其变体的优化器主要聚焦在优化梯度前进的方向上,而AdaGrad首次使用二阶动量来关注学习率(步长),开启了自适应学习率算法的里程。
缺点: (1)因为\sqrt{V_t} 是单调递增的,会使得学习率单调递减至0,可能会使得训练过程提前结束,即便后续还有数据也无法学到必要的知识。另一个叫做Adadelta的算法改善了这个学习率不断衰减的问题。
3.2 Adadelta - 二阶动量
由于AdaGrad单调递减的学习率变化过于激进,考虑一个改变二阶动量计算方法的策略:不累积全部历史梯度,而只关注过去一段时间窗口的下降梯度。这也就是AdaDelta名称中Delta的来历。 修改的思路很简单。前面讲到,指数移动平均值大约就是过去一段时间的平均值,因此我们用这一方法来计算二阶累积动量: V_t = \beta_2 \cdot V_{t-1} + (1-\beta_2) g_t^2
梯度更新规则为:
此时m_{t}当前梯度信息而不是一阶距
v_t = \beta_2 \cdot v_{t-1} + (1-\beta_2) g_t^2
\begin{align}w_{t+1} &=w_t - \alpha \cdot m_t / \sqrt{v_t}\\&=w_t - \alpha \cdot m_t / \sqrt{ \beta_2 \cdot v_{t-1} + (1-\beta_2) g_t^2} \end{align}
其中\beta_{1}、\beta_{2}设置为与动量项相近的值,约为0.9。其中\alpha是学习率,g_{t}是当前参数的梯度。
这样就避免了二阶动量持续累积、导致训练过程提前结束的问题了。
3.3 RMSprop
RMSprop 是 Geoff Hinton 提出的一种自适应学习率方法。
RMSprop 和 Adadelta 都是为了解决 Adagrad 学习率急剧下降问题的,
3.4 Adam(Adam:Adaptive Moment Estimation)
SGD-M在SGD基础上增加了一阶动量,AdaGrad和AdaDelta在SGD基础上增加了二阶动量。AdaGrad存储了梯度过去的平方 g_{t}^{2} , Adadelta 和 RMSprop 存储了梯度过去平方 g_{t}^{2} 的指数衰减平均值。
Adam把一阶动量和二阶动量都用起来—Adaptive + Momentum。除了像 Adadelta 和 RMSprop 一样存储了梯度过去平方 g_{t}^{2} 的指数衰减平均值,也像 momentum一样保持了过去梯度 m_{t} 的指数衰减平均值:
SGD的一阶动量:
m_t = \beta_1 \cdot m_{t-1} + (1-\beta_1)\cdot g_t,momentum过去梯度m_{t}的指数衰减平均值:
加上AdaDelta的二阶动量:
V_{t} = \beta_2 \cdot V_{t-1} + (1-\beta_2) g_t^2,过去梯度的平方g_{t}的指数衰减平均值
Adam参数更新公式如下,其中 \alpha 是学习率, g_{t} 是当前参数的梯度
\begin{align}w_{t+1}&=w_t - \alpha \cdot m_t / \sqrt{V_t}\\&=w_t - \alpha \cdot (\beta_1 \cdot m_{t-1} + (1-\beta_1)\cdot g_t) / \sqrt{ \beta_2 \cdot V_{t-1} + (1-\beta_2) g_t^2} \end{align}优化算法里最常见的两个超参数\beta_1, \beta_2 就都在这里了,前者控制一阶动量,后者控制二阶动量。
如果 m_{t} 和 v_{t} 被初始化为 0 向量,那它们就会向 0 偏置,所以做了偏差校正,通过计算偏差校正后的m_{t}和v_{t}来抵消这些偏差:
梯度更新规则:
超参数设定值: 建议 β1 = 0.9,β2 = 0.999, = 10e8
实践表明,Adam 比其他适应性学习方法效果要好。
优点:通过一阶动量和二阶动量,有效控制学习率步长和梯度方向,防止梯度的振荡和在鞍点的静止。
缺点:
(1)可能不收敛:
二阶动量是固定时间窗口内的累积,随着时间窗口的变化,遇到的数据可能发生巨变,使得V_t可能会时大时小,不是单调变化。这就可能在训练后期引起学习率的震荡,导致模型无法收敛。
修正的方法。由于Adam中的学习率主要是由二阶动量控制的,为了保证算法的收敛,可以对二阶动量的变化进行控制,避免上下波动。
V_t = max(\beta_2 * V_{t-1} + (1-\beta_2) g_t^2, V_{t-1})。通过这样修改,就保证了 ||V_t|| \geq ||V_{t-1}|| ,从而使得学习率单调递减。
(2)可能错过全局最优解:自适应学习率算法可能会对前期出现的特征过拟合,后期才出现的特征很难纠正前期的拟合效果。后期Adam的学习率太低,影响了有效的收敛。
三、如何选择优化算法
Adam在实际应用中效果良好,超过了其他的自适应技术。
对于稀疏数据集,应该使用某种自适应学习率(Adagrad, Adadelta, RMSprop, Adam)的方法,且另一好处为:不需要人为调整学习率,使用默认参数就可能获得最优值。SGD、NAG和动量项等方法可能效果不好
如果想使训练深层网络模型快速收敛或所构建的神经网络较为复杂,则应该使用Adam或其他自适应学习速率的方法,因为这些方法的实际效果更优。
RMSprop, Adadelta, Adam 在很多情况下的效果是相似的。随着梯度变的稀疏,Adam 比 RMSprop 效果会好。
四、优化算法的常用tricks
(1)首先,各大算法孰优孰劣并无定论。如果是刚入门,优先考虑SGD+Nesterov Momentum或者Adam。 (2)选择你熟悉的算法——这样你可以更加熟练地利用你的经验进行调参。
(3)充分了解你的数据——如果模型是非常稀疏的,那么优先考虑自适应学习率的算法。
(4)根据你的需求来选择——在模型设计实验过程中,要快速验证新模型的效果,可以先用Adam进行快速实验优化;在模型上线或者结果发布前,可以用精调的SGD进行模型的极致优化。
(5)先用小数据集进行实验。有论文研究指出,随机梯度下降算法的收敛速度和数据集的大小的关系不大。因此可以先用一个具有代表性的小数据集进行实验,测试一下最好的优化算法,并通过参数搜索来寻找最优的训练参数。
(6)考虑不同算法的组合。先用Adam进行快速下降,而后再换到SGD进行充分的调优。
(7)数据集一定要充分的打散(shuffle)。这样在使用自适应学习率算法的时候,可以避免某些特征集中出现,而导致的有时学习过度、有时学习不足,使得下降方向出现偏差的问题。
(8)训练过程中持续监控训练数据和验证数据上的目标函数值以及精度或者AUC等指标的变化情况。对训练数据的监控是要保证模型进行了充分的训练——下降方向正确,且学习率足够高;对验证数据的监控是为了避免出现过拟合。
(9)制定一个合适的学习率衰减策略。可以使用定期衰减策略,比如每过多少个epoch就衰减一次;或者利用精度或者AUC等性能指标来监控,当测试集上的指标不变或者下跌时,就降低学习率。
五、参考文章
[*]忆臻:通俗理解指数加权平均
[*]杰奏:优化器(Optimizer)
[*]Adam那么棒,为什么还对SGD念念不忘 (3)-- 优化算法的选择与使用策略
页:
[1]