☰
YOLOV5数据集整理指南:目录结构、标签转换与智能小车实战排查
2026/9/26 16:25:02 网站建设 项目流程

简介:这是一份面向智能小车物品识别与智能购买场景的单类别目标检测数据集,按YOLOV5标准目录结构整理,图片与标注文件一一对应,可直接作为训练输入,无需额外转换。数据集中唯一类别为purchase,所有图片分辨率统一为416×416像素的RGB格式;训练集包含710张图片及711个标签文件,验证集包含177张图片及177个标签文件,并附带类别字典文件,便于后续类别映射。除JPG图片与文本标注外,资源内还提供1个Python可视化脚本和1张示例图,运行脚本即可随机读取图片并绘制边界框,结果自动保存到当前目录,无需修改任何参数。整个资源共1777个文件,JPG图片887张、文本标注888个,压缩包约26.48MB,体量轻、目录清晰,适合学习目标检测流程或快速搭建智能购买识别实验的开发者直接使用。目前已有157人学习下载,性价比高,开箱即用。

1. 目标检测数据集按YOLOV5目录格式交付:为什么这是智能小车识别项目的第一道坎

目标检测数据集以YOLOV5目录格式整理好,意味着你拿到的不是一堆散图,而是一套开箱即训的标准结构。做过智能小车物品识别的人都知道,训练一个能用的模型,最耗时间的往往不是调参,而是数据:标注格式对不对、train和val有没有交叉、标签和图片能不能对上——这些数据侧的坑,比模型结构更隐蔽,报错也更难懂。这份资源适合两类人:一类是带智能小车做竞赛或课设,想在最短时间内把物品识别链路跑通;另一类是刚接触YOLOV5,想找一份规范的数据集当"标准答案",对照自己手头数据的组织方式。下面从目录约定、标签计算、转换脚本和训练前检查四条线展开,把最容易翻车的几个位置一次说清。

2. 目录结构与标签文件:读懂YOLOV5数据集的三个关键约定

2.1 目录树:images与labels、train与val的对应关系

拿到这份数据集,先不要急着开train.py,花两分钟把目录结构过一遍。标准的YOLOV5目录树长这样:

dataset/ ├── images/ │ ├── train/ │ │ ├── 000001.jpg │ │ └── ... │ └── val/ │ ├── 000101.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 000001.txt │ │ └── ... │ └── val/ │ ├── 000101.txt │ └── ... └── data.yaml

这个结构里有三条对应关系必须成立。第一,images和labels两个目录必须同在一层,YOLOV5默认在data.yaml指定路径的基础上,把image目录的上一级当根目录,再拼labels目录;如果你的data.yaml里train写的是./images/train,那labels文件就必须在./labels/train,名字必须和图片同名。

第二,train和val是两个独立的数据集,不能有同一张图既进train又进val。我见过不少人在手动拷贝时图省事,直接把所有图片丢进train,val从train里随机抽——这种交叉会导致mAP虚高,训练时模型其实"见过"验证集图片,部署到小车上实测才发现指标完全对不上。判断方法也简单:对比两个目录的文件名集合,取交集,非空就有问题。

第三,图片和标签文件必须一一对应。每张训练图片必须有一个同名txt,哪怕这张图里没有任何目标,txt也要存在并且为空。有些标注工具导出的目录里丢弃了无目标图片的标签,训练时YOLOV5会报找不到label文件,或者静默跳过,让你误以为数据质量很好,实际有效样本已经悄悄缩水。

另外注意图片后缀的坑。标注文件靠文件名配对,不靠后缀,所以000001.jpg和000001.png如果同时存在,会互相覆盖或产生歧义。整理数据集时统一把所有图片转成同一种后缀,避免同名不同格式的文件混在同一个目录里。这套目录约定不只YOLOV5在用,YOLOV8等后续版本也沿用同样的标签规范,学会一次,后面换框架不换思路。

2.2 标签txt:class、归一化中心点、宽高的换算逻辑

labels目录下的每个txt是纯文本,每一行描述一个目标。和VOC的xml存绝对像素坐标不同,YOLO格式存的是归一化坐标,一行五个浮点数:

0 0.485 0.321 0.184 0.275 1 0.712 0.668 0.093 0.149

第一列是类别id,从0开始,顺序严格对应data.yaml里names列表的下标;后面四列依次是目标中心点的x、y坐标和目标宽w、高h,全部除以图片宽高做了归一化,取值范围在0到1之间。

归一化这个设计是YOLO系格式能被广泛复用的核心原因:同一份标注,放在640×640的训练输入和放在1280×1280的推理输入里都不需要修改,比例天然一致。你只需要在做转换时记住换算公式:x_center = (xmin + xmax) / 2 / 图片宽度,y_center同理;w = (xmax - xmin) / 图片宽度,h = (ymax - ymin) / 图片高度。很多标注工具直接勾选YOLO格式导出就能得到正确结果,但如果你拿到的原始数据是xml或json,转换脚本里最容易错的就是忘了除以图片宽高,导致标签框整体偏移。

类别id从0开始这一点,和很多人的直觉相反。你也许习惯从1开始编号,但在YOLO体系里0就是第一类,是合法的。如果某份数据里出现负的id或小数id,说明转换脚本写错了或标注工具配置有误,这类标签优先排查。

提示:看txt里是否有大于1或小于0的值,是判断标签是否出界最快的方式。后面第5章会给专门的排查方法。

2.3 data.yaml:nc、names和路径,三个字段错一个都白搭

data.yaml是整个训练入口的配置文件,YOLOV5训练时通过--data参数指向它。一个完整可用的data.yaml长这样:

train: ./images/train val: ./images/val nc: 4 names: ['bottle', 'can', 'box', 'ball']

这里的坑集中在三个字段。一是nc必须和names的长度一致,且等于所有txt中出现的最大类别id加1。比如txt里出现了类别3,nc至少要填4,否则训练过程会在compute_loss里报index越界的错。二是names的下标就是txt里class id的映射,names列表写错顺序,模型学到的东西就张冠李戴。三是train和val路径,YOLOV5相对路径是相对于当前工作目录解析的,如果你在项目根目录外执行train.py,./images/train就可能找不到;我一般建议在yaml里写绝对路径,或者在项目根目录下执行训练命令,两种方式任选其一,不要混用。

字段含义常见错误
train训练图片目录写成图片文件而非目录
val验证图片目录与train目录交叉
nc类别总数与names长度不一致
names类别名列表顺序和txt里的class id不对应

这三个约定是整个数据集的地基,第3章的转换脚本、第5章的排查思路都建立在它们之上。如果你手里的数据是VOC格式,或者自己用别的工具标了一批图,接着看第3章的转换方法。

3. 从零整理自己的数据集:标注工具选型与VOC转YOLO实战脚本

3.1 标注工具怎么选:LabelImg、X-AnyLabeling、Roboflow三选一

大部分数据集不会正好是YOLO格式,到手之后先确认原始格式,再决定用什么工具转。常见的三种情况:已经有xml标注文件,需要转YOLO;手里只有图片,需要从头标注;标注量很大,想用辅助标注。对应工具我都用过,按场景选就行。

LabelImg是老牌工具,轻量、纯本地运行,标注界面简单直接,输出可以是VOC的xml,也能切到YOLO模式直接存txt。缺点是框多了以后界面有点卡,且单张图片操作效率一般。X-AnyLabeling是目前我常用的替代品,它带模型辅助标注,框一个目标后自动跟踪类似物体,对几百上千张同场景图片效率提升明显。Roboflow是在线方案,标注、增强、格式导出一条龙,还能自动处理数据集划分,但图片要上传到云端,如果涉及竞赛规则或数据隐私,慎用。

对于智能小车这类物体相对固定、拍摄背景单一的数据集,我的建议是:量小于500张用LabelImg或X-AnyLabeling本地标,量大或需要快速迭代用Roboflow,但本地始终留一份原始图片备份。标注工具只能输出标注,不能替代你检查标签质量,标完必须随机抽几十张图人工过一遍。

3.2 VOC转YOLO:一个能直接用的xml_to_yolo.py

如果你手上是VOC的xml标注,我在项目里一般用一个固定脚本转换,几十行搞定:

import xml.etree.ElementTree as ET import os def xml_to_yolo(xml_path, out_dir, classes): tree = ET.parse(xml_path) root = tree.getroot() size = root.find('size') w = int(size.find('width').text) h = int(size.find('height').text) lines = [] for obj in root.iter('object'): name = obj.find('name').text if name not in classes: continue box = obj.find('bndbox') xmin = float(box.find('xmin').text) ymin = float(box.find('ymin').text) xmax = float(box.find('xmax').text) ymax = float(box.find('ymax').text) x_center = (xmin + xmax) / 2.0 / w y_center = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{classes.index(name)} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}") if lines: txt_name = os.path.splitext(os.path.basename(xml_path))[0] + '.txt' with open(os.path.join(out_dir, txt_name), 'w') as f: f.write('\n'.join(lines)) # 用法 classes = ['bottle', 'can', 'box', 'ball'] os.makedirs('labels_train', exist_ok=True) for xml in os.listdir('xmls'): if xml.endswith('.xml'): xml_to_yolo(os.path.join('xmls', xml), 'labels_train', classes)

这个脚本的核心逻辑是把像素坐标转成归一化坐标。注意几个关键点:图片宽高取自xml里的size节点,而不是从图片文件读取,因为标注时的图片分辨率可能和你现在手里的不完全一致,以标注时记录的为准;转换后的坐标保留6位小数,精度足够训练使用,不用纠结更多位数;如果xml里出现了classes列表之外的类别,脚本直接跳过,避免把脏数据带进训练集。转换完之后,强烈建议随机打开几张图,把txt画上去对比原标注,这一步能拦截90%的坐标错误。

3.3 划分train/val并生成YOLOV5需要的路径索引文件

标注整理好之后,接下来要划分训练集和验证集。经典的划分比例是8:2或9:1,小车数据集一般量不大,我习惯用随机抽样的方式固定随机种子,保证多次转换结果一致:

import os import random from shutil import copy2 src_img = 'images_all' src_lbl = 'labels_all' train_img = 'dataset/images/train' train_lbl = 'dataset/labels/train' val_img = 'dataset/images/val' val_lbl = 'dataset/labels/val' for d in [train_img, train_lbl, val_img, val_lbl]: os.makedirs(d, exist_ok=True) random.seed(42) imgs = [f for f in os.listdir(src_img) if f.endswith('.jpg')] random.shuffle(imgs) split = int(len(imgs) * 0.8) for img in imgs[:split]: base = os.path.splitext(img)[0] copy2(os.path.join(src_img, img), train_img) copy2(os.path.join(src_lbl, base + '.txt'), train_lbl) for img in imgs[split:]: base = os.path.splitext(img)[0] copy2(os.path.join(src_img, img), val_img) copy2(os.path.join(src_lbl, base + '.txt'), val_lbl) print('train:', len(imgs[:split]), 'val:', len(imgs[split:]))

这里用shutil.copy2保留文件时间戳等信息,方便后面排查;随机种子固定为42,改比例时不会影响顺序可复现性。注意划分的是图片文件名集合,标签跟随图片同名移动,绝对不能分别shuffle,否则train和val的配对关系就乱了。划分完成后,在数据集的根目录执行find . -name "*.txt" | wc -l对比一下labels总数是否等于images总数,数值对不上就先别训练。

注意:YOLOV5新版通过data.yaml自动扫描目录,不需要额外的train.txt和val.txt索引文件。如果你在旧版代码或网上教程里见到txt索引,那是v3之前的写法,不要混用。

4. 智能小车场景下的数据坑:光照、视角和类别不均衡

4.1 小车摄像头视角的特殊性:低机位、运动模糊和反光

普通目标检测数据集大多是手持相机、俯视或平视拍摄,而智能小车的摄像头高度通常只有十厘米上下,视角更低、离目标更近,这两种数据训练的模型,部署时性能差距明显。低机位拍摄意味着目标的透视形变更强,同样的瓶子,俯视图看到的是瓶盖圆面,小车平视看到的是瓶身侧面和标签纹理;运动模糊就更直接,小车跑起来摄像头抖动,画面比静态拍摄糊一截;开灯巡检场景还会遇到地面反光,目标边缘在反光区域直接消失。

所以整理或扩充数据集时,我的原则是:以实际小车摄像头拍摄的画面为主,网上找的图片最多做补充,不能反过来。拍的时候尽量复现真实工况,小车以巡线速度跑,拍一段视频然后抽帧,比静态摆拍更接近推理时的输入分布。实拍画面整理时我一般直接抽帧而不是一张张拍照,用ffmpeg一条命令就能从视频里按固定间隔取帧:ffmpeg -i run_video.mp4 -vf fps=10 frame_%04d.jpg,fps=10表示每秒抽10帧。帧率太高会造成大量相似图片,训练时冗余样本会放大某一段轨迹的场景特征,导致模型对其他位置的目标泛化变差。抽完帧再按时间间隔二次采样,尽量覆盖不同位置、不同角度。如果发现验证集mAP不低但小车行驶中漏检,优先怀疑训练图和实拍图存在视角、清晰度上的分布差异。

4.2 类别不均衡:少数类只占5%时怎么处理

物品识别里类别不均衡是很常见的情况,比如"易拉罐"出现几百次,"乒乓球"只有几十次。模型训练时损失被多数类主导,少数类学不到位,最终表现为多数类recall很高,少数类几乎不报,或者全部误判成多数类。在小车抓取、分拣这类任务里,漏检一个少数类目标可能直接影响执行逻辑,比误检更致命。

处理方式按优先级排序。第一步先统计类别分布,把所有txt读一遍,统计每一行的class id出现次数;第二步对少数类做简单过采样,把包含少数类的整张图复制几份放进训练集,注意复制的是图片加对应的标注txt,不是只复制txt;第三步如果还不行,可以针对少数类做目标级复制粘贴增强,把小目标的标注区域扣出来贴到背景图上,同时生成对应标注。多数类不做处理,保持原始数量即可。过采样之后train和val之间仍然不能有重叠,验证集保持原始分布,否则mAP会虚高。

4.3 数据增强参数:mosaic、mixup与hsv的合理边界

YOLOV5默认开启的增强里,mosaic和mixup对小车场景影响最大。mosaic把四张图拼成一张训练,能显著提升小目标检测能力,但训练到后期mosaic会拖慢收敛、让loss波动变大,常见的习惯是训练超过100个epoch时,在最后30个epoch把mosaic关闭,让模型在接近真实分布的图上精调。mixup把两张图半透明叠加,对遮挡鲁棒性有帮助,但叠加后的画面和真实场景差异较大,不要开太强。

hsv增强里,我一般保留默认的hsv_h=0.015、hsv_s=0.7、hsv_v=0.4,这个组合已经覆盖了光照变化。不过要注意,如果小车识别的是颜色敏感物体,比如颜色区分是否成熟的果实,hsv_h建议降到0.01以内,否则色相抖动会让模型混淆不同颜色类别的物体。fliplr水平翻转增强默认开着,但对于有方向语义的目标要关掉,比如瓶身上的文字标签、巡线赛里的方向箭头,翻转之后语义就反了。

参数默认值小车场景建议说明
mosaic1.0前中期1.0,最后30个epoch关小目标增益大,后期拖收敛
mixup0.00~0.2遮挡场景可开,别超过0.5
fliplr0.5有方向性目标时设0文字、箭头翻转后语义错误
hsv_h0.015颜色敏感时降至0.01色相抖动太大导致颜色混淆

增强参数的调整要在训练配置里改,不是在数据集里改。把增强写进图片本身会让验证集失真,正确做法是在train.py对应的hyp.yaml里只调训练侧增强,验证集始终用原始未增强的图。

5. 训练前必查的五个坑:YOLOV5数据集常见问题与排查

5.1 标签越界:训练时报越界错误或标签画错位置

现象:训练日志里出现类似IndexError: index 12 is out of bounds for axis 0 with size 8,或者debug里看到标签框超出图片边界。

原因:txt里的归一化坐标为负或大于1,通常来自转换脚本没除以图片宽高,或标注框本身超出图片像素范围。YOLOV5在读取标签时会做边缘裁剪,超界不致命,但标注框位置已经失真,模型学到的是偏了心的目标。

解决:写个脚本扫一遍全部txt,把任何坐标小于-0.01或大于1.01的标签打出来,再回原图核实。修正的办法不是手改坐标,而是回到标注源或转换脚本重跑。我一般在dataloader里临时加一段打印逻辑,把超界的标注打出来,先定位是哪张图再修转换脚本。

5.2 class not in dataset警告:某个类别id在数据集里不存在

现象:训练开始阶段日志显示WARNING: class 3 not in dataset,但进程不中断,训练照常跑。

原因:data.yaml里nc配了5,names长度也写了5,但txt里实际只出现了0到3的类别,类别4在整个数据集一张都没有;或者names顺序和你标注时用的class id对不上,某个id对应的名称为空。

解决:统计全部txt的类别出现频率,把没有样本的类别从names里移除,nc改为实际出现数。这个警告的危害是静默的,它不报错,但模型最后一层的输出维度包含空类别,虽然不影响收敛,会浪费一部分计算量,且拉低mAP的统计口径。

5.3 图片读取异常:EXIF旋转和RGBA通道

现象:训练时个别图片无法加载,或者模型学出来的目标角度始终偏转90度。

原因:手机拍的JPG带EXIF方向信息,有的库读出来是正的,有的库不解析EXIF直接按像素原始方向显示,导致标注和实际画面旋转不一致;另一种情况是图片是RGBA四通道,YOLOV5默认按三通道处理,读图时报通道数不符。

解决:训练前统一转格式,用脚本把所有图片转成RGB的JPG,并去掉EXIF方向;PNG带透明通道的合成一张纯色背景再存JPG。这个预处理直接做一次,比每次训练都排查来得省事。

5.4 mAP很高但小车实测漏检:数据集和部署环境的分布差

现象:验证集mAP 0.92,模型部署到小车上,实拍画面里目标明明很清楚,就是识别不到,偶尔框还乱跳。

原因:这是数据集问题里最隐蔽的一类。训练图片是静态高清,部署画面是运动模糊加低照度;训练输入resize到640,部署输入可能被工程代码resize到416或320;另外验证集里如果包含和训练集相似的图,高mAP本身就可疑。

解决:训练时就把输入尺寸定成和部署一致,不要训练用640、部署用320。然后把小车实拍的视频抽帧,单独做一个小而真的评测集,不用标注很多,三五十张就够,跑一遍val脚本看实际准确率。这一步通常能暴露差距,比盯训练mAP有用得多。

5.5 data.yaml和实际标注对不上:names顺序错了

现象:训练正常,推理时框和类别对不上,比如明明标的是盒子,模型一直输出瓶子。

原因:标注工具导出时class id从0开始顺序编号,但你参考的文档或示例里names顺序不同,比如别人数据集的names是['box','bottle'],你直接套用,类别映射就整体错位。

解决:这类问题有一个笨但有效的办法:训练完看val_batch预测图,上面会印出预测类别名,拿实际图片对照立刻能发现错位。改names顺序比重新训练代价小,改完再训一遍。从那以后我每次开局先输出一遍names和id对照表,再往模型里塞数据。

6. 半小时跑通快速基线:用训练日志验证数据集本身的质量

6.1 用默认超参跑30个epoch当体检

数据集整理完,不要急着调优,先用一套完全默认的超参做一次快速基线,目的是体检数据集,不是追求精度。命令长这样:

python train.py --data dataset/data.yaml --weights yolov5s.pt --img 640 --epochs 30 --batch 16 --project baseline_check

参数说明:weights用yolov5s.pt,体积小、训练快,只验证数据链路;img固定640,和部署尺寸保持一致,后面部署到树莓派或者其他边缘设备时,再根据实际算力换更小的输入尺寸;epochs设30个,一般跑到第10到15个epoch就能看出loss是否下降;batch大小根据显存调整,6G显存用16,12G以上可以加到32。关键看两个指标:loss曲线是否平滑下降,以及第1个epoch的P、R、mAP是否从接近0的水平开始上升。如果loss从一开始就是nan,先查标签数据和图片数据,不需要查模型配置。

6.2 盯紧三个日志文件:train_batch、val_batch、results.csv

训练输出目录里,除了终端日志,最值得看的是三个文件。第一个是results.csv,直接打开看epoch、box_loss、cls_loss、mAP这几列的变化趋势,loss锯齿状上下是正常的,但必须整体下行;第二个是train_batch0.jpg,这是第一张训练batch图,图上画了标注框,一眼就能看出标签和实际目标是否对齐;第三个是val_batch0.jpg,展示验证集的预测结果,框是否准、类别名对不对都在上面。

我的习惯是训练跑完,先看train_batch0.jpg,再开results.csv,最后才看mAP数字。如果图片上标签框明显偏移,说明转换脚本有坐标错误,这时的mAP数字再好看也不能信。这次快速基线跑完,再根据第4章的增强策略和第5章的排查清单调整数据,之后才进入正式训练。从那以后我每次整理数据集都强制走一遍这个流程:转格式、划分布、画框检查、跑30个epoch基线、看三份日志,这套流程看着笨,但真能拦住大部分数据侧的隐蔽问题,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询