第4章:YOLO系列与目标检测实战:YOLOv5/v8原理讲解,模型训练与部署到边缘设备
目标检测这块,YOLO系列绝对是绕不开的坎。我入行那会儿还是YOLOv3称霸,现在v5和v8已经成了工业界的主流选择。说实话,v5和v8虽然名字像,但骨子里差别不小。今天咱们就掰开揉碎,把原理讲透,再带你把模型训出来、部署到边缘设备上。
4.1 YOLOv5:经典中的经典
YOLOv5刚出来的时候,很多人质疑它是不是蹭热度。但用过的都知道,这玩意儿是真的稳。我个人习惯把它当作目标检测的“基准线”——新项目先拿v5跑一遍,心里就有底了。
4.1.1 网络结构:CSPDarknet + PANet
v5的主干网络用的是CSPDarknet。说白了,就是把残差块和CSP结构结合了一下。CSP(Cross Stage Partial)的核心思想是:把特征图分成两部分,一部分正常卷积,另一部分直接拼过来。这样做的好处是——计算量降了,梯度信息还能更好地流动。
核心组件一览:
- Focus层:把输入图切成4份,拼成通道数4倍的特征图。我刚开始觉得这操作有点多余,后来发现它确实能减少信息丢失。
- C3模块:v5里的核心构建块,3个卷积加CSP结构。比v3/v4的残差块更轻量。
- SPP层:空间金字塔池化,用不同尺寸的池化核提取多尺度特征。嗯,这里要注意,池化核大小要跟特征图尺寸匹配,不然会出问题。
颈部网络用的是PANet(路径聚合网络)。它跟FPN的区别在于:FPN是自顶向下传语义,PANet再加一条自底向上的路径传位置信息。说白了,就是让大目标和小目标都能被照顾到。
4.1.2 损失函数:CIoU + 分类损失
v5的边界框回归损失用的是CIoU Loss。它比原始的IoU多了两个惩罚项:中心点距离和宽高比一致性。我在项目中遇到过一个问题:用GIoU训练时,某些长条形目标的框总是偏大。换成CIoU后,这个问题就解决了。
# CIoU Loss 核心计算逻辑
def ciou_loss(pred, target):
# pred, target: [x, y, w, h]
iou = bbox_iou(pred, target)
center_dist = (pred[:, :2] - target[:, :2]).pow(2).sum(dim=1)
enclose_diag = (enclose_w.pow(2) + enclose_h.pow(2))
v = (4 / (torch.pi ** 2)) * (torch.atan(target_w/target_h) - torch.atan(pred_w/pred_h)).pow(2)
alpha = v / (1 - iou + v)
return 1 - iou + center_dist / enclose_diag + alpha * v
4.2 YOLOv8:v5的全面进化
v8是Ultralytics在2023年推出的。说实话,第一次看到v8的架构图,我第一反应是:“这不就是v5改改吗?”但仔细研究后发现,改动的地方都很关键。
4.2.1 核心改进点
| 改进项 | v5的做法 | v8的做法 | 我的评价 |
|---|---|---|---|
| 主干网络 | CSPDarknet | C2f模块(更密集的跨层连接) | 梯度流动更顺畅了 |
| 检测头 | 耦合头(分类+回归一起) | 解耦头(分类和回归分开) | 收敛更快,精度更高 |
| Anchor策略 | 预设Anchor | Anchor-Free | 省了调Anchor的麻烦 |
| 损失函数 | CIoU + BCE | DFL + VFL | 对小目标更友好 |
你想想看,v8把检测头拆成两个独立的分支,分类和回归各干各的。这样做的好处是——两个任务的优化目标不同,硬绑在一起反而互相干扰。我曾经在v5上试过解耦头,精度确实涨了1-2个点。
4.2.2 DFL损失:让框更准
DFL(Distribution Focal Loss)是v8的一个亮点。它把边界框的回归问题变成了一个分布估计问题。说白了,就是不再直接预测框的位置,而是预测框位置的概率分布。
我的经验:DFL对遮挡场景特别有效。有一次做行人检测,人挤人的情况下,v5的框经常偏移,v8的框就稳得多。代价是推理时多了几步计算,但边缘设备上也能接受。
4.3 模型训练:从数据到权重
训练这部分,我踩过的坑比走过的路还多。下面直接给出一套我验证过的流程。
4.3.1 数据准备
数据集格式推荐用YOLO格式:每张图片对应一个txt文件,每行是“类别 x_center y_center width height”,坐标归一化到0-1。
# 数据集目录结构
dataset/
├── images/
│ ├── train/
│ └── val/
├── labels/
│ ├── train/
│ └── val/
└── data.yaml # 配置文件
data.yaml的内容很简单:
train: ./images/train
val: ./images/val
nc: 2 # 类别数
names: ['car', 'pedestrian']
4.3.2 训练配置
我个人习惯用以下配置作为起点:
- 输入尺寸:640x640(边缘设备建议用320或416)
- Batch size:能多大就多大,但边缘设备训练时建议8-16
- 学习率:0.01,配合余弦退火调度
- Epochs:300,但早停法(patience=50)
- 数据增强:Mosaic + MixUp + HSV抖动
避坑指南:我曾经在训练时把Mosaic增强开到1.0,结果模型对小目标完全失效。后来发现Mosaic比例太高会让小目标被切碎。建议Mosaic概率设为0.5-0.7。
4.3.3 训练命令
# YOLOv5
python train.py --data data.yaml --weights yolov5s.pt --batch-size 16 --epochs 300
# YOLOv8
yolo train model=yolov8s.pt data=data.yaml batch=16 epochs=300
训练过程中,重点关注三个指标:mAP@0.5、mAP@0.5:0.95、以及loss曲线。如果loss降不下去,先检查数据标注有没有问题。我遇到过好几次,训练半天loss不降,最后发现是标注框的坐标反了。
4.4 部署到边缘设备
边缘设备上跑模型,说白了就是“戴着镣铐跳舞”。算力有限,内存有限,还得保证实时性。
4.4.1 模型转换
最常用的部署格式是ONNX和TensorRT。以TensorRT为例:
# 导出ONNX
python export.py --weights best.pt --include onnx
# 转TensorRT(需要NVIDIA显卡)
trtexec --onnx=best.onnx --saveEngine=best.trt --fp16
这里要注意,FP16精度下,某些模型精度会掉1-2个点。如果任务对精度要求极高,可以考虑INT8量化,但需要校准数据集。
4.4.2 边缘设备选型
| 设备 | 算力 | 功耗 | 适合场景 |
|---|---|---|---|
| Jetson Nano | 472 GFLOPS | 10W | 入门级,适合v5n/v8n |
| Jetson Orin NX | 70 TOPS | 15W | 主流选择,v5s/v8s流畅 |
| RK3588 | 6 TOPS (NPU) | 8W | 国产方案,性价比高 |
我个人推荐Jetson Orin NX,性价比最高。如果预算有限,RK3588的NPU跑YOLOv5s也能到30fps以上。
4.4.3 推理优化技巧
- 输入尺寸裁剪:640x640降到416x416,速度能翻倍,精度只掉1-2个点
- Batch推理:边缘设备上batch=1最快,别想着用大batch
- 异步处理:用双缓冲机制,一个线程采集图像,一个线程推理
- 后处理优化:NMS用CUDA实现,别用CPU跑
实战经验:我在Jetson Nano上部署YOLOv5n,原始模型跑不到10fps。做了三件事后跑到了25fps:输入尺寸降到320、用FP16推理、NMS换成TensorRT内置的。嗯,这三板斧基本够用。
4.5 本章知识体系
下面这张图把YOLO系列的核心逻辑串起来了。从数据到训练,再到部署,每一步都有坑,但也都有解法。
YOLOv5和v8的选择,说白了就是看你的场景。如果追求稳定、社区资源多,选v5准没错。如果追求极致精度、愿意折腾新特性,v8值得一试。我个人两个都在用——老项目用v5维护,新项目直接上v8。
最后说一句:模型训出来只是第一步,部署到边缘设备上跑起来才是真本事。别怕踩坑,每个坑都是经验。我当年在Jetson上折腾了整整一周才把第一个模型跑通,现在回想起来,那些坑反而让我对部署流程理解得更深。