在深度学习中,我们通常需要优化我们所定义的损失函数,优化的方法思想就是通过梯度下降的方式不断逼近一个局部最优解,更新模型参数然后来保证损失函数预测误差最小。在本文主要综述了几种常见的优化策略,包括梯度下降的策略以及学习率变化的策略。这些策略有一些基本的概念需要重点掌握,这些策略有简单有复杂, 但是依照“没有免费午餐“的定理,在具体情景中,要具体运用比较,没有绝对的最优策略,只有相对的最优策略。

虽然在实践中我们可以直接调用深度学习框架的API来完成优化,但是要更加深入地理解其原理和过程才是一个优秀的深度学习工程师啊,面试肯定会涉及一些原理概念解释滴。下面就开始总结啦!

批量梯度下降法 Batch gradient descent

批量梯度下降法(也叫确定性梯度算法)指的是在一个大批量的数据样本中同时处理所有样本,在更新参数的时候,会同时利用所有样本的梯度变化。公式表达如下:

$$\theta_{t+1} = \theta_{t} - \eta \bigtriangledown L(\theta_t) = \theta_t - \frac{\eta}{N} \sum_{n=1}^{N}\bigtriangledown l(y_n, f(x_n; \theta_t))$$

从公式上可以看到模型参数$\theta$开始更新的时候,是当计算得到所有样本$x_n$输出预测结果$f(x_n; \theta_t)$后得到损失函数梯度值时,进行模型参数的更新。

可以看到这整个过程的缺点就是训练慢,处理棘手,而且内存消耗大,但是可以保证找到一个最小的损失函数值。

伪代码如下:

1
2
3
for iter in range(nb_epoches):
params_grad = eval_grad(loss_func, dataset, params)
params = params - learning_rate * params_grad

随机梯度下降法 Stochastic gradient descent

随机梯度下降法简称SGD,与批量梯度下降法相比,随机梯度下降法是在每训练一个样本的时候就进行的函数模型的更新,在每次的训练迭代中,会先随机打乱训练集,然后每抽取一个样本进行训练时,就进行更新。

公式表达如下:

$\theta_{t+1} = \theta_t - \eta \bigtriangledown L(\theta_t) = \theta_t - \eta \bigtriangledown l(y_n , f(x_n; \theta_t))$

显而易见,在处理大数据样本的时候,模型更新速度很快,但是存在收敛震荡的情况,可能会跳出局部最优解,但是也很接近局部最优解。从计算设备的角度来考虑,随机梯度下降无法做到很好的并行计算,没有很好地利用多核架构,每一次更新都得依赖前一个样本的计算。

伪代码如下:

1
2
3
4
5
for iter in range(epoches):
np.random.shuffle(dataset)
for example in dataset:
param_grad = eval_grad(loss_func, example, params)
params = params - learning_rate * params_grad

小批量梯度下降 Mini-batch gradient descent

小批量梯度下降法,其实就是介于批量下降法和随机梯度下降法之间的优化方法,关键在于每次更新模型参数时,训练样本的大小。我们知道大批量的运算将会使我们的计算设备和内存不堪重负,而随机梯度下降法的中的单样本更新则无法充分使用我们的多核计算架构。我们想要达到的目的就是:既能有效的保证计算设备的良好充分使用,又能保证较短的训练时间和较好的性能 。而小批量梯度下降法就是我们比较常用的一种优化方法。

小批量的大小通常使用以下几个因素决定的:

  • 更大的批量将计算得到更为精确的梯度估计,但是回报却是小于线性的。
  • 极小批量将无法充分利用多核架构,这促使我们给批量设定一个最小阈值。
  • 如果批量处理中所有样本可以并行地处理,那么内存消耗和批量大小将会呈正比,所有批量有一个上限。

小批量随机梯度下降法用数学公式表达如下:

$$\theta_{t+1} = \theta_{t} - \eta\bigtriangledown L(\theta_t) = \theta_t - \frac{\eta}{S} \sum_{(x_n, y_n) ∈S} \bigtriangledown l(y_n, f(x_n; \theta_t))$$

S 代表批量的大小,通常为10~300之间。

整个过程如下:

1
2
3
4
5
for i in range(nb_epochs):
np.random.shuffle(dataset)
for batch in get_batches(dataset, batch_size = 64):
params_grad = eval_grad(loss_func, batch, params)
params = params - learning_rate * params_grad

对于小批量梯度下降法来说,用小批量可以比较好的近似真实梯度,但是对学习率的自适应调整有了一定的要求。

动量 Momentum

虽然SGD仍然是非常接收欢迎的优化方法,但是其学习过程有时是相当的漫长,动量方法就是为了加速学习,特别是在处理高曲率,小但一致的梯度,或者带噪声的梯度。动量算法简单来说就是在原来SGD的基础上,利用了之前所有梯度指数级衰减的移动平均,并且沿该方向继续移动。

在描述该方法的时候,引入了变量 v 来充当速度的角色,它代表参数在参数空间移动的方向和速率。数学描述如下:

$v_{t+1} = \gamma v_t + \eta\bigtriangledown L(\theta_t)$

$\theta_{t+1} = \theta_t - v_{t+1}$

该迭代式可以看出:

$v_{t+1} = \eta { \bigtriangledown L(\theta_t) + \eta L(\theta_{t-1}) + \eta^2 \bigtriangledown L(\theta_{t-2})+ … }$

这种方法有效的利用了前面所有的梯度信息,可以有效的减少SGD,mini-batch的震荡,但是这种算法有时会导致跳出最优解,而且随着计算次数的增加,有些梯度信息已经几乎可以忽略不记了。

自适应梯度下降法 Adagrad

自适应可以理解为学习率的自适应变化,在基础的下降法种的学习率$\eta$ 是一个常数,在自适应法中,学习率需要除以之前所有导数的平方和的平方根。

表达式如下:

$g_{t+1, i} = g_{t, i} + (\frac{\partial L(\theta_t)}{\partial \theta_i})^2$

$\theta_{t+1, i} = \theta_{t, i} - \frac{\eta}{\sqrt{g_{t+1, i}} + \epsilon} \frac{\partial L(\theta_t)}{\partial \theta_i}$

该方法实现了学习率的自动调整,当迭代次数增加的时候,学习率将会变得极小。

RMSprop (root mean square propagation)

为了解决Adagrad学习率单调递减的原因,我们对于每一个时刻的导数计算,引入了$\gamma $来避免学习率极低的情况,是简单利用线性加权的方式来描述这一方法的:

$g_{t+1, i} = \gamma g_{t, i} + (1 - \gamma)(\frac{\partial L(\theta_t)}{\partial \theta_i}) ^2$

$\theta_{t+1, i} = \theta_{t, i} - \frac{\eta}{\sqrt{g_{t+1, i} + \epsilon}} \frac{\partial L(\theta_t)}{\partial \theta_i}$

一般$gamma$可以取0.9。

Adam (Adaptive moment estimation)

Adam 简单来说就是在RMSProp的基础之上,添加了momentum和bias的方法,过程如下:

从上文可以看到,Adam其实是对学习率和要更新的梯度值进行了修改,学习率近似于RMSProp的方法,而梯度的修改则是参照了Momentum方法,当前的梯度信息需要依赖以往所有梯度信息的线性加权求和,权重随着迭代次数增加而减少。