理论基础(3)优化算法
总结:
- SGD为随机梯度下降,每一次迭代计算数据集的mini-batch的梯度,然后对参数进行更新。
- Momentum参考了物理中动量的概念,前几次的梯度也会参与到当前的计算中,但是前几轮的梯度叠加在当前计算中会有一定的衰减。
- Adagard在训练的过程中可以自动变更学习的速率,设置一个全局的学习率,而实际的学习率与以往的参数模和的开方成反比。
- Adam利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率,在经过偏置的校正后,每一次迭代后的学习率都有个确定的范围,使得参数较为平稳。
引言
最优化问题是计算数学中最为重要的研究方向之一。而在深度学习领域,优化算法的选择也是一个模型的重中之重。即使在数据集和模型架构完全相同的情况下,采用不同的优化算法,也很可能导致截然不同的训练效果。
梯度下降是目前神经网络中使用最为广泛的优化算法之一。为了弥补朴素梯度下降的种种缺陷,研究者们发明了一系列变种算法,从最初的 SGD (随机梯度下降) 逐步演进到 NAdam。然而,许多学术界最为前沿的文章中,都并没有一味使用 Adam/NAdam 等公认“好用”的自适应算法,很多甚至还选择了最为初级的 SGD 或者 SGD with Momentum 等。
一、常见的优化算法
1.1 Gradient Descent 框架
梯度下降是指,在给定待优化的模型参数 \(\theta \in \mathbb{R}^d\) 和目标函数 \(J(\theta)\) 后, 算法通过沿梯度 \(\nabla_\theta J(\theta)\) 的相反方向更新 \(\theta\) 来最小化 \(J(\theta)\) 。学习率 \(\eta\) 决定了每一时刻的更新步长。对于每一个时刻 \(t\), 我们可以用下述步骤描述梯 度下降的流程:
- 计算目标函数关于参数的梯度 \[ g_t=\nabla_\theta J(\theta) \] (2) 根据历史梯度计算一阶和二阶动量 \[ \begin{aligned} & m_t=\phi\left(g_1, g_2, \cdots, g_t\right) \\ & v_t=\psi\left(g_1, g_2, \cdots, g_t\right) \end{aligned} \]
- 更新模型参数 \[ \theta_{t+1}=\theta_t-\frac{1}{\sqrt{v_t+\epsilon}} m_t \] 其中, \(\epsilon\) 为平滑项,防止分母为零,通常取 1e-8。
1.2 Vanilla SGD
朴素 SGD (Stochastic Gradient Descent) 最为简单,没有动量的概念,即 \[ m_t=\eta g_t \]
\[ v_t=I^2 \]
\[ \epsilon=0 \]
这时,更新步骤就是最简单的 \[ \theta_{i+1}=\theta_t-\eta g_t \] SGD 的缺点在于收玫速度慢, 可能在鞍点处震荡。并且, 如何合理的选择学习率是 SGD 的一大难点。
1.3 Momentum
SGD 在遇到沟壑时容易陷入震荡。为此,可以为其引入动量 Momentum,加速 SGD 在正确方向的下降并抑制震荡。
\[ m_t=\gamma m_{t-1}+\eta g_t \] SGD-M 在原步长之上,增加了与上一时刻步长相关的 \(\gamma m_{t-1} , \gamma\) 通常取 0.9 左右。这意味着参数更新方向不仅由当前的梯度决定,也与此前累积的下降方向有关。这使得参数中那些梯度方向变化不大的维度可以加速更新,并减少梯度方向变化较大的维度上的更新幅度。由此产生了加速收敛和减小震荡的效果。从图中可以看出,引入动量有效的加速了梯度下降收敛过程。
1.4 Adagrad 【二阶动量 改变学习率】
SGD、SGD-M 和 NAG 均是以相同的学习率去更新 \(\theta\) 的各个分量。而深度学习模型中往往涉及大量的参数, 不同参数的更新频率往往有所区别。对于更新不频繁的参数(典型例子:更新 word embedding 中的低频词), 我们希望单次步长更大, 多学习一些知识; 对于更新频繁的参数, 我们则希望步长较小, 使得学习到的参数更稳定, 不至于被单个样本影响太多。
Adagrad算法即可达到此效果。其引入了二阶动量: \[ v_t=\operatorname{diag}\left(\sum_{i=1}^t g_{i, 1}^2, \sum_{i=1}^t g_{i, 2}^2, \cdots, \sum_{i=1}^t g_{i, d}^2\right) \] 其中, \(v_t \in \mathbb{R}^{d \times d}\) 是对角矩阵, 其元素 \(v_{t, i i}\) 为参数第 \(i\) 维从初始时刻到时刻 \(t\) 的梯度平方和。
此时, 可以这样理解: 学习率等效为 \(\eta / \sqrt{v_t+\epsilon}\) 。对于此前频繁更新过的参数, 其二阶动量的对应分量较大, 学习率就较小。这一方法在稀疏数据的场景下表现很好。
1.5 RMSprop 【定制之前梯度的重要性】
在 Adagrad 中, \(v_t\) 是单调递增的, 使得学习率逐渐递减至 0 , 可能导致训练过程提前结束。为了改进这一缺点, 可以考虑在计算二阶动量时不累积全部历史梯度, 而只关注最近某一时间窗口内的下降梯度。根据此思想有 了RMSprop。记 \(g_t \odot g_t\) 为 \(g_t^2\) ,有 \[ v_t=\gamma v_{t-1}+(1-\gamma) \cdot \operatorname{diag}\left(g_t^2\right) \] 其二阶动量采用指数移动平均公式计算, 这样即可避免二阶动量持续累积的问题。和 SGD-M 中的参数类似, \(\gamma\) 通常取 0.9 左右。
1.6 Adam 【RMS prop + momentum】
Adam可以认为是 RMSprop 和 Momentum 的结合。和 RMSprop 对二阶动量使用指数移动平均类似, Adam 中对一阶动量也是用指数移动平均计算。 \[ \begin{gathered} m_t=\eta\left[\beta_1 m_{t-1}+\left(1-\beta_1\right) g_t\right] \\ v_t=\beta_2 v_{t-1}+\left(1-\beta_2\right) \cdot \operatorname{diag}\left(g_t^2\right) \end{gathered} \] 其中, 初值 \[ \begin{aligned} & m_0=0 \\ & v_0=0 \end{aligned} \] 注意到, 在迭代初始阶段, \(m_t\) 和 \(v_t\) 有一个向初值的偏移(过多的偏向了 0) 。因此, 可以对一阶和二阶动量做偏置校正 (bias correction), \[ \begin{aligned} & \hat{m}_t=\frac{m_t}{1-\beta_1^t} \\ & \hat{v}_t=\frac{v_t}{1-\beta_2^t} \end{aligned} \] 再进行更新, \[ \theta_{t+1}=\theta_t-\frac{1}{\sqrt{\hat{v}_t}+\epsilon} \hat{m}_t \] 可以保证迭代较为平稳。
参考文献
- 从 SGD 到 Adam —— 深度学习优化算法概览(一):https://zhuanlan.zhihu.com/p/32626442