MMyBlog
← 返回博客
9 分钟·3,219 ·26 次阅读

YOLOv5 行人检测实战:从训练到边缘部署

故事的起点:智能头盔项目

去年我加入了一个创新实验室的项目组,任务是做一款面向工地和矿区工人的智能安全头盔。头盔上集成了摄像头、GPS 模块和一个边缘计算单元(NVIDIA Jetson Nano),需要实现的核心功能是实时行人检测——当工人进入危险区域时自动预警。

当时导师给了两个方案:方案 A 是用百度或阿里的云端视觉 API,省事但每个请求都花钱,而且矿区信号不好延迟很高;方案 B 是自己在边缘设备上跑一个检测模型。

我选了方案 B。说实话,选的时候我心里也没底,毕竟当时连 YOLO 是什么都不知道。但我觉得这是难得的学习机会,就算最后效果不好,至少能弄清楚"AI 落地"到底是怎么回事。

学习路径

从零开始学目标检测,我走了一条比较"笨"的路线:

第 1-2 周:理解卷积神经网络基础
├── 卷积核、池化、激活函数这些基本概念
├── 在 MNIST 和 CIFAR-10 上跑了几个小实验
└── 把李沐的《动手学深度学习》目标检测章节过了一遍

第 3-4 周:深入 YOLO 系列
├── YOLOv1→v5 的演进历史,每代改了什么
├── 理解了 Anchor Box、NMS(非极大值抑制)的原理
└── 在 COCO 数据集上跑了预训练模型

第 5-6 周:准备自己的数据集
├── 爬取 + 拍摄约 5000 张工地/街道场景图片
├── LabelImg 手动标注 1500 张(标注格式 YOLO txt)
└── 数据增强扩充剩余 3500 张(翻转/裁剪/亮度/噪声)

第 7-8 周:训练 + 调优
├── 用 yolov5s.pt 预训练权重迁移学习
├── 100 epochs,batch size 16,img size 640×640
└── 调参:学习率规划、Mosaic + MixUp 数据增强

第 9-10 周:模型部署
├── 剪枝 → 量化 → 导出 TensorRT 引擎
├── 在 Jetson Nano 上跑推理,25 FPS ✓
└── 集成到头盔的预警系统中

为什么选 YOLOv5

在当时(2024 年底),YOLOv5 是工业落地的首选。不是因为精度最高(YOLOv8 更高),而是因为生态最成熟。导出 ONNX、转 TensorRT、部署到边缘设备,每一步都有大量的社区教程和踩坑经验可以参考。对于新手来说,这一点比精度重要得多。

YOLOv5 提供四个版本:n、s、m、l、x。我选了 s 版本,原因是:精度比 n 高不少(mAP 高约 3-5 个点),但推理速度在 Jetson Nano 上仍然能达到 25 FPS 以上——刚好满足实时检测的需求。m/l/x 版本在这些小设备上跑不出实时效果。

数据标注:最耗时的一步

预处理这块花了最多时间。原始图片来自多个渠道——开源数据集、自己拍摄的工地照片、网上爬的街道图片。尺寸不一、质量参差不齐,标签格式也不统一。我先写了一个 Python 脚本把所有图片统一缩放到 640×640,剔除了模糊严重的图片,又对亮度极端的图片做了直方图均衡化。

然后开始标注——每个人标注成一个 bounding box,格式是 YOLO 要求的归一化坐标:

# class x_center y_center width height (全部归一化到 0-1)
0 0.523 0.418 0.082 0.215 # 行人 A
0 0.712 0.556 0.095 0.238 # 行人 B
0 0.315 0.672 0.078 0.196 # 行人 C

这个步骤花了我整整两周。每天对着屏幕画框,眼睛都快瞎了。但后来我发现这是最关键的一步——数据质量直接决定模型上限。有几张我随便标的图,训练时 loss 一直下不来,排查了很久才发现是标注错了。

标注完 1500 张后,用 Albumentations 库做了数据增强扩充到 5000 张来增加样本多样性。

训练过程中的关键决策

训练脚本很简单,关键参数如下:

python train.py \
--data helmet.yaml \
--weights yolov5s.pt \
--epochs 100 \
--batch-size 16 \
--img-size 640 \
--hyp data/hyps/hyp.scratch-low.yaml

学习率从 0.01 开始,用余弦退火(Cosine Annealing)逐步衰减。衰减到最后几个 epoch,学习率降到 1e-5 以下,模型基本稳定收敛。优化器选了带动量的 SGD(momentum=0.937),这种配置在 YOLO 社区是被反复验证过的"确定性配方"。

数据增强方面,训练时开启了 Mosaic(把四张图拼成一张训练)和 MixUp(把两张图按比例混合)。这两种增强对提升小目标检测效果很明显,但也带来了一个问题:训练时的 loss 波动非常大。有好几个 epoch,loss 突然飙到正常值的两三倍。后来查了社区讨论才知道,Mosaic 在训练后期(最后 20 个 epoch 左右)应该关闭,否则模型无法稳定收敛到最优解。

模型压缩与部署

训练完成后,模型文件大小 14MB。这个尺寸放到云端服务器上完全没问题,但放到 Jetson Nano 这种边缘设备上就比较紧张了。

第一步是通道剪枝。YOLOv5s 的很多卷积通道在训练后权重接近零,对检测几乎没有贡献。我用了一套 prune 脚本,把对 mAP 影响小于 0.5% 的通道全部砍掉。剪完模型缩小到 7MB。

第二步是量化。PyTorch 的默认精度是 FP32(32 位浮点数),而 Jetson Nano 对 INT8(8 位整数)的计算有硬件加速。通过 TensorRT 导出时做 INT8 量化,推理速度从 15 FPS 提升到 25 FPS。

第三步是引擎部署。在 Jetson Nano 上安装 TensorRT 和 PyCuda,把量化后的模型导出为 .engine 文件,用 C++ 调用 TensorRT C++ API 做推理。这一套流程走通之后,整体延迟控制在 40ms 以内,画面流畅无感知延迟。

最终效果

测试集上 mAP@0.5 达到了 82.3%。虽然离 SOTA 还有距离,但在特定场景(工地、街道、室内走廊)下,检测效果完全满足项目需求。最重要的是,整个方案在 3000 元级别的边缘设备上跑出了实时效果,不需要昂贵的 GPU 服务器。

我的收获

做这个项目是我第一次把"算法"跑出 Jupyter Notebook,变成真正可用的系统。最大的感受是:工程化的工作量远超调参。从数据集清洗到模型量化,从 C++ 推理封装到系统集成测试,每一步都是坑。但正是这些坑,让我从"只会调包"变成了"知道怎么把模型推到生产环境"。

检测效果展示


如果你也在做类似的目标检测项目,建议先从 YOLOv5s 开始。不要一上来就追最新的 YOLOv8/v9——稳定性比先进性重要,尤其是在工业场景里。

文章链接: