在之前的章节中,我们只研究和使用了随机梯度下降法(SGD)来优化网络模型,但是,在深度学习中还有其他高级的优化算法,这些高级方法可以加速训练过程或者提高准确度:
- 在可接受的准确度下,高级算法可以减少训练时间(比如更少的迭代次数epochs)。
- 模型可以更好的适应其他超参数,而不仅仅是学习率。
- 理想情况下,可以获得比SGD更高的分类准确度。
随着深度学习的不断发展,新的优化技术层出不穷,每一种新的优化技术都试图改进SGD算法,并且可以自适应学习速率。正如我们所知道的,在给定学习率情况下,SGD算法对网络中的所有参数进行同等的优化。但是,考虑到学习率既是优化网络过程中最重要的参数之一,又很难把握学习率的大小。因此,相关学者提出了,随着网络训练的进行,可以自适应地调整学习速率(某些情况下,可能是每一个超参数)的算法。
在本章中,我们将回顾自适应学习率相关算法。
自适应学习率
为了理解本节中的每一种优化算法,我们将用伪代码——特别是更新步骤,来表示。本章的大部分内容来自于Karpathy[26]和Ruder[27]对优化方法的概述文章。在此基础上,对原文描述内容做一定的修改,使内容更易于理解。
Vanilla SGD
首先,让我们看一看我们已经熟悉的算法—— Vanilla SGD(就是我们常见的批量梯度下降法)的更新阶段:
1 | W += -lr * dW |
其中:
- W:模型权重矩阵
- lr::学习率
- dW:W的梯度
学习率lr是固定的。当然只要学习率足够小,模型的loss会在训练中减少[太大可能出现loss增大情况]。在之前训练模型过程中,我们也使用了SGD的扩展,如momentum和Nesterov。
具体的随机梯度下降法流程,可以查看图7.1:
我们将要学习的第一个自适应学习速率方法是Adagrad,由Duchi[28]等人提出。Adagrad是解决不同参数应该使用不同的更新速率的问题。Adagrad自适应地为各个参数分配不同学习率的算法。对于某些变量,可能已经优化到了极小值附近,但是,有的变量仍然处在梯度很大的地方,这时候一个统一的全局学习率是可能出现问题的。如果学习率太小,则梯度很大的变量会收敛很慢,如果梯度太大,已经优化差不多的变量可能会不稳定。AdaGrad的基本思想是对每个变量用不同的学习率,这个学习率在一开始比较大,用于快速梯度下降。随着优化过程的进行,对于已经下降很多的变量,则减缓学习率,对于还没怎么下降的变量,则保持一个较大的学习率。
下面我们可以看到Adagrad更新的伪代码表示:
1 | cache += (dW ** 2) |
你会发现第一个参数是cache——这个变量主要是在mini-batch更新过程中,计算每个参数的梯度的平方和[累加和]。通过cache变量,我们可以看到哪些参数经常更新,哪些参数不经常更新。第2行代码,我们将lr * dW除以cache的平方根(eps一般是一个很小的值,作用是防止分母为0)。不同的参数,其cache值是不同的,因此通过cache,我们可以自适应地更新网络中的参数。从上伪代码中可以看到,随着优化过程的进行,对于已经下降很多的变量,则减缓学习率,对于还没怎么下降的变量,则保持一个较大的学习率。Adagrad的主要好处是,我们不再需要手动调优学习率——大多数实践中,Adagrad算法的初始学速率一般设置为0.01,并允许算法在每个参数的调优学习速率。
在机器学习的应用中,Adagrad非常适合样本稀疏的问题,因为稀疏的样本下,每次梯度下降的方向,以及涉及的变量都可能有很大的差异。Adagrad的缺点是虽然不同变量有了各自的学习率,但是初始的全局学习率还是需要手工指定。如果全局学习率过大,优化同样不稳定;而如果全局学习率过小,我们知道的,将一个小数字(梯度)除以一个很大的数字(cache)将会得到一个非常小的值,因为Adagrad的特性,随着优化的进行,学习率会越来越小,很可能还没有到极值就停滞不前了,网络的更新能力会越来越弱,能学到的更多知识的能力也越来越弱。
总的来说,我们发现Adagrad存在问题,即随着优化的进行,分母中的平方梯度累加和cache不断变大,整个训练过程中的学习率将继续下降,最终降至零,完全停止训练,这就是为什么现在我们很少看到Adagrad用于训练深度神经网络。但是,有必要回顾一下,以便我们能够理解Adagrad算法的扩展。
具体的Adagrad算法流程,可以查看图7.2:
Adadelta算法是由Zeiler在2012年的论文《ADADELTA: An Adaptive Learning Rate Method 》[29]提出。Adadelta是对Adagrad的扩展,以处理Adagrad学习率一直单调递减的问题。
在Adagrad算法中,Adagrad会累加之前所有的梯度平方,即cache会不断的增加。但是,Adadelta只累加固定大小的项,并且也不直接存储这些项。仅仅是近似计算对应的平均值——实际实现时,梯度和是递归的定义成历史梯度平方的衰减平均值。即:
因此,Adadelta可以被视为Adagrad的改进,然而,与之密切相关的RMSprop算法(它也执行cache衰减)通常比Adadelta更受欢迎。
RMSprop
RMSprop算法是一个非常高效的算法,它是Geoffrey Hinton在Coursera课程[2]中提到的一种(未发表的)优化算法。与Adadelta类似,RMSprop试图通过将对cache计算指数加权移动平均而不是计算所有过去cache的累加带来的负面影响。
让我们来看看RMSprop的更新过程伪代码:
1 | cache = decay_rate * cache + (1 - decay_rate) * (dW **2) |
你会注意到,RMSprop对权重矩阵W的更新与Adagrad的更新是一样的——主要是cache的更新。其中decay_rate,通常定义为,一般设置为0.9。而唯一不同的是就在于累积平方梯度的求法不同。RMSProp算法不是像AdaGrad算法那样暴力直接的累加平方梯度,而是加了一个衰减系数来控制历史信息的获取多少。见下:
鉴于神经网络都是非凸条件的,RMSProp在非凸条件下结果更好。通过指数衰减的移动平均计算梯度累积,可以丢弃时间间隔较大的历史信息。经验上,RMSProp被证明是一个有效且实用的深度学习优化算法。
除了SGD,RMSprop可以说是最近深度学习文献中使用最多的优化算法,然而,我们将要讨论的下一个优化方法——Adam,现在使用更多。
具体的RMSprop算法流程,可以查看图7.3:
Kingma和Ba在2014年的论文《Adam: A Method for Stochastic Optimization》中提出的Adam(自适应矩估计)优化算法,本质上只是添加了动量的RMSprop。具体的更新伪代码如下:
1 | m = beta1 * m + (1-beta1) * dW |
m和v的值类似于SGD的momentum,依赖于t-1时刻之前的值,m表示梯度的第一时刻平均值,v表示梯度的第二时刻非中心方差值。
Adam算法同时获得了 AdaGrad 和 RMSProp 算法的优点。Adam 不仅如 RMSProp 算法那样基于一阶矩均值计算适应性参数学习率,它同时还充分利用了梯度的二阶矩均值(即有偏方差/uncentered variance)。具体来说,Adam算法计算了梯度的指数移动均值(exponential moving average),其中由超参数 beta1 和 beta2 控制了这些移动均值的衰减率。
具体的Adam算法流程,可以查看图7.4:
Nadam
就像Adam是带有momentum的RMSprop, Nadam是带有Nesterov加速度的RMSprop。Nadam是由斯坦福大学的博士生Timothy Dozat提出的。我们通常不会看到在实际应用中使用Nadam,但重要的是要理解Adam的扩展确实存在。
如何选择优化算法
给定所有优化算法,您应该选择哪个?其实答案是非常不确定——2014年,Schaul等人在2014发表的《Unit tests for Stochastic Optimization》,其中试图对许多优化方法进行了测试,发现自适应学习率算法表现良好,但没有明确的哪一个是最好的。
深度学习优化算法(以及如何选择它们)仍然是一个开放的研究领域,而且可能会持续很多年。因此,与其针对数据尝试每一种优化算法,然后找到一个有用的,还不如掌握两种或三种优化算法。深度学习项目的成功通常是优化算法(以及相关参数)和研究人员如何熟练地“驱动”算法的结合。
应该了解的三种优化算法: SGD, Adam, and RMSprop
考虑到自适应学习率算法(如RMSprop和Adam)的成功,你可能会忽略SGD算法,把它当作一种过时的工具。毕竟,“更好”的方法还是存在的,不是吗?
然而,忽视SGD算法将是一个巨大的错误。看看最近关于图像分类数据集的最新的深度学习算法,比如ImageNet: AlexNet[6], VGGNet[11], squeezeNet[32], Inception[17], ResNet[33]——这些最新的网络结构都是使用SGD进行训练的。
既然自适应学习率算法很好,为什么还使用SGD进行训练呢?我们可以清楚地看到,应用自适应学习速率算法(如RMSprop和Adam ),可以让网络更快地收敛。然而,收敛速度虽然重要,但并不是最重要的因素——模型的超参数仍然更加重要。在给定优化器(以及相关的模型)情况下,如果你不能将超参数调优到最佳,那么你的网络将永远不会获得合理的准确度。
虽然SGD的收敛速度比自适应学习速率算法要慢,但它也是一个更深入研究的算法。研究人员对SGD更为熟悉,多年来一直使用它来训练网络。
例如,一位职业赛车手,他驾驶同一款赛车的车型和型号已经有五年了。然后,有一天,司机的赞助商改变了主意,逼他们开一辆新车且没有时间练习新的赛车,车手会在他们的前几场比赛中表现出色吗?最可能的情况是,车手对车辆并不熟悉(但由于车手毕竟是专业人士,所以仍可能表现得比较合理)。
深度学习架构和优化算法也是如此。我们对给定的网络体系结构和优化算法进行的实验越多,我们就越能了解训练过程的复杂性。近60年以来,训练神经网络的算法基本都是以SGD为主,毫无疑问,SGD算法至今仍然在沿用——与模型的性能(准确度)相比,它的收敛速度并不重要。
简单地说:如果我们使用SGD算法在给定的数据集上获得更高的准确度,我们很可能会使用SGD,即使训练时间比使用Adam或RMSprop要慢1.5倍,因为我们更好地理解了模型的超参数。目前最常用的深度学习优化算法有:
- SGD
- RMSprop
- Adam
在对新数据集建模或者新的模型测试时,建议优先使用SGD算法。在某些情况下。它可能会获得很好的效果,当然,也存在某些情况下,结果很差。你可以通过特定的优化算法了解更多的深度学习问题,以及对相关超参数进行调优。记住,深度学习既是科学又是艺术——掌握优化算法绝对是一门需要大量实践的艺术。通过本文,你还可以选择RMSprop或Adam。
我个人建议在优先学习Adam,以我的经验来看,在大多数情况下,Adam的性能都要优于RMSprop。
总结
在本章中,我们讨论了自适应学习速率优化算法。选择哪种优化算法来训练深度神经网络是高度依赖于你是否对:
- 数据的熟悉
- 网络结构的熟悉
- 优化算法的熟悉(包括相关的超参数)
与其盲目地尝试各种优化算法,还不如熟练掌握两三中优化算法,以及如何调优相关的超参数。精通这些优化算法将使你能够更轻松地将新的模型体系结构应用到以前从未使用过的数据集。
我个人的建议是,在你刚开始学习深度学习时,多花时间去掌握如何使用SGD,尤其是带有动量的SGD。一旦你觉得可以将SGD应用到各种架构和数据集上,那么就可以学习Adam和RMSprop。
最后,请记住,模型收敛速度仅次于损失和精度——选择一个你可以熟练的调优超参数的优化算法,这样你可以得到一个高性能的网络模型。