第Y3周:yolov5s.yaml文件解读
C3*2
修改为C3*1
,第6层的C3*3
修改我C3*2
。./models/yolov5s.yaml
文件YOLOv5配置了4种不同大小的网络模型,分别是YOLOv5s
、YOLOv5m
、YOLOv5l
、YOLOv5x
,其中YOLOv5s
是网络深度和宽度最小但检测速度最快的模型,其他3中模型都是在YOLOv5s
的基础上不断加深、加宽网络使得网络规模扩大,在增强模型检测性能的同时增加了计算资源和速度消耗。出于对检测精度、模型大小、检测速度的综合考量,本文选择YOLOv5s
作为研究对象进行介绍。
./models/yolov5s.yaml
文件是YOLOv5s
网络结构的定义文件,如果你想改进算法的网络结构,需先修改该文件中的相关参数,然后再修改./models/common.py
与./models/yolo.py
中的相关代码。
cmd
输入nvcc -V
或nvcc --version
指令可查看)
# Parameters
nc: 80 # number of classes
depth_multiple: 0.33 # model depth multiple 控制模型深度
width_multiple: 0.50 # layer channel multiple 控制COnv通道channel个数(卷积核数量)
- depth_multiple:控制子模块数量,
= int(number*depth)
,该参数与任务有关- width_multiple:控制卷积核的数量,
= int(number*width)
通过这两个参数可以实现不同复杂度的模型设计。YOLOv5s
、YOLOv5m
、YOLOv5l
、YOLOv5x
这四个模型的区别仅在于depth_multiple
与width_multiple
这两个参数不同。
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,检测大目标,共三组
小 目标
3
组:[10, 13], [16, 30], [33, 23]
中 目标3
组:[30, 61], [62, 45], [59,119]
大 目标3
组:[116,90], [156,198], [373,326]
YOLOv5初始化了9个anchor,在3个Detect层(3个feature map)中使用,每个feature map的每个grid_cell都有3个anchor进行预测。分配规则是:尺度越大的feature map越靠前,相对原图的下采样率越小,感受野越小,则相对可以预测一些尺度比较小的物体,所有分配到定anchor越小;尺度越小的feature map越靠后,相对原图的下采样率越大,感受野越大,则相对可以预测一些尺寸比较大的物体,所有分配到的anchor也越大。即可以在小特征图(feature map)上检测大目标,也可以在大特征图上检测小目标。
YOLOv5根据工程经验得到了这么3组anchors(9对尺寸参数),对于很多数据集而言已经很合适了。但也不能保证这3组anchor就适用于所有数据集,所以YOLOv5还有一个anchor进化的策略:使用k-means
和遗传进化算法,找到与当前数据集最吻合的anchors。
k-means:对当前数据集中所有的标注信息中的目标框的尺寸做聚类,输出9对anchors的值。下面是我找到的一段聚类输出anchors的代码。
yolo_kmeans.py
import numpy as np
def wh_iou(wh1, wh2):
# Returns the nxm IoU matrix. wh1 is nx2, wh2 is mx2
wh1 = wh1[:, None] # [N,1,2]
wh2 = wh2[None] # [1,M,2]
inter = np.minimum(wh1, wh2).prod(2) # [N,M]
return inter / (wh1.prod(2) + wh2.prod(2) - inter) # iou = inter / (area1 + area2 - inter)
def k_means(boxes, k, dist=np.median):
"""
yolo k-means methods
refer: https://github.com/qqwweee/keras-yolo3/blob/master/kmeans.py
Args:
boxes: 需要聚类的bboxes
k: 簇数(聚成几类)
dist: 更新簇坐标的方法(默认使用中位数,比均值效果略好)
"""
box_number = boxes.shape[0]
last_nearest = np.zeros((box_number,))
# 在所有的bboxes中随机挑选k个作为簇的中心。
clusters = boxes[np.random.choice(box_number, k, replace=False)]
while True:
# 计算每个bboxes离每个簇的距离 1-IOU(bboxes, anchors)
distances = 1 - wh_iou(boxes, clusters)
# 计算每个bboxes距离最近的簇中心
current_nearest = np.argmin(distances, axis=1)
# 每个簇中元素不在发生变化说明以及聚类完毕
if (last_nearest == current_nearest).all():
break # clusters won't change
for cluster in range(k):
# 根据每个簇中的bboxes重新计算簇中心
clusters[cluster] = dist(boxes[current_nearest == cluster], axis=0)
last_nearest = current_nearest
return clusters
# YOLOv5 v6.0 backbone
backbone:
# [from, number, module, args]
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
[-1, 3, C3, [128]],
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
[-1, 6, C3, [256]],
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
[-1, 9, C3, [512]],
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
[-1, 3, C3, [1024]],
[-1, 1, SPPF, [1024, 5]], # 9
]
这是YOLOv5s
的backbone
,可以看到每一行是一个模块,每行都是由四个参数构成,分别是:
-1
表示来自上一层的输出,层编号由0
开始计数。depth_multiple
共同决定,该参数影响整体网络模型的深度。common.py
中寻找相应的类,进行模块化的网络搭建。list
,对应到模块类搭建时需要的参数,主要是channel
、kernel_size
、stride
、padding
、bias
等。# YOLOv5 v6.0 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, C3, [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, C3, [256, False]], # 17 (P3/8-small)
[-1, 1, Conv, [256, 3, 2]],
[[-1, 14], 1, Concat, [1]], # cat head P4
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
[-1, 1, Conv, [512, 3, 2]],
[[-1, 10], 1, Concat, [1]], # cat head P5
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
]
这是YOLOv5s
的head
,数据格式和backbone
一样。
我们需要调整的地方主要是backbone
部分,调整如下:
第4层的C3*2
修改为C3*1
,第6层的C3*3
修改我C3*2
# YOLOv5 v6.0 backbone
backbone:
# [from, number, module, args]
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
[-1, 3, C3, [128]],
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
[-1, 3, C3, [256]], # 第4层改为C3*1
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
[-1, 6, C3, [512]], # 第6层改为C3*2
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
[-1, 3, C3, [1024]],
[-1, 1, SPPF, [1024, 5]], # 9
]
Overriding model.yaml nc=80 with nc=4
from n params module arguments
0 -1 1 3520 models.common.Conv [3, 32, 6, 2, 2]
1 -1 1 18560 models.common.Conv [32, 64, 3, 2]
2 -1 1 18816 models.common.C3 [64, 64, 1]
3 -1 1 73984 models.common.Conv [64, 128, 3, 2]
4 -1 1 74496 models.common.C3 [128, 128, 1]
5 -1 1 295424 models.common.Conv [128, 256, 3, 2]
6 -1 2 460800 models.common.C3 [256, 256, 2]
7 -1 1 1180672 models.common.Conv [256, 512, 3, 2]
8 -1 1 1182720 models.common.C3 [512, 512, 1]
9 -1 1 656896 models.common.SPPF [512, 512, 5]
10 -1 1 131584 models.common.Conv [512, 256, 1, 1]
11 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest']
12 [-1, 6] 1 0 models.common.Concat [1]
13 -1 1 361984 models.common.C3 [512, 256, 1, False]
14 -1 1 33024 models.common.Conv [256, 128, 1, 1]
15 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest']
16 [-1, 4] 1 0 models.common.Concat [1]
17 -1 1 90880 models.common.C3 [256, 128, 1, False]
18 -1 1 147712 models.common.Conv [128, 128, 3, 2]
19 [-1, 14] 1 0 models.common.Concat [1]
20 -1 1 296448 models.common.C3 [256, 256, 1, False]
21 -1 1 590336 models.common.Conv [256, 256, 3, 2]
22 [-1, 10] 1 0 models.common.Concat [1]
23 -1 1 1182720 models.common.C3 [512, 512, 1, False]
24 [17, 20, 23] 1 24273 models.yolo.Detect [4, [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]], [128, 256, 512]]
YOLOv5s summary: 200 layers, 6824849 parameters, 6824849 gradients, 14.9 GFLOPs
从日志打印出来的当前网络结构可以看到:
· 网络第4层为C3模块,重复1次;
· 网络第6层为C3模块,重复2次;