Yolov5的结构解析
yolov5拥有强大的效果,但是在其实现强大效果的同时,其完备的封装及复杂的参数使得我们难以对其结构进行深入分析
以下为我对yolov5结构的解析
yolov5s.yaml
以下为yolov5s.yaml
# parameters
nc: 9 # number of classes
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.50 # layer channel multiple
# anchors
anchors:
- [10,13, 16,30, 33,23] # P3/8
- [30,61, 62,45, 59,119] # P4/16
- [116,90, 156,198, 373,326] # P5/32
# YOLOv5 backbone
backbone:
# [from, number, module, args]
[[-1, 1, Focus, [64, 3]], # 0-P1/2 [3,32,3]
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4 [32,64,3,2]
[-1, 3, BottleneckCSP, [128]], # 2
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
[-1, 9, BottleneckCSP, [256]], # 4
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
[-1, 9, BottleneckCSP, [512]], # 6
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
[-1, 1, SPP, [1024, [5, 9, 13]]], # 8
[-1, 3, BottleneckCSP, [1024, False]], # 9
]
# YOLOv5 head
head:
[[-1, 1, Conv, [512, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 6], 1, Concat, [1]], # cat backbone P4
[-1, 3, BottleneckCSP, [512, False]], # 13
[-1, 1, Conv, [256, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 4], 1, Concat, [1]], # cat backbone P3
[-1, 3, BottleneckCSP, [256, False]], #17
[-1, 1, Conv, [256, 3, 2]],
[[-1, 14], 1, Concat, [1]], # cat head P4
[-1, 3, BottleneckCSP, [512, False]], # 20
[-1, 1, Conv, [512, 3, 2]],
[[-1, 10], 1, Concat, [1]], # cat head P5
[-1, 3, BottleneckCSP, [1024, False]], # 23
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
]
以下为具体分析
parameters(参数)
nc: 9 # number of classes
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.50 # layer channel multiple
nc:
为你数据集检测物体的类别个数
depth_multiple:
控制你模型的深度,是用在backbone中的number≠1的情况下, 即在Bottleneck层(瓶颈层)使用,控制模型的深度,yolov5s中设置为0.33,假设yolov5l中有三个Bottleneck,那yolov5s中就只有一个Bottleneck。(因为一般number=1表示的是功能背景的层,比如说下采样Conv、Focus、SPP(空间金字塔池化))
width_multiple:
控制卷积核的个数,使用该数值,对之后的卷积核参数进行解析,可以推导得到卷积核的个数(在之后backbone部分会详细说明)
anchors (多尺度滑动窗口)
简介:
传统的检测过程是:
1、生成图像金字塔,因为待检测的物体的scale是变化的。
2、用滑动窗口在图片的特征金字塔上面滚动生成很多候选区域。
3、各种特征提取hog和分类器svm来对上面产生的候选区域中的图片信息来分类。
4、NMS非极大值抑制得到最后的结果。
所以,anchor其实就是对预测的对象范围进行约束,用最常出现,最具有代表性的几种先验框为基础,有助于模型快速收敛,并加入了尺寸先验经验,从而实现多尺度学习的目的。
anchors:
- [10,13, 16,30, 33,23] # P3/8
- [30,61, 62,45, 59,119] # P4/16
- [116,90, 156,198, 373,326] # P5/32在yolov5中,可根据自己的数据集来更新,添加新的anchors(可使用yolov5自带的模块处理,也可使用网上找到的代码处理)
backbone head detect
其中橙色的数字表示层号,0-9层构成backbone,10-23层构成head,17、20、23 层的输出是Detect()函数的输入
backbone
# [from, number, module, args]
[[-1, 1, Focus, [64, 3]], # 0-P1/2 [3,32,3]
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4 [32,64,3,2]
[-1, 3, BottleneckCSP, [128]], # 2
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
[-1, 9, BottleneckCSP, [256]], # 4
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
[-1, 9, BottleneckCSP, [512]], # 6
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
[-1, 1, SPP, [1024, [5, 9, 13]]], # 8
[-1, 3, BottleneckCSP, [1024, False]], # 9
]
from列参数:-1 代表是从上一层获得的输入,-2表示从上两层获得的输入(head同理)。
number列参数:1表示只有一个,3表示有三个相同的模块,其数量即为该层该模块的个数
module列参数:表示该层使用的模块,Focus、SPP、Conv、Bottleneck、BottleneckCSP的代码可以在./models/common.py中获取到
args列参数:即为传入该模块的参数,且输入都省去了(由上层提供),其输入获取是从./models/yolo.py的def parse_model(d, ch)函数中解析得到的。(后文有具体的解析结果)
模块具体参数解释:
Conv和Focus参数:(c_in, c_out, kernel_size, stride),
SPP后的参数:(c_in, c_out, [kernel_size1,kernel_size2,kernel_size3])
BottleneckCSP后的参数:(c_in,c_out)*该模块堆叠的次数(在number列)
yaml中注释的解释:(如0-P1/2 1-P2/4)
0是第一层,从0开始计算;P1指的是含有卷积层的第一层,2是降低了一半
同理,可得注释解为:
a - Pb / c
a:从0开始,表示第几层,层数分布详见上文综述
P后的b:从1开始,若该层含有卷积层则会出现,表示这是含有卷积层的第b层
c:表示图片变为原来的1/c倍(像素大小)
args列传参的具体解释:
首先,我们可以从之前的参数解释得到,这里面的数据会主要要考虑的是通道数,但是,其第一个参数并不完全是其通道数,还需进行解析,所以,解释如下
承接上文,有一个名为width_multiple的参数,我们在很早之前就对其进行了设置,而该参数,就会对该处的args传参解析造成影响,这就是为什么,对于YOLO V5,无论是V5s,V5m,V5l还是V5x其Backbone,Neck和Head一致。其唯一的区别就在与模型的深度和宽度设置,我们只需要修改这两个参数就可以调整模型的网络结构。其中V5l 的参数是默认参数。
例:
[[-1, 1, Focus, [64, 3]], # 0-P1/2 [3,32,3]
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4 [32,64,3,2]
[-1, 3, BottleneckCSP, [128]], # 2在上部分中,第一个args参数为[64, 3],但经过解析后的结果应该为[3,32,3],解析缘由为参数width_multiple,使用该参数与64相乘,即可得到c_out = 32,而c_in = 3是输入图片默认的3通道,最后的 3 为卷积核的大小3*3
而第二个args参数[128, 3, 2],经过解析后,结果为[32,64,3,2],2位stride(步长)
第三个args参数[128],经过解析后,结果为[64,64]*(3)(模型堆叠次数)
由上方可以看出:主干网就是图片从大到小,深度不断加深。
head
head检测头:一般表示的是经过主干网后输出的特征图,特征图输入head中进行检测,包括类别和位置的检测
# YOLOv5 head
head:
[[-1, 1, Conv, [512, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 6], 1, Concat, [1]], # cat backbone P4
[-1, 3, BottleneckCSP, [512, False]], # 13
[-1, 1, Conv, [256, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 4], 1, Concat, [1]], # cat backbone P3
[-1, 3, BottleneckCSP, [256, False]], # 17
[-1, 1, Conv, [256, 3, 2]],
[[-1, 14], 1, Concat, [1]], # cat head P4
[-1, 3, BottleneckCSP, [512, False]], # 20
[-1, 1, Conv, [512, 3, 2]],
[[-1, 10], 1, Concat, [1]], # cat head P5
[-1, 3, BottleneckCSP, [1024, False]], # 23
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
]
解析如上面所述