当前位置: 代码迷 >> 综合 >> 关于周期性学习率(Cyclical Learning Rate, CLR)
  详细解决方案

关于周期性学习率(Cyclical Learning Rate, CLR)

热度:59   发布时间:2023-12-14 22:22:29.0

背景介绍

在神经网络训练的过程中,一个需要调节的非常关键的超参数就是学习率。合理的学习率的设置决定了模型训练的精度和效率。学习率设置的过大,模型可能很难收敛,设置的过小,则参数更新过于缓慢,因此模型训练的会比较慢,需要更多轮的迭代才能达到与合理的学习率在更少的迭代轮数上取得相同的效果。我们遇到的主要问题有两个:一个是初始学习率该怎么设置,另一个是在模型训练的过程中学习率该怎样变化才能使得训练的模型更优。目前的一些主流的方法,比如随机梯度下降(SGD),可以设置一个参数,使得学习率随着模型的训练而下降,一直降到一个非常小的值。但是这样的话,还是有可能会遇到模型收敛到一个局部最优点,或者鞍点上。本文将介绍一种周期性学习率的技术来解决上面的第二个问题。第一个关于初始学习率的设置的问题会在另外的博文中介绍。

图1.采用传统学习率下降和周期性学习率下降示意图。
  • 采用单调下降的学习率的一些可能的弊端:

1.我们的模型已经优化器对于初始的学习率是敏感的。

2.我们不知道该选什么样的初始学习率,因此我们可能需要尝试几十甚至几百次的试验来选定一个比较合理的初始学习率。

3.当初始学习率下降时,我们也不能保证模型会收敛到一个局部最小值。事实上,它有可能会收敛在鞍点上。

而利用Cyclical Learning Rate 可以解决上面这三个弊端。

 

 

三种 Cyclical Learning Rate (CLR)

周期性学习率遵从从小到大再从大到小,然后又是从小到大再从大到小,如此这般循环下去。1个Cycle定义为从小到大再从大到小的变化。1个Cycle由两个step_size组成,见下图1:

图1. Triangular型的周期性学习率。

对于CLR,需要设定一个最大的学习率(max_lr) 和一个最小的学习率(base_lr), 整个学习率在训练期间会在这两个限定值之间来回振荡。按照振荡的方式不同,这里介绍三种CLR。

1.第一种CLR命名为"triangular",每一个Cycle的max_lr是不变的,也即为图1所示情况。
2.第二种CLR命名为"triangular2",每一个Cycle的max_lr会逐渐减半,如下图2所示。
3.第三种CLR命名为"exp_range",每一个Cycle的max_lr随着Cycle指数下降,如下图3所示。

图2.Triangular2型的周期性学习率。

 

图3.Exp_range型周期性学习率。

对于第一种Triangular型周期性学习率,由于即便到了训练的末期,其学习率仍然可以达到最大或者比较大的一个值,因此就有可能会使得原本已经训练到了最优的模型又跳出最优点位置,使得模型的训练结果变得不太稳定。因此后面的trainglar2 和 exp_rangle两种类型的周期性学习率可以保证避免这种不稳定性问题。

有两点需要注意的是:

1.上面的图显示,一开始的学习率是一个非常小的值,也就是说学习率由小变大。(为什么不一开始由大到小呢?我的理解是,最终我们设定的参数应该要保证使得最后一个训练的iteration所对应的学习率是最小的。否则很难保存模型训练的最后一个iterations对应的是局部最优点。)

2.参考资料中的作者(Adrian Rosebrock) 在实践中发现,他不需要用“”exp_range策略“来调节学习率,实际上,在大部分情况下,triangular和triangular2两种策略已经可以取得足够好的效果了


关键实现代码介绍:

1.github(https://github.com/bckenstler/CLR)上也给出了上述三个周期性学习率调整策略的关键代码。

2.详细的使用上述代码例子可以在提供的参考资料主页上找到索取方法。需要向Adrian Rosebrock发邮件索要。

这里我只挑选重要的地方讲解。

关键的定义这三种周期学习率的代码就只有下面几行:

# CLR
cycle = np.floor(1+iterations/(2*step_size))
x = np.abs(iterations/step_size - 2*cycle + 1)
lr= base_lr + (max_lr-base_lr)*np.maximum(0, (1-x))*scale_fn(x)

其中不同的scale_fn(x)就代表了不同的周期性学习率策略。具体可以见如下代码片段:

if self.mode == 'triangular':self.scale_fn = lambda x: 1. # triangularself.scale_mode = 'cycle'
elif self.mode == 'triangular2':self.scale_fn = lambda x: 1/(2.**(x-1)) # triangular2self.scale_mode = 'cycle'
elif self.mode == 'exp_range':self.scale_fn = lambda x: gamma**(x) # exp_rangeself.scale_mode = 'iterations'

在keras中可以按照下面的代码来使用CLR策略:

cycle = np.floor(1+iterations/(2*step_size))
x = np.abs(iterations/step_size - 2*cycle + 1)
lr= base_lr + (max_lr-base_lr)*np.maximum(0, (1-x))*gamma**(iterations) # exp_rangeclr = CyclicLR(base_lr=0.001, max_lr=0.006,step_size=2000., mode='exp_range',gamma=0.99994)
model.fit(X_train, Y_train, callbacks=[clr])

需要说明的是,你也可以自己去定义scale_fn。下面例举两个例子:

1.cycle的最大幅度按照类似sin函数变化:

cycle = np.floor(1+iterations/(2*step_size))
x = np.abs(iterations/step_size - 2*cycle + 1)
lr= base_lr + (max_lr-base_lr)*np.maximum(0, (1-x))*0.5*(1+np.sin(cycle*np.pi/2.))clr_fn = lambda x: 0.5*(1+np.sin(x*np.pi/2.))
clr = CyclicLR(base_lr=0.001, max_lr=0.006,step_size=2000., scale_fn=clr_fn,scale_mode='cycle')
model.fit(X_train, Y_train, callbacks=[clr])

2.cycle的最大幅度是cycle的iterations的函数:

cycle = np.floor(1+iterations/(2*step_size))
x = np.abs(iterations/step_size - 2*cycle + 1)
lr= base_lr + (max_lr-base_lr)*np.maximum(0, (1-x))*1/(5**(iterations*0.0001))clr_fn = lambda x: 1/(5**(x*0.0001))
clr = CyclicLR(base_lr=0.001, max_lr=0.006,step_size=2000., scale_fn=clr_fn,scale_mode='iterations')
model.fit(X_train, Y_train, callbacks=[clr])

一些说明:

batch_size: 每一批同时输入模型进行前向和反向传播的数据量;

iteration: 表示每一轮(epoch)中,参数更新的次数,它等于 “总的样本数/batch_size”(向上取整)。

               从上面两张图可以看出,学习率在每一个iteration都是变化的。不要把它理解为在某一段

               iterations区间它是不变的,进入下一个区间它才变化。

Cycle: 它表示学习率从base_lr增大到max_lr,再从max_lr减少到base_lr所需要的iteration数目。

           通常1个Cycle所包含的iteration数目比一轮(epoch)训练所包含的iteration要多。也就是说,

           经过几个epoch才完成1个Cycle。

step_size: 半个Cycle所需要的iterations, CLR的发明者Leslie Smith建议,1个step_size可以取

          (2-8)*training_iterations_in_epoch。而前面提到的Adrian Rosebrock发现在大部分情形下,

          取4或者8就足以得到不错的结果。也就是说通常step_size也要跨越4个或者8个epoch。

比如,num_epochs=96, step_size=8, 那么:  1 cycle = 96/(8*2) = 6 。也就是说在96轮迭代中,一共

有6个学习率调整周期。个人理解:通常设置的 num_epochs 能够被 2*step_size 整除,这样才能保证最后一个

iteration对应的学习率最小,从而保证模型收敛于一个局部最优解。

一个小问题:

结合early_stop技术,有可能在做early_stop时,对应的学习率不是最小值。会不会出现这种情况呢?应该是有可能的,

具体是否如此,就在实践中检验吧。
 

参考资料:

1. https://www.pyimagesearch.com/2019/07/29/cyclical-learning-rates-with-keras-and-deep-learning/

2. https://arxiv.org/pdf/1506.01186.pdf

 

 

 

 

  相关解决方案