简介这是一份面向YOLO系列目标检测算法的可回收废物数据集覆盖玻璃瓶、纸瓶、塑料瓶、塑料袋四类常见回收物整体包含6000张已标注图像可有效解决垃圾分类场景中训练样本稀缺的问题。数据已按训练、验证、测试划分完毕并能直接用于YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLO11等主流算法既适合智慧回收、环卫分拣等项目快速落地也方便目标检测初学者系统练习数据准备与模型训练流程。压缩包共收录2000个文件以XML格式的VOC标签为主压缩后约229.98MBXML标签记录类别和边界框坐标另提供YOLO格式的TXT标注类别索引从0开始坐标采用归一化中心点与宽高两种格式分目录存放便于在VOC与YOLO工作流间灵活切换。已有47人学习下载数据集目录清晰训练验证划分现成解压后即可投入训练并评估模型性能省去整理与转换标注的时间。1. 打开一个“可回收废物YOLO数据集”之前先想清楚你要什么训练一个能识别玻璃瓶、纸瓶、塑料瓶、塑料袋的检测模型最缺的不是GPU不是yolo算法本身而是一份像样的图像数据集。标题里这个6000张带标签的zip包直接命中了一个真问题可回收废物的形态差异大到让人头疼——透明玻璃瓶在光照下近乎隐形塑料袋揉成一团后和纸团长得一模一样。如果你正在做智能垃圾分类、回收仓视觉识别或者产线分拣这个数据集的定位就是省掉你从零采集标注的两周时间。但别急着解压跑训练拿到zip只是第一步标签格式是否规范、类别分布是否均衡、验证集划分是否合理这些才是决定模型最终能不能用的关键。这篇就按我实际处理这类数据集的流程从解压到训练再到坑点完整过一遍。2. 拆开zip之前6000张图像和标签到底以什么形式组织常见做法是可回收废物数据集打包后会有几个固定组成部分图像文件夹、标签文件夹、类别名字文件偶尔附带一份说明文档。以YOLO格式为例目录结构一般是这个样子。recyclable_waste_dataset/ ├── images/ │ ├── train/ │ │ ├── glass_bottle_001.jpg │ │ ├── paper_bottle_001.jpg │ │ └── ... │ └── val/ │ ├── glass_bottle_002.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── glass_bottle_001.txt │ │ ├── paper_bottle_001.txt │ │ └── ... │ └── val/ │ ├── glass_bottle_002.txt │ └── ... ├── classes.txt ├── data.yaml └── README.md如果你的zip解压出来是这种结构那恭喜你省掉大半整理功夫。但很多标签文件不起眼地躺在某个二级子目录里所以第一步永远是先看清有没有data.yaml和classes.txt而不是急着复制图像路径。先说为什么YOLO格式会成为这类数据集的默认选择。因为YOLO系列从v5到v8、v9训练时读取的就是txt标签文本里每一行代表一个目标格式为class_id center_x center_y width height五个值都是空格分隔坐标基于图像宽高的归一化比例区间在0到1之间。比如0 0.5 0.5 0.4 0.8表示一个类别id为0的目标中心点在图像中央宽占图像比例40%高占80%。这套格式最大的优势是缩放不变性图像resize不会影响标签有效性也是为什么数据增强能放心做随机缩放。先写一个快速检查脚本确认标签格式没有翻车#!/bin/bash # 统计images和labels下的文件数量判断配对是否完整 echo 图像数量: $(find images -name *.jpg | wc -l) echo 标签数量: $(find labels -name *.txt | wc -l) # 随机抽5个标签看内容 for f in $(ls labels/train/*.txt | head -5); do echo $f head -3 $f done这个脚本的价值在于很早就暴露两个问题文件总数对不对以及标签文件是空的还是真有坐标。我见过不止一次数据集标题写6000张解压出来图像确实6000张但标签目录只有一半文件另一半点开是0字节。出现这种情况通常是标注工具导出中断没跑完就打包了。如果标签文件普遍只有一两行说明单张图像目标数量少背景占比大。这对训练本身不算坏事但会直接影响loss收敛速度——模型前期会倾向学背景特征检测头迟迟学不到有效梯度。如果目标是检测堆叠在回收箱里的多种废物单图目标数少于3个的数据集会让你后期很被动。然后是类别文件。classes.txt里维护的是类别ID到类别名的映射顺序绝对不能动。比如0 glass_bottle 1 paper_bottle 2 plastic_bottle 3 plastic_bag如果标题说的是四类那classes.txt应该正好四行。但实际拿到手的数据集可能有第五类——像“metal_can”混在里面或者四类里夹着一个空行。训练前把classes.txt打印出来逐行看一遍是成本最低的检查。你不想训练到第50个epoch才发现类别标签错位那就是训练过程中最让人抓狂的黑匣子问题了。3. 标签工程从原始标注到YOLO训练集校验与转换脚本这一章节解决的核心问题是标题写“带标签”但没保证标签一定可用。比如有人从VOC格式转换过来XML标签和图像对不上有人用了labelImg但导出时选了YOLO格式却忘记归一化还有更隐蔽的标签坐标超出图像边界训练时不报错但AP值怎么调都上不去。3.1 用Python做一次完整的标签合法性校验不管原始标签是YOLO txt还是XML第一件事都是写一个校验脚本。校验维度至少覆盖文件配对、类别ID越界、坐标是否在0-1区间、宽高是否为负、以及单标签多空格导致的解析错位。import os label_dir labels/train image_dir images/train bad_files [] for label_file in os.listdir(label_dir): if not label_file.endswith(.txt): continue base os.path.splitext(label_file)[0] img_path os.path.join(image_dir, base .jpg) if not os.path.exists(img_path): bad_files.append(fmissing image: {label_file}) continue with open(os.path.join(label_dir, label_file), r) as f: lines f.read().strip().split(\n) num_classes 4 # 按classes.txt数量改 for line in lines: parts line.split() if len(parts) ! 5: bad_files.append(fbad format: {label_file} - {line}) continue cls_id, cx, cy, w, h int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) if cls_id num_classes: bad_files.append(fclass id out of range: {label_file}) if not (0 cx 1 and 0 cy 1): bad_files.append(fcenter out of range: {label_file}) if w 0 or h 0: bad_files.append(fnon-positive size: {label_file}) print(f问题文件共 {len(bad_files)} 个) for item in bad_files[:20]: print(item)逻辑上这个脚本在做三件事校验标签和图像文件名一一对应解析标签内容并捕获行列错位检查数值范围是否合法。注意classes数量是硬编码的如果你解压后有classes.txt脚本应改成动态读取不然换数据集就得改代码。参数说明部分center_x center_y理论上允许0和1但接近边界的中心点需要留意比如cx0.01且width0.05时框的左边缘会到-0.015超过图像范围。严格的做法是把坐标还原成像素再判断是否在[0, width]内而只看归一化值容易漏过这种情况。3.2 从VOC XML转YOLO标签的脚本与四个坑大量标注工具至今仍默认输出VOC的XML格式所以zip里如果混着xml文件你需要自己转。这个转换流程的常见写法是遍历XML文件提取每个object的name和bndbox坐标然后按图像尺寸归一化写入txt。import xml.etree.ElementTree as ET # 类别到id映射顺序必须和训练data.yaml一致 class_map { glass_bottle: 0, paper_bottle: 1, plastic_bottle: 2, plastic_bag: 3, } def voc_to_yolo(xml_path, out_path, img_width, img_height): tree ET.parse(xml_path) root tree.getroot() with open(out_path, w) as f: for obj in root.iter(object): name obj.find(name).text if name not in class_map: continue # 跳过未定义类别 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 / img_width y_center (ymin ymax) / 2 / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height f.write(f{class_map[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n)这个转换有四个坑每个都亲自踩过。第一个坑是xmin不一定是左上角。有些标注工具的坐标系原点是图像中心而非左上角导致转换后坐标全部偏移。保险做法是转换后随机抽几张图把预测框画出来和原图对比比看坐标值有效得多。第二个坑是XML里的坐标可能带小数。如果标注时用了缩放显示bndbox里存的是显示坐标而非原始分辨率坐标直接转换出来所有框全部偏小。处理方式是先读取图像真实尺寸用cv2.imread拿到 shape而不是信任XML里的任何宽度信息。第三个坑是多个object框在同一位置重叠比如一瓶水外面套了一层塑料袋。转换后两个txt行几乎一样的坐标训练时目标检测头会同时匹配两个类别导致loss震荡。这种情况要么手工删一帧要么保留数量更多的类别二选一。第四个坑更隐蔽XML文件名和图像文件名前缀不一致。有的标注工具会生成img_001.xml对应IMG_001.JPG大小写不同在Linux下就是两个文件脚本匹配不上标签和图像错位。所以转换脚本里最好做lower()统一否则跑到一半永远在报missing image。3.3 样本平衡问题塑料瓶太多纸瓶太少可回收废物数据集的典型问题是类别极端不平衡。玻璃瓶容易拍塑料瓶满大街都是但纸瓶——就是那种利乐包装类——很多人根本分不清和普通纸箱的区别标注比例自然低。训练前统计一下每个类别的目标数量from collections import Counter counter Counter() label_dir labels/train for f in os.listdir(label_dir): if not f.endswith(.txt): continue with open(os.path.join(label_dir, f)) as fp: for line in fp: cls_id line.split()[0] counter[cls_id] 1 for cls_id, cnt in sorted(counter.items()): print(f类别 {cls_id}: {cnt} 个目标)输出后如果发现最多和最少差一个数量级别直接开训。常见做法是给少样本类别增加复制增强或者用mixup把少样本类别和背景合成新图。这里提到复制增强是可行的因为YOLO的mosaic增强本身就会随机组合四张图只要类别出现在批次里就能被采样到。但复制增强如果做太狠模型容易过拟合到特定角度泛化性反而下降。稳妥的策略是把少样本类别提取出来配合随机旋转、亮度扰动扩充到接近中位数水平而不是直接翻10倍。4. 用YOLOv8训练可回收废物检测从配置到跑通的完整命令训练部分我以YOLOv8为例因为它是当前社区用的最多的版本安装简单推理速度快对6000张图像这种量级的数据集有很好的收敛效果。先把zip解压并整理成标准结构然后写data.yaml。# data.yaml path: /path/to/recyclable_waste_dataset train: images/train val: images/val nc: 4 names: 0: glass_bottle 1: paper_bottle 2: plastic_bottle 3: plastic_bag这里path是绝对路径建议不要用相对路径因为YOLO训练时的工作目录和脚本位置经常不一致相对路径最容易成为第一个报错来源。装好依赖后直接开始训练pip install ultralytics yolo detect train \ datarecyclable_waste.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience15 \ lr00.01 \ augmentTrue拆解一下参数。modelyolov8s.pt是最关键的选择s版本在精度和速度间平衡最好300万参数量在这个数据规模下足够拟合而m或l版本在6000张图像上反而容易欠拟合因为参数量太大数据到不了充分训练的规模。imgsz640是默认值对可回收废物这类目标大小差异悬殊的场景先别改大改大只会增加显存压力而不会直接提升小目标精度。batch16要结合显存设置12G显存跑16没问题8G显存建议降到8。patience15表示验证集mAP连续15个epoch不提升就停止这是防止过拟合的后悔药。关于lr00.01如果你之前训过其他模型习惯性地把学习率调成0.001那要注意YOLOv8设置的默认值是0.01配合warmup机制前几个epoch会线性升到目标值所以不用手动降。真正该调的是mosaic这个augment的具体开关——在下面的排除命令里单独处理。yolo detect train \ datarecyclable_waste.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience15 \ augmentTrue \ mosaic0.8 \ mixup0.2 \ close_mosaic10mosaic0.8表示80%概率启用mosaic增强close_mosaic10表示最后10个epoch关闭。为什么可回收废物数据集要单独调这两个参数我后面会详细说。这里先记住结论贴近真实场景比如垃圾桶边缘、地面阴影、光照暗角mosaic会切碎目标形态如果用全局概率1.0换来的多样性反而有害。训练完成后model目录下会出现best.pt和last.pt前者是按验证集表现最优的权重后者是最后一轮权重。我的习惯是训练完别急着删last.pt因为best.pt的epoch往往停在很早例如30轮就最优了但最后的模型可能在部署环境适应性上更好比如处理图像分辨率变化的能力更稳。5. 踩坑记录可回收废物数据集训练中反复出现的5个问题这一章是整篇最有价值的沉淀。以下每条都是训练时真的遇到过的现象直接按现象、原因、解决的逻辑写。5.1 玻璃瓶在验证集里AP全线崩溃现象是训练时loss下降正常验证集mAP也不错但单独查看玻璃瓶类别的AP只有别人的一半。原因分析后发现透明物体的本质问题是标签本身的语义模糊。玻璃瓶在不同背景下边缘特征完全变化白墙前是透明轮廓线暗色背景下整瓶变成高光区域模型学到的并非“瓶子形状”而是“背景对比度”。更严重的是某些图像里瓶子后面的物体透过玻璃显现标注时框的范围包含了透射物体正样本里混入了大量噪声。解决方式是在训练配置里开启hsv_h、hsv_s和亮度增强让模型对光照变化更鲁棒。但更有效的步骤是把训练集中所有玻璃瓶图像的亮度统计出来如果发现亮度集中在100以下说明原数据集拍摄场景偏暗补充光照样本比调参更直接。至少先给玻璃瓶类别单独生成一份亮度增强副本混入训练集。5.2 塑料瓶和塑料袋互相误检现象是预测时把揉皱的塑料袋识别成塑料瓶或者把白色洗衣液瓶识别成塑料袋。原因是这两类目标在形状和颜色上高度混淆。塑料袋揉成团后轮廓接近瓶子截面塑料瓶如果被压扁侧面看和袋子塌下来的形态类似。数据集里如果大量出现站立塑料瓶平摊塑料袋的样本模型学到的特征是“白色塑料质感”而不是形状判别。处理办法是在数据增强里给这两类单独增加角度变换。塑料袋平摊时是矩形揉皱时是多边形瓶子站立时是矩形横放时依旧接近矩形。关键判别特征是长宽比训练阶段通过旋转增强让模型看到更极端的长宽比分布能有效拉开两类特征的区分度。同时考虑在类别设置里增加一个“wrinkled_bag”子类的可行性但这个动作会导致标注工作翻倍如果数据集本身不分此类就别追加。5.3 训练到中期loss反弹现象是loss曲线前30轮一路降到2上下40轮突然跳回3.5然后反复震荡。原因是YOLOv8的mosaic增强在后期和关闭策略冲突。默认配置会在最后10个epoch关闭mosaic但如果数据集中小目标特别多强增强阶段的loss已经压得很低关掉增强后模型面对“干净”的输入反而无所适从loss自然反弹。解决方式有两种习惯我通常选第二种。第一种是直接改close_mosaic0让增强一直开验证时用mAP评估不追求loss绝对低。第二种是给关mosaic后的阶段配一个更小的学习率让模型从增强空间平稳过渡到真实分布。后者的实现要写在回调里比较麻烦前者的效果在多数可回收废物任务里已经够用。5.4 6000张图像里有重复样本验证集结果虚高现象是训练集loss表现优秀但现场测试时同一瓶饮料换个背景就检测不到了。原因可能是数据集打包时没有做去重同一个物体在不同角度拍了几十张这些图像被同时分到train和val集合里导致验证集泄露。这种情况叫做“数据穿透”模型其实见过验证集mAP显示95%但真实场景只有60%。检查和处理的方法是清洗掉感知哈希值接近的重复图像。对图像做一致性哈希比较相似度高于0.95的只保留一张。采样时注意如果某个玻璃瓶出现20次这20张在文件层面的哈希并不一样但背景结构高度相似用简单的md5去重没用要用感知哈希。建议对全部图像计算pHash后做聚类删除。5.5 标签框和物体不对齐推理框整体偏移现象是训练时模型输出框总是大一圈或偏右下角不是精度不够是系统性地偏移。原因是标注工具导出的坐标没有经过严格对齐比如标注员习惯把玻璃瓶的瓶口也算进框里但地面分拣线把瓶口截断在外。另一种常见情况是数据集制作时用了批量自动标注工具锚点漂移没有被人工修正。解决方案分两步。第一步是提取误差分布随机抽50张预测图不只看mAP而是手动比较预测框和标注框的IoU均值。如果IoU普遍在0.6-0.7之间说明对齐本身有问题。第二步是写脚本把数据集里所有标签框的中心点坐标按类别计算均值如果某一类别的中心点普遍偏离图像中心方向恒定说明整体偏移可以补偿直接对txt坐标做平移修正即可。6. 从验证到部署NMS阈值、类别合并和一次跑通的推理命令训练结束不等于项目完事。可回收废物的检测场景和传统的通用物体检测有个重要区别现场相机角度固定、拍摄距离固定所以推理阶段可以充分发挥固定场景的优势。6.1 验证和推理的标准命令# 在验证集上完整评估 yolo detect val \ modelruns/detect/train/weights/best.pt \ datarecyclable_waste.yaml \ imgsz640 \ conf0.25 \ iou0.45 # 单张推理 yolo detect predict \ modelruns/detect/train/weights/best.pt \ sourcetest_images/ \ conf0.4 \ iou0.5 \ saveTrueconf和iou是部署前必调的两个阈值。conf是置信度门槛低于这个值的目标直接丢弃iou是NMS的交并比阈值决定重叠框去留。在垃圾回收场景如果目标是自动分类进仓我一般把conf调到0.45以上因为它宁可漏检也不允许错检错捡会把瓶子送错料仓。反之如果是实时视频流做提醒辅助conf可以降到0.3漏检的代价小于误报打扰人工的代价。6.2 TensorRT导出后的一个核心技巧训练好的模型导出到边缘设备部署时INT8量化是个大坑尤其是玻璃瓶这种边缘对比度低的目标直接量化容易掉3-5个AP。我的一般做法是先用FP16导出跑通再根据现场反馈决定是否做INT8。FP16的精度损失可以忽略显存减半对绝大多数分拣设备足够。yolo export modelruns/detect/train/weights/best.pt formatonnx # 转engine文件用trtexec /usr/src/tensorrt/bin/trtexec \ --onnxbest.onnx \ --saveEnginebest_fp16.engine \ --fp16导出ONNX后先用onnxruntime在电脑上跑一遍确保输出节点的形状正确。典型错误是导出时imgsz和训练时不一致导致推理尺寸报错或者dynamic batch没有设置导致批量推理失败。6.3 现场最有效的技巧固定场景下的锚框调优这是今天我特别想讲的一个习惯。很多人在部署时直接沿用训练时的输入尺寸640×640但可回收废物场景的相机是固定的检测距离固定物体在画面里的大小分布也固定。正确的做法是拍摄100张现场图标注好目标的位置大小统计目标的宽高分布然后在推理阶段用自定义anchor。yolo detect train \ datarecyclable_waste.yaml \ modelruns/detect/train/weights/best.pt \ epochs30 \ imgsz640 \ batch16 \ patience8 \ freeze10用现场数据微调30轮冻结前10层骨干网络只更新检测头。这个做法的本质是保持特征提取能力不变把检测层的anchor分布调整为现场真实比例。比如数据集里的训练图像多是近距离平拍但现场相机是俯视角度瓶子的框在画面里会呈现上窄下宽的梯形锚框需要更扁长。不用现场数据微调模型去现场跑效果五五开。最后一句经验之谈别迷信mAP可回收废物检测的成败往往在数据清洗上一张标注错位的标签图比100张模糊图像更有破坏力。我如今处理任何yolo数据集都要先花两小时做标签工程和重复样本清洗训起来顺心很多。希望今天这篇能帮你在训练这条路上少几次翻车。本文还有配套的精品资源点击获取