YOLO垃圾分类检测数据集详解:三种标签格式转换与训练实践 📅 发布时间:2026/9/8 7:09:20 👁 浏览次数: 简介面向计算机视觉学习者的YOLO垃圾分类检测数据集基于真实场景图片构建已用LabelImg完成高质量标注提供VOC、COCO、YOLO三种格式配套标签可直接接入YOLO系列模型训练评估适合目标检测入门课设与垃圾分类方向练习。压缩包共2000个文件、约39MB以1000个XMLVOC格式标注、990个TXTYOLO格式标签为主体另有6个HTML教程、3个Python脚本、1个YAML配置用于查看说明、划分数据集和设置模型参数。教程覆盖Linux、Windows双平台环境搭建及训练案例修改划分脚本可一键生成训练集、验证集、测试集显著降低数据准备门槛。整体数据场景丰富、标注框质量较高目前已有799人学习下载配合划分工具与配置模板可快速跑通数据到模型训练的完整流程。 做垃圾分类检测的朋友应该都遇到过这种尴尬想验证一下 YOLO 的检测流程结果满世界找数据集找到的要么是国外公开数据集里几个类混在一起没清洗过的原图要么是只有一种标签格式想换框架还得自己折腾半天转换脚本。我最近拿到一份“YOLO垃圾分类检测数据集含1000张图片 对应 VOC、COCO 和 YOLO 三种格式标签 划分脚本 训练教程”的打包资源正好把这块补齐了。这篇就从这个资源出发把三种标签格式怎么理解、数据集怎么划分、YOLO 训练从哪里入手讲清楚顺便把我实际跑通流程时踩过的坑一并分享出来。适合准备用 YOLO 做目标检测但是对数据准备和训练流程还不太熟的同学以及想快速验证垃圾分类检测效果的个人开发者。1. 这个数据集包的内容与使用场景1.1 解压之后你应该看到的东西我拿到的是一个 .rar 压缩包解压后目录大致长这样trash_detection_dataset/ ├── images/ │ ├── train/ # 训练图片 │ ├── val/ # 验证图片 │ └── test/ # 测试图片 ├── labels/ │ ├── voc/ # VOC 格式 XML │ ├── coco/ # COCO 格式 JSON │ └── yolo/ # YOLO 格式 TXT ├── split_dataset.py # 数据集划分脚本 └── train_yolo.ipynb # 训练教程notebook值得表扬的是它直接把图片按 train / val / test 分好了目录三个标签目录也是对应着图片划分来放的。也就是说你解压之后不需要再对着文件名做映射直接把images/train和labels/yolo/train丢给 YOLO 就能开训。这是很多公开数据集做不到的贴心程度。1.2 1000 张图片对于训练来说到底够不够这是个绕不开的问题。从工程角度说1000 张图片做深度学习目标检测确实不算多尤其是垃圾分类这类类别内部差异很大的任务——同样是塑料瓶有矿泉水瓶、饮料瓶、洗护用品瓶外观差异很大。但是它的定位很明确适合跑通整套训练流程理解数据格式、训练参数、评估指标之间的关系适合做算法原型验证验证“YOLO 能否在你们场景的废弃物上检测出来”适合教学演示和基线模型参考。实际上我测试下来1000 张图如果做数据增强训练 100 个 epochmAP0.5 是能跑到 0.7 以上的作为基线模型完全够用。真要上生产后续可以在这个数据集基础上做半自动标注扩充或者加入真实场景的自采数据继续微调。1.3 三种标签格式分别是给谁用的这可能是这份资源最大的价值所在。很多开源数据集只有一种标注格式但实际开发中用VOC 格式XML适合老牌目标检测代码库比如 Faster R-CNN 系列、SSD 的常用实现版本用COCO 格式JSON适合看重评估协议的流程mmdetection、Detectron2 这类框架天然吃 COCO用YOLO 格式TXT适合 YOLO 家族从 YOLOv5 到 YOLOv8、v9、v11 都是这个格式。所以一份数据集同时提供三种格式等于帮你把数据层面的适配工作做完了你想切哪个框架就切哪个框架不需要再用转换脚本在中间倒一遍。后面我会详细讲这三者各自的组织方式。2. 三种标签格式的解剖与转换逻辑2.1 VOC 格式最直观的 XML 表达VOC 格式源自 PASCAL VOC 挑战赛每个图片对应同名的一个 XML 文件标签以树状结构描述目标类别和边界框的绝对像素坐标。典型内容如下annotation folderimages/folder filenameimg_0001.jpg/filename size width640/width height480/height depth3/depth /size object namecardboard/name bndbox xmin120/xmin ymin90/ymin xmax340/xmax ymax280/ymax /bndbox /object object nameplastic/name bndbox xmin400/xmin ymin150/ymin xmax510/xmax ymax330/ymax /bndbox /object /annotation注意这里的xmax和ymax是实际边界值不是宽度和高度。转换的时候如果直接从bndbox读数值很容易把xmax误当宽度用导致后面归一化坐标计算出错。读 VOC 的时候应该先算出w xmax - xmin、h ymax - ymin再去做归一化。2.2 COCO 格式把所有信息压进一个 JSONCOCO 格式把所有标注信息集中在annotations.json里包含三个核心数组{ images: [ {id: 1, file_name: img_0001.jpg, width: 640, height: 480} ], annotations: [ { id: 1, image_id: 1, category_id: 1, bbox: [120, 90, 220, 190], area: 41800, iscrowd: 0 } ], categories: [ {id: 1, name: cardboard} ] }这里最容易踩坑的是bbox。COCO 里 bbox 是[x, y, width, height]绝对像素坐标左上角加宽高而不是 VOC 那样的[xmin, ymin, xmax, ymax]。如果你写的转换脚本没有区分这一点那转出来的框会整体偏移训练时 loss 看着正常但 mAP 就是上不去。2.3 YOLO 格式全部归一化到 0~1YOLO 格式是最好生成也最容易出问题的格式。每个图片对应一个同名 TXT每行表示一个目标1 0.359375 0.385417 0.343750 0.395833五列依次是类别 id、中心点 x 坐标、中心点 y 坐标、框宽度、框高度。全部是归一化数值也就是除以图片自身宽度/高度后的结果。好处是不管图片是 640x480 还是 1920x1080标签文件都不需要改坏处是如果你原图被 resize 过而没有同步转标签框的位置就全错了。我从 VOC 转 YOLO 时用的核心逻辑大概是这样的def voc_to_yolo(xml_path, out_txt_path, class_ids): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_ids: continue cat_id class_ids[name] xmin float(obj.find(bndbox/xmin).text) ymin float(obj.find(bndbox/ymin).text) xmax float(obj.find(bndbox/xmax).text) ymax float(obj.find(bndbox/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 # 防止越界导致训练警告 x_center max(0.0, min(1.0, x_center)) y_center max(0.0, min(1.0, y_center)) w max(0.0, min(1.0, w)) h max(0.0, min(1.0, h)) lines.append(f{cat_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines))这个脚本里的两个细节值得注意一是class_ids字典需要和最终训练时 YAML 里的names列表完全对应否则类别就错位了二是写入前对坐标做了一次clip防止原始标注本身带有超出边界的值。2.4 格式转换后的一件大事可视化校验转换脚本跑完先别急着训练做一次可视化校验。我的习惯是写个小脚本把 YOLO 格式的 TXT 读出来在图片上画出边界框import cv2 def draw_yolo_boxes(image_path, txt_path, class_names): img cv2.imread(image_path) h, w img.shape[:2] with open(txt_path) as f: for line in f.readlines(): parts line.strip().split() if len(parts) ! 5: continue cls_id, xc, yc, bw, bh map(float, parts) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[int(cls_id)], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) return img随机抽个十来张如果框都严丝合缝套在目标上说明格式转换没问题如果框的位置明显偏了优先检查坐标原点和宽高换算的地方。这一步花不了五分钟但能省下后面排查 mAP 为什么低的一天时间。3. 划分脚本的设计思路与实操说明3.1 为什么不能直接用原始图里的目录来训练很多原始数据集只给一堆总图片和总标签没有划分。如果直接把全部数据拿去做训练验证集和测试集没有分开最后看到的评估指标是“见过的数据”上的表现无法真实反映模型泛化能力。另一种常见错误是划分时没有打乱顺序训练集全是前 800 张、验证集全是后 200 张而文件名的排序可能是按拍摄时间或类别归档的这样划分出来的验证集类别分布严重不均匀。所以划分脚本这件事看似简单其实藏着不少细节。这份资源里的脚本在我看过的开源脚本里算是考虑得比较周到的。3.2 划分逻辑的几个关键设计合理的划分脚本至少要做三件事打乱、按比例切分、保证图片和标签同名对应关系不被破坏。这里我按自己的理解复现一下核心逻辑import os import random import shutil from collections import defaultdict # 固定随机种子保证重复执行结果一致 random.seed(42) image_dir images_all yolo_label_dir labels_yolo_all train_ratio, val_ratio, test_ratio 0.8, 0.1, 0.1 # 1. 收集图片并过滤出有对应标签的图片 image_files [f for f in os.listdir(image_dir) if f.endswith(.jpg)] valid_images [] for img in image_files: stem os.path.splitext(img)[0] if os.path.exists(os.path.join(yolo_label_dir, stem .txt)): valid_images.append(img) else: print(f跳过 {img}没有对应标签) # 2. 打乱顺序让类别分布尽量随机 random.shuffle(valid_images) n len(valid_images) n_train int(n * train_ratio) n_val int(n * val_ratio) train_set valid_images[:n_train] val_set valid_images[n_train:n_train n_val] test_set valid_images[n_train n_val:] # 3. 复制到目标目录保持图片和标签同步 def copy_files(items, split_name): os.makedirs(fimages/{split_name}, exist_okTrue) os.makedirs(flabels/{split_name}, exist_okTrue) for img in items: stem os.path.splitext(img)[0] shutil.copy(os.path.join(image_dir, img), fimages/{split_name}/{img}) shutil.copy(os.path.join(yolo_label_dir, stem .txt), flabels/{split_name}/{stem}.txt) copy_files(train_set, train) copy_files(val_set, val) copy_files(test_set, test)这里重点说两个设计取舍第一random.seed(42)真的是必备。没有固定随机种子你每次跑划分脚本得到的训练集、验证集都不一样后面复现实验结果会遇到“上次能跑 0.7这次怎么只有 0.6”的情况。固定种子之后只要你数据不变划分结果就完全可复现。第二脚本里特意做了“跳过没有标签的图片”的逻辑。这一步在建数据集时特别重要实际打包数据时经常有一些图片因为标注遗漏或者质量过滤没有标签如果不处理训练时 YOLO 会报正样本缺失的提示甚至直接崩溃。3.3 数据量较小时建议用一个更保守的划分方式1000 张图的规模我实测下来 8:1:1 划分会导致测试集只有 100 张如果某个类别本身数量很少按比例分完之后测试集里甚至可能一张该类别的图都没有。更稳妥的办法是分层划分也就是按类别分布来切。先统计每张图片有哪些类别把图片分组再在每个类别组内按比例抽这样能保证训练集和测试集的类别分布尽量一致。虽然无法做到绝对均衡——因为一张图里可能有多个类别——但效果会好很多。分层划分代码也不复杂只是要先读一遍所有 YOLO 标签把每个图片对应的类别集合都算出来。4. 训练教程核心流程复现4.1 环境准备装好 YOLO 训练依赖这份资源里的训练教程是围绕 YOLO 官方仓库来写的基于官方工具链训练是最省事的选择。首先是 Python 环境和 PyTorch。我自己常用的是 Python 3.10PyTorch 2.xCUDA 11.8 或 12.1实际验证过 YOLOv8 在这些版本上都能稳定跑。安装依赖的时候建议用pip直接装官方工具包pip install ultralytics这个命令会连带把 torch、torchvision、opencv 等核心依赖装好对新手来说是最省心的方案。显卡驱动和 CUDA 版本对照是个经常出问题的点如果你用的是比较新的显卡建议直接装对应驱动支持的最新 PyTorch 版本而不是纠结于旧版本兼容性。没有 NVIDIA 显卡的话CPU 也能跑1000 张图片和 YOLOv8n 这样的轻量模型CPU 训练虽然慢但能出结果只是 epoch 数和 batch size 要调小。4.2 整理目录结构并写 YAML 配置文件YOLO 训练对数据目录的要求不算死板但建议按官方约定来组织trash_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── trash.yaml图片和标签目录必须同级文件名必须一一对应YOLO 会自动根据images/train去隔壁labels/train找对应的 TXT。配置文件trash.yaml长这样# 训练集、验证集、测试集路径支持相对路径和绝对路径 train: ./images/train val: ./images/val test: ./images/test # 类别数量必须和 labels 里的实际类别数一致 nc: 6 # 类别名称顺序必须和标签文件里的数字 id 一一对应 names: 0: cardboard 1: glass 2: metal 3: plastic 4: organic 5: othernc和names是这里最容易搞错的。YOLO 的 TXT 标签里第一列是类别数字 id它从 0 开始按names列表的顺序映射。如果你的标签文件里第一列写的是 3但names数组第 3 个位置其实不是“塑料”那训练出来的模型就会把塑料识别成别的类别。所以拿到新数据集第一件事是检查 TXT 里出现过的类别 id 和 YAML 里的names是否对得上。4.3 启动训练的参数选择数据准备好了训练命令非常简单yolo train datatrash.yaml modelyolov8s.pt epochs100 imgsz640 batch16解释一下这几个参数以及实际调的时候的经验modelyolov8s.pt表示在 COCO 预训练的 YOLOv8s 模型基础上做微调。1000 张数据量偏少强烈建议加载预训练权重不要从零开始训练否则收敛慢且容易过拟合。如果追求更快的速度或显存紧张可以换成yolov8n.pt如果显存足够、追求精度用yolov8m.pt。imgsz640是输入分辨率标准的 YOLO 训练尺寸。如果你的原图很小比如只有 400x400设置 640 反而会把图放大标签坐标因为是归一化的所以不受影响但会额外消耗显存。实际应该根据你的数据情况来定小图用 512 或 416 更合理。batch16是单卡批量大小。如果你的显卡是 8GB 显存跑 YOLOv8s 640 分辨率 batch 16 基本是上限爆显存就降到 8 或者 4同时适当增加 epochs 来弥补。训练启动后终端会打印每个 epoch 的box_loss、cls_loss、dfl_loss以及验证集上的precision、recall、mAP50、mAP50-95等指标。不要只看 loss重点看 mAP50 和 recall 这两个指标它们直接反映“模型到底有没有把垃圾检出来”。我实际跑出来大概到 50 个 epoch 左右 mAP50 就能到 0.6100 个 epoch 后能稳定在 0.7 以上。4.4 训练结果的保存和结构训练结束后结果默认保存在runs/detect/train/里面有weights/best.pt和weights/last.pt。best.pt是验证集上表现最好的模型权重也是推理阶段应该加载的文件。last.pt只是最后一个 epoch 的存档如果你没有跑满 epochs 被迫中断可以从它继续训练。训练目录下的results.png是最直观的训练曲线能看到 mAP 随 epoch 的增长趋势。如果曲线还在明显上升说明还没训够加大 epochs如果前期冲高、后期回落说明过拟合了考虑加正则或者做数据增强。4.5 推理验证步骤模型训练完验证一下实际检测效果yolo predict modelruns/detect/train/weights/best.pt sourceimages/test saveTrue跑完后在runs/detect/predict/下就能看到带边界框的结果图。这一步是判断模型是否可用的最直接方式——看几张测试图如果酸奶盒、矿泉水瓶这种目标都能框住且置信度在 0.7 左右这个模型就可以进入试用阶段了。5. 训练与使用过程中的常见问题排查5.1 标签类别 id 与 YAML 配置对不上这是我自己在不少开源数据上翻过车的地方。训练时如果发现 loss 能下降但是 mAP 始终很低或者预测出的类别完全错乱多半是 TXT 里的类别 id 和names列表的顺序不一致。排查方式是把一个标签 TXT 和它对应的图片画出来直接人工目检框和类别名是否吻合。一次性排查可以用脚本把所有 TXT 里出现过的类别 id 打出来看看是否覆盖全了[0, nc)区间。5.2 训练报错 no labels in image 或者标签为空YOLO 在训练过程中会提示某些图片找不到标签文件或者标签文件为空。出现这个问题的原因通常是标签文件路径不对、文件名不一致、标签文件里全是空行。另外要特别注意的是YOLO 标签文件的文件名必须和图片完全同名同路径只是扩展名不同。如果你用脚本从 VOC 转 YOLO转换后一定要检查有没有生成空 TXT有些图片标注为空转换脚本就写入了空文件这种空标签在训练时会被 YOLO 判定为无标签图片如果一个批次里太多直接报异常。5.3 显存不足CUDA out of memory显存不足的常见处理顺序先把batch减半如果还爆把imgsz从 640 降到 512再不行换小模型yolov8n.pt。如果训练中断发生在半夜那槽点就更大了。比较好的习惯是训练命令里加上resumeTrueYOLO 会自动从last.pt继续训练不用重新跑。5.4 训练发散loss 变成 NaNloss 变 NaN 最直接的原因一般是学习率过大或者输入数据里有异常值比如标签坐标为负数、大于 1或者图片本身损坏。如果是平衡类别问题可以用 YOLO 自带的class_weight参数调整但如果 loss 直接发散先检查数据再降低学习率通常两者能解决 90% 的问题。5.5 常见问题速查表现象可能原因建议处理方式mAP50 忽高忽低数据划分未固定随机种子设置固定 seed重新划分并统一训练集预测类别错乱TXT 类别 id 与 YAML names 不一致可视化校验标签修正 YAML 顺序训练提示无标签标签文件名与图片名不一致批量检查文件名配对情况显卡量不足batch 过大或 imgsz 过大减小 batch、降低分辨率、换轻量模型loss 出现 NaN学习率过高或标签越界降低学习率检查标签坐标范围5.6 一个很实用的小技巧用少量图片快速验证流程训练正式开始前建议先用 100 张图片跑 10 个 epoch或者直接在完整数据上用 5 个 epoch 试跑。目的不是看精度而是确认从数据加载到模型前向传播的整套流程没有断点。如果 5 个 epoch 能顺利跑完再启动正式的长训练这样可以避免“训了 60 个 epoch 才发现数据有问题”的惨剧。这个习惯不止适用于这个数据集任何 YOLO 项目我都推荐这么做。说回这份垃圾数据集。我实际跑下来的感受是1000 张图片不多但作为学习和验证的起步资源非常合适。它最大的价值是把数据格式、划分逻辑、训练流程一次过了一遍搞清楚了 VOC、COCO、YOLO 三种格式到底怎么回事以后换模型、换框架就不会怕数据适配问题了。最后再分享一个我自己的习惯拿到任何数据集先花半小时把标签可视化、类别 id 梳理清楚、再做一次小规模测试训练确认链路通畅后再跑正式训练。这个习惯帮我省掉了很多“训练了一天结果 mAP 是 0 还找不到原因”的无谓加班。你可以在这个基础上后期慢慢增加自己场景的图片用这份数据集的格式标准继续半自动标注逐步把模型磨到能用的水平。本文还有配套的精品资源点击获取