没想到 YOLOv1 到 YOLOv4 就已经写了快 20000 字……
编辑页面卡的不行,我不得不分几篇写完。YOLOv1 到 YOLO 26一共 12 篇,正好分上中下三篇。
YOLOv5
重量级来了,Ultralytics 闪亮登场了。第一个没有论文的 YOLO 正式版本,但它成为了工业界的事实标准。
YOLOv5 的一大变化就是提供了多种模型尺寸:n/s/m/l/x,通过统一的的 depth_multiple(深度倍数)和 width_multiple(宽度倍数)参数缩放。基准模型也就是没有缩放的版本是 l 。其中 depth_multiple 参数实际上控制的是可重复模块(如 C3、BottleneckCSP)的堆叠次数。width_multiple 控制的则是卷积层的通道数。
YOLOv5 还是提供了 P5(640×640)和 P6(1280×1280) 两套不同输入分辨率的模型。P6 在 Backbone 多了一层特征提取。咱常用的是 P5。
Pytorch
YOLOv4 仍然基于 DarkNet 框架,没有使用 PyTorch,底层用 C 语言实现,网络结构通过 cfg 文件定义,而非 yaml 文件或者 python 代码,训练和二次开发门槛高,部署也很麻烦。
YOLOv5 完全使用 PyTorch 框架实现,原生支持 Onnx/TensorRT 等部署导出,十分方便。
俺寻思,后来 Pytorch 一统天下, YOLOv5 功不可没。
网络设计上,延续 YOLO 经典结构: Input --> Backbone(特征提取) --> Neck(特征融合) --> Head(检测头)。
Input
YOLOv4 引入了 Mosaic,v5 则是直接集成 Mosaic 到默认训练流程。
YOLOv5 加入了 AutoAnchor,K - means 之后用遗传算法优化得到最佳 Anchor,每次训练开始之前,会根据数据集自适应计算 Anchor。在此之前 Anchor 需要手动调整。
Focus
Focus 模块位于网络最前端,紧接输入,作用是将空间维度上的信息聚焦到通道维度,实现2倍下采样,但是不丢失任何信息。
假设输入一个 640 × 640 × 3 的图像,空间维度上拆分成 2 × 2 的Grid,就是 4 块区域,注意这里不是从整体上切分,看下图:
然后就得到了 4 张 320 × 320 × 3的特征图,然后在通道维度上做拼接得到 12 通道的 320 × 320 特征图,最后用 3 × 3 卷积映射到目标通道数。
后来这个模块被一个 6×6 Conv(k=6, s=2, p=2)给替代了。主要是考虑到内存连续访问和推理引擎原生优化的问题。
CSP + C3
也就是从这时候开始,YOLO 开始出现很多网络结构(初看会感觉意义不明)的缩写,罪大恶极。你叫 CBL 我能理解为 Conv + BN + LeakyReLU,可这 C3 是个什么玩意儿?
先说一下 BottleNeck,这个在前面没有提到,是一种先压缩、再提取、再恢复信息的残差模块。举个简单的例子:
输入:H × W × 256
通道投影(1×1 Conv):
→ H × W × 64
空间特征提取(3×3 Conv,stride=1,padding=1):
→ H × W × 64
通道映射恢复(1×1 Conv):
→ H × W × 256
类似瓶颈,先宽后窄再宽,降维之后再用 3 × 3 卷积可以有效降低计算量。这里的降维类似一次通道投影,可以朝PCA那个方向去理解,降维之后得到的是比较有价值的特征,而不是白白损失掉了。
还可以接残差 shortcut,把网络堆的更深。在 Backbone 一般用带残差的,缓解深层网络梯度消失的问题。 Neck 一般用不带残差的,因为 Neck 用于特征融合,本来就浅。
C3 就是多个 BottleNeck 融合 CSP 思想的产物。Cross Stage Partial Bottleneck with 3 convolutions
那我们复习一下 CSP 是啥呢……感觉距离看完上篇文章已经过了5分钟了捏……
CSP 就是把特征从通道维度拆分,一部分通道继续向深处走,另一部分直接走捷径,最后再融合;和ResNet的区别是,ResNet需要全部通道参与运算。
YOLOv5 的 C3 结构可以理解成 n 个 BottleNeck串联又 加了3个卷积层。具体结构可以参考下图的 CSPLayer,其中的 ConvModule 是 CBS。
C3 替代的是 YOLOv4 中的 BottleneckCSP;而 BottleneckCSP 替代的是原始 Darknet53 中的 Residual Block。
SPPF
Faster 的 SPP,YOLOv5 的重要工程优化,速度更快,但是精度上……YOLOv5没论文,没消融实验,俺不知道……
为啥串行反而 Fast 了呢?不是并行更快么?
SPPF 是用串行 5 × 5 的 MaxPooling 来取代并行的 5/9/13 的 SSP,上图对 SPPF 的绘制有误,实际上是跳连后才 Concat 的。
SPP 的计算量:
5×5 池化:O(25 \times H \times W \times C)
9×9 池化:O(81 \times H \times W \times C)
13×13 池化:O(169 \times H \times W \times C)
总计算量:O((25 + 81 + 169) \times H \times W \times C) = O(275 \times H \times W \times C)
SPPF 的计算量:
3 次 5×5 池化:O(3 \times 25 \times H \times W \times C) = O(75 \times H \times W \times C)
计算量小了不少。在 YOLOv5 的实现里,SPPF 与原先 SPP 的多尺度池化输出效果是等价的,但实现方式更高效。
YOLOv6
美团视觉智能部提出的。其实在 YOLOv5 之后,我对 Ultralytics 以外的团队突出的 YOLO 就不是很感兴趣了……
YOLOv6 也是针对工程部署做的优化,更关注:高FPS + 高精度 + 易于部署。也是第一次训练时支持 INT8 量化,其他模型都是 PTQ 训练后用 TensorRT 之类的做 INT8 量化。
Backbone
引入(chong)重参数化 Reparameterization 的思想,重参数化就是:把模型的参数重新组织一遍,让训练时的结构和推理时的结构不一样,但数学上等价。
Backbone 采用 RepVGG 风格设计。RepVGG 和 ResNet 一样都是图像分类网络。
训练阶段:典型的重参数化基本模块会包含多条并行分支,例如 3×3 卷积、1×1 卷积,以及在满足维度和步长条件时可用的恒等映射(Identity),并分别接入 BatchNorm。这种多分支结构能够提供更丰富的梯度流和特征表示能力。
推理阶段:通过结构重参数化技术,把这些分支的卷积和 BN 参数代数融合为一个等价的 3×3 卷积层。也就是说,训练时用多分支提升优化性,推理时再折叠成更适合部署的单分支结构。[4]
Neck
使用 Rep-PAN + RepBlock/CSPStackRep。小规模模型用 RepBlock,大规模模型用 CSPStackRep。所以看得出来和 YOLOv5 的区别,并不是按深度/宽度缩放网络,而是针对不同规模的网络做不同设计。
Rep-PAN 就是 PAN + 重参数化模块替换。
CSPStackRep 就是 CSP 分流结构 + RepBlock 重参数化。
BiC (Bi-directional Concatenation) 双向级联,是 Rep-PAN 中的一个关键结构。BiC是在 Top-Down 的过程中引入一个更浅层的特征,以保留更多定位信息,对小目标检测更友好。注意看下图,Backbone 的 C3(更高分辨率)和 C4(更低分辨率) 进入 BiC 模块 Concate 的同时,还有一个来自Neck P5(融合后的特征)的特征经过卷积再 Concate。
Decoupled Head
Anchor-Free 解耦头,把分类和回归在一个输出分支里面拆开,让它们做各自的事。
Anchor 这玩意儿是犯什么罪了吗一会用一会不用的……我感觉一开始 v1 没用 Anchor 是因为没想到,v2 之后借鉴了 R-CNN 的 Anchor,效果有提升,就沿用了下去,但是后面随着 DL 的发展,发现 Anchor 缺点也多,毕竟是个超参数,调不好会对精度有影响,从 YOLOX 之后就引入了 Anchor-Free + 解耦头,证明无需 Anchor 也能达到 Anchor-Based 的效果。
YOLOv5之前 输出 [ x, y, w, h, obj, cls... ] 格式的结果,位置(回归)和语义(分类)信息是掺和在一起的。
Loss Function
使用解耦头和 Anchor-Free 带来的变化就是:不再单独设置传统的 objectness 损失,总体上由分类损失和边界框回归损失构成;其中回归部分又可以细分为 IoU 类损失,以及在更大规模模型中引入的 DFL。YOLOv6 在实验后发现,在 Anchor-Free 架构下,单独设计目标置信度损失并不会带来明显收益。
YOLOv6 的损失函数一般写作,其中 DLF 全称 Distribution Focal Loss:
分类损失采用 VFL (Varifocal Loss),原因是检测任务中正负样本极不均衡。如果使用 BCE,梯度容易被海量 Easy 负样本主导。Focal Loss 方法的核心是降低易分样本的权重,让模型更关注难分样本。
为了同时监督定位质量 IoU,让正样本的监督信号不再是硬指示函数1,而是作为一个带定位质量信息的软标签,VFL 让分类分数同时反应类别正确性和定位质量好坏,目标是 classification score = classification conf × localization score。
VFL 对正样本:
对负样本:
其中
- q∈[0,1] :软标签,质量分数
- \alpha:负样本权重系数
- \gamma:聚焦参数,类似 Focal Loss)
- 负样本使用不对称加权,抑制低质量预测
边界框回归损失本质就是 IoU + DFL, IoU 部分在这里描述框的重叠质量,不再赘述。DFL 描述四条边界的精确位置差异,计算公式为:
YOLOv7
跟 YOLOv6 的同期作品,仍然是 Anchor-Based。并非又把 Anchor 又加了回来,YOLOv5 之后的版本更迭,也就那样吧……相邻版本间的关联并没有你想象得那么大。
BoF
我还注意到很有趣的一点,很多网站说 YOLOv7 使用了免费赠品包……这是机翻的锅,英文原文是 Bag-of-Freebies(BoF),这个概念来自对应名称的论文 [7]。BoF 并不只是狭义的数据增强 Pipeline,而是泛指一类只作用于训练阶段、能够提升效果但不增加推理开销的策略集合,因此才起了这么个名字。不过机翻成这个雷霆名字,实在是……还是机翻作者的锅。
除了负责输出的Lead Head,YOLOv7 在 BoF 基础上,为模型设计了 Auxiliary Head(辅助头),可以为 Backbone 提供更多监督信号。
训练结束会把它给删掉,推理速度不变。
但是有个问题,辅助头标签怎么分?因为 Lead Head 学得快,Auxiliary Head 学得慢。因此 YOLOv7 为辅助头分配了粗粒度的标签,让它学个大概就行。
Backbone
提出了 E-ELAN(Extended Efficient Layer Aggregation Network)扩展高效层聚合网络。
那啥是不扩展的 ELAN 呢……可以把它理解成 E-ELAN 的前身思路:核心关注点都是控制梯度路径、提升特征聚合效率。后来的《Designing Network Design Strategies Through Gradient Path Analysis》更像是对这套设计思想的进一步整理和论文化表述。
E-ELAN 的核心思想是在保持原有梯度传播路径长度稳定的前提下,通过扩展并行计算分支(expand cardinality)、重组不同分支间的信息流(shuffle cardinality)以及最终特征融合(merge cardinality),增强网络的特征表达能力。其实现中通常结合组卷积以控制计算复杂度,从而在不显著增加参数量的情况下获得更丰富的特征组合能力。
写完 YOLOv12 之后决定再回来仔细研究一下 ELAN,因为理解 ELAN 的本质很重要。起初我认为 ELAN 类似 CBL、C3 一类组装出来的 Block,但 ELAN 本质上是一种拓扑结构或者说设计范式,然后在训练框架中被实例化成一个模块。
上图展示了从 VoVNet 到 CSPVoVNet,再到 ELAN 和 E-ELAN 的拓扑结构演进过程,这些都是 YOLOv4 的作者搞出来的。
这张图我觉得不需要关注卷积,只需要关注箭头走向。
VoVNet 采用连续计算后统一聚合的方式,将多层特征在阶段末端一次性融合,但随着网络加深,梯度传播路径容易变长。
CSPVoVNet 在此基础上引入 CSP 结构,将输入特征拆分为计算分支与直连分支,通过跨阶段连接减少重复梯度信息并降低计算开销。
ELAN 进一步调整特征聚合方式,通过保留不同深度层的输出并统一聚合,在增加最短梯度路径长度的前提下增强特征复用能力,从而提升网络训练稳定性与表达能力。
E-ELAN 则进一步扩展并行计算分支,并通过 shuffle 与 merge 重新组织分支间的信息流,在保持原有梯度传播路径稳定的同时提升特征组合能力和表示能力。
Model Scaling
之前模型深度和宽度都是分别缩放,YOLOv7 提出了联合缩放。
试验表明,参数更可控,精度提升明显。
YOLOv8
也是 Ultralytics 发布的,延续了 YOLOv5 提供多尺度模型的传统。
网络实际上延续 Backbone -> Neck -> Head 的三段式结构,但是官方代码中的 Neck 被融合到了 Head 中,因此代码上只有 Backbone 和Head 的部分。
C2f
Backbone 仍然使用 CSP,Neck 也是 PAN,但是将两者的 C3 全部替换成了新的 C2f。Cross Stage Partial Bottleneck with 2 convolutions and Fast。
C2f 设计基于对 CSP 的改进和对之前 YOLOv7 E-ELAN 的借鉴,都是并行更多梯度流分支,获取更多层次的梯度信息。
C3 的 BottleNeck 是串联的,C2f 让特征经过每个 BottleNeck 之后,分出一路走 shortcut,最后都 Concat 到一起。此外 Conv 数量变成了2个。
Anchor-Free
YOLOv8 采用 Anchor-Free 方式回归边界框,不过它并不是直接输出 x, y, w, h,而是预测 left, top, right, bottom,也就是当前特征点(更准确地说,是网格上的 anchor point)到 GT 四条边的距离;再由这四个距离解码出最终的框坐标。
这么多版本 YOLO 的发展,让人们意识到,Anchor 存在几个问题:
- Anchor 的尺寸、长宽、数量都是需要调的超参数,哪怕经过聚类之后用 GA 来优化,在模型迁移和部署之后都会导致之前的 Anchor 不再适用
- 正负样本不均衡的问题,一个 GT 框需要与大量 Anchor 框进行 IoU 计算,这个过程中存在大量无效负样本
- Anchor 长宽比有限,对数据集中极端长宽比的物体,可能找不到合适的 Anchor 进行匹配
注意 Anchor-Free 仍然需要 NMS,因为 YOLOv8 仍然是 Dense Prediction。
每个特征点都会输出一个候选框,假设一个目标覆盖了 3 × 3 的特征区域,那可能回归出 9 个框,同一个目标得到多个框,推理时必须 NMS 去重。
YOLOv8 被归类为无锚点模型,是因为它不依赖预定义的 anchor box 尺寸和长宽比[10]。
但在训练和解码过程中,它仍然会在特征图上生成一组规则的参考点(通常称为 anchor points),用这些点来表示预测位置、分配正负样本,并把预测的
ltrb距离还原成边界框。
所以更准确地说,YOLOv8 是无锚框(anchor-box-free),但不是完全不使用参考点。
Label Assignment
YOLOv8 在训练阶段采用动态标签分配(Task-Aligned Assigner,TAL)。
相比早期 YOLO 的固定匹配方式:
- YOLOv3:固定 Anchor 匹配
- YOLOv5:Anchor + IoU 匹配
- YOLOv8:动态选择正样本
核心思想是:
分类得分高且定位准确的预测点,更应该成为正样本。
训练时,对于每个 GT(真实框):
- 在多个尺度特征图上找到候选预测点;
- 计算候选点的分类得分与定位质量(IoU);
- 构造任务对齐分数:
其中:
- s:分类预测分数
- IoU:预测框与 GT 的重叠程度
- \alpha,\beta:超参数
最终选取 Top-K 个得分最高的候选作为正样本参与训练。
相比固定 Anchor 匹配方式:
- 不需要人为设计 Anchor;
- 自动调整正样本数量;
- 分类与定位目标更加一致;
- 对密集目标、小目标更加友好。
Decoupled Head
依然是被分成了回归头和分类头,两个任务目标不同,共享特征容易造成梯度冲突。
Head 会输出 3 个不同尺度的特征图:
| 输出层 | 特征图尺寸 | 目标类型 |
|---|---|---|
| P3 | 80×80 | 小目标 |
| P4 | 40×40 | 中目标 |
| P5 | 20×20 | 大目标 |
在使用 COCO 数据集的情况下,YOLOv8 的检测头实际上输出:B × 144 × 8400,其中:
- B:Batch Size
- 144:每个候选点输出维度
- 8400:全部预测位置 = 80 × 80 + 40 × 40 + 20 × 20
这里的 144 由两部分组成,分类输出的 80 类别置信度和回归输出的 4 × 16 = 64,其中 4 是 tblr,16 是 RegMax。
tlrb 在实际展开的时候,结果是:
L : [p0,p1,...,p15]
T : [p0,p1,...,p15]
R : [p0,p1,...,p15]
B : [p0,p1,...,p15]
每组是 16 个概率,每组分别计算期望,这个期望的结果就是解码狂的实际位置。在 3 个不同尺度的特征图上乘当前层的 Stride,得到真实的像素尺寸。
解码后,最终输出维度是 B × 84 × 8400。
Loss Function
YOLOv8 的损失函数由三部分组成:
**BBox Loss ** 使用 CIoU Loss:
Classification Loss 使用 BCE,对每个类别独立计算分数。检测任务里这样做的好处是实现简单,也便于和动态标签分配配合;但在常规目标检测设定下,每个正样本通常仍只对应一个 GT 类别,而不是把检测任务本身改成多标签分类。
Distribution Focal Loss(DFL)预测距离的离散概率分布,假设 RegMax=16,左侧 l 预测结果为:[0.01,0.03,0.10,0.25, 0.40,0.15,0.04,0.02, ...],那距离最可能在 p3 和 p4 之间,不过不是取最大值而是算数学期望:
假设最终结果是 3.33。那么 GT 咋监督呢?假设 GT 为 3.7,DFL不会强制标签变成 [0,0,0,1,0...],而是拆成 3 和 4 两个相邻整数并分配权重,并按距离分配权重。
- 距离 3:w_l=4-3.7=0.3,也就是30%可能在3
- 距离 4:w_r=3.7-3=0.7,也就是70%可能在7
得到监督目标:[0,0,0,0.3,0.7,0,...]
参考文章
[1] ultralytics/yolov5: Ultralytics YOLOv5 in PyTorch > ONNX > CoreML > TFLite
[2] A Comprehensive Review of YOLO Architectures in Computer Vision: From YOLOv1 to YOLOv8 and YOLO-NAS
[3] YOLOv11 改进 - 基础知识 为什么SPPF比SPP更快?深入解析YOLO中多尺度特征提取的效率优化与代码实现 - 魔改工程师 - 博客园
[4] 2101.03697v1
[5] [2209.02976] YOLOv6: A Single-Stage Object Detection Framework for Industrial Applications
[7] [1902.04103] Bag of Freebies for Training Object Detection Neural Networks
评论