2. YOLOv5基础架构:Backbone、Neck、Head三大组件详解

YOLOv5 的结构,说白了就是三个大模块串在一起:Backbone(骨干网络)负责提特征,Neck(颈部)负责融合特征,Head(头部)负责输出预测。我刚开始接触时也觉得这分类有点抽象,但拆开看其实很清晰。

一句话总结:Backbone 把图片变成特征图,Neck 把不同尺度的特征图揉在一起,Head 从揉好的特征图里读出框和类别。

2.1 三大组件总览

先看一张整体流程图,我习惯把它叫做“YOLOv5 的流水线”:

YOLOv5 三大组件流水线 Backbone CSPDarknet Focus / Conv CSP 模块 × N SPP 模块 输出: 3 层特征图 Neck FPN + PAN 上采样 + 下采样 CSP 模块融合 跨尺度连接 输出: 3 层融合特征 Head Detect 模块 1×1 卷积 3 个检测头 输出: 框 + 类别 大/中/小目标 输入 → Backbone(3层特征) → Neck(特征融合) → Head(3个检测头) → 最终输出

你看,数据从左到右流一遍,就完成了从图片到检测结果的转换。每个组件都有自己的活,谁也替不了谁。

2.2 Backbone:CSPDarknet 结构

Backbone 用的是 CSPDarknet,这是 YOLOv4 就开始用的结构,v5 做了些优化。核心思想就两个:跨阶段局部连接(CSP)和残差学习。

2.2.1 CSP 模块

CSP 的全称是 Cross Stage Partial,说白了就是把特征图分成两路:一路正常走卷积,另一路直接拼过来。这样做的好处很明显——减少计算量,同时梯度能更直接地回传。

我的经验:我在做嵌入式部署时,CSP 结构帮了大忙。同样的精度下,它的计算量比普通残差网络少 20% 左右,这对边缘设备来说太重要了。

一个标准的 CSP 模块内部长这样:

class CSP(nn.Module):
    def __init__(self, c1, c2, n=1, shortcut=True, e=0.5):
        super().__init__()
        c_ = int(c2 * e)  # 隐藏通道数
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(2 * c_, c2, 1)
        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut) for _ in range(n)))

    def forward(self, x):
        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))

注意看 forward 里的 cat 操作——这就是 CSP 的精髓:一路经过多个 Bottleneck,另一路直接 shortcut,最后拼起来再卷积融合。

2.2.2 SPP 模块

在 Backbone 的最后,YOLOv5 加了一个 SPP(Spatial Pyramid Pooling)模块。它的作用是用不同大小的池化核提取多尺度信息,让网络对目标尺寸变化更鲁棒。

池化核大小 感受野 作用
5 × 5 局部 捕捉小目标细节
9 × 9 中等 平衡局部和全局
13 × 13 全局 捕捉大目标上下文
注意:SPP 模块只加在 Backbone 最后一层,不是每层都加。加多了反而会丢失细节信息,我刚开始调参时就犯过这个错。

2.3 Focus 层的作用与替代

Focus 层是 YOLOv5 早期版本的一个特色设计。它的原理很简单:把输入图片每隔一个像素取一个值,拼成 4 张下采样后的特征图,再拼在一起。

举个例子,输入是 640×640×3 的图片,经过 Focus 层后变成 320×320×12。相当于把空间信息压缩到了通道维度上。

# Focus 层的核心操作
def forward(self, x):
    # x shape: [B, 3, 640, 640]
    x = torch.cat([
        x[..., 0::2, 0::2],  # 左上
        x[..., 1::2, 0::2],  # 左下
        x[..., 0::2, 1::2],  # 右上
        x[..., 1::2, 1::2],  # 右下
    ], dim=1)
    # x shape: [B, 12, 320, 320]
    return self.conv(x)

但后来大家发现,Focus 层其实可以用一个 6×6 的步长为 2 的卷积来替代。效果差不多,但实现更简洁。YOLOv5 v6.0 之后,官方默认就把 Focus 换成了 Conv 层。

我的建议:如果你是从头训练,直接用 Conv 替代 Focus 就行。但如果你在加载旧版预训练权重,记得检查一下模型定义是否匹配。

2.4 Neck:FPN + PAN 结构

Neck 部分用的是 FPN(特征金字塔) + PAN(路径聚合网络)的组合。FPN 从上往下传语义信息,PAN 从下往上传递位置信息,两者互补。

我画了一张图,帮你理解这个双向融合的过程:

Neck:FPN + PAN 双向融合 P3 (80×80) P4 (40×40) P5 (20×20) 上采样 上采样 N3 (80×80) N4 (40×40) N5 (20×20) 下采样 下采样 M3 (80×80) M4 (40×40) M5 (20×20) 小目标 中目标 大目标 Backbone 输出 FPN 融合 PAN 融合 输出到 Head

你看,FPN 把深层的语义信息往上采样,和浅层特征融合;PAN 又把浅层的位置信息往下采样,和深层特征融合。这样每一层都同时拥有了语义和位置信息。

2.5 Head:检测头设计

Head 部分相对简单。YOLOv5 有 3 个检测头,分别对应大、中、小目标。每个检测头就是一个 1×1 卷积,输出通道数 = (类别数 + 5) × 每个网格的 anchor 数。

这里的 5 代表:x, y, w, h, 置信度。加上类别数,就是每个 anchor 的预测向量长度。

检测头 特征图大小 负责目标 Anchor 尺寸
Head 1 80 × 80 小目标 小 anchor
Head 2 40 × 40 中目标 中 anchor
Head 3 20 × 20 大目标 大 anchor
避坑指南:我曾经在训练小目标数据集时,发现小目标检测效果很差。后来检查发现是 anchor 尺寸没调,默认的 anchor 对小目标太大了。记得用 k-means 重新聚类一下 anchor。

2.6 整体结构总结

最后,我用一个表格把 YOLOv5 的完整结构串起来:

组件 核心模块 输入 → 输出 关键参数
Backbone CSPDarknet + SPP 640×640×3 → 3 层特征图 depth_multiple, width_multiple
Neck FPN + PAN 3 层特征图 → 3 层融合特征 上采样方式, CSP 模块数
Head Detect 模块 3 层融合特征 → 预测结果 anchor 数, 类别数

嗯,到这里 YOLOv5 的基础架构就讲完了。你可能会问,为什么 v5 能成为工业界的“万金油”?说白了就是这三个组件配合得好——Backbone 够轻量,Neck 融合得充分,Head 简单高效。后面讲 v6 到 v10 时,你会发现很多改进都是在这三个组件上做文章。