前言
模型准确率的提高除了模型本身的设计学习的过程也十分重要,好的学习过程可以让模型更快更好地趋近于最优。而学习过程中除了学习算法,学习率的控制也尤为重要,固定值的学习率很容易最终不断振荡,无法实现收敛。本文主要整理介绍一些学习率衰减算法,同时也简单介绍一些例如warm up和batch size有关的学习率控制经验。
学习率衰减算法
注意:代码方面的用法仅供参考,还没来得及具体考证,准确起见还是看相应的注释吧。
1、分段常数衰减:固定步长/多步长
分段常数衰减比较好理解,基本思想是事先定义好训练区间,在不同的区间定义不同的区间设置不同的学习率的常数值。而根据区间长度是否固定还有固定步长衰减和多步长衰减。前者是每隔固定步数衰减一次,后者是根据设定的不同区间来更新。
PyTorch可以分别通过下面的代码实现,参数根据实际需要修改即可:
torch.optim.lr_scheduler.StepLR(optimizer_StepLR, step_size=step_size, gamma=0.65)
torch.optim.lr_scheduler.MultiStepLR(optimizer_MultiStepLR,
milestones=[200, 300, 320, 340, 200], gamma=0.8)
2、指数衰减/自然指数衰减
指数衰减即根据指数曲线进行衰减,一般来说开始衰减的快后来慢,根据设置的参数决定底数。
torch.optim.lr_scheduler.ExponentialLR(optimizer_ExpLR, gamma=0.98)
指数衰减的公式如下:
而自然指数衰减即再乘个e,其代码和公式如下:
torch.optim.lr_scheduler.ExponentialLR(optimizer_ExpLR, gamma=0.98)
decayed_learning_rate = learning_rateexp(-decay_rateglobal_step)
3、多项式衰减
这个也很好理解,从初始学习率到结束学习率以多项式幂的形式衰减,当global_step达到decay_steps时即衰减到最终学习率的时刻。从图像和解析式中可以看到一般来说越往后下降的越快。
PyTorch中的实现是否会截断后面的以及调用方式目前还不太了解,以后有机会补上吧,但其思路大概就是这样,通过多项式的曲线进行学习率下降。
4、余弦衰减
余弦衰减就是采用余弦的相关方式进行学习率的衰减,衰减图和余弦函数相似。
从公式可以看到,学习率乘以的衰减系数从1随着global_step增大衰减到α,最终衰减部分曲线类似于cosine曲线,下降速度率逐渐变快。
PyTorch中并没有截断后面的,也就说经过半个周期还是会按照余弦曲线回升,调用方式如下:torch.optim.lr_scheduler.CosineAnnealingLR(optimizer_CosineLR, T_max=150, eta_min=0)
eta_min表示学习率的最小值,默认它是0表示学习率至少为正值。对应到这个公式eta_min = α * learning_rate,T_max 对应1/2个cos周期所对应的epoch数值。
比较
下面在ImageNet-2012上使用SGD对各类学习率衰减算法的学习效果进行测试,可以看到不同的算法表现存在一定差异但也不存在绝对好的算法,在不同的迭代次数各有不同的表现。例如poly前期速度较慢但后期比较稳定,exp前期速度较快后期又所收敛。建议选择一些普遍效果效果较好的然后根据实际情况进行调整。(来源:https://github.com/ducha-aiki/caffenet-benchmark/blob/master/Lr_policy.md)
另外下面的学习率衰减方法与上面介绍的不完全一样,分的更加细致,仅供参考。
其他学习率控制算法
学习率除了训练过程中随着loss降低不断衰减在训练初期还有一些小的技巧,统称为学习率控制算法,这里介绍两个具有代表性的算法。一个与训练初始阶段有关,一个与batch size和lr的关系有关。(摘自:https://blog.csdn.net/Swocky/article/details/105809498)
1、Warm up
由于刚开始训练时模型的权重(weights)是随机初始化的,此时选择一个较大的学习率,可能会带来模型的不稳定。学习率预热就是在刚开始训练的时候先使用一个较小的学习率,训练一些epoches或iterations,等模型稳定时再修改为预先设置的学习率进行训练。
上述的方法是constant warmup,18年Facebook又针对上面的warmup进行了改进,因为从一个很小的学习率一下变为比较大的学习率可能会导致训练误差突然增大。提出了gradual warmup来解决这个问题,即从最开始的小学习率开始,每个iteration增大一点,直到最初设置的比较大的学习率。
2、Linear scaling learning rate
实验证明,大的batch size在相同的epoch下准确率会更小,使用warm up可以在一定程度上解决这个问题,而Linear scaling learning rate也是一种有效的方法。
在mini-batch SGD训练时,梯度下降的值是随机的,因为每一个batch的数据是随机选择的。增大batch size不会改变梯度的期望,但是会降低它的方差。也就是说,大batch size会降低梯度中的噪声,所以我们可以增大学习率来加快收敛。
具体做法很简单,比如ResNet原论文中,batch size为256时选择的学习率是0.1,当我们把batch size变为一个较大的数b时,学习率应该变为 0.1 × b/256。即线性的根据batch大小设置学习率,从而达到更好的学习效果。
简单的说,大的batch size计算得到的梯度噪声更小,所以可以使用更大的学习率来加大收敛。那么这里就有一个问题了,为什么小的batch size一般收敛的更快呢?这是因为小的batch size尽管方向不一定准确,但是更新次数多,最终收敛速度会更快。而大的batch size虽然噪声小,方向也更准确,但是由于学习率效果不会很好,这样线性的增加学习率其实也是相当于用单次更新量变大弥补更新次数小的事实。
总结
学习率衰减算法多种多样,除了上面介绍的还有许多别的方式。当然如同激活函数、优化算法等等,也没有绝对的最优,只能说某种算法对于一般情况表现较好。一般来说可以先选择普遍表现还不错的算法进行尝试,然后再尝试一些别的算法,具体问题具体分析,得到比较好的学习率衰减算法,加快学习速度。当然,最后提到的warm up与linear scaling learning rate也值得注意,也是学习率控制的重要技巧。
另外整理的比较粗略,可能存在一些问题,很多地方也不够细致,有机会再完善一下。
参考资料
https://mp.weixin.qq.com/s/ky52jO92jsRZ9qpkcUmxEA
https://www.jianshu.com/p/125fe2ab085b
https://zhuanlan.zhihu.com/p/93624972
https://blog.csdn.net/hongxue8888/article/details/78652602