☰
智能小车物品识别实战:YOLOv5目录格式与数据集构建全解析
2026/10/9 14:45:07 网站建设 项目流程

简介:本资源为面向目标检测入门与智能小车视觉项目实践者的YOLOv5格式数据集,聚焦智能购买场景下的物品识别任务,可用于训练与验证单类别检测模型,帮助读者快速跑通从数据加载到边界框可视化的完整流程。压缩包共1777个文件,以jpg图像与txt标注文件为主,另含1个可视化py脚本和1个png示意图,整体约26.48MB,目录按YOLOv5规范组织,无需额外转换即可直接投入训练。数据分为训练集710张图片与711个标签、测试集177张图片与177个标签,图像为416×416的RGB图片,类别仅purchase一类,并附检测类别字典文件。可视化脚本随机传入一张图片即可绘制边界框并保存至当前目录,无需修改即可运行,便于检查标注质量与模型效果。目前已有157人学习,适合作为课程设计、竞赛验证或算法调试的轻量级数据基础。

1. 智能小车物品识别:为什么 YOLOv5 目录格式是绕不开的第一道坎

做智能小车物品识别的人,十有八九在数据集这一步翻过车。模型结构抄得再顺、训练脚本跑得再溜,只要目录格式和标注文件对不上,训练照样在第一个 epoch 就报错退出。YOLOv5 目录格式之所以成为事实标准,是因为它把「图片放哪、标签放哪、类别怎么编号」这三件事用一套极简约定固定下来,让数据加载器不用猜。智能购买场景下的图像目标检测,本质是让小车摄像头识别货架或桌面上的物品,输出类别和位置,再驱动抓取或结算逻辑。这套数据集格式适合两类人:一是刚接手智能小车视觉模块、需要快速跑通训练闭环的嵌入式开发者;二是手里有一批标注数据、想迁移到 YOLOv5 训练管线却卡在格式转换上的算法同学。下面从目录结构讲到训练验证,把每一步的参数和坑都摊开。

2. YOLOv5 目录格式拆解:images、labels 与 data.yaml 的三角关系

2.1 标准目录树长什么样

YOLOv5 官方推荐的数据集目录结构并不复杂,但每一层的命名和位置都有硬性要求。常见做法是建一个数据集根目录,下面分 images 和 labels 两个平行文件夹,各自再分 train、val,有时加 test。标签文件必须和图片文件同名,只是扩展名从 .jpg 换成 .txt。这个「同名不同扩展」的约定是数据加载器定位标签的唯一依据,改一个字都会导致找不到标签。

dataset/ ├── images/ │ ├── train/ │ │ ├── item_0001.jpg │ │ └── item_0002.jpg │ └── val/ │ ├── item_0101.jpg │ └── item_0102.jpg ├── labels/ │ ├── train/ │ │ ├── item_0001.txt │ │ └── item_0002.txt │ └── val/ │ ├── item_0101.txt │ └── item_0102.txt └── data.yaml

上面这段目录树里,images/train 和 labels/train 是成对出现的,val 同理。注意 labels 下不要放任何图片,images 下也不要放 txt,混放会让加载器把非图片文件当样本读,直接抛异常。data.yaml 放在根目录,路径写法有两种:绝对路径最稳,相对路径则相对于训练时的工作目录,容易因为启动位置不同而失效。

2.2 标签 txt 的每一行到底在写什么

每个标签 txt 文件里,一行对应一个目标框,格式是「类别编号 中心x 中心y 宽 高」,后四个值都是归一化到 0 到 1 之间的小数。类别编号从 0 开始,和 data.yaml 里 names 列表的顺序严格对应。这里最容易翻车的是归一化:有人直接拿像素坐标除以图片宽度,忘了中心点要先算 (xmin+xmax)/2,结果框整体偏移。

# 把 VOC 的 xmin,ymin,xmax,ymax 转成 YOLO 归一化格式 def voc_to_yolo(xmin, ymin, xmax, ymax, img_w, img_h): # 计算中心点坐标 cx = (xmin + xmax) / 2.0 cy = (ymin + ymax) / 2.0 # 计算宽高 w = xmax - xmin h = ymax - ymin # 全部除以图片尺寸做归一化,保留 6 位小数足够 return round(cx / img_w, 6), round(cy / img_h, 6), round(w / img_w, 6), round(h / img_h, 6) # 示例:一张 640x480 的图,框在 (100,80) 到 (300,280) print(voc_to_yolo(100, 80, 300, 280, 640, 480)) # 输出 (0.3125, 0.375, 0.3125, 0.416667)

这段函数的关键参数是 img_w 和 img_h,必须传原图真实尺寸,不能传网络输入尺寸。有人图省事统一按 640 算,遇到非 640 宽的图就全错。round 到 6 位是经验值,YOLOv5 内部会再处理,但保留太少位数在极端小目标上会有精度损失。类别编号那一位是整数,不要写成 0.0 这种浮点,虽然部分版本能容错,但没必要给自己埋雷。

2.3 data.yaml 里三个字段决定训练能不能启动

data.yaml 是数据集和训练脚本之间的契约,最少要写 path、train、val、nc、names 五项。path 是数据集根目录,train 和 val 是相对 path 的图片路径,nc 是类别数量,names 是类别名列表。nc 和 names 长度必须一致,差一个就会在算损失时维度不匹配。

# data.yaml 示例:智能小车物品识别,5 类物品 path: /data/smart_cart_dataset # 数据集根目录,绝对路径最稳 train: images/train # 相对 path 的训练图片目录 val: images/val # 相对 path 的验证图片目录 nc: 5 # 类别数量,必须和 names 长度一致 names: # 类别名,顺序即类别编号 0-4 - bottle - box - can - bag - cup

path 用绝对路径能避免「换个终端就找不到数据」的玄学问题。train 和 val 写相对路径时,YOLOv5 会拼在 path 后面,所以不要重复写 dataset 前缀。names 里的顺序就是标签文件里类别编号的含义,改顺序等于把所有标签的语义打乱,必须和标注阶段约定的一致。如果只有训练集没有验证集,可以把 val 指向 train,但这样验证指标会虚高,不建议。

3. 从零构建智能购买物品检测数据集:采集、标注到划分

3.1 采集阶段就要想好类别边界

智能购买场景的物品识别,类别定义直接决定后面标注和训练的工作量。常见做法是按「抓取策略」而不是按「商品名称」来分类,比如所有圆柱形可抓取物归为 can,所有软质袋装归为 bag。这样类别数少、类间差异大,小车抓取逻辑也好写。如果按具体商品名分几十类,标注成本翻几倍,模型在小样本类上还容易崩。采集时每个类别至少 200 张,光照覆盖顺光、逆光、室内暖光三种,角度覆盖正面、侧面、俯视。图片分辨率不用追求 4K,长边 1280 足够,太大反而拖慢训练。

3.2 标注工具选型与导出格式

标注工具常见的有 labelImg、CVAT、Labelme 几类。做目标检测矩形框,labelImg 最轻量,导出直接选 YOLO 格式,省掉转换步骤。CVAT 适合多人协作,但导出后往往还是 VOC 或 COCO,需要自己转。Labelme 默认是多边形,做实例分割才用,纯检测别选它。标注时框要贴紧目标边缘,留白超过 10 像素就会让回归分支学偏。遮挡目标如果露出面积小于 30%,建议标为 difficult 或不标,硬标会让模型在正常样本上反而犹豫。

# 批量检查标签文件是否有越界或空文件 import os def check_labels(label_dir): issues = [] for fname in os.listdir(label_dir): if not fname.endswith('.txt'): continue fpath = os.path.join(label_dir, fname) if os.path.getsize(fpath) == 0: issues.append(f'{fname}: 空标签文件') continue with open(fpath) as f: for i, line in enumerate(f): parts = line.strip().split() if len(parts) != 5: issues.append(f'{fname} 第{i+1}行: 字段数不是5') continue vals = [float(v) for v in parts[1:]] if any(v < 0 or v > 1 for v in vals): issues.append(f'{fname} 第{i+1}行: 归一化值越界') return issues # 用法:打印所有问题标签 for msg in check_labels('/data/smart_cart_dataset/labels/train'): print(msg)

这个检查脚本在标注完成后跑一遍,能提前发现空文件、字段缺失、归一化越界三类高频问题。空标签文件在 YOLOv5 里会被当作「无目标」样本,少量可以,大量会让模型学不到正样本。字段数不是 5 说明标注导出时格式选错了,比如导成了 VOC 的 xml 内容。归一化值越界通常是坐标算错或图片尺寸传错,必须在训练前修掉。

3.3 训练集验证集划分的两种可靠做法

划分比例常见 8:2 或 9:1,但比比例更重要的是「同场景不跨集」。如果同一段视频抽帧的图片既在训练集又在验证集,验证指标会虚高,模型实际泛化能力被高估。可靠做法是按采集批次划分:第 1 到 8 批进训练,第 9 到 10 批进验证。另一种是按时间划分,先采的进训练,后采的进验证,更接近真实部署时遇到新数据的场景。

# 按文件名前缀批次划分:batch01-08 进 train,batch09-10 进 val for f in images/all/batch0[1-8]*.jpg; do cp "$f" images/train/ cp "labels/all/$(basename "$f" .jpg).txt" labels/train/ done for f in images/all/batch09*.jpg images/all/batch10*.jpg; do cp "$f" images/val/ cp "labels/all/$(basename "$f" .jpg).txt" labels/val/ done

这段 shell 按文件名前缀做批次划分,图片和标签同步复制,保证同名对应。basename 去掉 .jpg 再拼 .txt 是标签定位的标准写法。如果文件名没有批次前缀,可以先重命名再加前缀,别用随机划分糊弄,随机划分在物品识别这种场景下很容易把同一物品的不同角度分到两边,指标好看但上线就露馅。

4. 用 YOLOv5 训练智能小车物品识别模型:命令、参数与验证

4.1 训练命令与关键参数含义

YOLOv5 的训练入口是 train.py,核心参数有 --data、--weights、--img、--batch、--epochs、--device。--data 指向 data.yaml,--weights 用预训练权重能显著加快收敛,--img 是网络输入尺寸,--batch 受显存限制,--epochs 一般 100 到 300。智能小车物品识别类别少、场景相对固定,从 yolov5s 预训练权重起步,100 epoch 通常够用。

# 从预训练权重开始训练,输入 640,批次 16,100 轮 python train.py \ --data /data/smart_cart_dataset/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --device 0 \ --project runs/train \ --name cart_item_v1

--img 640 是 YOLOv5 的默认输入,改成 416 能提速但小目标召回下降,改成 1280 精度可能升但显存翻倍。--batch 16 在 8G 显存上跑 yolov5s 比较稳,报显存不足就降到 8。--device 0 指定第一块 GPU,CPU 训练把 0 换成 cpu,但速度会慢一个数量级。--name 给这次训练起个可追溯的名字,后面看结果和导出权重都靠它定位。

4.2 训练过程看哪些指标才算没白跑

训练日志里重点看三个:box_loss、obj_loss、mAP@0.5。box_loss 下降说明框回归在学,obj_loss 下降说明目标置信度在学,mAP@0.5 是综合指标。前 10 个 epoch 指标波动大是正常的,30 epoch 后还不降就要查数据和参数。如果 box_loss 降但 mAP 不涨,多半是标签框质量差或类别不平衡。如果 obj_loss 一直高,检查是否有大量背景图被误标成正样本。

# 从 results.csv 里提取每轮 mAP,找最佳 epoch import csv best_map, best_epoch = 0, 0 with open('runs/train/cart_item_v1/results.csv') as f: reader = csv.DictReader(f) for row in reader: # 列名带空格,strip 一下 m = float(row[' metrics/mAP_0.5'].strip()) e = int(row['epoch'].strip()) if m > best_map: best_map, best_epoch = m, e print(f'最佳 mAP@0.5={best_map:.4f} 出现在第 {best_epoch} 轮')

results.csv 是 YOLOv5 每轮自动写的训练记录,列名前后有空格,读取时要 strip。找到最佳 epoch 后,可以用 --weights 加载对应轮次的权重做推理,不一定用最后一轮,因为后面可能过拟合。这个脚本在调参阶段很实用,能快速对比不同配置的峰值表现。

4.3 推理验证:拿小车实拍图跑一遍

训练完不能只看验证集指标,要拿小车摄像头实拍的、没参与训练的图跑推理,看框的位置和类别是否符合预期。用 detect.py 指定权重和图片目录,--conf 控制置信度阈值,--iou 控制 NMS 的 IoU 阈值。

# 用训练好的权重对实拍图做推理 python detect.py \ --weights runs/train/cart_item_v1/weights/best.pt \ --source /data/cart_test_photos \ --img 640 \ --conf 0.4 \ --iou 0.45 \ --save-txt

--conf 0.4 意味着置信度低于 0.4 的框不输出,智能小车抓取场景宁可漏检也别误抓,可以调到 0.5。--iou 0.45 是 NMS 合并重叠框的阈值,物品密集摆放时调低到 0.3 能减少框粘连。--save-txt 把检测结果存成 YOLO 格式 txt,方便和标注对比算实际精度。实拍图推理翻车最常见的原因是训练集光照和实拍差异大,补采对应光照的图重训比调参有效。

5. 避坑与排查:智能小车物品识别数据集最常见的五个翻车点

5.1 训练报「No labels found」但标签明明存在

现象是启动训练几秒后报错退出,提示找不到标签。原因通常是 images 和 labels 的目录层级不对,比如 labels 下多套了一层 train,或者标签文件名和图片名大小写不一致。解决方法是打印 data.yaml 里 train 路径下的图片列表,再打印对应 labels 路径,逐个人工核对同名文件是否存在。Linux 下大小写敏感,Item_0001.jpg 和 item_0001.txt 不算同名。

5.2 训练正常但 mAP 始终低于 0.3

现象是 loss 在降但 mAP 卡在低位。原因多半是类别编号和 names 顺序错位,或者标注框大面积偏移。排查方法是抽 10 张训练图,用可视化脚本把标签框画到图上,肉眼看框是否贴住目标。如果框整体偏移,检查归一化时用的图片尺寸是否和实际一致。如果框位置对但类别乱,检查 data.yaml 的 names 顺序和标注时的类别映射表。

5.3 验证集指标高但实拍推理一塌糊涂

现象是验证 mAP 0.9 以上,实拍图却大量漏检误检。原因是训练集和验证集同源,实拍图分布不同。解决方法是按采集批次划分数据集,并补采实拍场景的图进训练集。另一个常见原因是验证集图片分辨率统一,实拍图分辨率不同,推理时 --img 和实际不匹配,把 --img 设成接近实拍长边的值。

5.4 显存溢出导致训练中断

现象是训练到一半报 CUDA out of memory。原因是 batch 太大或 --img 太高。解决方法是把 --batch 减半,或把 --img 从 640 降到 512。YOLOv5 支持 --multi-scale,但小显存下开多尺度会加剧波动,建议先关掉。如果减 batch 后指标下降明显,用 --accumulate 做梯度累积,等效大 batch 但显存占用低。

5.5 标签文件里混入非目标类别

现象是模型把背景里的货架、桌面也框出来。原因是标注时把一些「看起来像目标」的背景物体也标了,或者类别定义太宽泛。解决方法是重新审视类别定义,把容易混淆的背景物单独设一类或明确不标。YOLOv5 对背景样本有一定容忍度,但大量误标正样本会让 obj_loss 居高不下,最终影响所有类别的精度。

6. 进阶技巧:用锚框聚类和类别权重把智能购买识别再提一档

默认锚框是基于 COCO 通用数据集聚类的,智能小车物品识别的目标尺寸分布往往和 COCO 差很远,直接用默认锚框会让回归分支起步就偏。用训练集标签跑一遍 k-means 聚类,生成适配自己数据的锚框,替换模型配置里的 anchors,通常能带来 1 到 3 个点的 mAP 提升。聚类时把图片缩放到网络输入尺寸再算框的宽高,k 取 9,和 YOLOv5 的三层检测头对应。

# 用 k-means 对训练集标签做锚框聚类 import numpy as np from sklearn.cluster import KMeans def load_boxes(label_dir, img_size=640): boxes = [] for fname in os.listdir(label_dir): if not fname.endswith('.txt'): continue with open(os.path.join(label_dir, fname)) as f: for line in f: _, _, _, w, h = map(float, line.split()) # 反归一化到像素尺寸 boxes.append([w * img_size, h * img_size]) return np.array(boxes) boxes = load_boxes('/data/smart_cart_dataset/labels/train') kmeans = KMeans(n_clusters=9, random_state=0).fit(boxes) anchors = kmeans.cluster_centers_ # 按面积排序,小框给浅层检测头 anchors = anchors[np.argsort(anchors[:, 0] * anchors[:, 1])] print('聚类锚框(宽 高):') for a in anchors: print(f'{a[0]:.1f} {a[1]:.1f}')

这段代码把标签里的归一化宽高还原成像素尺寸再聚类,k=9 对应 YOLOv5 的 3 个检测层各 3 个锚框。聚类结果按面积从小到大排序,小的给负责小目标的浅层,大的给深层。替换锚框后要重新训练,不能接着旧权重微调,因为检测头的先验变了。如果类别样本数差异大,还可以在训练时给稀有类加权,YOLOv5 的损失函数里可以通过调整类别权重来缓解,但权重别设太猛,否则稀有类精度上去了,常见类反而崩。

我自己踩过最深的坑是锚框聚类时忘了把图片缩放到网络输入尺寸,直接用原图尺寸算,结果聚类出的锚框偏大,小目标召回掉了一截。后来养成习惯,任何和尺寸相关的计算,先确认当前处于哪个坐标系,是原图、网络输入还是特征图。这个习惯比任何调参技巧都值钱。希望帮到你。

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

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

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

立即咨询