【教程】估算一个最佳学习速率,以更好地训练深度神经网络

时间:2022-05-03
本文章向大家介绍【教程】估算一个最佳学习速率,以更好地训练深度神经网络,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

对于训练深度神经网络来说,学习速率是调优的最重要的超参数之一。在这篇文章中,我将描述一个简单而有力的方法来找到一个合理的学习速率。这种方法是我从Fast AI网站的深度学习课程中了解到的。

学习速率如何影响训练

深度学习模型通常由随机梯度下降优化器训练。许多随机梯度下降的变体,如Adam, RMSProp, Adagrad等等,都可以让你设置学习速率。学习速率能够告诉优化器,在一个小批次处理的梯度方向移动权重的距离有多远。

如果学习速率很低,那么训练就更可靠,但是优化需要花费大量的时间,因为最小的损失函数的步长是很小的。

如果学习速率很高,那么训练可能不会收敛甚至是扩散的。权重的变化会非常大,以至于优化器会超过最小值,并使损失变得更严重。

梯度下降与小(上)和大(下)学习速率。来源:Coursera的机器学习课程

训练应该从一个相对较大的学习速率开始,因为在开始的时候,随机的权重远远不是最优的,然后在训练过程中学习速率会下降,从而允许更优的权重更新。

有多种方法可以为学习速率选择一个好的起点。一个简单的方法是尝试一些不同的值,看看哪一个值能给你最好的损失,同时又不牺牲训练的速度。我们可能从一个很大的值开始,比如0.1,然后尝试以指数方式降低的值,如0.01, 0.001等等。当我们开始以一个大的学习速度进行训练时,损失并没有得到改善,甚至可能在我们进行最初的几次训练的时候就会增长。当以较小的学习速率进行训练时,在某些时候,损失函数的值在开始的几次迭代中开始减少。这个学习速率是我们可以使用的最大值,任何更高的值都不会让训练收敛。即使这个值也太高了:它不能足够好地为多个epoch进行训练,因为随着时间的推移,网络将需要更优的更新。因此,一个合理的开始训练的学习速率可能会降低1-2个数量级。

更聪明的方法 在2015年的“训练神经网络的循环学习速率”论文的3.3章节,Leslie N.Smith描述了一种强大的技术,为神经网络选择一系列的学习速率。

关键是要训练一个从低学习速率开始的网络,每一批次都要以指数级的速度增加学习速率。

论文地址:https://arxiv.org/abs/1506.01186

每个小批次处理后,学习速率提高

记录每个批次的学习速率和训练损失。然后,把损失和学习速率画出来。通常情况下是这样的:

开始时的损失减少,然后在训练过程中开始扩散

首先,低学习速率的损失会慢慢提高,然后训练会加速,直到学习速率变大,并且损失增加:训练过程会扩散。

我们需要在图上选择一个点,以最快的速度减少损失。在本例中,当学习速率在0.001到0.01之间时,损失函数就会迅速下降。

另一种观察这些数字的方法是计算损失的变化率(损失函数关于迭代次数的导数),然后绘制y轴上的变化率和x轴上的学习速率。

损失变化率

它看起来波动有些大,让我们用简单的移动平均数的方法来平滑它。

损失的变化率,简单的移动平均数

这样看起来更好。在这张图上,我们需要找到最小值。它接近于学习速率=0.01。

实现 美国USF数据研究所的杰里米霍华德和他的团队开发了fast.ai。fast.ai是一个在PyTorch之上的一个高级抽象的深度学习库,它是一种易于使用又强大的工具集,用于训练艺术深度学习模型。杰里米在最新版本的深度学习课程中使用了这个库。该库提供了一个学习速率查找器的实现。你只需要几行代码就可以绘制对你的模型的学习率造成的损失。

fast.ai库:https://github.com/fastai/fastai

代码:

# learn is an instance of Learner class or one of derived classes like ConvLearner
learn.lr_find()
learn.sched.plot_lr()

这个库没有代码来绘制损失函数的变化率,但是计算它是很简单的:

def plot_loss_change(sched, sma=1, n_skip=20, y_lim=(-0.01,0.01)):
 """
 Plots rate of change of the loss function.
 Parameters:
 sched - learning rate scheduler, an instance of LR_Finder class.
 sma - number of batches for simple moving average to smooth out the curve.
 n_skip - number of batches to skip on the left.
 y_lim - limits for the y axis.
 """
 derivatives = [0] * (sma + 1)
 for i in range(1 + sma, len(learn.sched.lrs)):
 derivative = (learn.sched.losses[i] - learn.sched.losses[i - sma]) / sma
 derivatives.append(derivative)
 
 plt.ylabel("d/loss")
 plt.xlabel("learning rate (log scale)")
 plt.plot(learn.sched.lrs[n_skip:], derivatives[n_skip:])
 plt.xscale('log')
 plt.ylim(y_lim)

plot_loss_change(learn.sched, sma=20)

请注意,在训练前选择一次学习速率是不够的。最佳学习速率在训练时下降。你可以周期性地重新运行相同的学习速率搜索过程,以在训练过程的后期找到学习速率。

使用其他库实现该方法 我还没有准备好使用像Keras这样的其他库的学习速率的搜索方法。只要多次运行训练,每次只训练一个小批次就可以了。在每次小批次训练后,通过将它乘以一个小的常数增加学习速率。当损失比先前观察到的最好的值(例如,当当前损失>最好损失乘以4)高很多时,停止该程序。

选择一个学习速率的起始值只是问题的一部分。另一件要优化的事情是学习进度:如何在训练中改变学习速率。传统观点认为,随着时间的推移,学习速率会逐渐下降,有多种方法来设置:当损失停止改进、指数学习速率衰减,等等情况发生时,学习速率就会降低。

Fast AI网站:http://www.fast.ai/