2、YOLOv8模型结构解析:Backbone、Neck、Head详解

YOLOv8的结构,说白了就是三个大模块串起来:Backbone(骨干网络)负责提特征,Neck(颈部)负责融合特征,Head(头部)负责输出预测。我刚开始接触时也觉得这名字挺唬人,拆开看其实没那么复杂。

一句话总结:Backbone是眼睛,Neck是大脑的整合区,Head是嘴巴——把看到的东西说出来。

2.1 整体架构概览

先看一张我画的整体流程图,你跟着箭头走一遍就明白了。

YOLOv8 整体架构流程图 Backbone Conv + C2f × N SPPF 池化 多尺度特征图 P3 / P4 / P5 Neck FPN + PAN 结构 上采样 + 拼接 C2f 特征融合 特征增强 Head 解耦检测头 分类分支 回归分支 输出预测框 输入图像 → Backbone提取特征 → Neck融合多尺度 → Head输出分类+回归结果

2.2 Backbone:特征提取的基石

Backbone 的任务很简单——把一张 640×640 的输入图,逐步下采样成不同尺度的特征图。YOLOv8 用的是 修改版的 CSPDarknet,核心是两个模块:C2f 和 SPPF。

C2f 模块:跨阶段部分连接

C2f 是 YOLOv8 对比 v5 最大的改动之一。我刚开始看代码时也觉得它跟 C3 差不多,仔细一对比才发现门道。

对比项 YOLOv5 的 C3 YOLOv8 的 C2f
结构 3 个卷积 + 1 个 Bottleneck 2 个卷积 + n 个 Bottleneck
梯度流 单路分流 多路分流 + 拼接
参数量 相对较大 更轻量
特征复用 一般 更强(梯度丰富)

我的经验:C2f 的「f」代表「feature fusion」。它把输入分成多路,每路经过 Bottleneck 后再拼回来。这样做的好处是——梯度可以沿着多条路径回传,训练时不容易梯度消失。我在做小目标检测时,换成 C2f 后召回率明显提升了。

看一段简化版的 C2f 实现逻辑:

class C2f(nn.Module):
    def __init__(self, c1, c2, n=1, shortcut=True):
        super().__init__()
        self.cv1 = Conv(c1, c2 * 2, 1, 1)  # 先升维
        self.cv2 = Conv(c2 * (n + 2), c2, 1)  # 再降维
        self.m = nn.ModuleList(
            [Bottleneck(c2, c2, shortcut) for _ in range(n)]
        )

    def forward(self, x):
        y = list(self.cv1(x).chunk(2, 1))  # 分成两路
        y.extend(m(y[-1]) for m in self.m)  # 每路过 Bottleneck
        return self.cv2(torch.cat(y, 1))    # 拼接后输出

SPPF 模块:快速空间金字塔池化

SPPF 是 SPP(空间金字塔池化)的加速版。为什么要做池化?因为网络最后需要固定尺寸的特征图,但输入图像大小不一。SPPF 用多个不同大小的池化核并行处理,然后把结果拼起来。

YOLOv8 用的 SPPF 比原版 SPP 快了 2 倍以上,效果还差不多。怎么做到的?

  • 原版 SPP:分别用 5×5、9×9、13×13 三个池化核并行算
  • SPPF:只用 5×5 的核,串行连三次,等效于 5、9、13 的感受野

避坑指南:我曾经在自定义数据集上尝试把 SPPF 换回原版 SPP,结果训练速度慢了 30%,mAP 只涨了 0.1%。得不偿失。除非你有特殊需求,否则别动 SPPF。

2.3 Neck:特征融合的桥梁

Neck 的作用是让不同尺度的特征图「对话」。YOLOv8 沿用了 FPN + PAN 的结构,我习惯叫它「双向特征金字塔」。

  • FPN(自顶向下):把高层语义信息传递到低层,让大特征图也有「全局视野」
  • PAN(自底向上):把低层位置信息传递到高层,让小特征图也有「细节感知」

你想想看,检测小目标时,低层的特征图分辨率高但语义弱;检测大目标时,高层的特征图语义强但分辨率低。Neck 就是来解决这个矛盾的。

YOLOv8 的 Neck 里也用了 C2f 模块做特征融合,而不是简单的卷积。这样融合后的特征更「干净」,噪声更少。

2.4 Head:解耦检测头

这是 YOLOv8 另一个大改动——从耦合头变成了解耦头。

类型 YOLOv5 Head YOLOv8 Head
结构 一个卷积同时输出分类+回归 两个独立分支
分类分支 共用特征 独立卷积 + 分类损失
回归分支 共用特征 独立卷积 + 回归损失
训练稳定性 一般 更稳定(任务冲突少)

为什么要解耦?因为分类和回归其实是两个不同的任务。分类关注「这是什么」,回归关注「它在哪里」。共用特征时,两个任务会互相干扰。解耦后,每个分支可以专注学自己的东西。

关键点:YOLOv8 的 Head 输出三个尺度的预测结果,分别对应 P3(小目标)、P4(中目标)、P5(大目标)。每个尺度上,特征图每个格子预测 4 + 1 + num_classes 个值:4 个坐标、1 个置信度、C 个类别概率。

2.5 模型配置文件解读(yaml)

YOLOv8 的模型结构定义在 .yaml 文件里。我刚开始看这文件时也觉得像天书,后来发现它其实就是个「积木清单」。

以 yolov8n.yaml 为例:

# Ultralytics YOLOv8n
nc: 80  # 类别数
scales:  # 模型缩放系数
  n: [0.33, 0.25, 1024]  # depth, width, max_channels

backbone:
  - [-1, 1, Conv, [64, 3, 2]]   # 0-P1/2
  - [-1, 1, Conv, [128, 3, 2]]  # 1-P2/4
  - [-1, 3, C2f, [128, True]]   # 2
  - [-1, 1, Conv, [256, 3, 2]]  # 3-P3/8
  - [-1, 6, C2f, [256, True]]   # 4
  - [-1, 1, Conv, [512, 3, 2]]  # 5-P4/16
  - [-1, 6, C2f, [512, True]]   # 6
  - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
  - [-1, 3, C2f, [1024, True]]  # 8
  - [-1, 1, SPPF, [1024, 5]]    # 9

head:
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']]  # 10
  - [[-1, 6], 1, Concat, [1]]                    # 11
  - [-1, 3, C2f, [512]]                          # 12
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']]  # 13
  - [[-1, 4], 1, Concat, [1]]                    # 14
  - [-1, 3, C2f, [256]]                          # 15 (P3/8-small)
  - [-1, 1, Conv, [256, 3, 2]]                   # 16
  - [[-1, 12], 1, Concat, [1]]                   # 17
  - [-1, 3, C2f, [512]]                          # 18 (P4/16-medium)
  - [-1, 1, Conv, [512, 3, 2]]                   # 19
  - [[-1, 9], 1, Concat, [1]]                    # 20
  - [-1, 3, C2f, [1024]]                         # 21 (P5/32-large)
  - [[15, 18, 21], 1, Detect, [nc]]              # 22

解读这个文件,记住三个字段:

  • 第一个数字:输入来自哪一层(-1 表示上一层)
  • 第二个数字:该模块重复几次
  • 第三个字段:模块类型(Conv / C2f / SPPF / Detect)

我的习惯:想改模型结构时,直接改 yaml 文件比改 Python 代码快得多。比如我想让 Backbone 更深,就在 C2f 那行把重复次数从 3 改成 4。想换检测头,直接替换 Detect 那行就行。

2.6 不同规模的模型对比

YOLOv8 提供了 n / s / m / l / x 五种规模,区别就在 scales 里的三个系数:

模型 depth(深度系数) width(宽度系数) 参数量(M) mAP@0.5:0.95 推理速度(T4)
YOLOv8n 0.33 0.25 3.2 37.3 1.2ms
YOLOv8s 0.33 0.50 11.2 44.9 1.6ms
YOLOv8m 0.67 0.75 25.9 50.2 2.5ms
YOLOv8l 1.00 1.00 43.7 52.9 3.8ms
YOLOv8x 1.00 1.25 68.2 53.9 5.5ms

避坑指南:别一上来就用 x 模型。我见过很多新手直接上最大模型,结果显存爆了、推理跑不动。先拿 n 或 s 跑通流程,确认数据没问题了,再换大模型提精度。

depth 控制 C2f 里 Bottleneck 的重复次数,width 控制每层卷积的通道数。这两个系数乘到基础值上,就得到了不同规模的模型。

嗯,到这里 YOLOv8 的结构你应该心里有数了。Backbone 提特征,Neck 做融合,Head 出结果——三个模块各司其职。下回你看到 yaml 文件,就知道每一行在干什么了。