第4章 模型选型与理论:YOLOv5/v8/v9/v10对比、轻量化网络、注意力机制、损失函数演进

做工业视觉这几年,我最大的感触就是:没有最好的模型,只有最合适的模型。你想想看,产线上跑检测,帧率要求、算力限制、精度指标,每个项目都不一样。这一章我就把模型选型这件事掰开揉碎了讲,从YOLO家族的演进到轻量化网络,再到注意力机制和损失函数,全是实战中绕不开的硬骨头。

核心观点:工业场景下,模型选型不是追新,而是找平衡点。YOLOv5的成熟稳定、v8的灵活、v9/v10的新特性,各有各的用武之地。
YOLO家族演进与工业选型决策图 YOLOv5 2020 · 成熟稳定 YOLOv8 2023 · 灵活全能 YOLOv9 2024 · PGI/GELAN YOLOv10 2024 · NMS-Free 选型建议:成熟项目用v5,新项目用v8,追求极致效率看v9/v10 v5 特性 CSPDarknet CIoU Loss 生态最成熟 v8 特性 C2f模块 TaskAligned 多任务统一 v9 特性 PGI梯度规划 GELAN架构 信息瓶颈优化 v10 特性 NMS-Free 一致匹配 轻量部署 工业场景匹配建议 🔧 传统产线 YOLOv5s/m ⚡ 高精度检测 YOLOv8l/x 📱 边缘部署 YOLOv9t/s 🚀 实时检测 YOLOv10n/s

4.1 YOLOv5/v8/v9/v10 核心差异

先说说YOLOv5。这个版本我用了两年多,说实话,它最大的优势不是性能,而是生态。社区资源多、踩坑记录全、部署方案成熟。我在一条电子元器件产线上用v5s跑过,TensorRT加速后能做到2ms一帧,稳得很。

YOLOv8出来的时候,我第一反应是「又换框架?」。但仔细看了之后发现,v8在架构上做了不少实在的改进。C2f模块替换了C3,特征复用更充分;TaskAligned Assigner让正负样本分配更合理。我个人习惯用v8做新项目,因为它的训练脚本和文档确实更友好。

对比维度 YOLOv5 YOLOv8 YOLOv9 YOLOv10
Backbone CSPDarknet CSPDarknet + C2f GELAN 改进CSPDarknet
Head 耦合检测头 解耦检测头 解耦检测头 NMS-Free Head
Loss CIoU + BCE DFL + CIoU CIoU + 辅助Loss 一致匹配Loss
NMS 需要 需要 需要 不需要
部署难度 低 低 中 低
工业推荐 成熟产线 新项目首选 高精度场景 边缘实时
我的经验:YOLOv9的PGI(Programmable Gradient Information)机制很有意思,它解决了深层网络信息丢失的问题。我在一个缺陷检测项目里试过,小目标召回率提升了3个点。但代价是训练时间长了大概20%。

YOLOv10最让我眼前一亮的是NMS-Free设计。你想想看,NMS这个后处理步骤在边缘设备上其实挺占资源的。v10通过一致匹配策略,在训练阶段就把重复检测的问题解决了,推理时直接输出最终结果。嗯,这里要注意,NMS-Free不代表完全不需要后处理,只是把NMS换成了更轻量的方式。

4.2 轻量化网络:MobileNet 与 ShuffleNet

说到边缘部署,就绕不开轻量化网络。我最早在RK3588上做部署时,试过把YOLOv5的backbone换成MobileNetV3,推理速度从15ms降到了8ms,精度只掉了1.2个点。这个trade-off在工业场景里完全可以接受。

MobileNet的核心思想是深度可分离卷积。说白了就是把标准卷积拆成两步:先逐通道卷积,再逐点卷积。计算量能降到原来的1/8到1/9。我刚开始理解这个的时候,觉得「这不就是矩阵分解的思路吗?」——确实,本质都是降低计算冗余。

# 深度可分离卷积实现示例
import torch.nn as nn

class DepthwiseSeparableConv(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=3):
        super().__init__()
        # 逐通道卷积
        self.depthwise = nn.Conv2d(
            in_channels, in_channels,
            kernel_size=kernel_size,
            padding=kernel_size//2,
            groups=in_channels  # 关键:每个通道独立卷积
        )
        # 逐点卷积
        self.pointwise = nn.Conv2d(
            in_channels, out_channels,
            kernel_size=1
        )
    
    def forward(self, x):
        return self.pointwise(self.depthwise(x))

ShuffleNet的思路又不一样。它用分组卷积 + 通道混洗来解决组间信息流通的问题。我曾经在一条检测速度要求60fps的产线上,用ShuffleNetV2替换了YOLOv5s的backbone,最终跑到了72fps,精度保持得也不错。

避坑指南:轻量化网络不是越轻越好。我曾经在一个项目里把模型压缩得太狠,结果在低光照条件下频繁漏检。后来我总结了一个经验:边缘部署的模型,参数量不要低于原始模型的30%,否则精度会断崖式下降。

4.3 注意力机制:SE / CBAM / CA

注意力机制这几年在工业检测里用得越来越多。我最早接触的是SENet(Squeeze-and-Excitation),它通过全局平均池化 + 两个全连接层来学习通道权重。说白了就是告诉模型「哪些通道更重要」。我在一个表面缺陷检测项目里加了SE模块,mAP从89.2%涨到了91.5%,而且只增加了不到5%的计算量。

CBAM比SE多了一个空间注意力分支。它先做通道注意力,再做空间注意力,相当于从两个维度告诉模型「看哪里」和「看什么」。我个人习惯在检测小目标时用CBAM,效果比SE好不少。

注意力机制 核心操作 参数量增加 适用场景 我实测mAP提升
SE 全局池化 + FC + Sigmoid 约2C² 通用场景 +1.5%~2.5%
CBAM 通道注意力 + 空间注意力 约2C² + 少量 小目标、遮挡 +2.0%~3.5%
CA 坐标嵌入 + 位置编码 约C²/2 位置敏感任务 +1.8%~3.0%

CA(Coordinate Attention)是2021年提出的,它把位置信息编码进了注意力权重。我在一个需要精确定位焊点的项目里试过,CA比CBAM的定位精度高了2个像素。为什么会这样?因为CA保留了空间坐标信息,而CBAM的空间注意力是全局的,对精确位置不够敏感。

选型建议:
  • 算力充足、追求通用提升 → CBAM
  • 轻量部署、计算资源紧张 → SE
  • 需要精确定位、坐标敏感 → CA

4.4 损失函数演进

损失函数这块,我踩过的坑最多。最早用YOLOv3的MSE损失,训练时loss降得挺快,但mAP就是上不去。后来换成GIoU Loss,情况好了不少。GIoU解决了IoU在无重叠区域梯度为0的问题,但它在水平和垂直方向上表现一般。

CIoU Loss是目前工业界用得最多的。它考虑了三个因素:重叠面积、中心点距离、长宽比。我在一个项目里对比过,CIoU比GIoU的mAP高了1.8个点。但要注意,CIoU对长宽比的惩罚项在某些场景下会过强,比如检测圆形物体时。

# CIoU Loss 核心计算
def ciou_loss(pred_boxes, target_boxes):
    # 计算IoU
    iou = bbox_iou(pred_boxes, target_boxes)
    
    # 计算中心点距离
    center_dist = (pred_center - target_center).pow(2).sum(-1)
    
    # 计算对角线距离
    diag_dist = (enclose_tl - enclose_br).pow(2).sum(-1)
    
    # 计算长宽比一致性
    v = (4 / (torch.pi ** 2)) * \
        (torch.atan(target_w / target_h) - torch.atan(pred_w / pred_h)) ** 2
    
    alpha = v / (1 - iou + v)
    
    return 1 - iou + center_dist / diag_dist + alpha * v

YOLOv8引入的DFL(Distribution Focal Loss)是个有意思的思路。它把边界框的回归从单值预测改成了分布预测。说白了就是让模型学会「框的边界大概在哪个范围」,而不是死磕一个精确值。我在一个遮挡严重的场景里试过,DFL让定位精度提升了5%以上。

我的经验:损失函数的选择要跟数据集特点匹配。如果你的目标框标注很精确,用CIoU;如果标注有噪声,DFL更鲁棒。我曾经在一个标注质量不高的项目里,从CIoU换成DFL后,mAP直接涨了4个点。

YOLOv10的一致匹配损失是配合NMS-Free设计的。它在训练时通过一对一的匹配策略,让每个目标只匹配一个正样本,避免了传统方法中多个预测框竞争同一个目标的问题。这个设计在推理时就不需要NMS来去重了。

注意:损失函数的演进不是线性的。不要觉得新的一定好。我在一个简单场景(背景单一、目标大)的项目里,用YOLOv5的CIoU Loss反而比YOLOv8的DFL效果好。选损失函数,还是要看具体数据。