为什么五年后还有人在用 YOLOv5?拆解这套工业检测的“老兵“架构
Ai算法爱好者学习
如果你去一家做视觉检测的公司仓库里逛一圈,问他们产线上跑的是什么模型,十有八九会听到一个名字:YOLOv5。
这玩意2020年出来,到现在五六年了。后面v8、v10、11迭代了好几轮,各种新结构、新损失函数层出不穷,但不少工控机里,大概率还是Ultralytics那套train.py在稳稳地跑。不是大家不想追新,而是这套东西实在太皮实——改个yaml配置,对好数据路径,一行命令下去,第二天就能拿到一个能用的权重。对于要交付的项目来说,这种确定性比论文里高几个点的mAP更值钱。
YOLOv5的真正价值,可能不在于它提出了什么惊天动地的理论突破,而在于它第一次把目标检测从"实验室调参"变成了一件"工程上可落地"的事。它用纯PyTorch重写,把训练、验证、导出、推理串成了一条完整的流水线,让写Python的工程师不用再对着C++的Darknet Makefile发愁。这篇文章,我想把YOLOv5的骨架拆开来看。从输入端的Mosaic增强,到Backbone的CSPDarknet,再到Neck的FPN+PAN,最后到Head的三个检测层,把这些模块为什么这样设计怎么配合,尽量讲清楚。
官方文档:https://docs.ultralytics.com/yolov5/
官方实现仓库:https://github.com/ultralytics/yolov5

一、基础知识点
1.1 网络架构
YOLOv5采用了一种模块化的网络架构,主要由三个部分组成:
-
Backbone(主干网络): 作为特征提取的核心,通常是在大规模数据集(如ImageNet或COCO)上预训练的卷积神经网络,如ResNet-50或Darknet53。
-
Neck(颈部网络) 介于Backbone和Head之间,用于整合不同层级的特征图,以提升检测性能。
-
Head(头部网络) 位于模型的末端,负责预测目标的类别和边界框位置
目标检测模型的工作流程可概括为:输入数据通过主干网络提取特征,然后颈部网络进一步提炼特征,最终由头部网络完成目标的类别和位置预测,输出检测结果。 YOLOv5与Yolov3及Yolov4进行比较。主要的不同点:
-
(1)输入端 : Mosaic数据增强(v4中使用到了)、自适应锚框计算、自适应图片缩放
-
(2)骨干网络(Backbone) : Focus结构,CSP结构
-
(3)颈部网络(Neck) : FPN+PAN结构
-
(4)头部网络(Head) : GIOU_Loss
1.2 自定义锚框
在 yolov3、v4 中是采用 kmean 和遗传算法对自定义数据集进行分析,获得合适自定义数据集中对象边界框预测的预设锚点框。在 yolov5 中锚点框是基于训练数据自动学习的。(Auto Learning Bounding Box Anchors)具体代码实现可以参考 general.py 文件中的 check_anchors 函数。
1.3 激活函数
在 yolov5 中,中间 / 隐藏层使用了 Leaky ReLU 激活函数,最后的检测层使用了 Sigmoid 激活函数;

1.4 优化器
在 yolov5 中提供了两个优化函数 Adam 和 SGD,并预设了与之匹配的训练超参数,默认是 SGD。
1.5 损失函数
yolo 系列的损失计算是基于 objectness score, class probability,bounding box regression score。
yolov5 中使用 GIoU Loss 作为 bounding box 的损失;
yolov5 中使用 二进制交叉熵(BCE) 和 Logits 损失函数 计算类概率和目标得分的损失,同时我们也可以使用 fl_gamma 参数来激活 focal loss 计算损失函数。
二、创新点
2.0 Mosaic数据增强
Yolov5的输入端采用了和Yolov4一样的Mosaic数据增强的方式。随机缩放、随机裁剪、随机排布的方式进行拼接,对于小目标的检测效果很不错。Yolov4中使用的Mosaic是参考2019年底提出的CutMix数据增强的方式,但CutMix只使用了两张图片进行拼接,而Mosaic数据增强则采用了4张图片,随机缩放、随机裁剪、随机排布的方式进行拼接。

Mosaic数据增强的主要步骤:
-
随机选择四张不同的图像作为输入
-
分别对四张图片进行翻转(对原始图片进行左右的翻转)、缩放(对原始图片进行大小的缩放)、色域变化(对原始图片的明亮度、饱和度、色调进行改变)等操作。
-
操作完成之后然后再将原始图片按照 第一张图片摆放在左上,第二张图片摆放在左下,第三张图片摆放在右下,第四张图片摆放在右上四个方向位置摆好。
-
根据每张图片的尺寸变换方式,将映射关系对应到图片标签上。
-
依据指定的横纵坐标,对大图进行拼接。处理超过边界的检测框坐标。
主要有几个优点:
-
增加数据多样性,随机选取四张图像进行组合,组合得到图像个数比原图个数要多。
-
增强模型鲁棒性,混合四张具有不同语义信息的图片,可以让模型检测超出常规语境的目标。
-
加强批归一化层(Batch Normalization)的效果。当模型设置 BN 操作后,训练时会尽可能增大批样本总量(BatchSize),因为 BN 原理为计算每一个特征层的均值和方差,如果批样本总量越大,那么 BN 计算的均值和方差就越接近于整个数据集的均值和方差,效果越好。
-
Mosaic 数据增强算法有利于提升小目标检测性能。Mosaic 数据增强图像由四张原始图像拼接而成,这样每张图像会有更大概率包含小目标。
2.1 自适应锚框计算
在YOLOv5中,每次训练开始之前,它都会根据你的数据集来自适应计算anchor锚框;在 yolo v3 和 yolo v4 中是采用 kmean 和遗传学习算法对自定义数据集进行分析,获得适合自定义数据集中对象边界框预测的预设锚定框。在 yolo v5 中锚定框是基于训练数据自动学习的。若觉得计算的锚框效果不佳,可以将--noautoanchor参数设置True default值即可关闭。
parser.add_argument('--noautoanchor', default=True, action='store_true', help=' 不自动调整anchor,默认为False')
在 YOLOv5 的配置文件model/*.yaml 中已经预设了一些针对 COCO数据集在 640 × 640 640×640 640×640图像大小下锚定框的尺寸:
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
anchors参数共有三行,每行6个数值;且每一行代表应用不同的特征图;
第一行是在最大的特征图上的锚框, 80 x 80 80x80 80x80代表浅层的特征图(P3),包含较多的低层级信息,适合用于检测小目标,所以这一特征图所用的anchor尺度较小;
第二行是在中间的特征图上的锚框, 40 x 40 40x40 40x40特征图上就用介于这两个尺度之间的anchor用来检测中等大小的目标;
第三行是在最小的特征图上的锚框, 20 x 20 20x20 20x20代表深层的特征图,包含更多高层级的信息,如轮廓、结构等信息,适合用于大目标的检测,所以这一特征图所用的anchor尺度较大。
自定义锚框
锚框核查函数 /utils/autoanchor.py 文件中; YOLOv5 在开始训练前会计算数据集标注信息针对默认锚定框的最佳召回率,如果最佳召回率大于或等于0.98,则不需要重新计算锚定框,使用默认锚框;如果最佳召回率小于0.98,则需要重新计算符合此数据集的锚框。
def metric(k): # compute metric
r = wh[:, None] / k[None]
x = torch.min(r, 1 / r).min(2)[0] # ratio metric
best = x.max(1)[0] # best_x
aat = (x > 1 / thr).float().sum(1).mean() # anchors above threshold
bpr = (best > 1 / thr).float().mean() # best possible recall
return bpr, aat
stride = m.stride.to(m.anchors.device).view(-1, 1, 1) # model strides
anchors = m.anchors.clone() * stride # current anchors
bpr, aat = metric(anchors.cpu().view(-1, 2))
其中,bpr(best possible recall)参数就是判断是否需要重新计算锚定框的依据(是否小于 0.98) 重新计算符合此数据集标注框的锚定框,是利用 k均值聚类算法(k-means clustering)和遗传算法(genetic algorithm)实现的。
自定义锚框计算过程
-
读取训练集中所有图片的w、h以及检测框的w、h
-
将读取的坐标修正为绝对坐标
-
使用Kmeans算法对训练集中所有的检测框进行聚类,得到k个anchors
-
通过遗传算法对得到的anchors进行变异,如果变异后效果好将其保留,否则跳过
-
将最终得到的最优anchors按照面积返回
import utils.autoanchor as autoAC
# 对数据集重新计算 anchors
new_anchors = autoAC.kmean_anchors('./data/mydata.yaml', 9, 640, 5.0, 1000, True)
print(new_anchors)
2.2 自适应图片缩放
utils/datasets.py中
def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True):
# Resize image to a 32-pixel-multiple rectangle https://github.com/ultralytics/yolov3/issues/232
shape = img.shape[:2] # current shape [height, width]
if isinstance(new_shape, int):
new_shape = (new_shape, new_shape)
# Scale ratio (new / old)
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
if not scaleup: # only scale down, do not scale up (for better test mAP)
r = min(r, 1.0)
# Compute padding
ratio = r, r # width, height ratios
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
if auto: # minimum rectangle
dw, dh = np.mod(dw, 64), np.mod(dh, 64) # wh padding
elif scaleFill: # stretch
dw, dh = 0.0, 0.0
new_unpad = (new_shape[1], new_shape[0])
ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratios
dw /= 2 # divide padding into 2 sides
dh /= 2
if shape[::-1] != new_unpad: # resize
img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
return img, ratio, (dw, dh)
在常用的目标检测 算法中,不同的图片长宽都不相同,因此常用的方式是将原始图片统一缩放到一个标准尺寸,再送入检测网络中。 第一步:计算缩放比例
原始缩放尺寸是416*416,都除以原始图像的尺寸后,可以得到0.52,和0.69两个缩放系数,选择小的缩放系数。

第二步:计算缩放后的尺寸

原始图片的长宽都乘以最小的缩放系数0.52,宽变成了416,而高变成了312。
第三步:计算黑边填充数值

将416-312=104,得到原本需要填充的高度。再采用numpy中np.mod取余数的方式,得到8个像素,再除以2,即得到图片高度两端需要填充的数值。
此外,需要注意的是:
-
a.这里大白填充的是黑色,即(0,0,0),而Yolov5中填充的是灰色,即(114,114,114),都是一样的效果。
-
b.训练时没有采用缩减黑边的方式,还是采用传统填充的方式,即缩放到416*416大小。只是在测试,使用模型推理时,才采用缩减黑边的方式,提高目标检测,推理的速度。
-
c.为什么np.mod函数的后面用32?因为Yolov5的网络经过5次下采样,而2的5次方,等于32。所以至少要去掉32的倍数,再进行取余。
三、Backbone
YOLOv5的骨干网络(Backbone)是整个目标检测模型的基础,负责从输入图像中提取有用的特征并不断缩小特征图。YOLOv5的Backbone采用了New CSP-Darknet53架构,这是一种专门为目标检测任务优化的深度学习模型。 New CSP-Darknet53
3.1 CSP结构
YOLOv5的Backbone使用了Cross Stage Partial Network(CSPNet)结构,这是一种减少计算量的技术。CSP通过在卷积层之间共享权重,减少了模型的参数数量和计算量,同时保持了特征提取的效率。

CSPNet(Cross Stage Partial Network):跨阶段局部网络,以缓解以前需要大量推理计算的问题。
-
增强了CNN的学习能力,能够在轻量化的同时保持准确性。
-
降低计算瓶颈。
-
降低内存成本。
CSPNet通过将梯度的变化从头到尾地集成到特征图中,在减少了计算量的同时可以保证准确率。 CSPNet和PRN都是一个思想,将feature map拆成两个部分,一部分进行卷积操作,另一部分和上一部分卷积操作的结果进行concate。
Darknet架构: Darknet是一个为YOLO系列优化的深度学习框架,它以速度快和资源消耗低而著称。Darknet53是Darknet系列中的一个变种,拥有53层深度,它通过堆叠多个卷积层和池化层来逐步提取图像的深层特征。
CBS模块

C3模块

3由三个CBS模块和一个BottleNeck模块组成,得名C3。在Backbone中,C3是更为重要的提取特征的模块。其结构图如下:
SPPF模块

SPP是空间金字塔池化,采用 1 × 1 1×1 1×1, 5 × 5 5×5 5×5, 9 × 9 9×9 9×9, 13 × 13 13×13 13×13的最大池化的方式,进行多尺度融合。YOLOv5 6.0版本开始使用了在SPP基础上改进的SPPF
3.2 Focus结构
YOLOv5在Backbone的开始部分使用了Focus结构,这是一种有效的特征融合技术。Focus结构通过将输入图像分割成小块,然后分别提取特征,最后再将这些特征合并,从而提高了模型对小目标的检测能力。

YOLOv5 6.0开始将Focus模块替换成了一个 6 ∗ 6 66 6∗6的卷积层。两者的计算量是等价的,但使用 6 ∗ 6 66 6∗6的卷积会更加高效。

3.3 高效的激活函数
YOLOv5采用了Mish激活函数,这是一种比传统的ReLU激活函数更加平滑的激活函数,有助于在训练过程中提供更稳定的梯度流动。
Mish激活函数是YOLOv5中的一个关键创新,它在正区间的表现比传统的ReLU激活函数更为平滑,这有助于梯度更有效地流动,从而加快网络训练的收敛速度。Mish的设计避免了ReLU在负区间的梯度消失问题,同时在正区间保持了类似ReLU的线性增长,这对于提取深层次特征和提高模型的非线性表达能力至关重要。
Mish是光滑的非单调激活函数,可定义为:
・ς其中,ς,是一个激活函数和。


3.4 多尺度特征融合
YOLOv5的Backbone通过SPPF(Spatial Pyramid Pooling with Feature map Fusion)模块,实现了多尺度的特征融合,这使得模型能够同时检测不同大小的目标。
SPP是空间金字塔池化,采用 1 × 1 1×1 1×1, 5 × 5 5×5 5×5, 9 × 9 9×9 9×9, 13 × 13 13×13 13×13的最大池化的方式,进行多尺度融合。YOLOv5 6.0版本开始使用了在SPP基础上改进的SPPF。

SPP是将三个并行的MaxPool2d和输入Concat到一起,第一个MaxPool2d的kernel为 5 ∗ 5 55 5∗5,第二个为 9 ∗ 9 99 9∗9,第三个为 13 ∗ 13 1313 13∗13。用三个不同大小的kernel,代表三个尺度。 5 ∗ 5 55 5∗5的kernel可以理解为比较大的尺度,而 13 ∗ 13 13*13 13∗13就是比较小的尺度。这样就在图片的不同尺度下取到了最大的代表特征值,并Concat融合。
SPPF是将三个kernel为 5 ∗ 5 5*5 5∗5的MaxPool2d做串行计算。第一个MaxPool2d表示较大的尺度,第二个MaxPool在第一个MaxPool2d的基础上进一步做池化,那么产生的尺度将会进一步缩小,第三个同理。
通过这些设计,YOLOv5的Backbone能够高效地从输入图像中提取丰富的特征,为后续的检测任务提供了坚实的基础。这些特征随后会被传递到Neck和Head部分,进行更精细的目标检测和定位。
四、Neck
Neck的作用就是从Backbone中获取相对于较浅的特征,再与深层的语义特征Concat到一起。Yolov5现在的Neck和Yolov4中一样,都采用FPN+PAN的结构,但在Yolov5刚出来时,只使用了FPN结构,后面才增加了PAN结构,此外网络中其他部分也进行了调整。

Yolov4的Neck结构中,采用的都是普通的卷积操作。而Yolov5的Neck结构中,采用借鉴CSPnet设计的CSP2结构,加强网络特征融合的能力。 FPN 结构通过自顶向下进行上采样,使得底层特征图包含更强的图像强语义信息

FPN结构中,通过Upsample上采样的方式,向特征图中插值,使特征图的尺寸变大,以便于融合来自Backbone的特征图,做特征的向上融合,特征图不断变大;
PAN 结构自底向上进行下采样,使顶层特征包含图像位置信息,两个特征最后进行融合,使不同尺寸的特征图都包含图像语义信息和图像特征信息,保证了对不同尺寸的图片的准确预测。
五、Head
Head层为Detect模块,Detect模块的网络结构很简单,仅由三个 1 ∗ 1 11 1∗1卷积构成,对应三个检测特征层。

上述经过FPN特征金字塔,我们可以获得 20 ∗ 20 ∗ 512 2020512 20∗20∗512、 40 ∗ 40 ∗ 256 4040256 40∗40∗256、 80 ∗ 80 ∗ 128 8080*128 80∗80∗128三个加强特征,然后我们利用这三个shape的特征层传入Yolo Head获得预测结果。
对于每一个特征层,我们可以获得利用一个 1 ∗ 1 1*1 1∗1卷积调整通道数,最终的通道数和需要区分的种类个数相关,每一个特征层上每一个特征点存在3个先验框。
如果使用的是COCO训练集,类则为 80 80 80种,最后的维度应该为 255 = 3 ∗ 85 255 = 385 255=3∗85,三个特征层的shape为 20 ∗ 20 ∗ 255 2020255 20∗20∗255、 40 ∗ 40 ∗ 255 4040255 40∗40∗255、 80 ∗ 80 ∗ 255 8080*255 80∗80∗255;最后的255可以拆分成 3 3 3个 85 85 85,对应 3 3 3个先验框的 85 85 85个参数,85可以拆分成 4 + 1 + 80 4+1+80 4+1+80。这里的3是指每个位置先验框(锚框)的数量;前4个参数用于判断每一个特征点的回归参数,回归参数调整后可以获得预测框;第5个参数用于判断每一个特征点是否包含物体; 最后80个参数用于判断每一个特征点所包含的物体种类。
六、损失函数

YOLOV5默认应该是使用的Clou,但它源码提供了Giou,Diou等函数,可以在源码中调整
YOLOv5采用了CIoU(Complete Intersection over Union)Loss函数来优化边界框预测。CIoU Loss不仅考虑了预测框与真实框之间的重叠区域,还考虑了两者中心点的距离、宽高比和对角线长度,从而更全面地评估预测框与真实框之间的相似度。CIoU Loss的引入使得模型在训练过程中能够更精确地预测边界框,尤其是在目标尺寸和形状上。
其中Yolov5中采用其中的GIOU_Loss做Bounding box的损失函数

先计算两个框的最小闭包 区域面积 (通俗理解:同时包含了预测框和真实框的最小框的面积),再计算出IoU,再计算闭包区域中不属于两个框的区域占闭包区域的比重,最后用IoU减去这个比重得到GIoU。

两个框的最小闭包区域面积 = 红色矩形面积
IoU = 黄色框和蓝色框的交集 / 并集
闭包区域中不属于两个框的区域占闭包区域的比重 = 蓝色面积 / 红色矩阵面积
GIoU = IoU - 比重
def Giou(rec1,rec2):
#分别是第一个矩形左右上下的坐标
x1,x2,y1,y2 = rec1
x3,x4,y3,y4 = rec2
iou = Iou(rec1,rec2)
area_C = (max(x1,x2,x3,x4)-min(x1,x2,x3,x4))*(max(y1,y2,y3,y4)-min(y1,y2,y3,y4))
area_1 = (x2-x1)*(y1-y2)
area_2 = (x4-x3)*(y3-y4)
sum_area = area_1 + area_2
w1 = x2 - x1 #第一个矩形的宽
w2 = x4 - x3 #第二个矩形的宽
h1 = y1 - y2
h2 = y3 - y4
W = min(x1,x2,x3,x4)+w1+w2-max(x1,x2,x3,x4) #交叉部分的宽
H = min(y1,y2,y3,y4)+h1+h2-max(y1,y2,y3,y4) #交叉部分的高
Area = W*H #交叉的面积
add_area = sum_area - Area #两矩形并集的面积
end_area = (area_C - add_area)/area_C #闭包区域中不属于两个框的区域占闭包区域的比重
giou = iou - end_area
return giou
其中可使用的DIoU
DIoU要比GIou更加符合目标框回归的机制,将目标与anchor之间的距离,重叠率以及尺度都考虑进去,使得目标框回归变得更加稳定,不会像IoU和GIoU一样出现训练过程中发散等问题。



与GIoU loss类似,DIoU loss(L D I o U = 1 − D I o U L_{DIoU} = 1 - DIoUL
DIoU=1−DIoU)在与目标框不重叠时,仍然可以为边界框提供移动方向。
DIoU loss可以直接最小化两个目标框的距离,因此比GIoU loss收敛快得多。
对于包含两个框在水平方向和垂直方向上这种情况,DIoU损失可以使回归非常快,而GIoU损失几乎退化为IoU损失。
DIoU还可以替换普通的IoU评价策略,应用于NMS中,使得NMS得到的结果更加合理和有效。
def Diou(bboxes1, bboxes2):
rows = bboxes1.shape[0]
cols = bboxes2.shape[0]
dious = torch.zeros((rows, cols))
if rows * cols == 0:#
return dious
exchange = False
if bboxes1.shape[0] > bboxes2.shape[0]:
bboxes1, bboxes2 = bboxes2, bboxes1
dious = torch.zeros((cols, rows))
exchange = True
# #xmin,ymin,xmax,ymax->[:,0],[:,1],[:,2],[:,3]
w1 = bboxes1[:, 2] - bboxes1[:, 0]
h1 = bboxes1[:, 3] - bboxes1[:, 1]
w2 = bboxes2[:, 2] - bboxes2[:, 0]
h2 = bboxes2[:, 3] - bboxes2[:, 1]
area1 = w1 * h1
area2 = w2 * h2
center_x1 = (bboxes1[:, 2] + bboxes1[:, 0]) / 2
center_y1 = (bboxes1[:, 3] + bboxes1[:, 1]) / 2
center_x2 = (bboxes2[:, 2] + bboxes2[:, 0]) / 2
center_y2 = (bboxes2[:, 3] + bboxes2[:, 1]) / 2
inter_max_xy = torch.min(bboxes1[:, 2:],bboxes2[:, 2:])
inter_min_xy = torch.max(bboxes1[:, :2],bboxes2[:, :2])
out_max_xy = torch.max(bboxes1[:, 2:],bboxes2[:, 2:])
out_min_xy = torch.min(bboxes1[:, :2],bboxes2[:, :2])
inter = torch.clamp((inter_max_xy - inter_min_xy), min=0)
inter_area = inter[:, 0] * inter[:, 1]
inter_diag = (center_x2 - center_x1)**2 + (center_y2 - center_y1)**2
outer = torch.clamp((out_max_xy - out_min_xy), min=0)
outer_diag = (outer[:, 0] ** 2) + (outer[:, 1] ** 2)
union = area1+area2-inter_area
dious = inter_area / union - (inter_diag) / outer_diag
dious = torch.clamp(dious,min=-1.0,max = 1.0)
if exchange:
dious = dious.T
return dious
其中CIOU
作者考虑到bbox回归三要素中的长宽比还没被考虑到计算中,因此,进一步在DIoU的基础上提出了CIoU。Yolov4中采用CIOU_Loss作为目标Bounding box的损失。 完整的 CIoU 损失函数定义:

其中 α \alphaα 是权重函数,而 v vv 用来度量长宽比的相似性,定义为

def bbox_overlaps_ciou(bboxes1, bboxes2):
rows = bboxes1.shape[0]
cols = bboxes2.shape[0]
cious = torch.zeros((rows, cols))
if rows * cols == 0:
return cious
exchange = False
if bboxes1.shape[0] > bboxes2.shape[0]:
bboxes1, bboxes2 = bboxes2, bboxes1
cious = torch.zeros((cols, rows))
exchange = True
w1 = bboxes1[:, 2] - bboxes1[:, 0]
h1 = bboxes1[:, 3] - bboxes1[:, 1]
w2 = bboxes2[:, 2] - bboxes2[:, 0]
h2 = bboxes2[:, 3] - bboxes2[:, 1]
area1 = w1 * h1
area2 = w2 * h2
center_x1 = (bboxes1[:, 2] + bboxes1[:, 0]) / 2
center_y1 = (bboxes1[:, 3] + bboxes1[:, 1]) / 2
center_x2 = (bboxes2[:, 2] + bboxes2[:, 0]) / 2
center_y2 = (bboxes2[:, 3] + bboxes2[:, 1]) / 2
inter_max_xy = torch.min(bboxes1[:, 2:],bboxes2[:, 2:])
inter_min_xy = torch.max(bboxes1[:, :2],bboxes2[:, :2])
out_max_xy = torch.max(bboxes1[:, 2:],bboxes2[:, 2:])
out_min_xy = torch.min(bboxes1[:, :2],bboxes2[:, :2])
inter = torch.clamp((inter_max_xy - inter_min_xy), min=0)
inter_area = inter[:, 0] * inter[:, 1]
inter_diag = (center_x2 - center_x1)**2 + (center_y2 - center_y1)**2
outer = torch.clamp((out_max_xy - out_min_xy), min=0)
outer_diag = (outer[:, 0] ** 2) + (outer[:, 1] ** 2)
union = area1+area2-inter_area
u = (inter_diag) / outer_diag
iou = inter_area / union
with torch.no_grad():
arctan = torch.atan(w2 / h2) - torch.atan(w1 / h1)
v = (4 / (math.pi ** 2)) * torch.pow((torch.atan(w2 / h2) - torch.atan(w1 / h1)), 2)
S = 1 - iou
alpha = v / (S + v)
w_temp = 2 * w1
ar = (8 / (math.pi ** 2)) * arctan * ((w1 - w_temp) * h1)
cious = iou - (u + alpha * ar)
cious = torch.clamp(cious,min=-1.0,max = 1.0)
if exchange:
cious = cious.T
return cious
nms非极大值抑制
在目标检测的后处理过程中,针对很多目标框的筛选,通常需要nms操作。 因为CIOU_Loss中包含影响因子v,涉及groudtruth的信息,而测试推理时,是没有groundtruth的。所以Yolov4在DIOU_Loss的基础上采用DIOU_nms的方式,而Yolov5中采用加权nms的方式。 不同的nms,会有不同的效果,采用了DIOU_nms的方式,在同样的参数情况下,将nms中IOU修改成DIOU_nms。对于一些遮挡重叠的目标,确实会有一些改进。 比如下面黄色箭头部分,原本两个人重叠的部分,在参数和普通的IOU_nms一致的情况下,修改成DIOU_nms,可以将两个目标检出。虽然大多数状态下效果差不多,但在不增加计算成本的情况下,有稍微的改进也是好的。

七、 超参数详解
7.1 hpy超参数
文件位于data/hyps文件夹下
# Hyperparameters for VOC finetuning
# ython train.py --batch 64 --weights yolov5m.pt --data voc.yaml --img 512 --epochs 50
lr0: 0.01 # 学习率, SGD=1E-2, Adam=1E-3
lrf: 0.01 # 余弦退火超参数
momentum: 0.937 # 学习率动量
weight_decay: 0.0005 # 权重衰减系数
warmup_epochs: 3.0 # 预热学习epoch
warmup_momentum: 0.8 # 预热学习率动量
warmup_bias_lr: 0.1 # 预热学习率
box: 0.05 # Bounding Box Regeression 损失的系数
cls: 0.5 # 分类损失的系数
cls_pw: 1.0 # 分类BCELoss中正样本的权重
obj: 1.0 # 有无物体损失的系数
obj_pw: 1.0 # 有无物体BCELoss中正样本的权重
iou_t: 0.20 # 标签与anchors的iou阈值 iou training threshold
anchor_t: 4 # 标签的长h宽w/anchor的长h_a宽w_a阈值, 即h/h_a, w/w_a都要在(1/4, 4)之间anchor-multiple threshold
# anchors: 3.63
# 下面是一些数据增强的系数, 包括颜色空间和图片空间
fl_gamma: 0.0
hsv_h: 0.015 # 色调
hsv_s: 0.7 # 饱和度
hsv_v: 0.4 # 明度
degrees: 0.0 #旋转角度
translate: 0.1 # 水平和垂直平移
scale: 0.5 # 缩放
shear: 0.0 # 剪切
perspective: 0.0 # 透视变换参数
flipud: 0.0 # 上下翻转
fliplr: 0.5 # 左右翻转
mosaic: 1.0 #进行mosaic的概率
mixup: 0.0 #进行mixup的概率, 在mosaic启用时才可启用
copy_paste: 0.0 # segment copy-paste (probability), 在mosaic启用时, 才可以启用
7.2 Acnhor
YOLOv5在yaml文件中预设好了输入图像为 640 ∗ 640 640*640 640∗640分辨率对应的anchor尺寸,YOLOv5的anchor也是在大特征图上检测小目标,在小特征图上检测大目标。三个特征图,每个特征图上的格子有三种尺寸的anchor。
# anchors
anchors:
- [10,13, 16,30, 33,23] # P3/8 检测小目标 10,13是一组尺寸,一共三组
- [30,61, 62,45, 59,119] # P4/16
- [116,90, 156,198, 373,326] # P5/32 检测大目标
7.3 Backbone
# YOLOv5s v6.0 backbone
backbone:
# YOLOv5 v6.0 backbone
backbone:
# from 第一列 输入来自哪一层 -1代表上一层, 4代表第4层
# number 第二列 卷积核的数量 最终数量需要乘上width
# module 第三列 模块名称 包括:Conv Focus BottleneckCSP SPP
# args 第四列 模块的参数
# [from, number, module, args]
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4 卷积核的数量 = 128 * wedith = 128*0.5=64
[-1, 3, C3, [128]], # 模块数量 = 3 * depth =3*0.33=1
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
[-1, 6, C3, [256]], # 模块数量 = 6 * depth =6*0.33=2
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
[-1, 9, C3, [512]], # # 模块数量 = 9 * depth =9*0.33=3
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
[-1, 3, C3, [1024]],
[-1, 1, SPPF, [1024, 5]], # 9
]
7.4 网络结构参数
**backbone的前3个C3数量对应yolov5s.yaml的配置3,6,9分别除了3,变为1/3后的1,2,3,和模型深度参数有关depth_multiple: 0.33**
层数,第几层 from n params module arguments
ch[-1] 数量 参数量 模块名称(m) 网络结构参数:输入维度,输出维度,卷积核大小,卷积步长
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 2 115712 models.common.C3 [128, 128, 2]
5 -1 1 295424 models.common.Conv [128, 256, 3, 2]
6 -1 3 625152 models.common.C3 [256, 256, 3]
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]
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 229245 Detect [80, [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]], [128, 256, 512]]
7.5 Yolov5四种网络的深度

7.6 Yolov5四种网络的宽度

此篇文章学习记录到此结束,后续更多精彩内容!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)