这次我们来看两套非常典型的深度学习目标检测实战项目:一套用 YOLOv5 做细胞检测,另一套用 YOLOv4 做交通标志识别。两套项目都覆盖了数据集准备、模型训练、效果验证和部署上线的完整流程,特别适合正在学习 YOLO 系列算法、想自己完整跑通一个检测任务的读者。
先回答一个很多人关心的问题:这两套项目难不难?YOLOv5 和 YOLOv4 都属于成熟的开源目标检测框架,网上资料多、复现案例多、踩坑记录也多。相比从零手写网络结构,这两套项目的核心工作量其实集中在三块:数据集的获取与标注、训练参数调整、模型导出与接口部署。它们能帮你把“算法原理”和“工程落地”串起来,而不是停留在跑通一个 demo 的阶段。
本文会把两套项目的实操路径分开讲:先讲 YOLOv5 细胞检测的数据组织和训练命令,再讲 YOLOv4 交通标志识别的 Darknet 环境配置和训练流程,最后统一给出部署到 API 服务、摄像头实时检测和批量推理的方案。如果你想从零开始接触 YOLO 实战,这篇文章可以直接作为操作清单来用。
1. 核心能力速览
| 能力项 | 项目一:YOLOv5 细胞检测 | 项目二:YOLOv4 交通标志识别 |
|---|---|---|
| 任务类型 | 目标检测 | 目标检测 |
| 适用模型 | YOLOv5s / YOLOv5m 等 | YOLOv4 或 YOLOv4-tiny |
| 检测目标 | 细胞个体、细胞团块 | 限速、停止、人行横道等交通标志 |
| 数据集格式 | YOLO txt 格式 + data.yaml | VOC / YOLO txt 格式均可 |
| 训练框架 | PyTorch | Darknet |
| GPU 需求 | 建议 4GB 以上显存 | 建议 4GB 以上显存 |
| CPU 推理 | 可以运行,速度偏慢 | 可以运行,YOLOv4-tiny 更流畅 |
| 部署方式 | ONNX / TorchScript / API 服务 | Darknet 原生权重 / ONNX / API 服务 |
| 批量任务 | 支持,遍历目录推理 | 支持,遍历目录推理 |
| 适合读者 | 有 PyTorch 基础,关注精度 | 关注老硬件兼容性和速度 |
从能力速览可以看出,两套项目其实是一个互补关系:YOLOv5 代码更友好、训练生态更现代,适合做精细检测;YOLOv4 配合 Darknet 在 CPU 和低端显卡上更灵活,适合做资源受限场景的验证。实际工作中很多人会先跑通 YOLOv5,再回去对比 YOLOv4 的速度和部署差异。
2. 项目一:YOLOv5 细胞检测实战
2.1 任务定义与数据集准备
细胞检测的目标是在显微镜图像中定位每一个细胞,并给出矩形框。这个任务看起来简单,实际有两个难点:细胞尺寸小、细胞密度高,容易漏检;背景中有杂质和染色噪声,容易误检。YOLOv5 在这类中小目标场景下表现不错,主要因为它有 Mosaic 数据增强和多尺度训练机制,可以增加模型对小目标的敏感性。
数据集先解决“有没有”的问题,再解决“干不干净”的问题。建议按两种方式准备:
- 使用公开生物影像数据集,优先选择已经带有 COCO 或 YOLO 格式标注的版本,省去自己标注的时间。
- 使用实验室自有图像,自己用标注工具生成标注文件。
需要注意的是,细胞检测如果涉及医学研究或临床辅助,要确认数据来源授权,不能随意抓取未脱敏的病理图像。如果只是学习算法流程,公开的细胞数据集已经足够。
2.2 YOLOv5 数据集目录组织
YOLOv5 约定了一种固定的数据集目录结构。以细胞检测为例,目录可以这样组织:
datasets/cell_detection/ ├── images/ │ ├── train/ │ │ ├── cell_001.jpg │ │ ├── cell_002.jpg │ └── val/ │ ├── cell_100.jpg │ ├── cell_101.jpg └── labels/ ├── train/ │ ├── cell_001.txt │ ├── cell_002.txt └── val/ ├── cell_100.txt ├── cell_101.txtlabels 目录下每个 txt 文件与 images 下的图片一一对应,文件名相同,后缀不同。每个 txt 的每一行代表一个目标框,格式为:
class_id x_center y_center width height这里 x_center、y_center、width、height 都是归一化坐标,取值 0 到 1。比如一张 1000x1000 的图片中,一个细胞框左上角在 (200, 300),宽 100,高 80,那么 class_id 对应的归一化结果是:
0 0.25 0.34 0.10 0.082.3 数据标注与格式转换
如果自己标注,推荐使用 labelImg 或 Label Studio。labelImg 可以直接保存为 YOLO 格式,省去后续转换。Label Studio 支持团队协作,输出格式需要再转换一次。
一组常用的坐标转换逻辑如下:假设原始标注是 VOC 格式的 x_min、y_min、x_max、y_max,图片宽为 img_w,高为 img_h,则:
x_center = (x_min + x_max) / 2 / img_w y_center = (y_min + y_max) / 2 / img_h width = (x_max - x_min) / img_w height = (y_max - y_min) / img_h转换之后要注意检查边界值。如果归一化后坐标略小于 0 或略大于 1,可以做一个 clip 操作,避免训练时报错或边框异常。
2.4 修改 data.yaml 与模型配置
YOLOv5 训练前需要一个 data.yaml 文件,内容大致如下:
train: datasets/cell_detection/images/train val: datasets/cell_detection/images/val nc: 1 names: ['cell']这里 nc 是类别数,names 是类别名称。如果还想加“背景”“杂质”等负样本标注,要在标注阶段就把这些类别加进去,不能在训练时临时改名字。
模型方面,一般情况下先选 YOLOv5s,显存压力小,训练速度快,精度也够用于学习。如果检测效果不理想,再换 YOLOv5m 或调大输入图像尺寸。
2.5 训练命令与超参数
YOLOv5 训练命令是一个典型的一行式命令:
python train.py \ --data datasets/cell_detection/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --device 0几个关键参数:
--img 640:输入图像短边缩放到 640。细胞图像如果原始分辨率较大,可以尝试 960 或 1280,小目标识别通常会变好,但显存占用会明显上升。--batch 16:显存不够就降到 8 或 4,优先保证训练不爆显存。--epochs 100:数据集小时 100 轮足够,配合早停机制可以提前结束。--device 0:使用第一块 GPU。CPU 训练太慢,不太建议用大模型在 CPU 上长时间训练。
训练过程中会生成 runs/train/exp 目录,里面包括权重文件、TensorBoard 日志和每轮的验证结果图片。
2.6 训练结果评估与推理测试
训练完成后,先看 runs/train/exp/ 下的 results.csv 或 TensorBoard 曲线。重点观察 mAP@0.5 和 mAP@0.5:0.95 两个指标。mAP@0.5 达到 0.9 以上对细胞检测来说已经可以用;mAP@0.5:0.95 越高说明框的定位越准。
用训练好的权重做推理:
python detect.py \ --weights runs/train/exp/weights/best.pt \ --source datasets/cell_detection/images/val \ --conf 0.25 \ --iou 0.45 \ --save-txt--save-txt会输出检测结果的坐标文件,方便后续做批量统计,比如统计一张图里的细胞数量。
2.7 模型导出与部署
YOLOv5 自带导出脚本,可以导出 ONNX、TorchScript、TensorRT 等格式:
python export.py --weights runs/train/exp/weights/best.pt --include onnxONNX 模型可以接到 OpenCV DNN、ONNX Runtime 或 API 服务里,摆脱 PyTorch 运行环境的限制。TorchScript 则适合嵌入 PyTorch 环境中的服务。具体导出后的推理结果需要通过 ONNX Runtime 或 OpenCV 再次验证,防止某些算子导出后行为不一致。
3. 项目二:YOLOv4 交通标志识别实战
3.1 交通标志检测的技术要点
交通标志属于尺寸小、类别多、色彩信息明显的目标。与细胞检测相比,它的检测难点在野外环境:拍摄距离远导致标志占比小,逆光、雨雪、遮挡让特征不稳定。YOLOv4 用了 CSPDarknet53 骨干网络和 PANet 颈部结构,在中等分辨率输入下对这类目标有较强适应能力。
如果你在低算力设备上部署,比如树莓派或老款 CPU 主机,可以考虑 YOLOv4-tiny。它牺牲了一部分精度,但推理速度比完整版 YOLOv4 快很多。交通标志类别通常在 10 到 40 类之间,tiny 版本也有实际应用价值。
3.2 数据集准备与选择
交通标志识别有比较成熟的公开数据集,比如德国交通标志检测数据集 GTSDB、中国交通标志数据集 CCTSDB 等。使用公开数据集时注意两点:一是确认许可协议,是否允许商用和修改;二是关注标注格式,有的数据集是 VOC xml,有的是 txt,需要统一转换。
以 GTSDB 为例,标注包含 43 个类别。实际训练时你可以根据自己的任务重新聚合类别,比如只保留限速标志、禁令标志、警告标志三大类。类别越少,训练难度越低,但模型能区分的细节也会变少。
3.3 Darknet 环境准备
YOLOv4 常用 AlexeyAB 的 Darknet 分支训练。Linux 下编译最省心,Windows 下也可以用 CMake 编译,或者直接下载别人编译好的可用版本。编译前确保系统有 NVIDIA 驱动和 CUDA。没有 GPU 的环境也能编译仅 CPU 版本,但训练速度会非常慢。
Ubuntu 系统下一套简化编译流程:
git clone https://github.com/AlexeyAB/darknet.git cd darknet sed -i 's/GPU=0/GPU=1/' Makefile sed -i 's/CUDNN=0/CUDNN=1/' Makefile make -j4编译结束后运行./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights data/dog.jpg验证是否安装成功。
3.4 YOLOv4 训练配置文件
YOLOv4 的训练配置分为三类文件:数据配置文件、类别名称文件、网络结构文件。
数据配置文件 data/traffic.data 大致如下:
classes = 43 train = data/traffic/train.txt valid = data/traffic/val.txt names = data/traffic/traffic.names backup = backup/traffictrain.txt 和 val.txt 是图片路径列表,每行一张图片,Darknet 会在相同路径下寻找同名的 txt 标注文件。
traffic.names 每行一个类别名:
speed_limit_30 speed_limit_50 stop yield ...网络结构文件需要重点修改,因为 YOLOv4 的输出层通道数由类别数决定。找到 cfg/yolov4.cfg 中所有[convolutional]且filters=255的层,把它们改为filters=(classes + 5) * 3。同时把每个[yolo]层的 classes 改成实际类别数。如果 classes=43,则 filters 应该改成(43 + 5) * 3 = 144。
3.5 训练命令与监控
下载 YOLOv4 官方预训练卷积权重 conv.137,然后开始训练:
./darknet detector train data/traffic.data cfg/yolov4-traffic.cfg conv.137 -dont_show -map-map会在每个验证周期结束后计算 mAP,方便远程观察是否收敛。训练中每隔 100 轮会保存一个 weights 文件,存放到 backup 目录。
如果显存不够,可以把 cfg 文件里的 batch 从 64 改成 32 或 16,subdivisions 调高到 16 或 32,这样能显著降低单次前向的内存占用,代价是训练速度变慢。
3.6 测试与部署
训练完成后,用 backup 目录下最后的 weights 做测试:
./darknet detector test data/traffic.data cfg/yolov4-traffic.cfg backup/yolov4-traffic_final.weights data/test.jpgYOLOv4 的原生权重不能直接被 OpenCV DNN 使用,需要先转换成 ONNX 或 TensorRT。如果目标平台是 Jetson 系列设备,TensorRT 是更好的选择。对于普通 x86 服务器,转 ONNX 后用 ONNX Runtime 推理更通用。
4. YOLOv5 与 YOLOv4 技术对比
| 对比维度 | YOLOv5 | YOLOv4 |
|---|---|---|
| 代码生态 | PyTorch,结构清晰,上手快 | Darknet,C 语言,编译略繁琐 |
| 训练便捷度 | 自带增强、日志、导出脚本 | 需要手动配置文件名与路径 |
| 精度 | 中小目标场景有优势 | 中等目标、通用场景表现稳定 |
| 速度 | 与模型尺寸相关 | 完整版偏重,tiny 版轻量 |
| 部署兼容 | ONNX/TorchScript/TensorRT | darknet 权重转 ONNX/TensorRT 步骤略多 |
| 适合场景 | 快速原型、科研实验、中小目标检测 | 低算力设备、老显卡、嵌入式验证 |
如果只选一个项目先做,建议先做 YOLOv5 细胞检测,因为 PyTorch 生态对新手更友好,遇到问题更好搜资料。YOLOv4 交通标志识别可以放在第二个练手项目,主要用来理解 Darknet 训练流程和传统部署链路。
5. 模型部署与 API 服务
5.1 离线批量推理脚本
训练得到的模型不能只停留在命令行测试阶段。实际使用中更常见的需求是批量推理,比如一次性处理一个文件夹里的几百张细胞图像,输出每张图的检测结果。
下面给出一个基于 YOLOv5 官方 detect.py 的批量思路:
python detect.py \ --weights runs/train/exp/weights/best.pt \ --source datasets/cell_detection/images/val \ --project outputs/cell_result \ --save-txt \ --save-conf输出结果会保存到 outputs/cell_result/exp/ 目录,其中 labels 子目录保存每个目标的坐标和置信度。
对于 YOLOv4 的批量推理,可以写一个循环脚本逐个调用 Darknet 命令,也可以把 darknet 封装成 Python 调用。从工程效率上讲,尽快把模型转成 ONNX,然后用 ONNX Runtime 写推理脚本,比反复调用 darknet 子进程更可控。
5.2 FastAPI 部署接口
无论是细胞检测还是交通标志识别,最后都可以封装成一个 HTTP 接口。用 FastAPI 加 ONNX Runtime 可以快速搭出一个检测服务。下面是一个通用模板:
import io from fastapi import FastAPI, UploadFile import numpy as np import cv2 import onnxruntime as ort from PIL import Image app = FastAPI() session = ort.InferenceSession("best.onnx", providers=["CUDAExecutionProvider", "CPUExecutionProvider"]) def preprocess(image: np.ndarray, size: int = 640) -> np.ndarray: h, w = image.shape[:2] scale = min(size / h, size / w) new_w, new_h = int(w * scale), int(h * scale) resized = cv2.resize(image, (new_w, new_h)) canvas = np.full((size, size, 3), 114, dtype=np.uint8) canvas[:new_h, :new_w] = resized img = canvas[:, :, ::-1].transpose(2, 0, 1) img = np.ascontiguousarray(img, dtype=np.float32) img /= 255.0 return img[None, ...], scale @app.post("/detect") async def detect(file: UploadFile): data = await file.read() image = np.array(Image.open(io.BytesIO(data)).convert("RGB")) img, scale = preprocess(image) outputs = session.run(None, {session.get_inputs()[0].name: img}) # 这里需要根据模型输出格式解析 boxes、scores、classes return {"outputs_shape": [o.shape for o in outputs]} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)注意,ONNX 的输出解析因导出版本略有不同,上述代码只展示请求接收和预处理流程,实际解析逻辑要对照导出的输出节点来写。
调用接口时可以用 curl 测试:
curl -X POST http://127.0.0.1:8000/detect -F "file=@test.jpg"这种部署方式的最大好处是模型运行与业务代码解耦。前端、单片机、自动化脚本都只通过 HTTP 调用检测能力,不需要关心底层推理框架。
5.3 摄像头实时检测
实时检测相对复杂,重点不在模型推理,而在帧读取和显示。一个通用结构是:OpenCV 读取摄像头帧 → 缩放后送入模型 → 解析结果 → 在原图上绘制矩形框 → 显示或推流。
YOLOv5 的 detect.py 已经支持摄像头输入,传入--source 0即可调用默认摄像头。YOLOv4 可以运行./darknet detector demo命令。实际项目中要注意视频帧率是否稳定、推理耗时是否超过帧间隔、缓冲区是否会堆积,这几个指标比单帧精度更容易影响体验。
5.4 TensorRT 加速
如果你需要更低的推理延迟,TensorRT 是常见选择。具体做法是先把 PyTorch 或 Darknet 权重导出为 ONNX,再通过 trtexec 构建 TensorRT 引擎:
trtexec --onnx=best.onnx --saveEngine=best.engine --fp16这里没有给出精确的显存占用和耗时对比,因为不同显卡、不同分辨率差异很大。实际优化时应该在同一台设备上分别测 ONNX Runtime 和 TensorRT 的耗时,再判断是否值得增加这一层编译复杂度。
6. 资源占用与性能观察方法
6.1 观察工具
训练时用nvidia-smi查看 GPU 显存和利用率:
nvidia-smi -l 2-l 2表示每两秒刷新一次。还可以在 YOLOv5 训练时开启 TensorBoard 监控损失曲线和显存曲线。Darknet 训练没有自带图形界面,主要靠日志中的 avg loss 判断收敛情况。
6.2 影响显存的关键参数
- 输入分辨率:640 到 1280,显存占用近似平方增长。
- batch size:batch 越大,显存占用越高。
- 模型复杂度:YOLOv5s 比 YOLOv5x 显存占用低很多,YOLOv4-tiny 比 YOLOv4 低很多。
- 数据增强:Mosaic 和 mixup 会临时增加计算图,对显存也有影响。
6.3 降低显存占用的通用策略
训练阶段显存不足时,优先降低 batch size,其次是降低输入分辨率,最后再考虑换轻量模型。推理阶段可以用 FP16 量化或 TensorRT 压缩引擎,但要注意量化后精度下降是否在可接受范围内。
CPU 推理不是不能用,而是速度差距很大。如果只是接口演示或低并发场景,CPU 跑 YOLOv4-tiny 或 YOLOv5s 也可以接受。并发量上来以后,还是要回到 GPU 推理。
7. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 训练 loss 一直不下降 | 数据标注错误、学习率过高 | 检查标注框是否错位,查看 loss 曲线 | 重新标注有问题图片,降低学习率 |
| 检测不到目标 | 置信度阈值过高、训练类别不全 | 降低 conf,查看错误样本 | 增加训练数据,调整 conf 阈值 |
| 边框位置偏大或偏小 | 标注转格式时归一化出错 | 可视化标注结果 | 修正坐标转换脚本 |
| 显存不足 | batch、分辨率、模型过大 | nvidia-smi 查看占用 | 降低 batch、降低 img 尺寸、换轻量模型 |
| Darknet 编译失败 | CUDA 版本与显卡驱动不匹配 | 检查 nvcc -V 和 nvidia-smi | 安装匹配版本或改用 CMake 编译 |
| ONNX 导出后推理结果不一致 | 部分算子不支持或预处理不一致 | 用同一张图对比 PyTorch 与 ONNX 输出 | 修正预处理逻辑,升级导出库版本 |
| API 请求超时 | 模型推理较慢或并发过高 | 查看服务日志和请求耗时 | 增加超时时间,使用队列串行化任务 |
| 批量任务卡在中间图片 | 某张损坏图片导致推理崩溃 | 增加异常捕获,记录失败文件 | 跳过异常文件,单独重跑失败列表 |
8. 最佳实践、合规提醒与后续扩展
工程上,先按下面几条思路推进:
- 第一次练手时,数据集不用大,每类 200 到 500 张即可,先把流程跑通。
- 保存一套最小可运行配置,包括训练命令、数据路径、模型参数,方便复现。
- 模型文件、输入素材、输出结果分目录管理,不要全部堆在默认目录。
- 批量推理要加日志,记录哪些图片成功、哪些失败、耗时多少。
- 接口服务默认只监听 127.0.0.1,需要外部访问时再修改绑定地址,并考虑鉴权。
- 使用公开数据集前确认授权协议,特别是商用场景。
合规方面需要单独提醒:细胞检测相关模型如果用于医疗辅助,必须经过专业验证,文章中的实验只适合算法学习和科研验证,不能直接作为临床诊断依据;交通标志识别如果用于辅助驾驶研究,需要遵守相关测试法规,不能直接替代驾驶员判断。涉及隐私数据、人脸、车牌等敏感信息时,要先完成匿名化处理并确认数据来源合法。
后续扩展方向可以从三个角度切入:一是把 YOLOv5 细胞检测升级为实例分割,统计细胞面积和数量;二是给交通标志识别接入跟踪算法,比如 DeepSORT,在视频流中稳定标志 ID;三是把两个模型都做成 ONNX 或 TensorRT 服务,统一接到同一个检测平台。跑完两套项目后,你会发现数据集工程和部署链路才是花时间最多的地方,模型结构本身反而是最成熟的部分。
如果你正在找工作或做毕业设计,建议优先把 YOLOv5 细胞检测做完,再把 YOLOv4 交通标志作为对比实验加进去,形成“同一类任务、两个模型、两条部署链路”的完整项目经历。这套组合既能体现算法理解,也能体现工程能力,建议收藏备用。