1.前言
最近用YOLO V4做车辆检测,配合某一目标追踪算法实现车辆追踪+轨迹提取等功能,正好就此结合论文和代码来对YOLO V4做个解析。先放上个效果图(半成品),如下:
话不多说,现在就开始对YOLO V4进行总结。
YOLO V4的论文链接在这里,名为《YOLOv4: Optimal Speed and Accuracy of Object Detection》,相信大家也是经常看到这几个词眼:大神接棒、YOLO V4来了、Tricks 万花筒等等。
没错,通过阅读YOLO V4的原文,我觉得它更像一篇目标检测模型Tricks文献综述,可见作者在目标检测领域的知识(炼丹技术)积累之多。
从本质上,YOLO V4就是筛选了一些从YOLO V3发布至今,被用在各式各样检测器上,能够提高检测精度的tricks,并以YOLO V3为基础进行改进的目标检测模型。YOLO V4在保证速度的同时,大幅提高模型的检测精度(当然,这是相较于YOLO V3的)。
上图可以看出来,虽然检测精度不如EfficientDet这种变态,但是速度上是遥遥领先的,说明YOLO V4并没有忘记初心(速度和精度的trade off,我YOLO才是佼佼者)!
其实我是比较推荐大家看看YOLO V4原文的,就当炼丹手册来看也是挺好的,如果你懒得看,那这里我贴出来一张图,就是最终YOLO V4的炼丹配方,如下:
这么一看,这炼丹配方多清晰呀,和YOLO V3对比,主要做了以下改变:
- 相较于YOLO V3的DarkNet53,YOLO V4用了CSPDarkNet53
- 相较于YOLO V3的FPN,YOLO V4用了SPP+PAN
- CutMix数据增强和马赛克(Mosaic)数据增强
- DropBlock正则化
- 等等
这技巧太多了,着实让人数不过来。按照惯例,我喜欢结合代码对模型进行解析,论文的话看个思路,实现的细节还是在代码中体现的较具体。原作者YOLO V4的代码是基于C++的,如下:
YOLO V4 C++(原版)github.com这个解析起来太麻烦了,我找了个看起来不麻烦的,基于Keras+Tensorflow的,如下:
YOLO V4 Keras版本github.com本次YOLO V4论文和代码解析也将基于这个版本的进行的啦!
后面的内容将按照以下步骤进行介绍。
- (1)YOLO V4的网络结构
- (2)YOLO V4的损失函数
- (3)一些Tricks的具体代码实现
2. YOLO V4的网络结构
这里我先给出YOLO V4的总结构图,如下(这里感谢评论区细心网友指正,之前那个结构图有点错误,现已修正)
主要有以下三部分组成
- BackBone:CSPDarknet53
- Neck:SPP+PAN
- HEAD:YOLO HEAD
接下面将逐个分析!
2.1 BackBone:CSPDarknet53
目前做检测器MAP指标的提升,都会考虑选择一个图像特征提取能力较强的backbone,且不能太大,那样影响检测的速度。YOLO V4中,则是选择了具有CSP(Cross-stage partial connections)的darknet53,而是没有选择在imagenet上跑分更高的CSPResNext50,
原因很简单,如上表,作者说:
For instance, our numerous studies demonstrate that the CSPResNext50 is
considerably better compared to CSPDarknet53 in terms of object classification on the ILSVRC2012 (ImageNet) dataset [. However, conversely, the CSPDarknet53 is
better compared to CSPResNext50 in terms of detecting objects on the MS COCO dataset
意思就是结合了在目标检测领域的精度来说,CSPDarknet53是要强于 CSPResNext50,这也告诉了我们,在图像分类上任务表现好的模型,不一定很适用于目标检测(这不是绝对的!)。
那么这个带有CSP结构的Darknet53,到底长什么样呢?如果对CSP结构感兴趣的,欢迎点击原文链接。
这里我们直接从代码上看看这个CSPDarknet53什么样子,定义如下
def
如果把堆叠的残差单元(resblock_body)看成整体的话,那么这个结构和Darknet53以及ResNet等的确差别不大,特别是resblock_body的num_blocks为【1,2,8,8,4】和darknet53一模一样。
那么我们解析一下resblock_body的定义,如下:
def
这么一看,和传统的ResBlock差别就出来了,为了大家更清晰地了解结构,我把这个残差单元的结构绘制出来,如下:
对照代码和上面的图片,可以比较清晰地看出来这个CSP残差单元和DarkNet/ResNet的残差单元的区别了。当然了,图上的DarknetConv2D_BN_Mish模块定义如下
- (1) DarknetConv2D_BN_Mish
def
- (2) DarknetConv2D
def
至此,YOLO V4的backbone部分就讲解完毕了。
2.2 Neck:SPP+PAN & Head:YOLO HEAD
目标检测模型的Neck部分主要用来融合不同尺寸特征图的特征信息。常见的有MaskRCNN中使用的FPN等,这里我们用EfficientDet论文中的一张图来进行说明。
可见,随着人们追求检测器在COCO数据集上的MAP指标,Neck部分也是出了很多花里胡哨的结构呀。
本文中的YOLO V4就是用到了SPP(Spatial pyramid pooling)+PAN(Path Aggregation Network,上图的结构b)。
在YOLO V4 Keras代码中,通常将YOLO HEAD(图片上的橙色块)紧接在SSP+PAN后面。为了便于说明,这里我们根据总图上的process1-5与三个YOLO HEAD ,对SSP+PAN+YOLO HEAD 部分进行解析。
(1) 其中process1的代码实现为:
y19
显而易见,该进程接受CSPDarknet53最终的输出,返回变量y19(如总图上process1所示),这里我们也给出图示,如下:
(2) process2 代码如下
y19_upsample
即先将上述的y19进行上采样至大小38x38,然后再和CSPDarknet53的204层输出进行堆叠,最后通过一系列DarknetConv2D_BN_Leaky模块,获得特征图y38。
(3) process3
process3的代码接受y_38上采样后的特征图 y38_upsample以及darknet网络的第131层输出作为输入,从而获得特征图y_38,如下:
y38_upsample
(4)YOLO HEAD 1
紧接在process3之后,代码中使用简单的5+2层卷积层对上面的y76进行输出。其实这里的卷积层就是图中橙色区域YOLO HEAD1 ,在后面的y38_output和y19_output的输出过程中仍能够看到。其中代码如下:
#YOLO HEAD 1
该网络最后使用1x1卷积输出最大的一张特征图y76_output,维度为(76,76,num_anchor*(num_classes+5))。对应结构图中最大的输出特征图(最右边的淡蓝色特征图)。
(5) process4的代码如下:
#38x38 output
这一步骤比较关键,PAN和FPN的差异在于,FPN是自顶向下的特征融合,PAN在FPN的基础上,多了个自底向上的特征融合。具体自底向上的特征融合,就是process4完成的,可以看到该步骤先将y76下采样至38x38大小,再和y38堆叠,作为YOLO HEAD2的输入。
(6)YOLO HEAD 2
类似于YOLO HEAD 1,YOLO HEAD2也进行一系列卷积运算,获得维度大小为(38,38,num_anchor*(num_classes+5))的输出y38_output,其中代码如下:
#YOLO HEAD 2
其中process4和YOLO HEAD2如下图所示。
(7) Process5代码如下
#19x19 output
Process5和process4进程类似,不多赘述。后面接上YOLO HEAD 3。
(8)YOLO HEAD 3
和YOLO HEAD 1以及YOLO HEAD 2定义几乎类似,YOLO HEAD 3定义如下:
y19
YOLO HEAD 3输出为(19,19,num_anchor*(num_classes+5))的特征图y19_output。
3.结束语
上述有关YOLO V4的网络结构就讲到这里,我看了下,篇幅又有点长了,那关于损失函数和更多tricks实现的细节,我就放到后面再讲了,感谢大家支持!
有关下一篇损失函数的详解已经更新,请点击《YOLO V4 — 损失函数解析(特详细!)》。