背景:为什么目标检测毕设总“翻车”
做毕设最怕“选题一时爽,调试火葬场”。目标检测方向尤其如此,实验室的学长学姐几乎踩过同样的坑:
- 数据:开源数据集类别太多,想只挑“猫狗”两类,结果标注格式从 COCO 转 YOLO 就折腾三天;自己拍的照片只有 300 张,标注完发现类别分布极不均衡,mAP 不到 50。
- 算力:学校只给 1 张 RTX 3060 12 G,Batch 设到 8 就爆显存;用 CPU 训练,YOLOv5s 一轮 300 epoch 跑了整整两星期,导师天天催进度。
- 部署:好不容易训练完,把 .pt 拖到 Windows 笔记本上,PyTorch 版本不一致直接报错;用 Flask 写了个接口,并发 3 次请求就 OOM;最后答辩现场演示,模型加载 30 秒,评委老师开始刷手机。
痛点总结一句话:不是模型不好,而是“跑通”到“跑稳”之间缺一张工程化路线图。下面这张脑图把我踩过的坑一次性摊开,先给你打个预防针。
技术选型型:YOLOv5、YOLOv8、RT-DETR 怎么挑
选模型就像点外卖,精度、速度、依赖复杂度三选二,不可能全占。我把三份官方论文 + 实测数据拼成一张对照表,直接说结论:
| 维度 | YOLOv5s | YOLOv8n | RT-DETR-R18 |
|---|---|---|---|
| 输入尺寸 | 640×640 | 640×640 | 640×640 |
| mAP@0.5 | 37.4 | 37.3 | 40.2 |
| GPU 延迟 | 6.5 ms | 6.2 ms | 9.1 ms |
| 参数量 | 7.2 M | 3.2 M | 20 M |
| 依赖 | torch+torchvision | ultralytics | torch+transformers |
| 训练易用度 | ★★★★☆ | ★★★★★ | ★★☆☆☆ |
| 量化友好度 | ★★★★☆ | ★★★★☆ | ★★☆☆☆ |
一句话建议:
- 只想快速出 demo、论文里写“实时检测”——选 YOLOv5s,社区最大,GitHub Issue 基本搜得到答案。
- 数据少于 2 k、想写“轻量化改进”——选 YOLOv8n,参数量砍半,ultralytics 库一条命令训练。
- 追求 SOTA 精度、实验室有 30 系显卡——RT-DETR,transformer 结构发论文容易,但部署需要 TensorRT 8.6+,对毕设时间线不友好。
我自己的毕设场景是“电动车头盔检测”,数据 1.4 k 张,边缘盒子 Jetson Nano,最终折中选了 YOLOv8n,后续代码均以它为例,换成 v5 只需改 yaml。
核心实现:从 raw 图片到 TensorRT 引擎
1. 数据预处理:统一脚本一次到位
把标注精灵导出的 json 转 YOLO 格式,网上 90 % 脚本只给转换函数,我直接写了一个“一条龙”命令行工具,自动划分训练集/验证集、生成 yaml、统计类别分布,省得每次手动改路径。
python tools/coco2yolo.py \ --json datasets/annotations.json \ --out datasets/yolo \ --split 0.9关键注意:类别 id 必须从小到大连续,否则 ultralytics 会默默把缺失 id 当背景,mAP 骤降 5 个点。
2. 训练脚本:Clean Code + 可复现
训练文件train.py只保留最常用参数,其余全写进configs/yolov8n_helmet.yaml,保证“脚本即文档”。
from ultralytics import YOLO if __name__ == '__main__': model = YOLO('yolov8n.yaml') # 结构 model.train(data='configs/yolov8n_helmet.yaml', epochs=150, imgsz=640, batch=32, device='0', workers=4, seed=42, # 复现 patience=20) # 早停训练完在runs/detect/exp/weights/best.pt直接拿到最佳 ckpt。
3. ONNX 导出:动态 batch 留后路
model = YOLO('best.pt') model.export(format='onnx', imgsz=640, dynamic=False, # Jetson 静态尺寸更快 simplify=True)导出后记得用onnxruntime推理一次,验证输出节点是否为output0,部分版本会多出一个Concat_40,TensorRT 会解析失败。
4. TensorRT 加速:INT8 量化
TensorRT 7.x 之后支持 post-training quantization,只需 100 张校准图。
trtexec --onnx=best.onnx \ --saveEngine=best_int8.trt \ --int8 --calib=calib \ --memPoolSize=workspace:1024实测 INT8 掉点 0.7 %,延迟从 9 ms 降到 4.2 ms,论文里写“精度几乎无损”完全 hold 住。
部署架构:Flask + OpenCV 轻量服务
毕设答辩现场不可能给评委看命令行,一个带框图的 Web 页面最直观。我整了一套“最小可运行”架构,单文件启动,支持上传图片/视频、实时摄像头拉流。
核心设计三点:
- 请求解耦:Flask 只负责收图,把 numpy array 丢进队列,后台 Worker 用 TensorRT Python API 异步推理,防止前端阻塞。
- 冷启动优化:trt 引擎在 Worker 进程里一次性加载,加
--preload参数预编译,内存常驻留 1.2 G,首次请求 300 ms,后续 40 ms。 - 资源守护:用
psutil监控 GPU 内存,> 85 % 自动重启 Worker,防止并发打爆。
关键片段:
# app.py @app.route('/infer', methods=['POST']) def infer(): file = request.files['image'] img = cv2.imdecode(np.frombuffer(file.read(), np.uint8), 1) q.put(img) out = result_q.get(timeout=5) # 等待推理 return Response(cv2.imencode('.jpg', out)[1].tobytes(), mimetype='image/jpeg')性能与安全:别让模型“背锅”
1. 量化掉点可控
INT8 校准图必须覆盖真实场景,我一开始用 COCO 随机图,结果头盔类 mAP 掉 3 个点;后来换成自己数据 100 张,掉点降到 0.7 %。结论:校准分布 ≈ 推理分布,别偷懒。
2. 输入校验防注入
检测接口直接对外,务必做三件套:
- 文件头校验:只接受
image/jpeg、image/pngmagic number; - 尺寸限制:单图长宽 ≤ 2000 px,防止 100 M 大图打爆内存;
- 内容清洗:OpenCV 读图失败直接抛 400,不往后传。
def valid_image(stream): header = stream.read(512) stream.seek(0) return header.startswith(b'\xff\xd8') or header.startswith(b'\x89PNG')生产避坑指南:学长踩过的坑,你别再踩
- 标签格式:YOLO 要求
.txt与.jpg同名且同目录,Windows 不区分大小写,Linux 区分,跨系统打包 zip 容易漏文件。 - GPU 内存溢出:ultralytics 默认缓存 10 张图,数据增强大时 12 G 显存打满,设
workers=2立竿见影。 - 版本不一致:.pt 文件里自带
git_tag,训练完立即git commit,部署容器镜像用相同 tag,避免“本地能跑服务器报错”。 - 模型热更新:生产环境用软链指引擎,更新时先推新文件,再原子替换软链,防止服务中途读半文件。
- 论文配图:画 PR 曲线用 cocoapi 官方脚本,别手动调 matplotlib,评审一眼看出坐标 0.9 处毛刺。
结尾:把代码跑起来,再谈“泛化”
整套流程我已经放到 GitHub,目录结构、依赖版本、Dockerfile 全对齐,克隆下来make run就能在 2080 Ti 上复现 42 mAP。毕设不是发顶会,先让模型“稳”再谈“新”。等你把 INT8 引擎压到 4 ms、Flask 接口并发不崩,再去思考数据增强、伪标签、域适应这些泛化大招,评委才会相信你不只是“调包”。
下一步,不妨测测不同光照、不同角度的漂移,把结果写进“未来工作”——那就是你研究生阶段的故事了。祝你答辩顺利,代码不崩。