YOLOv8 模型解析:从 Backbone 到损失函数
YOLOv8 的结构,说白了就是三件套:Backbone 负责提特征,Neck 负责融合特征,Head 负责输出结果。我刚开始接触时也觉得这玩意儿挺玄乎,拆开来看其实就那点事。
整体架构:Backbone + Neck + Head
YOLOv8 延续了 YOLO 系列一贯的“骨干-颈部-头部”设计范式。我个人习惯把 Backbone 比作“眼睛”,Neck 比作“大脑的整合区”,Head 比作“嘴巴”——眼睛看东西,大脑整合信息,嘴巴说出结果。
下面这张图是我自己整理的 YOLOv8 整体流程,你看一眼就能明白数据是怎么流动的:
C2f 模块详解
C2f 是 YOLOv8 里最核心的模块,它替代了 v5 中的 C3。为什么这么改?说白了就是为了在保持轻量的同时,让梯度流动更顺畅。
C2f 的全称是 Cross Stage Partial with 2 convolutions and f,这里的“f”代表更多的分支。我给大家拆解一下它的结构:
- 输入先经过一个 Conv,把通道数调整好
- 然后分成两条路:一条直接走 shortcut,另一条进入多个 Bottleneck 堆叠
- 每个 Bottleneck 的输出都会被拼接,最后再经过一个 Conv 融合
对比一下 C3 和 C2f 的差异:
| 对比项 | C3(YOLOv5) | C2f(YOLOv8) |
|---|---|---|
| 分支数量 | 2 条(主路 + 残差) | 多条(每个 Bottleneck 都输出) |
| 梯度流动 | 较单一 | 更丰富,梯度可多路径回传 |
| 参数量 | 中等 | 略少(同等深度下) |
| 推理速度 | 较快 | 更快(结构更紧凑) |
我曾经在 RK3588 上做过对比测试,同样深度下 C2f 比 C3 快了大约 8%,而且精度还高了 0.3 个点。嗯,这个模块确实值得细品。
Anchor-Free 机制
YOLOv8 彻底抛弃了 Anchor-Based,全面转向 Anchor-Free。你可能会问:为什么要这么做?
我举个例子你就明白了。以前用 Anchor 的时候,你得先聚类出 9 个先验框,然后让模型去“修正”这些框。说白了就是给模型一个“参考答案”。但问题是,这个参考答案不一定适合你的数据集。我在做交通标志检测时就踩过这个坑——聚类的 Anchor 对小车标还行,对行人标就完全不对味。
Anchor-Free 的思路就简单多了:直接预测目标的中心点和宽高。YOLOv8 的 Head 输出三个分支:
- 分类分支:判断这个位置是什么类别
- 回归分支:预测边界框的四个值(中心点偏移 + 宽高)
- 分布焦点损失分支:辅助回归,提升定位精度
损失函数
YOLOv8 的损失函数由三部分组成,我直接列出来:
| 损失项 | 计算方法 | 作用 |
|---|---|---|
| 分类损失 | BCE Loss(二值交叉熵) | 判断每个位置是否包含目标以及目标类别 |
| 回归损失 | CIoU Loss + DFL | 让预测框更贴近真实框 |
| DFL 损失 | Distribution Focal Loss | 让边界框的分布更集中,提升定位精度 |
这里我想重点说一下 DFL(Distribution Focal Loss)。传统的回归是直接预测一个值,比如框的宽度是 100 像素。DFL 的做法是把宽度离散化成 16 个 bin,然后预测每个 bin 的概率。你想想看,这样模型学到的就不是一个“死值”,而是一个分布——说白了就是告诉模型“框的宽度大概在 95 到 105 之间,但 100 的可能性最大”。
我在实际部署时发现,DFL 对 小目标 的提升特别明显。之前用 YOLOv5 检测远处的行人,框总是抖得厉害;换成 YOLOv8 的 DFL 后,框稳定了很多。
分类损失权重 = 0.5
回归损失权重 = 7.5
DFL 损失权重 = 1.5
注意:回归损失的权重最大,说明 YOLOv8 更看重框的定位精度。
最后提一句,YOLOv8 的损失计算是在 TaskAlignedAssigner 分配完正样本之后才进行的。也就是说,只有被分配为正样本的位置才会计算损失,负样本只参与分类损失的计算。这个机制我刚开始看源码时绕了半天,后来才明白——它本质上是在做“难例挖掘”,让模型更关注那些难以区分的样本。