首先,什么是学习率?
学习率(Learning Rate,LR。常用η表示。)是一个超参数,考虑到损失梯度,它控制着我们在多大程度上调整网络的权重。值越低,沿着向下的斜率就越慢。虽然这可能是一个好主意(使用低学习率),以确保我们不会错过任何局部最小值;但也有可能意味着我们将耗费很久的时间来收敛——特别是当我们陷入平坦区(plateau region)的时候。
AI前线:如果使用很高的学习率,训练可能根本不会收敛,甚至会发散。权重的该变量可能会非常大,使得优化越过最小值,导致损失函数变得更糟。
下面的公式显示了这种关系:
new_weight = existing_weight — learning_rate * gradient
学习率很小(上图)与学习率很大(下图)的梯度下降。(来源:Coursera机器学习课程,Andrew Ng)
通常,学习率是由用户随机配置的。在最好的情况下,用户可以利用过去的经验(或者其他类型的学习材料)来获得关于设置学习率最佳值的直觉。
因此,很难做到这一点。下图演示了配置学习率时可能会遇到的不同场景。
不同学习率对收敛的影响:(图片来源:csn231n)
此外,学习率会影响模型收敛到局部最小值的速度(也就是达到最佳的精度)。因此,在正确的方向做出正确的选择,意味着我们只需更少的时间来训练模型。
训练时间越少,则花在GPU云计算上的钱就越少。:)
AI前线:目前深度学习使用的都是一阶收敛算法:梯度下降法。不管有多少自适应的优化算法,本质上都是对梯度下降法的各种变形。故初始学习率对深层网络的收敛起着决定性的作用。
有没有更好的方法来确定学习率?
在“训练神经网络的循环学习率(Cyclical Learning Rates (CLR)for Training Neural Networks)”[4]的第3.3节中。Leslie N. Smith认为,通过在每次迭代中以非常低的学习率来增加(线性或指数)的方式训练模型,可以估计好的学习率。
AI前线:周期性学习率(Cyclical Learning Rates,CLR),即学习率退火与热重启,最初由Smith于2015年首次提出。这是一种新的学习率方法,和以前的不同,或者固定(fixed)或者单调递减。要使用CLR,需指定这三个参数:max_lr、base_lr、stepsize。
学习率在每个小批量之后增加
如果我们在每次迭代中记录学习率和训练损失,然后据此绘出曲线图;我们将会看到,随着学习率的提高,将会有一个损失停止下降并开始增加的点。在实践中,理想情况下,学习率应该是在左图的最低点(如下图所示)。在该例中为0.001到0.01之间。
以上看起来很有用。我该如何开始使用它?
目前,它被作为fast.ai深度学习库的一个函数来支持。由Jeremy Howard开发,是用来抽象PyTorch深度学习框架的一种方式,就像Keras是对TensorFlow框架的抽象。
AI前线: fast.ai深度学习库是fast.ai基于PyTorch的基础上创建的自有软件库,并且他们认为,这将有助于更加清晰地展示深度学习的概念,同时有助于实现最佳编码。采用Apache 2.0许可证,可免费使用。
只需输入以下命令,就可以在训练神经网络之前找到最佳学习率。
# learn is an instance of Learner class or one of derived classes like ConvLearner
learn.lr_find()
learn.sched.plot_lr()
精益求精
在这个关键时刻,我们已经讨论了学习率的全部内容和它的重要性,以及我们如何在开始训练模型时系统地达到最佳的使用价值。
接下来,我们将讨论如何使用学习率来提高模型的性能。
一般看法
通常情况下,当一个人设定学习率并训练模型时,只有等待学习率随着时间的推移而降低,并且模型最终会收敛。
然而,随着梯度逐渐趋于稳定时,训练损失也变得难以改善。在[3]中,Dauphin等人认为,最大限度地减少损失的难度来自于鞍点,而非局部极小值。
AI前线:鞍点是梯度接近于0的点,在误差曲面中既不是最大值也不是最小值的平滑曲面,则一般结果表现为性能比较差;如果该驻点是局部极小值,那么表现为性能较好,但不是全局最优值。
误差曲面中的鞍点。鞍点是函数的导数变为零但点不是所有轴上的局部极值的点。(图片来源:safaribooksonline)
那么我们该如何摆脱呢?
有几个选项我们可以考虑。一般来说,从[1]引用一句:
……而不是使用一个固定值的学习率,并随着时间的推移而降低,如果训练不会改善我们的损失,我们将根据一些循环函数f来改变每次迭代的学习率。每个周期的迭代次数都是固定的。这种方法让学习率在合理的边界值之间循环变化。这有助于解决问题,因为如果我们被困在鞍点上,提高学习率可以更快速地穿越鞍点。
在[2]中,Leslie提出了一种“Triangular”的方法,在每次迭代之后,学习率都会重新开始。
Leslie N. Smith提出的“Triangular”和“Triangular2”循环学习率的方法。在左边的图上,min和max lr保持不变。在右边,每个周期之后的差异减半。
另一种同样受欢迎的方法是由Loshchilov和Hutter提出的热重启的随机梯度下降法(Stochastic Gradient Descent with Warm Restarts,SGDR)[6]。这种方法主要利用余弦函数作为循环函数,并在每个周期的最大值重新开始学习率。“热重启”一词源于这样的一个事实:当学习率重新开始的时候,并不是从头开始,而是来自模型在上一步收敛的参数开始[7]。
AI前线:热重启后的初始高学习率用于基本上将参数从它们先前收敛的最小值弹射到不同的损失表面。根据经验,热重启的随机梯度下降法需要的时间比学习率退火要少2~4倍,且能达到相当或更好的性能。
虽然有这种变化,下面的图表展示了它的一个实现,其中每个周期都被设置为同一时间周期。
SGDR图,学习率与迭代。
因此,我们现在有一种减少训练时间的方法,基本上就是周期性地在“山脉”周围跳跃(下图)。
比较固定学习率和循环学习率(图片来源:ruder.io)
除了节省时间外,研究还表明,使用这些方法往往可以提高分类准确性,而无需进行调优,而且可以在更少的迭代次数内完成。
迁移学习(Transfer Learning)中的学习率
在fast.ai课程中,在解决AI问题时,非常重视利用预先训练的模型。例如,在解决图像分类问题时,教授学生如何使用预先训练好的模型,如VGG或Resnet50,并将其连接到想要预测的任何图像数据集。
总结如何在fast.ai中完成模型构建(注意该程序不要与fast.ai深度学习库混淆),下面是我们通常采取的几个步骤[8]:
1. 启用数据增强,precompute=True。
2. 使用lr_find()
查找最高的学习率,在此情况下,损失仍在明显改善。
3. 训练最后一层从预计算激活1~2个轮数。
4. 在cycle_len=1的情况下训练最后一层数据增加(即precompute=False)2~3个轮数。
5. 解除所有层的冻结。
6. 将较早的层设置为比下一个较高层低3~10倍的学习率。
7. 再次使用lr_find()
。
8. 使用cycle_mult=2训练完整网络,直到过度拟合。
从上面的步骤中,我们注意到第2步、第5步和第7步关注了学习率。在这篇文章的前半部分,我们已经基本讨论了涵盖了上述步骤中的第2项——我们在这里讨论了如何在训练模型之前得出最佳学习率。
AI前线:轮数,epoch,即对所有训练数据的一轮遍历。
在接下来的部分中,我们通过使用SGDR来了解如何通过重新开始学习速率来减少训练时间和提高准确性,以避免梯度接近于0的区域。
在最后一节中,我们将重点讨论差分学习,以及它是如何被用来在训练模型与预先训练的模型相结合时确定学习率的。
什么是差分学习?
这是一种在训练期间为网络中的不同层设置不同的学习率的方法。这与人们通常如何配置学习率相反,即在训练期间在整个网络中使用相同的速率。
这是我为什么喜欢Twitter的原因之一——可以直接从作者本人得到答案。
在写这篇文章的时候,Jeremy和Sebastian Ruder发表了一篇论文,深入探讨了这个话题。所以我估计差分学习率现在有一个新的名字:判别式微调(discriminative fine-tuning)。 :)
AI前线:判别式微调对较底层进行微调以调到一个相较于较高层较低的程度,从而保留通过语言建模所获得的的知识。它可以避免微调过程中产生严重的遗忘。
为了更清楚地说明这个概念,我们可以参考下图,其中一个预训练模型被分成3个组,每个组都配置了一个递增的学习率值。
差分学习率的CNN样本。图片来自[3]
这种配置方法背后的直觉是,最初的几层通常包含数据的非常细粒度的细节,如线条和边缘——我们通常不希望改变太多,并且保留它的信息。因此,没有太多的需要去大量改变它们的权重。
相比之下,在后面的层中,比如上面绿色的层——我们可以获得眼球或嘴巴或鼻子等数据的详细特征;我们可能不一定要保留它们。
与其他微调方法相比,它表现如何?
在[9]中,有