YOLO V5 模型结构及迁移学习
YOLO 是一种快速紧凑的开源对象检测模型,与其它网络相比,同等尺寸下性能更强,并且具有很不错的稳定性,是第一个可以预测对象的类别和边界框的端对端神经网络。YOLO 家族一直有着旺盛的生命力,从YOLO V1一直到”V5“,如今已经延续五代,凭借着不断的创新和完善,一直被计算机视觉工程师作为对象检测的首选框架之一。
Ultralytics于5月27日发布了YOLOv5 的第一个正式版本,其性能与YOLO V4不相伯仲,是现今最先进的对象检测技术之一,并在推理速度上是目前最强。
我在前一篇文章:一文读懂YOLO V5 与 YOLO V4介绍了YOLO V5和YOLO V4的原理,相似点及区别。
在本文章中,我会详细介绍YOLO V5的网络结构及组成模块,并使用YOLO V5s对BDD100K自动驾驶数据集进行迁移学习,使得训练出的模型能够识别包括交通灯颜色在内的所有交通对象。
Github:https://github.com/williamhyin/yolov5s_bdd100k
Email: williamhyin@outlook.com
知乎专栏: 自动驾驶全栈工程师
本文分成两块:模型结构及迁移学习。
- Model architecture
- Overview
- Focus
- BottleneckCSP
- SPP
- PANET
- Transfer learning
- Data prepration
- Setup enviorment
- Configuration
- Modify model architecture
- Transfer learning theory
- Inference
Model architecture
YOLO网络由三个主要组件组成:
1)Backbone -在不同图像细粒度上聚合并形成图像特征的卷积神经网络。
2)Neck:一系列混合和组合图像特征的网络层,并将图像特征传递到预测层。
3)Head: 对图像特征进行预测,生成边界框和并预测类别。
本文主要采用YOLO V5 1.0结构,7月23日作者更新了2.0版本代码,对于模型定义做了些改变,我会后续进行更新。
YOLO V5 1.0中用到的重要的模块包括Focus,BottleneckCSP,SPP,PANET。模型的上采样Upsample是采用nearst两倍上采样插值。值得注意的是YOLO V5 1.0最初为COCO数据集训练的Pretrained_model 使用的是FPN作为Neck,在6月22日后,Ultralytics已经更新模型的Neck为PANET。网上很多的YOLO V5网络结构介绍都是基于FPN-NECK,本文的模型训练是基于PANET-NECK,下文中只介绍PANET-NECK。
对于YOLO V5,无论是V5s,V5m,V5l还是V5x其Backbone,Neck和Head一致。唯一的区别在与模型的深度和宽度设置,只需要修改这两个参数就可以调整模型的网络结构。V5l 的参数是默认参数。
- depth multiple是用来控制模型的深度,例如V5s的深度是0.33,而V5l的深度是1,也就是说V5l的Bottleneck个数是V5s的3倍。
- width_multiple是用来控制卷积核的个数,V5s的宽度是0.5,而V5l的宽度是1,表示V5s的卷积核数量是默认设置的一半,当然你也可以设置到1.25倍,即V5x。例如下面YOLO V5的yaml文件中的backbone的第一层是
[[-1, 1, Focus, [64, 3]]
,而V5s的宽度是0.5,因此这一层实际上是[[-1, 1, Focus, [32, 3]]
。 - from列参数:-1 代表是从上一层获得的输入,-2表示从上两层获得的输入(head同理)。
- number列参数:1表示只有一个,3表示有三个相同的模块。
下图为YOLO V5 1.0的网络结构图(默认对应YOLO V5l),引用自Laughing-q。
下图中存在三种括号,其中 In_channel:输入通道,out_channel:输出通道,Kernel_size:卷积核大小,Stride:步长,x N代表此模块的叠加次数,方框外数字:depth x weight x height,默认输入为宽高为640x640的三通道图像。
下文我将详细讲述Focus,BottleneckCSP,SPP,PANET这几个重要模块,由于本项目使用YOLO V5s网络结构训练模型,因此下文中的网络图及实例都基于YOLO V5s,并且输入图像为3x640x640。YOLO V5默认depth_multiple=0.33, width_multiple=0.50。即BottleneckCSP中Bottleneck的数量为默认的1/3,而所有卷积操作的卷积核个数均为默认的1/2。
Focus
下图为YOLO V5s的Focus 隔行采样拼接结构。
YOLO V5默认3x640x640的输入,复制四份,然后通过切片操作将这个四个图片切成了四个3x320x320的切片,接下来使用concat从深度上连接这四个切片,输出为12x320x320,之后再通过卷积核数为32的卷积层,生成32x320x320的输出,最后经过batch_borm 和leaky_relu将结果输入到下一个卷积层。
Focus的代码分析如下
class Focus(nn.Module):
# Focus wh information into c-space
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
super(Focus, self).__init__()
self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2)
return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))
我们拿上图举例,Focus是步长为2的隔行采样。
上图第一张图为原图,第二张图为Focus的特征图,第三张图为4x4的tensor代码测试。
核心为这段代码self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))
。x[…, ::2, ::2]是黄色部分,x[…, 1::2, ::2],是红色部分,以此类推。对于x[…, ::2, ::2]其中第一个参数“…"代表深度,也就是说三个通道都要切,第二个和第三个代表不论是宽和高都是每隔一个采样。对于x[…, 1::2, ::2],1::2代表从列位置1开始,也就是每序号奇数列采样。蓝色,绿色的生成方式以此类推。最后用cat连接这些隔行采样图,生成通道数为12的特征图。
BottlenneckCSP
下图为YOLO V5s的第一个BottlenneckCSP结构。
BottlenneckCSP分为两部分,Bottlenneck以及CSP。
Bottlenneck
Bottlenneck其实就是经典的残差结构,先是1x1的卷积层(conv+batch_norm+leaky relu),然后再是3x3的卷积层,最后通过残差结构与初始输入相加。
值得