ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

VOC 20分类数据集跑YOLOV5:从数据转换到模型验证的完整指南

VOC 20分类数据集跑YOLOV5:从数据转换到模型验证的完整指南 简介本资源为基于YOLOV5的VOC目标检测实战项目面向具备一定深度学习基础、希望快速上手目标检测训练与推理的开发者与学习者。项目覆盖火车、船、人、电视、飞机等20个类别的检测任务提供可直接运行的代码、数据集与训练好的权重参数帮助读者跳过环境搭建与数据整理的繁琐环节专注于模型训练与效果验证。资源包共约2000个文件以txt标签文件、Python脚本、yaml配置、sh运行脚本及md说明文档为主压缩包大小约357.51MB。项目迭代100个epoch训练结果保存在runs目录下最佳精度达到map0.50.62、map0.5:0.950.42并在runs/detect中保存了网络推理训练集的全部结果推理效果良好。训练集含13700张图片及对应标签测试集含3425张图片及对应标签数据划分清晰。目前已有161人学习适合作为目标检测入门与课程设计的完整参考方案。1. VOC 20 分类数据集跑 YOLOV5为什么它是目标检测入门最稳的一块跳板如果你手头有一份 Pascal VOC 格式、20 个类别的目标检测数据集想用 YOLOV5 把它跑通那这套流程几乎是绕不开的。VOC 的 20 类覆盖了人、车、猫狗、桌椅、瓶子这些日常物体标注质量稳定、图片数量适中是很多人从「看懂检测网络」到「自己训出一个能用的模型」之间最平滑的一段路。但真上手就会发现卡人的从来不是模型本身而是数据格式转换、类别映射、超参数怎么调、训练完怎么验证这几件事。这篇笔记就按一条完整的落地链路走先把 VOC 和 YOLOV5 的关系讲清楚再一步步把数据转成 YOLO 格式、配好训练参数、跑通训练、排查常见翻车点最后落到一个能验证模型到底学没学会的具体技巧。适合刚接触目标检测、手里有 VOC 数据、想用 YOLOV5 训出自己第一个 20 分类模型的工程师。2. VOC 转 YOLO 格式从 XML 标注到 txt 标签的完整转换VOC 数据集的核心是每张图对应一个 XML 标注文件里面用object记录每个目标的类别名和边界框的左上角、右下角坐标。YOLOV5 要的是每张图对应一个 txt 文件每行一个目标格式是类别索引 中心x 中心y 宽 高而且这四个坐标全部要归一化到 0 到 1 之间。这个转换看着简单但类别顺序、坐标归一化、空标注处理这三件事只要错一个训练就会静默地跑偏。2.1 先搞清楚 VOC 的 20 类和 YOLO 的类别索引怎么对齐VOC 的 20 个类别是固定的aeroplane、bicycle、bird、boat、bottle、bus、car、cat、chair、cow、diningtable、dog、horse、motorbike、person、pottedplant、sheep、sofa、train、tvmonitor。YOLOV5 训练时读的是data.yaml里的names列表列表的索引就是类别索引。所以你必须保证转换脚本里用的类别到索引的映射和data.yaml里的names顺序完全一致。常见做法是直接按 VOC 官方顺序建一个列表转换和配置都从这个列表生成避免两处手写不一致。# voc_classes.py # VOC 20 类的固定顺序转换脚本和 data.yaml 都从这里取保证索引一致 VOC_CLASSES [ aeroplane, bicycle, bird, boat, bottle, bus, car, cat, chair, cow, diningtable, dog, horse, motorbike, person, pottedplant, sheep, sofa, train, tvmonitor ] # 类别名到索引的映射转换 XML 时用 CLASS_TO_IDX {name: i for i, name in enumerate(VOC_CLASSES)}这段代码的作用是把类别顺序固化成一个唯一来源。参数上没什么可调的关键是不要在别处再手写一遍类别列表。如果你用的是自己筛选过的子集比如只保留 person、car、dog 这几类那也要重新生成一个子集列表并且索引从 0 重新排不能沿用原始 VOC 的索引否则data.yaml和标签对不上模型会把车学成人。2.2 写一个能处理边界情况的 XML 转 txt 脚本转换脚本要处理的不只是正常标注还有几个容易忽略的边界图片尺寸要从 XML 里的size读不能假设都是 500x375有些目标框可能超出图片边界归一化前要裁剪有些图可能一个目标都没有这种图要么跳过要么生成一个空 txt但 YOLOV5 默认会忽略空标签文件所以更稳的做法是直接不生成。下面这个脚本按 VOC 标准目录结构JPEGImages、Annotations来写。# voc2yolo.py import os import xml.etree.ElementTree as ET from PIL import Image from voc_classes import CLASS_TO_IDX def convert_box(size, box): 把 VOC 的 xmin,ymin,xmax,ymax 转成 YOLO 的 cx,cy,w,h 并归一化 dw 1.0 / size[0] dh 1.0 / size[1] x (box[0] box[1]) / 2.0 y (box[2] box[3]) / 2.0 w box[1] - box[0] h box[3] - box[2] return x * dw, y * dh, w * dw, h * dh def convert_voc(anno_dir, img_dir, out_dir): os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(anno_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(anno_dir, xml_file)) root tree.getroot() # 从 XML 读图片尺寸不假设固定分辨率 size root.find(size) w_img int(size.find(width).text) h_img int(size.find(height).text) lines [] for obj in root.iter(object): cls_name obj.find(name).text.strip() if cls_name not in CLASS_TO_IDX: continue # 跳过不在 20 类里的目标 cls_id CLASS_TO_IDX[cls_name] bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) # 裁剪到图片范围内防止越界框导致归一化后超出 0-1 xmin max(0, min(xmin, w_img)) xmax max(0, min(xmax, w_img)) ymin max(0, min(ymin, h_img)) ymax max(0, min(ymax, h_img)) if xmax xmin or ymax ymin: continue # 宽高为 0 的无效框直接丢 bb convert_box((w_img, h_img), (xmin, xmax, ymin, ymax)) lines.append(f{cls_id} { .join(f{v:.6f} for v in bb)}) if lines: # 没有目标的图不生成标签文件 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)) if __name__ __main__: convert_voc(VOCdevkit/VOC2007/Annotations, VOCdevkit/VOC2007/JPEGImages, datasets/voc20/labels/train)逻辑上convert_box负责坐标变换convert_voc负责遍历和边界处理。参数方面xmin等坐标裁剪到[0, w_img]和[0, h_img]是必须的VOC 里确实存在少量越界标注if lines这个判断决定了空图是否生成标签YOLOV5 的 dataloader 遇到空标签文件会跳过该图所以不生成更干净。转换完记得抽查几个 txt确认每行是 5 个值、第一个是整数、后四个在 0 到 1 之间。2.3 目录结构和 data.yaml 怎么配才不出错YOLOV5 对目录结构有约定图片和标签要分开放且路径在data.yaml里指定。常见做法是建一个datasets/voc20目录下面分images/train、images/val、labels/train、labels/val。图片从 VOC 的 JPEGImages 复制或软链过来标签就是上一步生成的 txt。data.yaml里写训练集和验证集的路径、类别数、类别名。# data.yaml path: ../datasets/voc20 # 数据集根目录相对训练脚本的位置 train: images/train val: images/val nc: 20 names: [aeroplane, bicycle, bird, boat, bottle, bus, car, cat, chair, cow, diningtable, dog, horse, motorbike, person, pottedplant, sheep, sofa, train, tvmonitor]这里nc必须等于names的长度names的顺序必须和转换脚本里的VOC_CLASSES完全一致。path是相对路径的基准YOLOV5 会把它和train、val拼起来。如果你把数据集放在别处改path就行不用动train和val。验证集建议从 VOC2007 的 trainval 里切 10% 出来或者直接用 VOC2007 test但要注意 test 没有公开标注所以一般还是从 trainval 切。3. YOLOV5 训练 20 分类模型的参数配置与启动数据准备好之后训练本身反而没那么复杂但超参数选不对要么 loss 不降要么训出来的模型只会框大物体。这一章把模型选型、关键超参数、启动命令和训练过程怎么看讲清楚。3.1 模型选型s/m/l/x 怎么选20 分类够不够用YOLOV5 官方提供 s、m、l、x 几个规格参数量和精度递增。对于 VOC 20 分类这种中等规模数据集我一般从yolov5s起步它的参数量小、训练快单卡几小时就能跑完一轮完整训练适合先验证整条链路通不通。如果 s 的 mAP 卡在某个值上不去再换yolov5m或yolov5l。不建议一上来就用 xVOC 的数据量撑不起那么大的模型容易过拟合而且训练时间成倍增加。选型时还要注意预训练权重用 COCO 预训练的yolov5s.pt做初始化比从头训收敛快很多20 分类里很多类别和 COCO 有重叠迁移效果明显。3.2 必调的 5 个超参数epochs、batch、imgsz、lr0、workersYOLOV5 的超参数很多但真正影响 20 分类训练效果的主要是这几个。下面这张表是我在 VOC 规模数据集上常用的起点值不是绝对最优但能让你第一轮就跑出一个合理结果。参数常用值作用调整方向epochs100-300训练轮数loss 还在降就加验证 mAP 连续多轮不升就停batch-size16 或 32每批图片数显存不够就减减到 8 还爆就降 imgszimgsz640输入分辨率VOC 图片普遍偏小640 够用小目标多可试 768lr00.01初始学习率用预训练权重时 0.01 合适从头训可降到 0.001workers8数据加载进程数按 CPU 核数设设太大反而拖慢epochs不是越大越好VOC 这种规模100 到 300 轮足够再往后基本是过拟合。batch-size和imgsz是一对显存吃紧时优先降 batch因为 imgsz 降了小目标更难检。lr0用预训练权重时保持 0.01YOLOV5 自带 warmup 和余弦退火不用手动调太细。workers在 Linux 上设成 CPU 核数的一半到全部Windows 上设太大容易卡死建议不超过 8。3.3 启动训练的命令和训练日志怎么看YOLOV5 的训练入口是train.py一条命令就能启动。下面这条命令指定了数据配置、模型、预训练权重、批次和轮数输出目录用--name指定方便区分不同实验。# 在 yolov5 仓库根目录下执行 python train.py \ --data data/voc20.yaml \ --weights yolov5s.pt \ --batch-size 16 \ --epochs 200 \ --imgsz 640 \ --workers 8 \ --name voc20_s \ --project runs/train参数含义--data指向刚才写的data.yaml--weights是预训练权重不写则从头训--name和--project决定结果存到runs/train/voc20_s。训练启动后日志里会打印每个 epoch 的 box_loss、obj_loss、cls_loss 和验证集的 mAP0.5、mAP0.5:0.95。重点看三个信号box_loss 和 cls_loss 是否稳定下降mAP0.5 是否在涨以及验证 loss 是否开始回升。如果 box_loss 降但 mAP 不涨多半是标签格式有问题如果 cls_loss 一直很高检查类别索引是否对错。3.4 训练完怎么用验证集确认模型真的学到了 20 类训练结束后runs/train/voc20_s/weights/下会有best.pt和last.pt。用val.py在验证集上跑一遍看每个类别的 AP而不是只看总体 mAP。VOC 20 类里person、car 这种样本多的类别 AP 通常很高bird、pottedplant 这种样本少、目标小的类别 AP 会明显低。如果某个类别 AP 接近 0基本可以断定是类别索引映射错了或者该类样本在训练集里几乎没有。python val.py \ --data data/voc20.yaml \ --weights runs/train/voc20_s/weights/best.pt \ --img 640 \ --task val这条命令会输出每个类别的 P、R、mAP0.5。参数--task val表示只做验证不训练。看结果时如果总体 mAP0.5 在 0.7 以上说明模型基本可用如果低于 0.5先别急着调模型回头查数据转换和类别映射。验证集的选择也很关键一定要是训练时没见过的图否则 mAP 虚高上线就翻车。4. 训练 VOC 20 分类时最容易踩的坑与排查这一章按「现象 → 原因 → 解决」列几条我实际踩过的坑都是训练能跑起来但结果不对的静默问题比报错更难查。4.1 现象mAP 一直是 0 或者极低原因通常是标签格式或路径不对。YOLOV5 读标签时如果找不到对应 txt或者 txt 里坐标没归一化会把目标当成背景。先检查labels/train下的 txt 文件名是否和images/train下的图片名一一对应再抽查 txt 内容确认每行是 5 个值且后四个在 0 到 1 之间。还有一个隐蔽原因data.yaml里的path写成了绝对路径但训练时工作目录变了导致图片加载不到模型全程在看空图。4.2 现象某个类别 AP 异常低其他类别正常原因基本是类别索引错位。比如转换脚本里VOC_CLASSES的顺序和data.yaml里names的顺序不一致或者你筛选了子集但索引没重排。解决方法是把data.yaml的names和转换脚本的类别列表放在同一个文件里生成杜绝两处手写。另外如果某个类别在训练集里样本极少比如少于 50 个AP 低是正常的需要补数据或做类别平衡。4.3 现象训练 loss 震荡剧烈不收敛原因可能是学习率太大或 batch 太小。用预训练权重时lr0保持 0.01如果 loss 在前几个 epoch 就炸到 nan降到 0.001 再试。batch 太小会导致梯度噪声大显存允许的话把 batch 提到 16 以上。还有一个容易被忽略的点VOC 图片尺寸不一YOLOV5 会统一 resize 到imgsz如果原图特别小resize 后目标更小可以适当提高imgsz到 768。4.4 现象训练能跑完但推理时框的位置偏移原因通常是坐标转换时 x 和 y 弄反了或者宽高计算用了 xmax-xmin 但中心点算错。VOC 的框是 xmin、ymin、xmax、ymaxYOLO 要的是中心点和宽高转换时cx(xminxmax)/2、wxmax-xmin顺序不能乱。排查方法是拿一张图用转换后的 txt 反算出框画到原图上看是否和标注重合。这个可视化步骤能一眼看出转换对不对比看 loss 曲线快得多。4.5 现象验证集 mAP 高但实际用起来效果差原因是验证集和训练集分布太接近或者验证集里某些类别根本没出现。VOC2007 的 trainval 和 test 分布有差异如果只用 trainval 切验证模型可能过拟合到 trainval 的拍摄风格。解决方法是尽量用不同年份或不同来源的图做验证或者在验证时按类别统计样本数确保每个类别都有足够验证样本。另外推理时的预处理要和训练时一致比如 letterbox 的填充方式不一致会导致框偏移。5. 用混淆矩阵和单图推理验证 20 分类模型的真实水平训练日志里的 mAP 只是一个聚合指标它不告诉你模型把什么错认成了什么。要真正判断一个 VOC 20 分类模型能不能用我习惯做两件事看混淆矩阵和拿几张有代表性的图做单图推理。混淆矩阵能暴露类别之间的系统性混淆比如 chair 和 diningtable、car 和 bus 这种视觉上接近的类单图推理则能直观看到框的松紧和漏检情况。YOLOV5 训练结束后会自动生成混淆矩阵存在runs/train/voc20_s/confusion_matrix.png。看的时候重点找非对角线的亮块如果 person 那一行在 chair 列有值说明模型把坐着的人认成了椅子如果 car 和 bus 互相混淆说明模型对车辆尺度不敏感。这些混淆往往不是模型容量不够而是训练数据里这两类的样本外观太像需要针对性补数据或做数据增强。单图推理用detect.py指定--source为单张图或一个目录--weights用best.pt--conf控制置信度阈值。我一般先用默认 0.25 跑一遍看漏检多不多再调到 0.5 看误检多不多。下面这条命令把结果存到指定目录方便对比。python detect.py \ --weights runs/train/voc20_s/weights/best.pt \ --source samples/ \ --img 640 \ --conf 0.25 \ --name voc20_infer \ --project runs/detect参数--conf 0.25是置信度阈值低于这个值的框不输出--img 640要和训练时一致否则精度会掉--source可以是单张图、目录或视频。跑完后打开输出图重点看三类问题小目标bird、bottle有没有漏密集场景person 多有没有框重叠以及背景复杂时有没有误检。如果小目标漏得多可以在训练时把imgsz提到 768或者用--augment做推理时增强。我自己的习惯是每次训完一个 VOC 20 分类模型先不看 mAP而是先跑混淆矩阵和 10 张单图推理确认没有系统性错认和明显漏检再回头调超参数。这个顺序能省掉很多无效调参的时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表