导语Darknet是一个比较小众的开源深度学习框架,与目前主流的深度学习框架不同的是,该框架由C语言实现,对于新手来说有一定的学习难度。但是即便这样,也挡不住我们使用该框架的热情(也可能是迫不得已),因为大名鼎鼎的目标检测算法YOLO(从v1~v4)就是基于该框架实现的。
学习率是训练深度学习模型时设置的一个关键的超参数,其值直接决定了训练的模型是否收敛、收敛的快慢程度:如果学习率设置得过大,则可能导致模型无法收敛;设置得过小则会导致模型收敛得很慢,从而使得训练时间过长。下图形象地说明了学习率对模型训练过程的影响:图1学习率过大,导致模型无法收敛;图2学习率过小,导致训练耗时较长;图3先设置了一个合适的学习率,然后在训练过程中还会适当地减少学习率,使得模型可以很好地收敛到全局最优点。
图片来源于网络
学习率是训练模型之前就设置好的一个超参数,在训练过程中还会基于一定的策略进行调整。
Darknet中的几种学习率调整策略
在Darknet中,神经网络的模型结构和训练时需要的一些超参数都是在一个`.cfg`文件中指定。学习率的调整策略则是在`.cfg`文件中通过`policy=`进行设置,在框架的代码中,则是由`parser.c`的`get_policy()`函数对`.cfg`文件中设定的调整策略字符串进行解析以确定本次训练所采用的学习率调整策略。`Darknet`框架支持的几种学习率调整策略定义在一个名为`learning_rate_policy`的枚举类型中,其中每个成员代表的调整策略的具体含义解释如下:
CONSTANT: 恒定的学习率,学习率初始值在`.cfg`文件设置好后,训练过程中不会再做调整。RANDOM: 也是恒定的学习率,但其值由下面的公式决定 其中,为初始学习率;为`0~1`之间的随机数;`power`是由`.cfg`文件中的`power=`设置的参数。POLY:训练过程中学习率调整方式由下面公式决定 其中,为初始学习率;`batch_num`为当前的迭代次数;`max_batches`为最大的迭代次数,该参数由`.cfg`文件中的`max_batches=`指定;`power`是由`.cfg`文件中的`power=`设置的参数。当时,训练过程中学习率的调整过程曲线如下图所示:
STEP: 训练过程中学习率调整方式由下面公式决定 其中,为初始学习率;`batch_num`为当前的迭代次数;`scale`是由`.cfg`文件中的`scale=`设置的参数;`step`是由`.cfg`文件中的`step=`设置的参数。当时,训练过程中学习率的调整过程曲线如下图所示:
EXP: 训练过程中学习率调整方式由下面公式决定 其中,为初始学习率;`batch_num`为当前的迭代次数;是由`.cfg`文件中的`gamma=`设置的参数。当时,训练过程中学习率的调整过程曲线如下图所示:
SIG:训练过程中学习率调整方式由下面公式决定 其中,为初始学习率;`batch_num`为当前的迭代次数;是由`.cfg`文件中的`gamma=`设置的参数;`step`是由`.cfg`文件中的`step=`设置的参数。当时,训练过程中学习率的调整过程曲线如下图所示:
STEPS: 一种阶段性调整策略,可以设置多个阶段,当前迭代次数达到设定值时,学习率调整为
在官方的`yolov3.cfg`文件中,有如下内容:
policy=steps steps=400000,450000 scales=.1,.1 意思是当迭代次数达到400000时,学习率乘以0.1;当迭代次数达到450000时,学习率再乘以0.1。学习率调整曲线如下图所示:
学习率预热在Darknet框架中,还可以通过`.cfg`文件设定一个`burn_in`参数。该参数用于在训练的前期,让学习率以一定的方式从零逐渐增大到指定的初始学习率,相当于是让学习率有一段时间的“热身(warm up)”过程,这样做的目的是避免训练初期由于初始学习率设置得过大导致训练过程出现震荡。这个阶段内,学习率的调整策略由下面的公式决定:当时,预热阶段学习率的调整过程曲线如下图所示:
如何在Darknet中新增学习率调整策略上文介绍了`Darknet`中的几种学习率调整策略,但是如果我们想用其他的策略应该怎么办呢?下面就以添加`COSINE`调整策略为例说明如何在Darknet中添加一种新的学习率调整策略。`COSINE`调整策略的公式如下其中,为初始学习率;`batch_num`为当前的迭代次数;`max_batches`为最大迭代次数。训练过程中学习率的调整过程曲线如下图所示:
添加`COSINE`调整策略的过程如下:1. 在`darknet.h`文件中,为枚举类型`learning_rate_policy`添加一个成员`COSINE`:
typedef enum { CONSTANT, STEP, EXP, POLY, STEPS, SIG, RANDOM, COSINE} learning_rate_policy;2. 在`parser.c`文件中的`get_policy()`函数中添加对`COSINE`调整策略的支持:
learning_rate_policy get_policy(char *s) { if (strcmp(s, "random")==0) return RANDOM; if (strcmp(s, "poly")==0) return POLY; if (strcmp(s, "constant")==0) return CONSTANT; if (strcmp(s, "step")==0) return STEP; if (strcmp(s, "exp")==0) return EXP; if (strcmp(s, "sigmoid")==0) return SIG; if (strcmp(s, "steps")==0) return STEPS; if (strcmp(s, "cosine")==0) return COSINE; fprintf(stderr, "Couldn't find policy %s, going with constant\n", s); return CONSTANT;}3. 在`parser.c`文件中的`parse_net_options()`函数中去解析`.cfg`文件设置的参数。由于`COSINE`调整策略所需的参数`max_batches`已经有解析接口了,所以这里不需要额外添加。4. 在`network.c`文件中的`get_current_rate()`函数中添加一个switch分支语句,实现`COSINE`调整策略:
case COSINE: return net.learning_rate * 0.5 * (1 + cos(batch_num *M_PI / net.max_batches))5. 重新编译Darknet工程。编译成功后只要在`.cfg`文件中设置`policy=cosine`,就可以使用`COSINE`学习率调整策略了。如果要添加其他的策略,参照以上方法也很容易就可以实现。
结语学习率是训练深度学习模型过程中的一个很重要的超参数,可以通过多种方式进行调整。由于Darknet框架没有学习率调整策略的相关文档,要想知道具体细节只能通过剖析源代码才能了解每个参数的含义。本文详细介绍了Darknet框架中的学习率调整策略,通过公式和图形的方式阐述每种调整策略各个参数的意义和调整的过程,同时也介绍了添加新的调整策略的方法,希望对本文的读者有所帮助。