4. 模型选型与理论
做工业视觉这几年,我最大的感触就是:没有最好的模型,只有最合适的模型。你想想看,产线上跑检测,帧率要求、算力限制、精度指标,每个项目都不一样。这一章我就把模型选型这件事掰开揉碎了讲,从YOLO家族的演进到轻量化网络,再到注意力机制和损失函数,全是实战中绕不开的硬骨头。
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 | 需要 | 需要 | 需要 | 不需要 |
| 部署难度 | 低 | 低 | 中 | 低 |
| 工业推荐 | 成熟产线 | 新项目首选 | 高精度场景 | 边缘实时 |
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,精度保持得也不错。
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%以上。
YOLOv10的一致匹配损失是配合NMS-Free设计的。它在训练时通过一对一的匹配策略,让每个目标只匹配一个正样本,避免了传统方法中多个预测框竞争同一个目标的问题。这个设计在推理时就不需要NMS来去重了。