3、YOLOv5详解:网络架构、CSPDarknet骨干、PANet颈部、损失函数
YOLOv5 这个版本,说实话,是我个人在实际项目中用得最多的一个系列。它不像 v4 那样有那么多复杂的调参技巧,也不像 v8 那样把一切都封装得严严实实。v5 的代码结构非常清晰,你打开它的源码,基本上能顺着把整个检测流程捋下来。
今天我们就来拆解一下它的核心。说白了,就是三块:骨干网络(Backbone)、颈部(Neck)和头部(Head)。我们重点讲前两个,以及它们背后的损失函数设计。
3.1 整体架构:一个清晰的流水线
YOLOv5 的整体架构,你可以想象成一条流水线:
- 输入端:图片进来,先做 Mosaic 数据增强和自适应锚框计算。
- Backbone:CSPDarknet53 结构,负责提取特征。
- Neck:PANet 结构,负责融合不同尺度的特征。
- Head:在融合后的特征图上做分类和回归。
嗯,这里要注意,v5 的 Head 其实和 v3 很像,都是基于锚框的。但前面的 Backbone 和 Neck 做了大改。
核心变化:YOLOv5 最大的贡献不是提出了什么全新的理论,而是把 CSPNet、PANet 这些已有的优秀模块,以一种极其工程化的方式组合了起来。它追求的是在速度和精度之间找到一个非常实用的平衡点。
下面这张图,是我根据源码梳理出来的结构流程,你可以对照着看:
3.2 CSPDarknet53:骨干网络的秘密
YOLOv5 的骨干网络叫 CSPDarknet53。你可能会问,这和 Darknet53 有什么区别?
区别就在于 CSP(Cross Stage Partial) 结构。我刚开始看的时候,觉得这玩意儿挺玄乎的。后来自己动手复现了一遍,发现它的思路其实很朴素:
- 传统的残差块:输入经过卷积,然后和原始输入相加。这能解决梯度消失问题。
- CSP 结构:把输入的特征图在通道维度上分成两部分。一部分走正常的残差块计算,另一部分直接拼接到最后。这样做的好处是,梯度在反向传播时,有一条更短的路径可以直接流回前面,避免了重复计算。
我的经验:我在项目中对比过,把 YOLOv5s 的 Backbone 换成普通的 Darknet53,参数量差不多,但推理速度慢了大约 15%,而且 mAP 还掉了 1-2 个点。CSP 结构确实在减少计算量的同时,保持了特征提取能力。
另外,v5 在 Backbone 的最后加了一个 SPPF(Spatial Pyramid Pooling Fast) 模块。它用三个串联的 5x5 最大池化,替代了原来 v4 中并行的不同尺寸池化核。效果几乎一样,但计算量小了很多。
3.3 PANet 颈部:让信息双向流动
Neck 部分,YOLOv5 用的是 PANet(Path Aggregation Network)。你可能会问,为什么不用 FPN?
FPN 是自顶向下的,它把高层的语义信息传递到低层。但问题是,低层的定位信息很难传递到高层。PANet 在 FPN 的基础上,增加了一条自底向上的路径。
说白了,就是:
- FPN 路径:把 20x20 的特征图上采样,和 40x40 的拼接,再上采样和 80x80 的拼接。这能让小目标检测头也看到大目标的语义信息。
- PAN 路径:把 80x80 的特征图下采样,和 40x40 的拼接,再下采样和 20x20 的拼接。这能让大目标检测头也看到小目标的细节信息。
我记得有一次做交通标志检测,标志牌有大有小。只用 FPN 的时候,大标志牌检测没问题,但小标志牌经常漏检。换成 PANet 之后,小标志牌的召回率提升了 8%。
避坑指南:我曾经在部署到边缘设备时,发现 PANet 的拼接操作会带来额外的内存开销。如果你的设备内存很紧张,可以考虑把 Neck 的通道数从 256 减到 128,精度损失不大,但内存占用能降一半。
3.4 损失函数:不只是算个差值
YOLOv5 的损失函数由三部分组成:
| 损失项 | 计算方法 | 作用 |
|---|---|---|
| 分类损失 | BCEWithLogitsLoss | 判断每个锚框属于哪个类别 |
| 定位损失 | CIoU Loss | 计算预测框和真实框的差异 |
| 置信度损失 | BCEWithLogitsLoss | 判断锚框内是否有物体 |
这里我想重点说一下 CIoU Loss。它比原始的 IoU Loss 多了两个惩罚项:
- 中心点距离:惩罚预测框和真实框中心点不重合的情况。
- 宽高比一致性:惩罚预测框和真实框的形状差异。
你想想看,如果只用 IoU,两个框完全不重叠时,梯度就是 0,模型根本不知道怎么优化。CIoU 解决了这个问题,只要中心点没对齐,就有梯度信号。
在代码层面,YOLOv5 的损失计算还有一个细节:正负样本匹配策略。它不像 v3 那样只取 IoU 最大的锚框作为正样本,而是采用了 跨网格匹配:
# 伪代码示意:YOLOv5 的正样本匹配
# 1. 计算每个真实框和所有锚框的宽高比
# 2. 如果宽高比在 [1/4, 4] 范围内,认为该锚框匹配
# 3. 对于匹配的锚框,不仅它所在的网格负责预测
# 它相邻的两个网格也负责预测(跨网格)
# 4. 这样每个真实框大约有 3-9 个正样本
这样做的好处是,正样本数量大大增加,训练收敛更快。我刚开始用 v5 的时候,发现它训练 100 个 epoch 的效果,比 v3 训练 200 个 epoch 还好,就是这个原因。
总结一下:YOLOv5 的损失函数设计,核心就是让模型在训练时有更多的梯度信号。CIoU 解决了梯度消失问题,跨网格匹配解决了正样本稀疏问题。这两招,是它训练速度快的关键。