YOLO垃圾分类检测数据集:VOC/COCO/YOLO三格式标注与训练实战

YOLO垃圾分类检测数据集:VOC/COCO/YOLO三格式标注与训练实战 简介面向垃圾分类检测实践的一套YOLO数据集及配套工具涵盖约一万张真实场景拍摄的生活垃圾图片图像清晰、场景多样适合日常垃圾分类识别、智能分拣等工程应用经LabelImg人工标注标注框质量较高可直接用于YOLO系列模型的训练、验证与测试。压缩包共两千个文件类型以XML标签、TXT文本、HTML教程和Python脚本为主整体约410.27MB其中XML与TXT分别对应VOC和YOLO格式标签同时提供COCO(JSON)标签目录并附带训练集、验证集、测试集划分脚本以及生成ImageSets下txt文件的工具便于按需求灵活拆分数据、适配不同训练流程。配套教程覆盖Windows与Linux双平台的YOLO环境搭建和训练案例可根据实际场景修改后训练自己的数据集省去从零探索环境的成本。目前已有1300余人学习浏览特别适合初次接触垃圾分类检测、期望快速搭建YOLO实战流程的开发者与研究者。1. 10000张真实场景图垃圾分类检测为什么绕不开这份数据集垃圾分类检测和通用目标检测有个本质差别类别内差异能大到不像同一类比如同一个瓶子类别里透明矿泉水瓶、绿色啤酒瓶、不透明药瓶在形状和颜色上完全是三套特征反过来不同类别之间又长得过于接近纸盒和塑料包装袋在视觉上经常分不清。很多团队拿着 COCO 预训练权重直接跑最后发现模型在自家场景里框得准但分类错问题基本都出在训练数据上而不是网络结构上。这份 YOLO垃圾分类检测数据集正好补这个缺口。10000 张图片全部来自真实场景用 lableimg 逐框标注同一批标注同时导出了 VOCxml、COCOjson和 YOLOtxt三种格式标签训练前不用再花时间做格式转换。更实用的是里面带了三个划分脚本和两套训练教程Linux 和 Windows 都有对应版本适合做课程设计、毕业设计也能当城市垃圾分类项目的第一版 baseline。2. VOC、COCO、YOLO 三格式标签目录结构、标注范式与互转思路拿到压缩包后先把目录结构看一遍别急着直接 train。资源里的图片和三种格式标签是分开存放的本质上是同一批标注结果的不同序列化形式但它们的读取逻辑、坐标表达和适用框架完全不同新手最容易在这里栽跟头。2.1 三种标签格式的底层差异把同一个标注框分别用三种格式打开你才会理解为什么有的框架认这一种、不认那一种。下面的对比围绕一个 1280×720 的瓶子目标展开。2.1.1 VOCxml的树形标注结构VOC 格式是一张图片对应一个 xml 文件整体是一棵树的形状所有信息都能直接读出来annotation folderJPEGImages/folder filenameglass_001.jpg/filename size width1280/width height720/height /size object namebottle/name difficult0/difficult bndbox xmin120/xmin ymin85/ymin xmax640/xmax ymax520/ymax /bndbox /object /annotationname节点是类别名bndbox里存的是绝对像素坐标xmin/ymin是左上角xmax/ymax是右下角。这种格式最大的优点是人和工具都能直接修改用 lableimg 打开一张图就能手工调整某个框的位置。缺点也同样明显一千张图就是一千个 xml 文件做大数据集统计时要遍历全部文件训练框架也很少直接用 xml需要先转成其他格式。2.1.2 COCOjson的数组式组织COCO 格式把整个数据集塞进一个 json 文件核心是images、annotations、categories三个数组{ images: [{id: 1, file_name: glass_001.jpg, width: 1280, height: 720}], annotations: [{id: 1, image_id: 1, category_id: 1, bbox: [120, 85, 520, 435], area: 226200, iscrowd: 0}], categories: [{id: 1, name: bottle}] }bbox里的四个数字是[x, y, width, height]不是[xmin, ymin, xmax, ymax]。这是从 VOC 转 COCO 时最高频的报错点很多人把xmax当成width填进去模型训练出来检测框全是歪的。COCO 的好处是切分训练集、验证集很方便改一段 json 就行不用动图片文件。2.1.3 YOLOtxt的归一化坐标YOLO 系列训练真正吃的是 txt 格式。每张图对应一个 txt 文件每一行是一个目标0 0.296875 0.420139 0.406250 0.604167第一个数是类别 id后面四个数是x_center y_center width height全部除以图片宽高做了归一化取值范围在 0 到 1 之间。注意坐标中心点和宽高都做了归一化不是只归一化中心点。这个设计让不同分辨率图片可以混合训练模型不关心输入图实际多大只关心目标在图上占的比例。用一张表把这三种格式的关键差异串起来格式文件组织坐标表达修改成本训练框架VOC一图一 xml绝对像素左上/右下低工具直接改Faster R-CNN 系COCO全数据集一个 json绝对像素x/y/w/h中改 json 段Detectron2、MMDetectionYOLO一图一 txt归一化中心点/宽高低但可读性差YOLO 全系列2.2 目录结构与同图多格式同步资源里的标签目录大致是这样一个布局waste_dataset/ ├── JPEGImages/ # 10000 张原图 ├── VOC/ # 10000 个 xml 文件 ├── COCO/ # 1 个 json 文件 └── YOLO/ # 10000 个 txt 文件三种格式来自同一次标注同一个glass_001.jpg在 VOC、COCO、YOLO 三个目录里对应的是同一个目标框。这个特性在实际排错时非常值钱训练阶段发现某个框的预测结果异常打开对应 VOC 格式的 xml用 lableimg 加载原图就能直接检查标注框是否错位不用写脚本去解析 txt。资源里的数据标注质量比较高一个重要表现是 txt 文件中基本没有空文件。空文件意味着图片里没有目标在 YOLO 训练中虽然不报错但在某些增强策略下会导致梯度异常。2.3 用一段代码看懂互转逻辑虽然资源已经给了三种格式但实际项目中经常会往现有数据集里补充新类别或者把别人给的 VOC 数据合并进来。这时候手写一段互转代码会派上用场。下面是最小可用的 XML 转 YOLO 逻辑import os import xml.etree.ElementTree as ET class_id {bottle: 0, can: 1, paper: 2} def xml_to_yolo(xml_path, txt_path): tree ET.parse(xml_path) root tree.getroot() width int(root.find(size/width).text) height int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text 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) / width y_center ((ymin ymax) / 2) / height box_width (xmax - xmin) / width box_height (ymax - ymin) / height lines.append(f{class_id[name]} {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}) with open(txt_path, w) as f: f.write(\n.join(lines)) xml_to_yolo(VOC/glass_001.xml, YOLO/glass_001.txt)归一化时除以的是 xml 里size节点的宽高不是图片实际解析出来的宽高两者通常一致但不排除某些标注工具写错的情况。中心点坐标用(xmin xmax) / 2而不是xmin box_width / 2前者可以直接套用原始坐标少积累一步浮点误差。类别映射字典class_id必须和之后 data.yaml 里的names顺序完全一致这个顺序错一个整个训练就是错的。COCO 转 YOLO 要更麻烦一些annotations里还混着iscrowd、segmentation这些字段转换时要过滤掉iscrowd1的目标。如果只是想跑通 YOLO 训练直接用资源里现成的 YOLO 目录就好不需要自己转换。提示检查一份标注是否可用的最快方式不是写脚本而是随手打开一张图和它对应的 txt目测几个坐标值是否在 01 之间。如果出现大于 1 的值说明归一化时用了错误的宽高。3. 三个划分脚本怎么选从 train_val_test 到 ImageSets 的落地逻辑数据准备好了下一步是把数据集切成训练集、验证集和测试集。资源里放了三个不同的划分脚本很多人不明白为什么给三个其实它们的适用场景完全不同选错脚本不会报错但会让后面的评估流程变得别扭。3.1 三个脚本各自解决什么问题先把三个脚本的差异理清楚脚本输出适用场景训练/验证/测试划分脚本三个独立文件夹需要保留 h留出测试集做最终评估训练/验证划分脚本两个文件夹只需验证集看收敛情况split_train_val 脚本ImageSets 下 txt 文件配合 VOC 系框架使用YOLO 原生训练只认目录结构images/train、images/val这样的路径最省事前两个脚本的输出正好对齐这个要求。第三个脚本生成的是 ImageSets 下的清单文件里面写的是图片相对路径列表这类约定常见于 R-CNN 系列的代码库和部分课程作业模板。如果训练框架是 ultralytics完全可以忽略第三个脚本。3.2 划分脚本的核心逻辑打开脚本会发现逻辑并不复杂核心步骤只有四步列出全部文件名、打乱顺序、按比例切片、成对拷贝图片和标签。等效实现如下import random import shutil import os random.seed(42) img_dir JPEGImages label_dir YOLO out_dir dataset files [f for f in os.listdir(img_dir) if f.endswith(.jpg)] random.shuffle(files) total len(files) train_files files[:int(total * 0.8)] val_files files[int(total * 0.8):int(total * 0.9)] test_files files[int(total * 0.9):] for split, names in { train: train_files, val: val_files, test: test_files }.items(): os.makedirs(f{out_dir}/images/{split}, exist_okTrue) os.makedirs(f{out_dir}/labels/{split}, exist_okTrue) for name in names: shutil.copy(os.path.join(img_dir, name), os.path.join(out_dir, images, split, name)) label_name name.replace(.jpg, .txt) shutil.copy(os.path.join(label_dir, label_name), os.path.join(out_dir, labels, split, label_name))图片和标签在同一个循环里被成对拷进对应目录这是划分脚本最关键的保证。如果先拷图片再单独拷标签某些文件在拷贝过程中出问题训练时就会出现Image not found或者标签文件缺失的报错。random.seed(42)的写法保证了每次运行脚本得到相同的划分结果方便实验对比。8:1:1 是目标检测里很常见的默认切分比例。10000 张图实际切出来大约是 8000/1000/1000。如果你的类别分布不均衡随机切分的风险在于少数类可能集中落在某一个分片里。比如数据集中某种垃圾只出现在 80 张图里随机打乱后这 80 张可能全部进了训练集验证集里这种类别一张都没有。3.3 划分之后目录怎么摆脚本跑完后标准的目标检测数据集目录应该长这样waste_dataset/ ├── images/ │ ├── train/ # 约 8000 张 │ ├── val/ # 约 1000 张 │ └── test/ # 约 1000 张 ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml训练时 data.yaml 里的train和val指向images下的子目录YOLO 会自动在同级的labels目录下找对应标签文件不需要在配置文件里单独指定标签路径。test 目录用于最终模型评估训练过程中不会碰它防止验证集信息泄漏到模型调参的循环里。资源中提到的train_list.txt记录的是训练集清单一行一个相对路径。这个文件在 YOLO 训练里不是必需项但部分课程设计或目标检测框架会要求提供。路径分隔符统一用正斜杠Windows 下生成的\在 Linux 环境跑会失效。3.4 划分结果的校验划分完成后先做基本校验再启动训练。命令行直接对比图片和标签数量find dataset/images/train -name *.jpg | wc -l find dataset/labels/train -name *.txt | wc -l两个数字不一致说明有图片缺标签或标签缺图片顺着文件名差集去查。随后检查类别分布是否均衡写一个简单的统计脚本from collections import Counter cat_map {0: bottle, 1: can, 2: paper} counter Counter() for file in os.listdir(dataset/labels/train): with open(fdataset/labels/train/{file}) as f: for line in f: cat_id int(line.split()[0]) counter[cat_map[cat_id]] 1 print(counter)如果统计出的比例和全量数据明显不一致比如某个类在 train 里占比突然掉了 30%说明这次随机划分的运气不好重新 seed 再切一次。这类检查在 10000 张图时问题不大但类别多、样本少的高校项目里几乎是必踩的坑。4. YOLO 环境搭建与训练Linux/Windows 双路径与 train 参数对照资源里附带四份教程文档分别覆盖 Linux 环境搭建、Windows 环境搭建和两套训练流程。整体思路是一致的先装 PyTorch 和 ultralytics再跑一个公开权重验证环境最后换成自己的数据训练。4.1 环境搭建的版本组合YOLOv8 之后的版本都统一封装在 ultralytics 包里不需要再从源码编译 darknet环境搭建过程被大幅简化。推荐用 conda 管理环境Linux 端典型命令序列conda create -n yolo python3.10 conda activate yolo conda install pytorch2.1.0 torchvision0.16.0 pytorch-cuda12.1 -c pytorch -c nvidia pip install ultralytics先装 PyTorch 再装 ultralytics顺序不要反。如果先装 ultralyticspip 可能拉一个 CPU 版本的 torch后面训练时device0直接报 CUDA 不可用。装完后验证 GPU 是否可见import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))打印True说明环境就绪。Windows 端流程一样只是 CUDA 验证前先确认显卡驱动版本和 CUDA 版本的兼容性。用nvidia-smi查看驱动对应的最高 CUDA 版本再选择不超过它的 PyTorch 版本。如果机器是 AMD 显卡Windows 下官方支持有限建议直接用 CPU 模式调试小数据集或者换 Linux 环境跑不要在这上面浪费太多时间。4.2 训练自己的垃圾分类模型修改 data.yaml环境搭好后训练自己的数据只需要改一个 yaml 文件。新建waste.yamlpath: D:/waste_dataset train: images/train val: images/val names: 0: bottle 1: can 2: paper 3: plastic 4: glasspath是数据集根目录的绝对路径Windows 下用正斜杠或双反斜杠单反斜杠会被当成转义字符。train和val是相对于path的路径YOLO 会自动拼接。names的顺序必须和 txt 标签里第一个数字的含义一致这里如果写错模型会一直学错目标。资源里的训练教程强调要按自己的案例修改实际上涉及的就是三块yaml 的 names、类别数量、预训练权重的选择。类别数量由你的标签决定不需要在 yaml 里显式填写YOLO 会自动从names的键值个数判断。4.3 train 关键参数与常见翻车点4.3.1 训练命令以及参数含义环境验证通过、yaml 配置完毕启动训练的基准命令yolo detect train datawaste.yaml modelyolov8s.pt epochs150 batch16 imgsz640 device0参数取值说明modelyolov8s.pts 是精度和速度的平衡点显存不够可换 yolov8nepochs15010000 张图的量级 100~150 轮足够batch166GB 显存建议 812GB 以上可到 32imgsz640默认输入尺寸小目标多可以提到 960device0指定第一块 GPUCPU 训练写 cpu用yolov8s.pt作为起点是迁移学习模型会从公开权重继承通用视觉特征比如边缘、纹理、颜色分布这让垃圾分类模型在几十轮内就能收敛到不错的效果。如果从零开始训练同样的数据量可能需要三倍以上的轮次才能达到接近的精度。训练过程中盯住两个东西box_loss和cls_loss。前者反映定位精度后者反映分类精度。如果cls_loss稳定下降但box_loss在某个值附近震荡大概率是标注框边界不准确。这份数据集的框质量高一般不会出现严重的 box_loss 震荡。4.3.2 数据增强的隐性参数经验上垃圾检测很容易遇到过拟合。原因是垃圾分类的拍摄场景高度集中背景重复度高模型容易记住背景而不是目标本身。处理方式是让默认的数据增强策略发挥作用。ultralytics 的默认增强里mosaic1.0和mixup0.0是关键项。mosaic 会把四张图拼成一张训练图这个策略对垃圾检测效果显著因为它强制模型在复杂背景下区分目标。如果显存不足导致 mosaic 拼图时报错关闭它yolo detect train datawaste.yaml modelyolov8s.pt epochs150 batch8 imgsz640 mosaic0.5mosaic0.5表示一半训练样本使用拼图另一半保持原始图。对垃圾这类尺度变化大的目标不建议完全关闭 mosaic。训练结束后模型权重保存在runs/detect/train/weights/目录下best.pt是验证集表现最好的一轮last.pt是最后一轮。日常使用和部署都优先选best.pt。5. 垃圾检测落地技巧类别不均衡、小目标与 mAP 验证训练完成后的第一步不是直接看精度而是跑验证集把输出的混淆矩阵调出来。这能直观地看出哪些类别互相混淆。如果发现瓶子和易拉罐经常被误判说明这两类在像素层面过于接近需要在类别定义或数据层面拆开处理。可视化这一阶段在垃圾检测里尤其重要因为许多垃圾类别边界本来就模糊比如纸盒和塑料袋都呈现蓬松质感。验证命令如下yolo detect val modelruns/detect/train/weights/best.pt datawaste.yaml结果目录下会生成confusion_matrix.png和results.png。results.csv里有每个类别的mAP50和mAP50-95。前者衡量框和类别都准确的占比后者对框位置更苛刻。垃圾检测场景中mAP50已经能反映业务表现mAP50-95更多用来做模型选型对比。资源数据的类别分布如果存在少数类比如电池、灯泡这类样本量少的垃圾在评估中会出现单类 AP 明显低于其他类别的情况。先用一条命令查看各分片里的类别频次确定是哪一类在拖后腿。对这类数据最直接的方式是让少数类参与过采样——把包含该类别的图片在 train 目录里多复制几份同时保留它原来那份。复制不会改变标签文件内容模型相当于多看到了几次这类样本对缓解类别不均衡比调损失函数更直观。小目标场景在垃圾检测里非常普遍碎纸屑、烟蒂在画面上可能只占 30×30 像素。这类目标在 640 输入尺寸下特征极弱训练时建议把imgsz提到 960验证和推理时也保持同样尺寸。实际上提升输入分辨率对 mAP50-95 的改善通常最直接代价是显存占用和推理时间上升。部署端如果对延迟敏感可以在训练时用 960 输入推理时保持 640精度损失可以接受。推理落地时给几个实用参数yolo predict modelbest.pt sourcetest_phone.jpg conf0.35 iou0.45conf0.35将置信度阈值从默认的 0.25 提高垃圾检测场景建议保留略高的阈值因为垃圾种类多、外观杂低置信度输出里噪声比例偏高。iou0.45控制 NMS 的合并严格度值越小越容易合并重叠框。如果同一目标在预测结果里出现两个重叠框适当降低iou如果两个靠得很近的不同目标被错误合并成一个框把这个值调大。调完这两个参数后再跑一遍同一张测试图对比两次预测结果的框数量和类别就能找到当前数据下最合适的组合。本文还有配套的精品资源点击获取