ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

YOLO目标检测数据集实战:VOC/COCO/YOLO三格式标注与训练避坑指南

YOLO目标检测数据集实战:VOC/COCO/YOLO三格式标注与训练避坑指南 简介面向目标检测学习者的真实道路车辆数据集适合课程设计、毕业设计及YOLO系列算法实战。内含1000张来自真实场景的高质量图片经LabelImg标注标注框质量高并同时提供VOCxml、COCOjson和YOLOtxt三种格式标签按文件夹分类存放可直接用于模型训练省去格式转换的额外工作。压缩包内文件总数达2000个包含1000个xml标注文件、991个txt标签/列表文件、5个html格式的环境搭建与训练教程、3个Python划分脚本及1个yaml配置文件整体约115.18MB。目前已有491人浏览学习此资源。配套内容还区分Windows和Linux两个版本讲解YOLO环境搭建和基于案例的数据集训练方法附带的数据集划分脚本可自动生成训练集、验证集、测试集及对应列表从数据准备到模型训练全流程都有覆盖对刚接触目标检测的读者较为友好。1. 真实道路车辆数据集把1000张图直接送进YOLO训练做目标检测最烦的不是写训练代码而是找数据。网上公开的车辆数据集要么是国外街景、要么只有COCO或VOC单一格式你下载下来还得自己写转换脚本更别提划分训练集验证集测试集这件事——很多人就是在这里耗掉了半天时间。这份YOLO真实道路车辆目标检测数据集1000张真实道路场景图片把这一步省掉了图片自带VOCxml、COCOjson、YOLOtxt三套标准标签分别存放在不同文件夹可以直接喂给YOLO系列做训练。随包还带了数据集划分脚本和YOLO环境搭建、训练教程Windows和Linux两个版本从装驱动到跑通训练闭环需要的东西基本齐了。适合正在上手YOLO、手头没有合适车辆数据、或者想一套流程从零跑到模型出炉的从业者。2. 数据集结构拆解VOC、COCO、YOLO三套标签怎么并存2.1 三套格式的存储与差异解开压缩包后图片会集中在images这类目录下三套标签则分别放在三个不同文件夹里。VOC格式的xml文件放在Annotations目录COCO格式的json汇总在独立目录YOLO格式的txt放在labels目录。这样布局的实际意义是你训YOLO时读txt直接喂给模型做对比实验或传统检测方案时读xml/json各路径互不干扰。不用在训练前花时间做格式转换这是这份资源最省事的地方。三套格式本质上是在用三种方式描述同一个矩形框VOC格式是PASCAL VOC竞赛定义的结构一个xml对应一张图片。打开后能看到 根节点、 里的宽高以及每个 和 坐标是xmin、ymin、xmax、ymax的整数像素值。因为是labelImg标注输出的结构很规整解析时按节点取就行。COCO格式则是把所有标注汇总到一个json文件里分info、images、annotations、categories四段。images段记录每张图的id和宽高annotations段记录每个框的image_id、category_id、bbox格式是左上角x、左上角y、宽度、高度categories段做类别id和类别名的映射。这里有个经典坑COCO的类别id从1开始而YOLO的类别id从0开始转换时不减1训练出来的类别就是错位的。YOLO格式最直接一张图对应一个同名txt每行一个目标五个数字分别是类别id、归一化中心点x、归一化中心点y、归一化宽、归一化高。全部是0到1之间的小数单独看txt根本看不出目标在原图里的实际位置必须结合图片宽高才能反推。维度VOC (xml)COCO (json)YOLO (txt)存储粒度一图一xml所有标注在一个json一图一txt坐标基准像素绝对坐标像素绝对坐标归一化相对坐标框表达xmin, ymin, xmax, ymaxx, y, width, heightx_center, y_center, width, height类别id类别名字符串从1开始从0开始这个表建议截图存着后面转换格式、排查类别错乱时翻出来对照能省不少时间。2.2 一张图如何被三份文件描述假设图片名是000001.jpg在VOC格式里是Annotations/000001.xml在COCO格式里是json文件images段中filename为000001.jpg的记录而annotations段中所有image_id匹配它的条目就是这张图的框在YOLO格式里则是labels/000001.txt。三套标注描述的是同一批框只是换了一种表达。如果你以后拿到只有VOC格式的数据集想转成YOLO格式给训练用核心转换逻辑就这么几行import xml.etree.ElementTree as ET def voc2yolo(xml_path, img_w, img_h, class_map, out_path): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text cls_id class_map[name] # 类别名转成从0开始的数字id box obj.find(bndbox) xmin int(box.find(xmin).text) ymin int(box.find(ymin).text) xmax int(box.find(xmax).text) ymax int(box.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}) with open(out_path, w) as f: f.write(\n.join(lines))注意两个地方一是分母必须用图片的实际宽高有些人图省事写死640一旦原图分辨率不是640所有框全部偏移二是class_map要把类别名字符串映射成从0开始的数字id顺序必须和训练时data.yaml里的names一致。2.3 拿到标签后做的第一个检查任何数据集到手我建议先抽5到10张图把txt内容打印出来看坐标分布再画几个框回原图上确认贴合程度。这个数据集的标签是labelImg标注的框质量整体可靠但「整体可靠」不等于不用查。之前遇到过一个来路不明的数据集所有txt里的坐标被统一加了个0.1偏移训练到第50轮mAP还在0.3附近徘徊最后才发现是数据本身的问题。最简单的合法性校验是这样with open(labels/000001.txt) as f: for line in f: parts line.split() assert all(0 float(v) 1 for v in parts[1:]), f坐标越界: {line}parts[1:]跳过第一列类别id只检查后面四个坐标值。只要有一个值小于0或大于1就说明该样本的标注坐标异常需要回到原始标注确认。这个脚本在任何数据集上都能用建议训练前无脑跑一遍。3. 数据集划分脚本实战从一张图到三份训练清单3.1 三个脚本的分工与选择资源里带了三个划分相关的脚本外加两份已经生成好的txt清单文件第一次拿到包的人确实容易搞混。我按实际使用场景理一下脚本名称功能适用场景训练集、验证集、测试集划分脚本图片标签划分写入新文件夹.py按比例把图片和对应的标签文件复制到train/val/test三个新目录从零开始拆分数据集训练集、验证集划分脚本图片标签划分写入新文件夹.py只拆成训练集和验证集样本量小不需要独立测试集split_train_val生成ImageSets下txt文件划分脚本.py生成VOC风格的train.txt、val.txt、test.txt等清单配合VOC格式的训练或管理流程如果只是跑通YOLO训练用第一个脚本拆一次就够了。如果要留固定测试集做最终评估或者需要反复调参对比用第三个脚本管理清单更合适。两份已经生成好的train_list.txt和trainval_list.txt则是YOLO训练直接读取的路径清单下面细说。3.2 全量划分脚本的运行逻辑第一个脚本的流程不复杂拆开看核心就是读取图片目录下所有文件名随机打乱按比例切片再把图片和同名标签文件复制到对应的新文件夹。我按常见实现还原一下关键逻辑import os import random import shutil img_dir images label_dir labels train_ratio, val_ratio, test_ratio 0.8, 0.1, 0.1 names [f for f in os.listdir(img_dir) if f.endswith(.jpg)] random.shuffle(names) n_train int(len(names) * train_ratio) n_val int(len(names) * val_ratio) train_names names[:n_train] val_names names[n_train:n_train n_val] test_names names[n_train n_val:] def move_file(name, split): # 图片和标签成对复制到新目录 shutil.copy2(os.path.join(img_dir, name), os.path.join(split, images, name)) label_name name.rsplit(., 1)[0] .txt shutil.copy2(os.path.join(label_dir, label_name), os.path.join(split, labels, label_name)) for name in train_names: move_file(name, train) for name in val_names: move_file(name, val) for name in test_names: move_file(name, test)改参数时注意三点train_ratio、val_ratio、test_ratio三个值加起来必须等于1想改成7:2:1就写0.7、0.2、0.1图片后缀如果带.png需要把endswith(.jpg)改成.png或同时匹配两种标签后缀默认按.txt处理如果你实际用的是VOC格式xml要把后缀和来源目录一并改掉。脚本只负责复制文件不负责校验图片和标签是否一一对应跑完后建议随手对比一下三个新目录里的文件数量。3.3 ImageSets清单是如何生成的split_train_val生成ImageSets下txt文件划分脚本做的事情和前面不同它不复制文件只在ImageSets/Main目录下生成VOC风格的清单文件内容是不带后缀的文件名每行一个。train.txt、val.txt、test.txt、trainval.txt这四个文件分别对应训练集、验证集、测试集、训练加验证集合并。VOC风格的清单长这样000001 000002 000003训练框架读取这份清单后会自己拼接路径去JPEGImages目录找图片、去Annotations目录找xml。所以这份清单本身不携带路径它的有效性完全依赖目录结构稳定。如果你把数据集挪了位置或者改了目录名清单内容不用动但读取清单的代码里的路径前缀必须跟着改否则必然FileNotFoundError。3.4 train_list.txt和trainval_list.txt的实际用途这两份是已经生成好的YOLO训练路径清单train_list.txt装的是训练集图片路径trainval_list.txt是训练集加验证集的图片路径。YOLO训练时data.yaml里的train和val两个字段可以直接指向这两个文件省去自己再生成一遍的功夫。这两份清单里的路径通常是相对路径换机器后要确认路径前缀是否还成立。我每次拿到新数据都会先看一眼前几行head -5 train_list.txt如果路径前缀和你当前工程目录对不上Linux下用sed批量替换前缀Windows下建议直接用Python脚本处理避开反斜杠转义的麻烦with open(train_list.txt, r) as f: lines [line.replace(D:\\old\\path\\, D:/new/path/) for line in f] with open(train_list.txt, w) as f: f.writelines(lines)注意Windows的路径反斜杠在Python字符串里一定要写成\\或者直接用正斜杠这地方踩坑的人特别多。4. YOLO环境搭建GPU驱动、CUDA和PyTorch的版本对齐4.1 先看显卡和驱动再决定装什么资源里附带的教程HTML有两个版本值得注意YOLO环境搭建.html是基础流程YOLO环境搭建GPU显卡驱动版本.html则专门讲了驱动这一环。装环境前第一件事不是装CUDA而是确认你的显卡型号和当前驱动版本。Windows下在cmd里运行命令Linux下在终端运行同一命令nvidia-smi输出表格右上角的Driver Version是驱动版本CUDA Version是当前驱动能支持的最高CUDA版本。这里有个普遍误解这个CUDA Version并不代表你系统里装了CUDA Toolkit它只是驱动兼容性的上限。比如驱动535.xx显示CUDA Version: 12.2意味着你能运行任何为CUDA 12.2或更低版本编译的程序。PyTorch本身自带CUDA runtime所以很多时候根本不需要单独装CUDA Toolkit只要驱动版本满足PyTorch要求即可。4.2 从零装到PyTorch能用GPU跑Windows和Linux两条路我走过多次核心步骤是一致的。前提是显卡驱动已经装好nvidia-smi能正常输出。先创建conda环境两条系统通用conda create -n yolo python3.8 -y conda activate yolo然后装PyTorch。最稳妥的做法是到pytorch.org的Get Started页面选择你的操作系统、包管理器、CUDA版本复制它给出的命令。以CUDA 11.8为例pip install torch2.0.0 torchvision0.15.0 --index-url https://download.pytorch.org/whl/cu118注意千万别直接从默认PyPI源装torch默认源给的是CPU版本装完torch.cuda.is_available()永远返回False。安装完成后必须验证环境是否真的可用python -c import torch; print(torch.__version__, torch.cuda.is_available(), torch.cuda.get_device_name(0))输出类似2.0.0cu118 True NVIDIA GeForce RTX 3060才算通。中间的True是关键如果是False要么torch装成了CPU版要么torch要求的CUDA版本高于当前驱动支持的上限。4.3 Linux和Windows的安装差别Linux下装NVIDIA驱动比Windows麻烦。Ubuntu安装教程里讲的那部分核心是先禁用nouveau开源驱动再通过apt或runfile方式装官方驱动装完重启再用nvidia-smi确认。runfile方式适合需要精确控制驱动版本的场景apt方式适合大多数人省事且和内核模块的兼容性由发行版维护者把过关。Windows下相对简单NVIDIA官网下载对应型号的驱动安装包一路下一步即可。安装时建议勾选「执行清洁安装」避免旧驱动残留导致版本冲突。另外Windows有个独特的坑如果机器上装了多个Python解释器激活conda环境后cmd里输入python可能还是跑到旧解释器。我习惯激活环境后先执行where python和python --version确认路径再继续装依赖避免装了一堆包最后训练的却是另一个解释器。5. 训练前避坑五个最容易翻车的地方5.1 现象torch.cuda.is_available()输出False原因最常见的是从默认PyPI源装了CPU版PyTorch或者PyTorch要求的CUDA版本高于当前驱动支持的最高版本。前者概率更高因为很多人没有意识到默认源和CUDA版源的区别。解决先卸载现有torch和torchvision再按pytorch.org给出的对应CUDA版本命令重装。注意命令里--index-url后面的cu111、cu118、cu121标识驱动版本低就选低版本的cu。装完重新跑验证命令确认输出True。5.2 现象训练刚启动就报CUDA out of memory原因batch_size给太大显存装不下。默认训练脚本里batch_size168G显存的卡在640分辨率下通常只能跑batch 4到8硬跑必然OOM。解决调小batch_size同时把图像尺寸从640降到512或416先把训练跑通。YOLOv5的命令是python train.py --data data.yaml --batch 4 --img 512 --epochs 100batch_size调整后显存占用会明显下降训练速度反而可能更快因为不用频繁做内存交换。调参时优先降batch_size其次降分辨率分辨率太低会影响小目标检测精度。5.3 现象loss不降或验证时类别名全部错位原因类别id对应关系错了。YOLO格式的类别数字从0开始COCO从1开始VOC用的是类别名字符串。如果某个环节的id转换没有做减1操作训练时标注的类别和data.yaml里的names配置就对不上模型在错误的目标函数下学习loss自然降不动。解决训练前统计整个labels目录里的类别id集合和data.yaml核对from collections import Counter import os cnt Counter() for f in os.listdir(labels): with open(os.path.join(labels, f)) as fp: for line in fp: cnt[int(line.split()[0])] 1 print(cnt)Counter输出的键必须恰好是0到nc-1且数量和data.yaml里的names一一对应。多一个少一个都说明类别映射有问题先解决这个再启动训练。5.4 现象画出来的框偏移、变形或跑到图片外原因坐标归一化公式写错了。最常见的错误是把xmin直接除以图片宽度当成中心点x正确写法是(xmin xmax)/2再除以宽度。还有一个容易被忽略的点用OpenCV读图时宽高顺序是width img.shape[1]、height img.shape[0]写反了会导致框整体偏移。解决用脚本把标注框画回原图逐张检查边缘车辆。更快的办法是抽查几个txt做一次坐标范围校验确认所有值都在0到1之间。如果偏移是系统性的比如所有框整体偏左上或右下优先检查图片宽高的获取顺序。5.5 现象Windows下训练报FileNotFoundError路径里有\t、\n原因Windows路径用反斜杠比如D:\train\images\000001.jpg。在Python字符串里直接写这个路径\t会被解析成制表符、\n会变成换行路径自然找不到。解决路径统一用正斜杠Python和训练框架完全兼容或者给字符串加raw前缀写成rD:\train\images\000001.jpg。另外如果资源里现成的train_list.txt是在Windows下生成的路径换到Linux机器训练时也要先检查路径前缀用sed或Python替换掉盘符和反斜杠。6. 把训练教程变成自己的流程从改yaml到导出验证6.1 data.yaml到底改什么资源里训练教程HTML的核心内容是「根据案例修改训练自己的数据集」一句话说就是改data.yaml。默认模板给的是COCO的80类配置你需要替换成自己的类别。假设这个车辆数据集区分car、truck、bus三类train: ./train_list.txt val: ./trainval_list.txt nc: 3 names: [car, truck, bus]train和val字段的值取决于你最终用的清单文件路径nc必须和names列表长度一致否则YOLO训练时类别数错乱loss曲线看起来正常但验证阶段类别名全是错位的。names顺序也要注意它对应txt标签里类别id的数字0对应car、1对应truck、2对应bus顺序反了模型记住的语义就是反的。6.2 从训练命令到导出模型以YOLOv5为例改完data.yaml后启动训练python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 8 --epochs 100训练结束后在runs/train/exp目录下会生成best.pt和last.ptbest.pt按验证集mAP取最优权重last.pt是最后一轮的权重。如果训练中断需要续跑用--resume last.pt。要部署到其他框架或做推理验证时我习惯先导出ONNXpython export.py --weights runs/train/exp/best.pt --include onnx导出的ONNX文件可以脱离PyTorch环境运行配合onnxruntime在CPU机器上也能推理。我可以写一小段脚本验证输出张量shape是否为(N, 6)对应x1、y1、x2、y2、conf、cls这六列确认结构没问题再交给部署端。整套流程跑通之后换任何数据集都是同一个套路。从那以后我每次拿到新数据都强制先做两件事看一眼data.yaml的nc和labels目录的类别id集合是否一致再启动训练训练完先导一次ONNX验证结构再谈部署。这套习惯帮我避开了大部分低级错误希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进