
简介本资源是一套面向深度学习初学者与环保AI项目开发者的高质量垃圾分类目标检测数据集专为YOLOv5等主流模型训练优化设计解决实际场景中细粒度垃圾类别识别难题。数据集涵盖烟蒂、纸箱、电池、一次性快餐盒、酒瓶、蛋壳、大骨头等40余类常见生活垃圾共1.5万张VOC格式图像全部使用labelImg精细标注并配套划分脚本prepare_data.py、YOLOv5-5.0定制化训练工程及图文并茂的小白级训练教程PDF。压缩包共5个文件含2个核心数据ZIP含VOCdevkit结构与测试集、1个预训练模型RAR、1份PDF教程与1个Python工具脚本总大小953.29MB目录组织规范开箱即用。目前已有2658人学习下载特别适合零基础入门目标检测、快速复现垃圾分类模型或开展课程实验与毕业设计。 不管你是刚入门做目标检测还是已经被垃圾分类项目折磨过几轮肯定都绕不开一个问题拿什么数据练手。分类—检测—分割这条技术链上垃圾分类这种多类别、小目标密集、拍摄环境杂乱的任务特别吃数据质量。公开数据集要么类别太偏要么数量不够要么标注格式还得自己折腾一遍。所以当看到“垃圾分类1.5万张VOC已标注数据集”这种资源时第一反应通常是这玩意儿到底能不能直接用VOC格式是不是还得转YOLO1.5万张的类别分布到底靠不靠谱训练完能不能真的上到实际场景里跑。这篇文章就围绕这份数据集展开。我默认你已经知道目标检测大概是怎么一回事但还没到信手拈来的程度。我会从数据集的实际价值、VOC标注结构的逐层拆解、质量检查的完整链路、YOLO格式转换的逐行代码、训练配置和踩坑记录这几个维度把一份看起来就一个压缩包的资源拆成你真正能拿去用的东西。说明原标题只有一句话很多具体信息如类别数量、文件命名、标注内容在未解包前无法100%确认。下文内容基于同类型VOC数据集的一般结构结合实践中最可能遇到的情况做合理展开你在实操时以自己解压后的实际目录为准。1. 这份VOC格式的垃圾分类数据集到底解决了什么问题1.1 垃圾分类目标检测的实际难点垃圾分类不是“把图扔给模型就完事”的任务。它的难点集中在几个方面类别粒度细。矿泉水瓶和饮料瓶纸箱和纸袋果皮和剩菜人眼能迅速区分但模型要学的是颜色、纹理、形状特征的综合差异。当类别数量超过20个时类间相似度会显著拉高训练难度。拍摄环境非受控。垃圾桶旁是户外/室内、白天/夜间、顺光/逆光混合的复杂场景不像工业检测那样背景统一、光照稳定。小目标占比高。烟头、电池、瓶盖这类小型废弃物在画面里可能只占几十个像素对检测器的多尺度特征融合能力要求比较高。标注成本高。一张图里可能同时出现七八个不同类别的垃圾标注员需要对照细则逐框标注一条框标错类别整个数据集的可信度都会受影响。所以一份“垃圾分类”垂直场景数据集的可贵之处不在数量而在它把上述这些难点用实际图像固化了下来。1.5万张对垃圾分类任务来说是一个比较合理的量级——太少比如2~3千张模型很容易过拟合太多五万张以上又需要更长的训练时间和更强的硬件。1.2 为什么VOC格式仍然是主流交换格式之一VOCVisual Object Classes格式诞生于2005年左右的PASCAL VOC挑战赛虽然COCO格式后来在学术论文里更流行但VOC在工业界、开源社区和许多标注工具里仍然被广泛支持。理由很实际结构简单直观。一个JPEG对应一个XMLXML里记录图片尺寸、目标类别、边界框坐标。没有嵌套的JSON关系用脚本解析非常快。转任意格式都容易。VOC转COCO、转YOLO、转LabelMe社区里都有现成脚本改改路径就能用。多数标注工具默认支持。LabelImg、X-AnyLabeling、Roboflow导出的公共格式里VOC都是首选之一。所以这份数据集使用VOC格式对使用者来说反而是个好消息。你拿到手做一次格式转换后面不管切到YOLO还是PaddleDetection都有一个干净的基础版本可以复用。2. 解包之后先别急着训练VOC目录结构与标注内容全解析2.1 标准的三层目录结构解压后你大概率会看到这样的目录VOCdevkit/ └── VOC2007或VOC2012名称取决于打包人 ├── JPEGImages/ # 存放所有原图统一为jpg格式 ├── Annotations/ # 存放所有xml标注文件 └── ImageSets/ └── Main/ # txt文件记录训练/验证/测试的图片文件名不含后缀这是最经典的VOC布局。三个文件夹的分工非常清晰JPEGImages训练素材本体。文件名通常是000001.jpg这样的六位数字也可能是img_0001.jpg之类。格式基本统一为JPEG但因为来源不同分辨率会参差不齐。Annotations每个XML文件名与原图一一对应没有多余文件。如果出现只有图没有XML或反过来只有XML没有图说明数据集在打包时做过一次筛选但没做好完整性校验。ImageSets/Main里面可能有一个或多个txt。最关键的是train.txt、val.txt有时还有trainval.txt和test.txt。每一行是一个不带后缀的文件名用来告诉训练代码哪些图属于训练集、哪些属于验证集。2.2 XML标注文件里每个字段的含义打开一个XML文件长这样annotation folderVOC2007/folder filename000001.jpg/filename source databaseUnknown/database annotationUnknown/annotation imageUnknown/image /source size width960/width height1280/height depth3/depth /size segmented0/segmented object nameplastic_bottle/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin312/xmin ymin421/ymin xmax567/xmax ymax698/ymax /bndbox /object /annotation字段逐个解释folder和filename标注时写的源文件夹和文件名。这个字段训练代码基本用不到但如果你要重新整理数据集这两个字段可以用来检查文件是否被移动过。size非常重要。里面是原图的真实宽、高、通道数。转换YOLO格式时归一化坐标要用到宽高如果宽高值写错所有框的位置都会偏。object每个目标一个块。一个图里有几个目标就有几个object。name类别名。这是整个XML里最关键的一行类别拼写不一致比如plastic_bottle和plasticbottle会导致训练时类别数量莫名多出来。truncated目标是否超出图像边界1表示在画面边缘被截断。difficult目标是否难以辨认。这个字段在一些新框架里会被忽略但在VOC时代的评测中difficult1的样本不参与计算。如果训练时发现验证集mAP异常偏低可以先检查是不是大量difficult1的框没被过滤。bndbox目标框的左上角和右下角坐标。坐标单位是像素使用整数。需要特别确认的是VOC格式里xmax和ymax是包含边界像素的YOLO格式里用中心点坐标和宽高来做归一化。转换时我习惯从坐标系边界上先做一次“减1再除宽高”或“直接除宽高”的处理具体按YOLO系代码的解析方式来决定不同框架在这个细节上有一点点出入但整体不影响结果。2.3 必备的完整性校验脚本拿到数据集的第一步不是训练而是校验。用一个Python脚本快速扫描三件事JPEGImages和Annotations的文件名集合是否完全一致每个XML里的width、height是否和对应JPEG实际分辨率一致所有XML里一共出现多少个类别名是否有拼写杂音每个类别有多少个框。import os import xml.etree.ElementTree as ET from collections import Counter from PIL import Image voc_root VOCdevkit/VOC2007 jpg_dir os.path.join(voc_root, JPEGImages) ann_dir os.path.join(voc_root, Annotations) jpgs {os.path.splitext(f)[0] for f in os.listdir(jpg_dir) if f.endswith(.jpg)} anns {os.path.splitext(f)[0] for f in os.listdir(ann_dir) if f.endswith(.xml)} print(只存在于JPEG:, len(jpgs - anns)) print(只存在于Annotations:, len(anns - jpgs)) class_counter Counter() all_files_ok True for ann_name in sorted(anns): xml_path os.path.join(ann_dir, ann_name .xml) tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) img_path os.path.join(jpg_dir, ann_name .jpg) if os.path.exists(img_path): with Image.open(img_path) as img: real_w, real_h img.size if (w, h) ! (real_w, real_h): print(f{ann_name}: XML尺寸({w},{h}) 与真实尺寸({real_w},{real_h})不一致) all_files_ok False for obj in root.iter(object): cls_name obj.find(name).text class_counter[cls_name] 1 print(类别统计:, len(class_counter)) for cls_name, count in class_counter.most_common(): print(f {cls_name}: {count}) print(尺寸一致性:, 全部一致 if all_files_ok else 存在不一致)这个脚本输出三份关键信息缺失配对的文件、类别清单和数量分布、XML尺寸与图像真实尺寸的偏差。拿到这三项你对这份数据集能不能用、需不需要清理心里就有底了。3. 数据质量检查的完整链路类别分布、标注框与脏数据清洗3.1 类别分布最容易被忽略的坑垃圾分类类别天然不均衡。可回收物里塑料瓶、纸箱的样本数量通常远多于“其他垃圾”里的尘土团、大棒骨。如果某个类别只有几十个框模型基本学不到有效特征推理时要么漏检要么误检成形状相近的类别。拿到类别统计后请记住这条经验线每个类的标注框数量建议不少于1000个低于300个的类别大概率是无效的需要考虑是否合并到相近的“其他垃圾”里。1.5万张图的总量听起来不少但如果类别超过40个单类别平均只有几百个框这时候不要急着训练先做一次类别合并或增加对应类别的外部数据。3.2 标注框质量的四个常见问题越界框xmin或ymin小于0xmax大于widthymax大于height。这种框在训练时要么直接报错要么被图像缩放逻辑裁掉后变成错误的ground truth。处理原则超出范围小于一定阈值比如10像素时把它裁剪回边界内超出过多则直接删除该框。退化框xmax和xmin的差值小于5像素ymax和ymin的差值小于5像素。这种极小的框被认为是标注噪声即使真实目标就是很小比如烟头也建议忽略避免给损失函数带来无意义的梯度。错位类别同一张图里两个框的坐标一模一样但类别不同。这通常是标注员误操作或二次标注时覆盖了原文件。重叠度过高同类别两个框的重叠率超过比如0.9可能是重复标注。在垃圾分类场景两个紧挨的瓶子被标成一个框和一个大框或两个几乎重合的小框这类问题需要人工抽查确认。3.3 一个可视化的快速抽检方法脚本统计只能发现“格式错误”识别不了“语义错误”。最有效的办法是抽200~300张图把标注框画出来之后人工过一遍import cv2 import os import xml.etree.ElementTree as ET xml_path Annotations/000121.xml img_path JPEGImages/000121.jpg img cv2.imread(img_path) tree ET.parse(xml_path) root tree.getroot() colors [(0,0,255), (0,255,0), (255,0,0), (0,255,255), (255,255,0)] color_idx 0 for obj in root.iter(object): name obj.find(name).text 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) color colors[color_idx % len(colors)] cv2.rectangle(img, (xmin, ymin), (xmax, ymax), color, 2) cv2.putText(img, name, (xmin, max(0, ymin-10)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2) color_idx 1 cv2.imwrite(check_000121.jpg, img)画面里如果出现大量类别明显标错的框比如把易拉罐标成纸盒那就需要谨慎权衡是否继续使用这份数据集。轻微噪声可以不处理模型本身有鲁棒性大面积语义错误则必须清洗或用干净子集重新标注。4. 从VOC到YOLO格式转换的逐行实操4.1 为什么要转成YOLO的txt格式YOLO系列训练代码从v5开始几乎全部采用“图同名txt”的标注格式每张图片对应一个txt文件每行代表一个目标框格式为class_id x_center y_center width height四个坐标值均归一化到0~1之间。转换成YOLO格式的原因很简单读取更快。txt是纯文本解析开销远小于XML数据加载阶段的IO瓶颈会明显降低。和YOLO系的训练管线完全对齐。Ultralytics/YOLOv5/YOLOv8的Dataset类默认就认这种格式。后续做数据增强更顺手。Albumentations等库对归一化坐标的支持更自然剪裁、缩放、翻转时不容易因为坐标参考系不一致而出错。4.2 转换代码与类别表生成先扫描所有XML生成类别与ID的映射表# generate_classes.py import os import xml.etree.ElementTree as ET from collections import OrderedDict ann_dir VOCdevkit/VOC2007/Annotations classes OrderedDict() for xml_file in sorted(os.listdir(ann_dir)): tree ET.parse(os.path.join(ann_dir, xml_file)) root tree.getroot() for obj in root.iter(object): cls obj.find(name).text if cls not in classes: classes[cls] len(classes) with open(classes.txt, w) as f: for cls in classes: f.write(cls \n) print(类别列表已写入 classes.txt共, len(classes), 类)然后执行核心转换脚本逐XML转txt# voc2yolo.py import os import xml.etree.ElementTree as ET from pathlib import Path voc_root Path(VOCdevkit/VOC2007) ann_dir voc_root / Annotations yolo_ann_dir Path(labels) yolo_ann_dir.mkdir(exist_okTrue) class_file Path(classes.txt) class_names class_file.read_text().strip().split(\n) class_to_id {name: idx for idx, name in enumerate(class_names)} def convert_xml_to_yolo(xml_path: Path, output_dir: Path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): cls obj.find(name).text if cls not in class_to_id: print(f警告: {xml_path.name} 包含未注册类别 {cls}) continue cls_id class_to_id[cls] 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) # 防止越界和退化框 xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) if xmax - xmin 5 or ymax - ymin 5: continue x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h # 归一化后如果越界说明原框越界严重跳过 if x_center 0 or x_center 1 or y_center 0 or y_center 1: continue lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) if lines: output_path output_dir / (xml_path.stem .txt) output_path.write_text(\n.join(lines) \n) for xml_path in sorted(ann_dir.glob(*.xml)): convert_xml_to_yolo(xml_path, yolo_ann_dir) print(转换完成)代码里有两个容易被忽略的点越界框的裁剪。我没有直接丢弃越界框而是先裁剪回图像边界只有裁剪后依然退化到宽或高小于5像素才丢弃。这样既保留了尽可能多的有效标注又不会让极端错误进训练集。归一化后的越界检查。如果原框严重越界裁剪逻辑也兜不住归一化后坐标可能小于0或大于1这是最终的“熔断”检查一旦触发就直接丢弃该框。4.3 训练集/验证集划分的常见做法VOC自带ImageSets/Main里的txt文件。转换到YOLO后train和val的划分有两种选择直接继承原txt。用原数据集划分的train.txt和val.txt当YOLO训练的数据列表。这种方式的好处是结果可以和VOC时代的基线横向对比。自己重新划分。如果原划分不友好比如val集类别分布严重不均就重新按比例切。我通常的做法是先把所有图片按类别做分层抽样再按9:1划分出train/val避免某个占比很低的类别在验证集里一个样本都见不到。5. 用YOLOv8训练这份数据集配置、超参与实测记录5.1 数据集YAML的写法Ultralytics系列的数据集配置是一个YAML文件指向训练/验证图片路径、类别名列表# df.yaml path: /path/to/your/dataset train: images/train val: images/val names: 0: plastic_bottle 1: paper_box 2: battery # 按classes.txt里的顺序继续写train和val都是相对path的图片目录标注txt放在和images同级的labels目录下Ultralytics会自动匹配上下文对应关系。5.2 训练命令与关键超参的经验值yolo train datadf.yaml modelyolov8m.pt epochs120 imgsz640 batch16 device0 projectruns/detect nameexp_garbage几个超参的取舍逻辑模型体积1.5万张的数据规模用YOLOv8mmedium比较均衡。s可能欠拟合l或x在数据量没上去时并不会带来显著收益反而更容易在小目标类别上过拟合。imgsz如果数据集里有大量小物品输入尺寸建议640起步条件允许可以试到960。分辨率提高对小目标框的召回率有直接帮助。epoch我一般先跑120个epoch配合早停。这个数据规模下模型通常在第60~80个epoch开始收敛平稳到100个epoch左右mAP不再上升就停掉不必死等完整跑完。batch根据显存来。如果GPU显存只有16GYOLOv8m在640分辨率下batch设置8~16比较稳妥。5.3 典型训练问题与处理记录loss居高不下先检查是不是有类别名没对齐。YAML里的类别顺序和classes.txt不一致时模型不会报错但精度会一直上不去。val mAP数值乱跳常见原因是验证集太小。如果val只有几百张图每个类别的评估样本太少mAP的波动是正常的可以增加epoch数量或重新划分更均衡的验证集。背景大量误检要么是训练数据里背景多样性不够要么是conf_thres阈值设置偏低。先调阈值到0.35~0.5试试如果还压不住得考虑收集一批负样本没有目标对象的垃圾场景背景图加入训练集。6. 训练完成后如何做实际场景验证与模型迭代6.1 从指标到实地验证的落差在测试集上mAP到了0.7以上不代表能直接部署到小区垃圾桶旁边。实际场景的视角、光线、遮挡和训练数据总会有偏差。我把真实场景验证分成三步样本外静态测试用手机在小区、学校、商场附近的垃圾桶拍摄50~100张照片放入一个单独的文件夹用训练好的模型批量推理。这一步是为了观察模型在相似场景下的泛化能力。视频流连续推理找一段长时间的视频或实时摄像头观察模型会不会在某个视角、某个光照条件下连续误检。这是部署前必须走的压力测试——静态图片看不出问题连续推理会把模型的不稳定暴露得淋漓尽致。类别维度混淆矩阵分析用Ultralytics保存的混淆矩阵结果逐类别看错误流向。两个类别互相误检比例很高时考虑是否需要合并或者在推理后处理里增加规则判断比如检测到电池但周围无其他垃圾桶物品抑制该框。6.2 基于这份数据集的迭代建议如果你打算把垃圾分类检测真正落地手头这份1.5万张VOC数据集更像是一个高价值起点而不是终点。我的建议是第一轮先把手头数据训到收敛拿到基线结果然后用小步快跑的方式采集真实场景数据优先补充的是基线上最容易混淆的类别通常是塑料瓶/玻璃瓶/易拉罐这三个每补充一批数据就重新训练一次而不是攒到一大把再训。这个迭代节奏在数据规模从1.5万涨到3万张的过程中模型精度的提升会比较明显。7. 最后再分享几个实操中的细节关于这份VOC数据集我实际用下来的几点体会尽量不要改原目录结构。哪怕你只想测试其中几百张图也建议复制出一个子集目录再动手而不是在原目录里删改。很多坑都是因为目录结构被二次改动后标注路径对不上导致的。类别名统一处理要趁早。在第一次跑完整脚本的时候就把所有类别名过一遍确认没有空格、大小写混用、中英文混杂。这个工作越早做后面返工的代价越小。保留一个原始解压副本。所有清洗、转换、裁剪操作都在副本上进行。因为你不知道后面某次实验会不会需要回到原始标注重新计算坐标。还有个小建议如果这份数据集的某个类别明显不够用去公开数据集平台找同类别的补充数据时最好先把目标框的尺寸分布拉出来对比一下。如果补充数据的图像分辨率和目标比例差别很大混入训练反而会干扰模型学习这时候先统一缩放到同一尺度再做混合训练效果会更稳定。本文还有配套的精品资源点击获取