简介:本资源为基于YOLOV5的VOC目标检测实战项目,面向具备一定深度学习基础、希望快速上手目标检测训练与推理的开发者与学习者。项目覆盖火车、船、人、电视、飞机等20个类别的检测任务,提供可直接运行的代码、数据集与训练好的权重参数,帮助读者跳过环境搭建与数据整理的繁琐环节,专注于模型训练与效果验证。压缩包共约2000个文件,以txt标签与说明、py训练推理脚本、yaml配置文件、sh运行脚本及md文档为主,整体约357MB。项目迭代100个epoch,在runs目录下保存了完整训练结果,最佳精度达到map0.5=0.62、map0.5:0.95=0.42,并在runs/detect中保留了网络推理训练集的可视化结果,便于直观评估检测效果。训练集含13700张图片及对应标签,测试集含3425张图片及标签,数据规模适中,适合作为课程设计、毕业设计或算法入门的实践案例。目前已有161人学习,配套脚本参数说明可进一步辅助理解训练与推理流程。
1. 20 分类 VOC 检测项目:380MB 里到底装了什么
如果你手头正好有一个 20 类的目标检测需求,比如识别火车、船、人、电视、飞机这些常见目标,又不想从零标注数据、从零调参,那这个 YOLOV5 + VOC 的实战包值得先拆开看看。它不是一个空壳 demo,而是把数据集、训练脚本、推理脚本、训练好的权重、100 个 epoch 的训练日志和推理结果一起打包,总大小 380MB。训练集 13700 张图配 13700 个 txt 标签,验证集 3425 张图配 3425 个标签,覆盖 20 个类别。训练到最好的精度是 map0.5=0.62、map0.5:0.95=0.42,runs/detect 下还存了网络对训练集的全部推理结果。换句话说,你拿到手就能跑推理、能接着训练、能对照日志看它当时是怎么收敛的。适合两类人:一类是想快速验证自己业务场景能不能套 VOC 格式跑通的工程师,另一类是想拿一份完整训练记录来对照自己调参过程的学习者。下面按「资源是什么 → 怎么用 → 坑在哪」的顺序拆。
2. 数据集结构与 VOC 转 YOLO 格式:13700 张图怎么对上 13700 个标签
2.1 目录布局与类别映射
这个项目的数据集不是原始 VOC 的 XML 结构,而是已经转成 YOLO 需要的 images + labels 配对形式。训练集在 datasets-images-train 下,13700 张图片和 13700 个 txt 标签文件一一对应;验证集在 datasets-images-val 下,3425 张图配 3425 个标签。20 个类别包括火车、船、人、电视、飞机等,类别顺序由数据配置文件里的 names 列表决定,这个顺序一旦定下就不能乱改,否则标签索引和模型输出会对不上。
常见做法是先把类别名按固定顺序写进一个 data.yaml,训练和推理都读同一份。下面是一个可直接抄的配置骨架:
# data.yaml path: ./datasets-images # 数据集根目录 train: train # 训练集相对路径 val: val # 验证集相对路径 nc: 20 # 类别数,必须和 names 长度一致 names: 0: train 1: boat 2: person 3: tv 4: airplane # ... 其余类别按你的实际顺序补齐到 20 个逻辑说明:path 是根目录,train/val 是相对 path 的子目录,YOLOV5 会去 path/train/images 找图、path/train/labels 找标签。nc 写错是最常见的翻车点,20 类写成 19 类,训练不报错但精度会莫名其妙掉。names 的索引必须和 txt 标签里每行第一个数字对应,标签行格式是class_id x_center y_center width height,坐标都是归一化到 0~1 的值。
2.2 从 VOC XML 转 YOLO txt 的脚本与边界坑
如果你手上是原始 VOC 的 Annotations XML,需要自己转。转换核心是把 VOC 的绝对坐标 xmin/ymin/xmax/ymax 转成归一化的中心点加宽高。下面这段脚本我一般会直接拿来改:
import os import xml.etree.ElementTree as ET # 类别到索引的映射,顺序必须和 data.yaml 的 names 完全一致 class_map = {"train": 0, "boat": 1, "person": 2, "tv": 3, "airplane": 4} # 其余 15 类按实际补齐 def convert(xml_dir, out_dir, img_w, img_h): for xml_file in os.listdir(xml_dir): if not xml_file.endswith(".xml"): continue tree = ET.parse(os.path.join(xml_dir, xml_file)) root = tree.getroot() lines = [] for obj in root.iter("object"): name = obj.find("name").text if name not in class_map: continue # 未登记类别直接跳过,避免索引错位 cls_id = class_map[name] bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 归一化并转中心点格式 x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") out_name = os.path.splitext(xml_file)[0] + ".txt" with open(os.path.join(out_dir, out_name), "w") as f: f.write("\n".join(lines)) convert("./Annotations", "./labels", 640, 640)参数说明:img_w、img_h 必须用每张图真实的宽高,不能统一写 640,否则非正方形图会坐标偏移。class_map 里没登记的类别我选择跳过而不是报错,因为 VOC 里常混着一些不需要的类别,直接跳过比中断转换更实用。转换完一定要抽查几张,用可视化脚本把框画回图上,确认框位置没偏。
提示:标签文件和图片文件必须同名,只差扩展名。13700 对里只要有一对名字对不上,训练时就会报「找不到标签」或者静默跳过,最后表现为某类精度异常低。
3. 训练脚本参数怎么设:100 epoch 跑出 map0.5=0.62 的配置拆解
3.1 关键超参与命令行写法
这个项目迭代了 100 个 epoch,最终 map0.5=0.62、map0.5:0.95=0.42。这个精度不算顶尖,但对于 20 类、1.7 万张图的规模,属于能直接用的水平。要复现或接着训,核心是 train.py 的参数。常见做法是先用预训练权重起步,再按自己的数据微调:
python train.py \ --data data.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --epochs 100 \ --batch-size 16 \ --img-size 640 \ --optimizer SGD \ --lr0 0.01 \ --lrf 0.01 \ --cache参数说明:--weights 指定预训练权重,从零训 20 类小数据集很容易不收敛,用预训练是省时间的关键。--batch-size 16 是 8G 显存左右的稳妥值,显存不够就降到 8,但 batch 太小 BN 层统计会不稳。--img-size 640 是 YOLOV5 的默认输入,改大能提小目标精度但显存和耗时都涨。--cache 把图片缓存到内存,1.7 万张图能明显加快每个 epoch,但内存小于 16G 就别开。--lr0 初始学习率和 --lrf 最终学习率比例决定余弦退火曲线,0.01 配 0.01 是官方推荐起点。
3.2 训练日志与 runs 目录怎么读
训练结果全在 runs 目录下,每次训练会新建一个 exp 文件夹,里面 results.csv 记录每个 epoch 的 loss 和 map,weights 下存 best.pt 和 last.pt。判断训练是否健康,重点看三个信号:box_loss 和 obj_loss 是否稳定下降、map0.5 是否在后期还在涨、验证集 loss 有没有先降后升。如果验证 loss 抬头而训练 loss 还在降,就是过拟合,该早停或加数据增强。
常见做法是训练完用 results.csv 画曲线,或者直接看 runs/train/exp 下的 results.png。这个项目 100 epoch 的曲线如果后期趋平,说明 100 轮基本够用;如果还在明显上升,可以接着加轮次。runs/detect 下存的是推理结果,可以直接看模型在训练集上的实际表现,注意这是训练集,精度会偏高,别拿它当泛化指标。
注意:best.pt 是按验证集 map 选的,不是按训练 loss。如果你换了验证集划分,best.pt 的含义就变了,别混用不同划分下的权重做对比。
4. 推理与部署:从 detect.py 到边缘设备量化
4.1 推理脚本参数与结果核对
推理入口是 detect.py,最常用的几条参数是权重、输入源、置信度和 NMS 阈值:
python detect.py \ --weights runs/train/exp/weights/best.pt \ --source datasets-images-val \ --img-size 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt参数说明:--source 可以是单张图、文件夹、视频或摄像头编号。--conf-thres 0.25 是置信度门槛,调低召回高但误检多,调高反之,业务上先看你要的是查全还是查准。--iou-thres 0.45 控制 NMS 合并重叠框的力度,密集场景可以适当调高到 0.5~0.6 减少漏合并。--save-txt 会把检测框按 YOLO 格式存下来,方便和标签对比算指标。推理结果默认存 runs/detect/exp,核对时重点看漏检和误检分别集中在哪些类,20 类里通常有几类样本少,精度会明显拖后腿。
4.2 后处理与边缘部署的量化思路
YOLOV5 的后处理主要是三件事:把输出解码成框、按置信度过滤、NMS 去重。部署到边缘设备时,常见做法是先导出 ONNX 再量化。以 RK3568 这类平台为例,流程是 PyTorch → ONNX → 量化校准 → 板端推理。量化会带来精度损失,map0.5 可能掉 1~3 个点,所以校准集要覆盖 20 个类的典型样本,不能只用一类图。树莓派 4B/5 上部署自己训练的模型,一般走 ONNX Runtime 或 ncnn,帧率取决于输入尺寸和是否量化,640 输入在树莓派 5 上通常是个位数到十几帧,想实时得降输入或换更小的模型。
提示:导出 ONNX 时注意 opset 版本和动态轴设置,动态 batch 在部分推理框架上会拖慢速度,固定 batch=1 往往更快。
5. 避坑与排查:20 分类项目里最容易翻车的五件事
现象一:训练不报错但 map 一直是 0。原因多半是标签路径或类别索引对不上,YOLOV5 找不到有效标签就当成全背景训。解决:先跑一遍数据检查,确认每张图都有同名 txt,且 txt 里 class_id 都在 0~19 范围内。
现象二:某一类精度特别低,其他类正常。原因通常是该类样本太少或标注质量差。解决:统计每类框数量,样本少于几百的类考虑补充数据或做重采样,同时抽查该类标签有没有漏标、错标。
现象三:显存爆了,batch 降到 1 还是 OOM。原因可能是 --img-size 太大或 --cache 把图全塞进内存。解决:先关 --cache,再把 img-size 降到 416 试跑,确认能跑通再逐步加回去。
现象四:推理结果框位置整体偏移。原因多是训练和推理的 img-size 不一致,或者 letterbox 填充参数没对齐。解决:保证 detect.py 的 --img-size 和训练时一致,检查预处理有没有做等比例缩放加灰边填充。
现象五:换验证集划分后 best.pt 精度暴跌。原因是你用的权重是按旧划分选出来的,和新划分的分布不匹配。解决:要么固定划分不动,要么在新划分上重新训一轮,别拿旧 best.pt 直接对比新指标。
6. 进阶技巧:用 100 epoch 日志反推超参该往哪调
拿到一份已经跑完 100 epoch 的日志,最大的价值不是直接用权重,而是反推这套超参在你的场景里该怎么改。我一般会先把 results.csv 拉出来,按 epoch 看 map0.5 的斜率:如果前 30 轮就冲到接近最终值然后走平,说明学习率偏大或数据太简单,可以降 lr0 让收敛更细;如果 100 轮结束还在缓慢上升,说明轮次不够或学习率衰减太慢,可以加 epoch 或调小 lrf。
再对照 20 个类的 per-class map,把低于平均的类挑出来。这些类要么补数据,要么在数据增强上针对性加强,比如小目标多的类可以开 mosaic 和 copy-paste。超参方面,YOLOV5 的 hyp 文件里有几个常调的:box、cls、obj 三个 loss 权重,默认 0.05/0.5/1.0,类别不平衡时可以适当提 cls;anchor 如果和你的目标尺寸差太多,可以用 k-means 在自己的数据上重聚一遍。
验证方法很简单:固定验证集,每次只改一个参数,跑 20~30 epoch 看 map 变化,别一次改一堆,否则出了问题根本不知道是哪个参数导致的。这套流程我踩过坑——有次同时改了学习率和增强,结果精度掉了却定位不到原因,白白多跑了两天。从那以后我每次调参都强制走一遍「单变量 + 固定验证集」的流程,宁可慢一点也要可复现。希望这份拆解帮到你,拿到资源后先跑推理确认环境通,再动训练参数。
本文还有配套的精品资源,点击获取