深度学习中的优化问题通常指的是：寻找神经网络上的一组参数$\theta$，它能显著地降低代价函数$J(\theta)$。

这里介绍的方法都基于以下两点：

- 梯度的负方向是函数在当前点减小最快的方向；

- 使用一阶泰勒展开式近似当前点的函数值，即：
  $$
  f(x)\approx f(x_0)+f'(x_0)(x-x_0)
  $$

下面介绍几种**常用优化算法**：

1. 梯度下降法及其三个变体

   - BGD(Batch Gradient Descent)

     这里的Batch指的是全量数据，即每次计算全量数据对于参数的梯度并然后进行更新：
     $$
     \theta=\theta-\eta\frac{\partial J(\theta)}{\partial \theta}
     $$
     优点：由于每一步迭代使用了全部样本，因此当损失函数收敛过程比较稳定。对于凸函数可以收敛到全局最小值，对于非凸函数可以收敛到局部最小值。

     缺点：每一步更新中，都要利用全部样本计算梯度，计算起来非常慢，遇到很大量的数据集也会非常棘手。

   - SGD(Stochastic Gradient Descent )

     随机梯度下降，即每次随机选取一个样本计算梯度并进行更新：
     $$
     \theta=\theta-\eta\frac{\partial J(\theta;x_i;y_i)}{\partial \theta}
     $$
     优点：由于每次迭代只使用了一个样本计算梯度，训练速度快，包含一定随机性，从期望来看，每次计算的梯度基本是正确的导数的。

     缺点：更新频繁，带有随机性，会造成损失函数在收敛过程中严重震荡

   - MBGD

     每一次选取一小批样本计算梯度并进行更新，假设每批样本中有n个样本：
     $$
     \theta=\theta-\eta\frac{\partial J(\theta;x_{i:i+n};y_{i:i+n})}{\partial \theta}
     $$
     Batch_Size 对训练效果的影响：

     - Batch_Size 太小，很多时刻优化方向都可能是远离最小点方向，因此模型表现效果极其糟糕(error飙升)。
     - 随着 Batch_Size 增大，处理相同数据量的速度越快。
     - 随着 Batch_Size 增大，达到相同精度所需要的 epoch 数量越来越多。
     - 由于上述两种因素的矛盾， Batch_Size 增大到某个时候，达到时间上的最优。
     - 由于最终收敛精度会陷入不同的局部极值，因此 Batch_Size 增大到某些时候，达到最终收敛精度上的最优。

   这三种梯度下降法都容易陷入鞍点和局部极小点，而且非常依赖于学习率的设置。

   下面介绍的方法都以MBGD为基础，以$g_t$表示当前批次的梯度。

2. 动量法（Momentum）

   如果把优化过程比作一个朝山谷滚落的小球，那么梯度下降法在每一次计算梯度时都会忽略掉小球之前下落的惯性，而动量法则考虑到了这一点，其更新公式为：
   $$
   v_t=\beta v_{t-1}+(1-\beta)g_t
   \\
   \theta=\theta-\eta v_t
   $$
   $v_t$称为动量，为各个时刻梯度的加权平均。动量法能够减少优化过程中的震荡，加速收敛。

   动量法增加了一个超参数$\beta$，一般取0.5，0.9，0.99。也可以动态设置，一开始设的小一点，后面逐步增大。

3. Adagrad

   深度学习模型中往往包含大量的参数，这些参数并不是总会用的到。对于经常更新的参数，我们已经积累了大量关于它的知识，不希望被单个样本影响太大，希望学习速率慢一些；对于偶尔更新的参数，我们了解的信息太少，希望能从每个偶然出现的样本身上多学一些，即学习速率大一些。那么如何去度量一个参数的历史更新频率呢？那就是二阶动量，是至今为止所有梯度值的平方和：
   $$
   r_t=r_t+g_t^2
   $$
   于是参数更新公式变为：
   $$
   \theta=\theta-\eta\frac{g_t}{\sqrt{r_t}+\epsilon}
   $$
   Adagrad虽然减少了学习率的手动调节，但二阶动量的不断累积会使得学习率越来越小，可能会使训练过程提前结束，即使后续有数据也无法学到必要的知识。Adagrad也对初始值敏感（如果一开始梯度就很大，那么极有可能很快就停止学习）。

4. RMSProp

   Adagrad对历史梯度一视同仁，简单的把所有梯度的平方加起来来衡量参数的更新频率，这中导致学习率单调递减的方式过于激进，RMSProp对其进行了改进。
   RMSProp在Adagrad基础上引入衰减因子，距离当前时刻越远的梯度影响越小，即主要关注一段时间窗口内的下降梯度：
   $$
   r_t=\rho r_t+(1-\rho)g_t^2
   $$
   如果最近一段时间某个参数更新的很频繁，那么久选择较小的更新幅度；如果最近一段时间几乎没有更新，则选择大幅度更新一下。这样就避免了二阶动量持续积累，导致训练过程提早结束的问题。

5. Adam

   动量法引入一阶动量来抑制震荡，Adagrad和RMSProp引入二阶动量使得各个参数的学习率能够自适应调整，将这二者都用起来就是Adam了。
   $$
   v_t=\beta_1 v_{t-1}+(1-\beta_1)g_t
   \\
   r_t=\beta_2 r_{t-1}+(1-\beta_2)g_t^2
   $$
   在实际应用中，参数的经验值为：
   $$
   \beta_1=0.9\qquad\beta_2=0.999
   $$
   v和r一般初始化为0，为了避免一开始二者过小，常常会用下面的式子进行误差修正：
   $$
   \widetilde v_t=\frac{v_t}{1-\beta_1^t}
   \\
   \widetilde r_t=\frac{r_t}{1-\beta_2^t}
   $$
   这样一开始二者都会得到放大，而后期分子趋近于1，就不需要对二者进行放大了。

   参数更新公式为：
   $$
   \theta=\theta-\eta\frac{\widetilde v_t}{\sqrt{\widetilde r_t}+\epsilon}
   $$

**如何选择优化算法**

- 对于稀疏数据，尽量使用学习率可自适应的优化方法，不用手动调节，而且最好采用默认值。

-  SGD通常训练时间更长，但是在好的初始化和学习率调度方案的情况下（很多论文都用SGD），结果更可靠。

- 如果在意更快的收敛，并且需要训练较深较复杂的网络时，推荐使用学习率自适应的优化方法。

- Adagrad，RMSprop，Adam是比较相近的算法，在相似的情况下表现差不多 。整体来讲，Adam 是最好的选择。



参考资料：

[https://www.cnblogs.com/zingp/p/11352012.html](https://www.cnblogs.com/zingp/p/11352012.html)

[https://blog.csdn.net/qq_16137569/article/details/81699535](https://blog.csdn.net/qq_16137569/article/details/81699535)