垃圾分类检测数据集实战:YOLO+VOC双格式训练与调优指南

发布时间:2026/9/23 10:59:50
垃圾分类检测数据集实战:YOLO+VOC双格式训练与调优指南 简介这是一份面向目标检测学习者的垃圾分类检测数据集适合从事计算机视觉入门、YOLO/VOC格式训练及环保识别项目实践的开发者与高校学生使用可解决垃圾材质分类检测任务中样本不足、标注不完整的问题。压缩包共约2000个文件以1999个xml标注文件和1个说明txt为主整体约128.05MB内含JPEGImages图片、Annotations标注与labels标签三类目录图片与标注一一对应便于直接投入训练。数据集共8341张清晰图片标签分为Cardboard、Glass、Metal、Paper、Plastic五类总框数达8815个各类别框数分布相对均衡其中Plastic最多为1911个Glass最少为1601个均为矩形框标注未做数据增强。目前已有224人学习下载读者可借此快速搭建垃圾分类检测基线模型验证不同算法在真实垃圾材质识别上的表现并作为课程设计或竞赛练手的可靠数据来源。1. 垃圾分类检测数据集8341 张 yolovoc 双格式到底能省多少事你拿到一个垃圾分类检测数据集第一反应大概率是翻目录看图片和标注然后发现标注格式不是自己训练管线要的那一种。8341 张图如果标注只有 VOC 的 XML而你用的是 YOLO 系列训练脚本就得先转格式反过来如果只有 txt想用 SSD 或 Faster R-CNN 又得倒回去。这个数据集标题里写的是 yolovoc 双格式意味着同一批图配了两套标注省掉的正是这道转换工序。垃圾分类本身是个很典型的检测任务可回收物、厨余、有害、其他四大类下面还可能细分到塑料瓶、纸箱、电池、果皮。类别粒度直接决定标注框的松紧程度也决定你后面调参时 mAP 能不能上去。适合谁用做课程设计的学生、想跑通 YOLO 训练全流程的入门者、需要快速验证某个改进模块是否有效的研究者以及做智能垃圾桶、分拣线视觉原型的工程师。8341 张不算大但足够把训练管线跑通并看出模型收敛趋势。2. 先看清标注质量再谈训练8341 张图的类别分布与格式核对2.1 类别粒度决定标注框松紧垃圾分类数据集的标注方式通常有两种一种只标四大类框住整个物体另一种标到细类比如“塑料瓶”“玻璃瓶”“纸箱”“电池”。细类标注的框往往更紧因为标注者需要区分材质边界会贴着物体轮廓画。粗类标注则可能把一堆可回收物框成一个大框或者只框主体。你拿到数据集后先统计每个类别的实例数。如果某个类别实例数低于 200训练时该类别的召回率大概率上不去需要考虑数据增强或者类别合并。常见做法是用脚本统计 VOC XML 里的 object 标签或者直接读 YOLO 格式的 classes.txt 和 labels 目录。import os import xml.etree.ElementTree as ET from collections import Counter # 统计 VOC 格式标注中每个类别的实例数 voc_dir annotations/voc counter Counter() for xml_file in os.listdir(voc_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(voc_dir, xml_file)) root tree.getroot() for obj in root.findall(object): name obj.find(name).text counter[name] 1 for cls, cnt in counter.most_common(): print(f{cls}: {cnt})这段代码遍历 VOC 标注目录解析每个 XML 里的 object 节点统计类别名出现次数。参数说明voc_dir指向 VOC 格式标注文件夹里面每个 XML 对应一张图。输出结果直接告诉你类别是否均衡。如果发现某个类别只有几十个实例先别急着训练要么做过采样要么在配置文件里给这个类别更高的损失权重。2.2 双格式目录结构核对清单yolovoc 双格式的数据集目录通常长这样images 放原图labels 放 YOLO 的 txtannotations 或 VOC2007 放 XML。你需要核对三件事图片文件名和标注文件名是否一一对应、YOLO 的 txt 里类别索引是否和 classes.txt 行号一致、VOC XML 里的 filename 字段是否和实际图片名匹配。常见翻车点是图片是IMG_001.jpgXML 里写的是IMG_001.JPG大小写不一致导致训练时找不到标注。另一个坑是 YOLO 的 txt 里坐标是归一化后的如果你直接拿 VOC 的绝对坐标去训练 YOLO框会飞到天上去。核对项正确状态常见错误图片与标注同名主文件名一致扩展名可不同大小写不一致、多了空格YOLO 类别索引从 0 开始对应 classes.txt 行号从 1 开始导致类别偏移YOLO 坐标范围0~1 归一化直接写像素绝对值VOC 坐标范围像素绝对值xmin xmax写反或超出图片尺寸图片尺寸一致性标注坐标不超过宽高图片被缩放但标注没同步提示先跑一遍核对脚本把不匹配的文件名打印出来手动改比训练时报错再回头找要省时间。3. 从 VOC 到 YOLO 的转换脚本与四个边界坑3.1 转换脚本逐行拆解VOC 转 YOLO 的核心逻辑读 XML 里的 size 拿到图片宽高读每个 object 的 bndbox 拿到 xmin、ymin、xmax、ymax然后计算中心点和宽高并归一化。类别名映射到索引写入 txt。下面这个脚本我一般会直接拿来用改一下路径就能跑。import os import xml.etree.ElementTree as ET # VOC 转 YOLO 格式 voc_dir annotations/voc out_dir labels_yolo classes [recyclable, kitchen, hazardous, other] # 按实际类别顺序改 os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(voc_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(voc_dir, xml_file)) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in classes: continue # 跳过不在类别表里的标注 cls_id classes.index(name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 边界裁剪防止坐标超出图片范围 xmin max(0, min(xmin, w)) xmax max(0, min(xmax, w)) ymin max(0, min(ymin, h)) ymax max(0, min(ymax, h)) cx (xmin xmax) / 2.0 / w cy (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) txt_name xml_file.replace(.xml, .txt) with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(lines))逻辑说明先读图片宽高再遍历 object。classes.index(name)拿到类别索引如果标注里有不在类别表里的名字直接跳过避免训练时类别数对不上。边界裁剪那四行是后悔药有些标注框会超出图片边缘不裁剪的话归一化后坐标可能小于 0 或大于 1YOLO 训练时虽然不会报错但框的位置会偏。最后写入 txt每行一个目标格式是类别 中心x 中心y 宽 高全部归一化到 0~1。3.2 四个边界坑空标注、越界框、类别名空格、图片缺失第一个坑是空标注。有些图片里没有目标XML 里没有 object 节点转换后 txt 是空文件。YOLO 训练时空 txt 是合法的表示这张图是负样本。但如果你用的是某些第三方训练脚本空文件可能导致 dataloader 报错。解决方法是保留空文件或者在配置里设置允许空标注。第二个坑是越界框。标注者手抖把 xmax 写得比图片宽度还大归一化后 bw 可能大于 1。上面脚本里的裁剪逻辑能兜住但裁剪后框会变形。如果越界严重建议直接丢弃这个标注而不是裁剪。第三个坑是类别名带空格。VOC XML 里写的是plastic bottle而 classes.txt 里写的是plastic_bottle转换时匹配不上目标被跳过。解决方法是统一用下划线或者在转换前做一次字符串替换。第四个坑是图片缺失。XML 里有标注但 images 目录里找不到对应图片。训练时 dataloader 会报 FileNotFoundError。写个脚本比对两个目录的文件名集合把差集打印出来手动补图或删标注。4. 用 YOLOv8 在本地跑通垃圾分类训练的最小命令4.1 环境配置与数据 YAML 写法YOLOv8 的环境配置不算复杂但版本对不上会出玄学问题。我一般用 conda 建一个干净环境Python 3.9 或 3.10然后 pip 装 ultralytics。显卡驱动和 CUDA 版本要匹配不然训练时要么跑在 CPU 上慢得离谱要么直接报 CUDA error。装完之后用yolo checks看一眼环境状态确认 GPU 能被识别。数据配置文件garbage.yaml长这样path: ./garbage_dataset train: images/train val: images/val nc: 4 names: 0: recyclable 1: kitchen 2: hazardous 3: otherpath是数据集根目录train和val是相对路径。nc是类别数names是类别名和索引的映射。注意这里的索引必须和 YOLO txt 里的类别索引一致否则训练出来的模型会把塑料瓶识别成电池。4.2 训练命令与关键参数含义最小训练命令yolo detect train datagarbage.yaml modelyolov8n.pt epochs100 imgsz640 batch16modelyolov8n.pt用的是 nano 版本参数量小适合快速验证。epochs100对 8341 张图来说够跑出趋势如果 loss 还在降可以加到 200。imgsz640是输入分辨率垃圾分类的物体通常占画面比例较大640 够用如果小目标多比如远处的小瓶子可以提到 800 或 1024但显存占用会上去。batch16根据显存调8G 显存跑 640 分辨率大概能到 16不够就降到 8。训练过程中重点看三个指标box_loss、cls_loss、mAP50。box_loss 降不下去说明框回归有问题可能是标注质量差cls_loss 震荡说明类别不均衡或者学习率太大mAP50 在 50 个 epoch 后还没明显上升检查数据里有没有大量错标。注意训练前把数据集按 8:2 划分 train 和 val划分时保证每个类别在 val 里都有实例否则验证集的 mAP 没有参考意义。5. 训练不收敛、mAP 上不去垃圾分类检测的排查清单5.1 现象loss 从第一个 epoch 就不降原因通常是标注格式不对。YOLO 的 txt 里坐标必须是归一化的如果你误把 VOC 的绝对坐标写进去模型看到的框全是几百像素的数值归一化后远超 1loss 直接爆炸。解决方法是检查 txt 里的数值范围正常应该在 0~1 之间且中心点坐标在 0.5 附近波动。另一个原因是类别索引越界。classes.txt 只有 4 行但 txt 里出现了类别 5训练时索引不到类别名loss 计算会出错。用脚本扫一遍所有 txt 的类别列把超出范围的找出来。5.2 现象mAP50 卡在 0.3 上不去先看验证集里的标注有没有问题。有时候训练集标注是对的验证集里混了几张错标图mAP 被拉低。把验证集预测结果可视化用yolo detect predict跑一遍看模型到底把框画在哪。如果框的位置大致对但类别错说明类别特征不明显考虑增加该类别的样本或者做针对性增强。如果框的位置完全不对检查验证集的图片和标注是否对应。垃圾分类有个特殊难点同一类物体外观差异大。比如“可回收物”里既有塑料瓶又有纸箱颜色、形状、纹理都不一样。模型需要学到的是材质相关的特征而不是固定形状。这时候数据增强里的颜色抖动、随机裁剪、马赛克增强能帮上忙。YOLOv8 默认开了马赛克增强如果效果不好可以关掉试试有些场景下马赛克会让小目标更难学。5.3 现象训练时显存溢出batch调太大了。8341 张图640 分辨率8G 显存跑 batch16 可能刚好卡在边缘跑几个 epoch 后显存碎片多了就 OOM。解决方法是降 batch 到 8或者开梯度累积。YOLOv8 支持nbs参数做梯度累积batch8 nbs16等效于 batch16 的梯度更新频率但显存占用按 8 算。另一个显存杀手是imgsz。640 提到 1280显存占用大概翻四倍。如果非要高分辨率先把 batch 降到 4 试试。5.4 现象验证集 mAP 正常但实际推理效果差训练时的验证集和实际场景分布不一致。比如验证集里的图片都是白底产品图实际场景是复杂背景的垃圾桶模型在验证集上表现好一到真实场景就翻车。解决方法是把实际场景的图片加进训练集哪怕只标几十张也能显著提升泛化能力。另一个原因是置信度阈值。YOLO 默认推理置信度阈值是 0.25垃圾分类场景下如果模型对某些类别信心不足框会被过滤掉。用conf0.1跑一遍看看是不是漏检。5.5 现象某些类别完全检测不到先确认训练集里这个类别的实例数。如果少于 100模型很难学到。解决方法是过采样把包含这个类别的图片复制多份或者在 dataloader 里给这个类别更高的采样权重。另一个原因是这个类别的标注框太小。YOLO 对小于 16x16 像素的目标检测能力有限如果标注框确实很小考虑提高输入分辨率或者用专门的小目标检测策略。6. 把 8341 张图用出复利数据增强、类别合并与增量训练6.1 针对垃圾分类的增强参数怎么调YOLOv8 的增强参数在训练命令里可以直接改。默认的hsv_h0.015、hsv_s0.7、hsv_v0.4对垃圾分类来说偏保守。垃圾物体在不同光照下颜色变化大可以把hsv_v提到 0.5让模型对亮度变化更鲁棒。degrees0默认不旋转但垃圾物体在垃圾桶里可能是任意角度把degrees10打开让模型学旋转不变性。flipud0默认不上下翻转垃圾场景里上下翻转是合理的可以设flipud0.5。mosaic1.0默认开如果发现小目标检测效果差降到 0.5 试试。yolo detect train datagarbage.yaml modelyolov8n.pt epochs150 imgsz640 batch16 hsv_v0.5 degrees10 flipud0.5 mosaic0.5这些参数不是拍脑袋定的是我在类似数据集上试出来的。hsv_v太高会让颜色失真模型学不到材质特征degrees太大会引入大量黑边反而干扰训练。建议每次只改一个参数跑 30 个 epoch 看 mAP 变化。6.2 类别合并的决策依据如果某个细类实例数太少比如“电池”只有 80 个框单独作为一类训练效果很差。这时候可以考虑合并到“有害垃圾”大类里。合并的依据是实际应用场景是否真的需要区分这个细类。如果只是做四分类垃圾桶的满溢检测不需要知道具体是什么有害垃圾合并完全合理。如果做分拣线需要区分电池和灯管那就不能合并得补数据。合并的操作很简单改 classes.txt把两个类别名改成同一个然后重新生成 YOLO txt 里的类别索引。注意 VOC XML 里的类别名也要同步改否则下次转换又对不上。6.3 增量训练在已有模型上继续学新数据你不可能一次收集完所有场景的垃圾图片。常见做法是先用 8341 张图训一个基线模型然后在实际部署中收集误检和漏检的图片标好后做增量训练。YOLOv8 支持从已有权重继续训练yolo detect train datagarbage_v2.yaml modelruns/detect/train/weights/best.pt epochs50 imgsz640 batch16 lr00.001关键是lr0要调小默认 0.01 是从头训练的学习率增量训练用 0.001 或更低避免把之前学到的特征冲掉。新数据里如果包含新类别nc要改模型头会重新初始化这时候学习率可以稍大一点。6.4 一个验证模型是否学到东西的小技巧训完之后别只看 mAP 数字。把验证集里预测错误的图片挑出来按错误类型分类框位置偏、类别错、漏检、误检。如果框位置偏的多说明回归分支没学好检查标注框是否一致如果类别错的多说明分类分支特征不够考虑加数据或换更大的模型。我一般会随机抽 20 张错图一张一张看看完基本就知道下一步该调什么了。这个数据集最大的价值不是 8341 这个数字而是它给了你一个完整的、双格式的起点。你可以用它跑通从数据核对、格式转换、训练调参到错误分析的完整闭环。跑完一遍之后再拿到其他检测数据集流程是一样的。希望帮到你。本文还有配套的精品资源点击获取