使用YOLOv5进行图像检测与训练指南
在智能摄像头、工业质检和自动驾驶系统中,实时目标检测早已不再是“能不能做”的问题,而是“如何做得又快又准”的工程挑战。面对海量视觉数据,开发者需要一个既能快速迭代又能稳定部署的解决方案——YOLOv5 正是当前最成熟的选择之一。
它不是最复杂的模型,但却是落地最快的那个。从数据准备到模型导出,YOLOv5 提供了一套端到端的工具链,让工程师可以把精力集中在业务逻辑上,而不是底层实现细节里。下面我们就以一次完整的自定义目标检测项目为线索,走一遍从原始标注到推理部署的全流程。
数据组织:结构清晰才能少踩坑
任何训练流程的第一步,都是把数据理清楚。YOLOv5 对输入路径有明确要求,建议统一使用如下目录结构:
dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── annotations/ # 原始XML或JSON标注所有图片按集合划分放入images/子目录;原始.xml文件集中存放在annotations/中;最终转换后的 YOLO 格式标签(.txt)输出至对应labels/目录。
这里的关键是使用相对路径。一旦硬编码绝对路径,项目迁移时就会出错。保持结构一致性和可移植性,比写几个脚本重要得多。
自动拆分数据集:别再手动复制粘贴了
很多人还在用“选中一批图拖进train文件夹”这种方式?太容易出错了。更可靠的做法是写个简单的 Python 脚本来自动完成拆分。
import os import random from shutil import copyfile image_dir = 'dataset/images' output_dir = 'dataset' train_ratio = 0.8 val_ratio = 0.1 test_ratio = 0.1 exts = ['.jpg', '.jpeg', '.png', '.bmp'] files = [f for f in os.listdir(image_dir) if os.path.splitext(f)[1].lower() in exts] random.shuffle(files) total = len(files) train_split = int(total * train_ratio) val_split = int(total * (train_ratio + val_ratio)) train_files = files[:train_split] val_files = files[train_split:val_split] test_files = files[val_split:] for name, subset in [('train', train_files), ('val', val_files), ('test', test_files)]: with open(f'{output_dir}/{name}.txt', 'w') as f: for file in subset: basename = os.path.splitext(file)[0] f.write(basename + '\n') copyfile(f'{image_dir}/{file}', f'dataset/images/{name}/{file}')运行后不仅生成了train.txt等索引文件,还把图像自动归类到了各自的子目录下。这一步看似简单,实则避免了后续因路径不匹配导致的“找不到图片”类低级错误。
XML转YOLO格式:别小看这个转换脚本
如果你的数据来自 LabelImg 或 PASCAL VOC 标注工具,那大概率是.xml文件。而 YOLO 只认归一化坐标的.txt文件。中间必须做一次格式转换。
import xml.etree.ElementTree as ET import os classes = ['person', 'car', 'dog', 'bicycle'] def convert(size, box): dw = 1. / size[0] dh = 1. / size[1] x = (box[0] + box[1]) / 2.0 - 1 y = (box[2] + box[3]) / 2.0 - 1 w = box[1] - box[0] h = box[3] - box[2] x *= dw w *= dw y *= dh h *= dh return x, y, w, h def convert_annotation(image_id): in_file = open(f'dataset/annotations/{image_id}.xml', encoding='utf-8') out_file = open(f'dataset/labels/{image_id}.txt', 'w') tree = ET.parse(in_file) root = tree.getroot() size = root.find('size') w = int(size.find('width').text) h = int(size.find('height').text) for obj in root.iter('object'): difficult = obj.find('difficult').text cls = obj.find('name').text if cls not in classes or int(difficult) == 1: continue cls_id = classes.index(cls) xmlbox = obj.find('bndbox') b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text)) bb = convert((w, h), b) out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n') image_sets = ['train', 'val', 'test'] for image_set in image_sets: image_ids = open(f'dataset/{image_set}.txt').read().strip().split() list_file = open(f'dataset/{image_set}_list.txt', 'w') for image_id in image_ids: if image_id.strip(): convert_annotation(image_id.strip()) list_file.write(f'dataset/images/{image_set}/{image_id}.jpg\n') list_file.close()有个细节要注意:读取文本文件时一定要加.strip(),否则换行符可能导致拼接路径失败。这种小问题调试起来特别耗时间,提前处理好能省不少事。
配置YAML文件:告诉模型“你要学什么”
YOLOv5 通过一个.yaml文件来获取数据集信息。在data/下创建custom.yaml:
train: dataset/train_list.txt val: dataset/val_list.txt test: dataset/test_list.txt nc: 4 names: ['person', 'car', 'dog', 'bicycle']这里的train和val指向的是包含完整路径的.txt文件(比如dataset/images/train/xxx.jpg),不是单纯的文件名列表。这一点很容易搞错,尤其是自己生成索引文件的时候。
类别数量nc必须和names列表长度一致,否则会报索引越界。建议先确认一下你的标注是否真的只用了这几个类,有没有拼写差异(如"Dog"vs"dog")。
开始训练:用预训练权重起飞
一切就绪后,就可以启动训练了。推荐命令如下:
python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data data/custom.yaml \ --weights yolov5s.pt \ --cfg models/yolov5s.yaml \ --name yolov5_custom_train \ --project runs/train \ --exist-ok参数说明:
---img: 输入尺寸,640 是默认值,适合多数场景
---batch: 批次大小,根据显存调整(16 对应 12GB GPU)
---weights: 加载yolov5s.pt作为起点,显著加快收敛
---exist-ok: 允许覆盖已有实验结果,方便调试
首次训练建议用yolov5s这种小模型跑通全流程。等确认没有路径或格式问题后再切到m/l/x版本追求更高精度。
训练过程中会在runs/train/自动生成:
- 权重文件:best.pt(验证集 mAP 最高)、last.pt
- 可视化图表:损失曲线、PR 曲线、混淆矩阵
- 控制台输出:每轮的Box_mAP@0.5、Precision、Recall
重点关注best.pt的性能指标。如果 mAP 上不去,可能是数据质量、类别不平衡或增强策略不合适的问题。
推理测试:看看模型到底学会了啥
训练完成后,用detect.py跑个推理试试效果:
python detect.py \ --source dataset/images/test/ \ --weights runs/train/yolov5_custom_train/weights/best.pt \ --conf 0.4 \ --img-size 640 \ --name yolov5_detection \ --save-txt \ --save-conf结果会保存在runs/detect/yolov5_detection/,包括:
- 带边界框和标签的可视化图像
- 每张图对应的.txt文件,记录检测结果(类别、坐标、置信度)
--save-txt和--save-conf很实用,特别是在做定量分析或构建下游任务时。比如你可以把这些检测框提取出来,送给分类网络做二次判断。
目标裁剪:不只是画框,还能切出来用
有时候我们不仅要知道“这里有辆车”,还想把它单独抠出来分析。这时候就需要 ROI 裁剪功能。
可以在detect.py基础上加一段裁剪逻辑:
import cv2 from pathlib import Path def save_crops(pred, im, im0s, save_dir, names): save_dir = Path(save_dir) / 'crops' for i, det in enumerate(pred): p, im0 = Path(im[i]), im0s[i].copy() if len(det): for *xyxy, conf, cls in reversed(det): c = int(cls) label = f'{names[c]}_{conf:.2f}' crop_path = save_dir / label / p.name crop_path.parent.mkdir(parents=True, exist_ok=True) cv2.imwrite(str(crop_path), im0[int(xyxy[1]):int(xyxy[3]), int(xyxy[0]):int(xyxy[2])])调用 OpenCV 的切片操作即可完成裁剪。应用场景很多:
- 工业质检中对缺陷区域放大分析
- 行人重识别(Re-ID)前的行人框提取
- OCR 系统中的车牌字符预分割
这类功能虽然不在主干流程里,但在实际项目中往往是决定成败的关键模块。
数据增强调优:小样本也能训得好
YOLOv5 内置了强大的增强策略,尤其适合样本有限的情况。修改hyps/hyp.scratch-low.yaml可以精细控制:
mosaic: 1.0 mixup: 0.2 hsv_h: 0.015 hsv_s: 0.7 translate: 0.1 scale: 0.5其中 Mosaic 和 MixUp 是提升泛化能力的利器。Mosaic 把四张图拼成一张,让模型学会在复杂背景下识别目标;MixUp 则是对两张图做线性插值,增加样本多样性。
不过也要注意:过度增强可能引入噪声。例如旋转(degrees)在文本检测中要慎用,否则会影响方向判断。建议先关掉所有变换跑 baseline,再逐步开启增强观察效果变化。
模型导出:准备好上线了吗?
训练结束只是第一步,真正考验在于部署。YOLOv5 支持多种格式导出:
python export.py \ --weights runs/train/yolov5_custom_train/weights/best.pt \ --include onnx engine torchscript coreml tflite paddle常用选项:
-ONNX:跨平台通用,适合 Windows/Linux/macOS 上的推理引擎(如 ONNX Runtime)
-TensorRT (.engine):NVIDIA GPU 上性能最强,支持 FP16/INT8 加速
-TorchScript:C++ 集成,适用于嵌入式设备或高性能服务
-TFLite:安卓或移动端部署首选
生产环境强烈推荐TensorRT + FP16组合。在 Tesla T4 上,yolov5s可达 150+ FPS,延迟低于 7ms,完全满足实时视频流处理需求。
如何读懂评估指标?
训练结束后,这些指标你必须懂:
| 指标 | 含义 |
|---|---|
Box_mAP@0.5 | IoU 阈值为 0.5 时的平均精度,反映定位准确性 |
Box_mAP@0.5:0.95 | 多阈值下的综合表现,COCO 标准 |
Precision | 查准率,预测为正的样本中有多少是真的 |
Recall | 查全率,真实目标有多少被检出 |
理想状态是两者都高。但在某些场景下要有侧重:
- 安防监控:优先保 Recall,不能漏警
- 自动驾驶:Precision 更关键,误刹车风险大
- 工业质检:通常要求双高,尤其是微小缺陷检测
如果发现 Precision 高但 Recall 低,说明模型过于保守,可以适当降低置信度阈值(如从 0.4 → 0.2)。
写在最后
YOLOv5 成为工业级标准并非偶然。它的设计哲学很清晰:不追求极致创新,而是专注于工程可用性。从开箱即用的训练脚本,到一键导出多格式模型,再到详尽的日志可视化,每一个环节都在降低落地门槛。
对于大多数视觉项目来说,真正的瓶颈从来不是算法本身,而是“能不能两周内跑通 demo”。YOLOv5 正是在这一点上做到了极致。
当然也要提醒一点:始终锁定版本。建议克隆时指定 release tag,比如:
git clone -b v7.0 https://github.com/ultralytics/yolov5.git避免主干频繁更新带来的兼容性问题。毕竟,稳定性才是生产的底线。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考