简介:这是一份面向垃圾分类场景的YOLO格式标注数据集,适用于目标检测模型训练、智能分类系统开发及环境工程研究。数据涵盖可回收垃圾、有害垃圾、厨余垃圾和其他垃圾四类,标注信息以文本文件形式保存,并配有类别定义文件,可直接接入YOLO系列主流检测框架。压缩包共含2000个文件,包括1999个标注文本和1个类别配置声明,总大小约540.75MB,目录结构清晰,便于按类别筛选和批次训练。该资源目前已有2088人学习使用,受到目标检测方向开发者的关注,适合深度学习和计算机视觉方向的入门及进阶用户。使用此数据集,可以免去人工标注的繁琐工作,直接获得带目标框与类别标签的样本,用于训练垃圾识别模型、评估网络性能,或部署于智能垃圾桶、社区分类督导等场景,促进垃圾分类自动化与准确率的提升。
1. YOLO垃圾四分类数据集:模型不背锅,数据先过关
很多人拿到一份YOLO垃圾四分类数据集,第一反应是赶紧调参、换网络结构,结果训练十几个版本,mAP卡在0.7上不去,换到真实垃圾桶旁边一测更是翻车。我的判断一直没变:这种任务的分水岭不在模型,而在数据集本身。所谓YOLO垃圾四分类数据集,核心不是一堆图片,而是把垃圾图像整理成一份能让YOLO正常训练目标检测模型的数据资产,包含类目怎么定义、样本怎么采、标注怎么管、格式怎么转、类别怎么平衡。它解决的是算法团队“一训就飘、一测就废”的问题,适合做智能分拣、环卫监管、社区垃圾房管理的工程师,也适合拿真实任务入门目标检测的初学者。这篇就按做数据集的完整顺序,把类目边界、采集清洗、格式转换、训练验收一次讲透。
2. 垃圾到底分哪四类:类目体系与标注边界
四分类的语义是处理流向上的差异,不是材料学上的差异。同一个塑料瓶,干净的瓶和沾了食物残渣的瓶,可能分属两类。所以做数据集的第一步不是找图,而是先把类目体系和标注边界定死,否则后面标注员每标一张图都在替你“做算法决策”。
2.1 四分类的内核是处置流向,不是材料学
国内最常见的清运体系把生活垃圾分为可回收物、有害垃圾、厨余垃圾、其他垃圾,也就是俗称的四分类。目标检测模型要回答的问题是“这张图里的物体属于哪个处置流向”,而不是“这个瓶子是什么材料”,所以类别粒度不需要精细到PET瓶、乳白玻璃瓶、铝罐,那种粒度属于材料分选场景,不在四分类讨论范围内。
四分类是检测任务里最低可用的粒度。再少就是二分类,失去场景意义;再多就进入三四十类的细分类,标注成本和相邻类别混淆会同时爆炸。实际工程里有一个很常见的麻烦:“其他垃圾”本质上是一个杂类,语义不收敛,很多团队做完第一版后会考虑把“其他”拆成“烟蒂”“使用过的纸巾”“灰土”等子类,训练时再合并回去,这个策略后面展开讲。
| 类别索引 | 类别名 | 典型实例 | 边界容易踩的实例 |
|---|---|---|---|
| 0 | 可回收物 | 塑料瓶、纸箱、易拉罐、玻璃瓶 | 沾油污的塑料餐盒,应归厨余 |
| 1 | 有害垃圾 | 电池、灯泡、过期药品、油漆罐 | 充电宝一般走可回收专项,不等于有害 |
| 2 | 厨余垃圾 | 菜叶、果皮、剩饭、蛋壳 | 带残渣的外卖打包盒,应归厨余 |
| 3 | 其他垃圾 | 烟蒂、灰土、陶瓷碎片、用过的纸巾 | 陶瓷碗碟在不同城市规则不同,需与需求方确认 |
类目清单建议用一份独立的配置文件管理,训练脚本和标注平台都从这里读取。我一般写成YAML,避免在代码里硬编码类别名:
# 类目清单,顺序即YOLO类别索引,发布后不要中途插队 categories: - id: 0 name: recyclable cn_name: 可回收物 deploy_name: recyclable - id: 1 name: hazardous cn_name: 有害垃圾 deploy_name: hazardous - id: 2 name: kitchen_waste cn_name: 厨余垃圾 deploy_name: kitchen_waste - id: 3 name: other_waste cn_name: 其他垃圾 deploy_name: other_waste逻辑说明:YOLO的类别索引从0开始,txt标签里写的数字就是这份清单的id。类别顺序一旦发布,后面不要再插入新类,否则旧标注文件里所有类别索引全部错位,这个错位不会报错,只会让模型安静地学错。deploy_name是部署时的输出名称,训练名和推理名必须完全一致,很多上线事故就是“训练叫recyclable,推理叫recycle”。
注意:类目清单每次修改都要打版本号,并且连同标注文件一起归档。没有版本管理的类目清单,三个月后连你自己都说不清0号到底代表什么。
2.2 标注边界规范:比模型更重要的是“不纠结”
多人标注同一个数据集时,最大的消耗不在画框,而在对边界对象的不同判断。同样一张“外卖盒里有剩饭”的照片,有人归厨余,有人归可回收,这个不一致会直接变成训练噪声,而且是模型自己消化不了的噪声。
我建议立三条硬规则。第一,维护一份难例清单,凡是标注员拿不准的物体,先查清单,查不到就开会定,定完同步给所有人。第二,当目标被遮挡超过50%或距离太远看不清时,宁可跳过不标,也不要强行画一个猜出来的框。第三,单张图的实例数上限控制在30个左右,超过上限的图改成挑大目标标,标注密度过高对训练收益的提升非常有限。
难例清单用JSON维护,每条记录包含物品特征、判定结论和理由,训练完一轮后把误检图追加进来,形成闭环:
{ "hard_cases": [ {"item": "带食物残渣的外卖盒", "target_class": "kitchen_waste", "reason": "内容物污染后不再具有可回收价值"}, {"item": "一次性纸杯", "target_class": "other_waste", "reason": "淋膜纸不易回收,按其他垃圾处理"}, {"item": "玻璃碎片", "target_class": "hazardous", "reason": "易划伤清运人员,按有害垃圾处理"} ] }逻辑说明:这份清单不是一次性工作,它是数据集的活文档。每次模型在真实场景里出现bad case,先检查是不是类目定义本身的问题,再决定改标注、改规则还是改模型。质检方面,前500张标注全量检查,之后每批抽检30%,错标率超过5%就全部返工。这5%的返工线是很多团队忽略的参数,但它的重要性不亚于学习率。
3. 图像从哪来、怎么变多:采集、清洗与增强实操
类目定义好之后,下一步就是把图集堆起来。很多团队找图就像拼布头,先不论版权问题,光是把分辨率混乱、光照差异大、带水印的图混进训练集,就比任何超参数都伤模型。这一章讲采集路线的选择、清洗脚本的写法、增强参数怎么定。
3.1 三条采集路线与数量目标
采集路线通常有三条。第一条是自采实拍,用手机或摄像头去垃圾桶旁、人行道、清运车、社区垃圾房拍,覆盖白天、夜晚、阴天等光照条件。优点是场景分布可控,后续做回放集测试也有地基。第二条是按四分类关键词批量抓取公开图片,注意关键词语义要拆细,“易拉罐 地上”比“垃圾”好用一个量级,还要确认图片的使用条款。第三条是复用已有的目标检测数据集或公开数据集做二次筛选,但必须逐类核对语义,有些数据集里的“报纸”是人在读报的场景,模型学到的是人,不是报纸,这种图要直接删掉。
数量目标我一般按一个底线来规划:每个类别至少要有1000张图像、2000个标注实例,四分类总量在6000到8000张图像起步。这里有个新手容易理解错的点:实例数比张数重要。YOLO在训练时吃的是框,不是图,300张图每张5个实例,效果会好过1000张图每张只有0.3个实例。
| 类别 | 建议图像张数 | 建议实例数下限 | 建议场景数 |
|---|---|---|---|
| 可回收物 | 1500 | 2500 | 8个以上 |
| 有害垃圾 | 800 | 1200 | 6个以上 |
| 厨余垃圾 | 1200 | 2000 | 6个以上 |
| 其他垃圾 | 1500 | 2500 | 8个以上 |
有害垃圾的收集难度最大,电池、灯泡这类物品在自然场景里出现频率低,我的经验是先用少量场景保证实例数,再通过增强补足,而不是硬凑张数。
3.2 清洗流程:一个脚本去掉模糊、重复、损坏图
抓来的图不能直接进标注流程,因为重复图、模糊图、损坏图会同时污染训练和验证。我常年保留一个清洗脚本,处理三类问题:无法解析的损坏文件、清晰度不够的模糊图、内容几乎一致的重复图。
import os import shutil import numpy as np from PIL import Image from scipy.ndimage import laplace def dhash(img, hash_size=8): small = img.convert("L").resize((hash_size + 1, hash_size), Image.LANCZOS) arr = np.array(small, dtype=np.uint8) return (arr[:, :-1] < arr[:, 1:]).flatten() def hamming(a, b): return int(np.count_nonzero(a != b)) def blur_score(path): img = np.array(Image.open(path).convert("L"), dtype=np.float32) return laplace(img).var() os.makedirs("bad", exist_ok=True) os.makedirs("blur", exist_ok=True) os.makedirs("dup", exist_ok=True) seen_hashes = [] for name in os.listdir("raw_images"): path = os.path.join("raw_images", name) if not name.lower().endswith((".jpg", ".jpeg", ".png")): continue try: img = Image.open(path) score = blur_score(path) except Exception: shutil.move(path, os.path.join("bad", name)) continue if score < 80: shutil.move(path, os.path.join("blur", name)) continue h = dhash(img) if seen_hashes and min(hamming(h, p) for p in seen_hashes) < 5: shutil.move(path, os.path.join("dup", name)) continue seen_hashes.append(h)逻辑说明:dhash把图片缩放到9×8的灰度图,比较相邻像素的明暗变化,得到一个64位哈希,两张图哈希距离小于5就判定为重复。这个阈值是关键参数,改太大会把同一场景不同角度的照片也误杀,改太小则漏掉压缩率不同的副本。blur_score用的是拉普拉斯算子的方差,方差越低说明边缘越弱、图像越糊。
参数说明:拉普拉斯方差阈值80不是普适值,分辨率越高通常数值越高,网络抓来的大图经常超过500,而手机暗光照片可能只有50到150。建议先跑一个统计分布,取明显偏低的那一段作为阈值,不要照着别人的参数抄。另外,blur目录里的图也不是直接删,偶尔会有人物主体模糊但垃圾很清晰的情况,我一般会二次人工扫一眼再清空。
3.3 离线增强与在线增强:哪些场景真的需要
增强策略分为两件事。离线增强服务于类别数量补差,对有害垃圾这种少数类,我会对原始图做复制、水平翻转、小幅HSV扰动,生成额外的训练副本。在线增强则在训练配置里开启,YOLO自带的Mosaic等策略会实时拼接图像,覆盖面比离线增强更随机。
需要强调的是,四分类场景的增强参数要克制,尤其是颜色扰动不能拉太大。可回收物里的纸箱饱和度偏低,其他垃圾里的包装袋是浅棕色,HSV的饱和度扰动过强会让这两个类互相渗透。我常用的起步参数是flip=0.5,HSV的h=0.015、s=0.5、v=0.4,mosaic=1.0,scale=0.5,全部保持中等强度。
注意:不要对四分类做大幅度随机旋转和透视变形。垃圾桶里的瓶子是自然摆放姿态,不是艺术创作,过度变形只会让模型学到扭曲的特征,真实场景里反而更差。
4. 把任意标注格式转成YOLO txt:转换脚本与硬性检查
YOLO训练要求的标签格式是每张图一个txt文件,每一行写“类别索引 中心点x 中心点y 宽度 高度”,坐标全部归一化到0到1之间。但标注平台导出的格式五花八门,最常见的是COCO JSON和自定义JSON,转换本身不难,坑全在转换完没有质检。这一章给出转换脚本、检查命令和划分逻辑。
4.1 YOLO txt格式的三个约定
先记住三个约定。第一,文件配对严格按图像主文件名走,a.jpg对应a.txt,两个文件要么放同一目录,要么在训练配置里分别指定图像目录和标签目录。第二,坐标必须归一化,计算方式是矩形中心点x除以图像宽度,中心点y除以图像高度,宽度除以图像宽度,高度除以图像高度。第三,类别索引从0开始,必须和类目清单保持一致。
COCO JSON里的bbox字段是[x, y, w, h],且是像素坐标,x、y是左上角点,这个细节搞反,转换出的框位置全部偏移。
4.2 从COCO JSON转YOLO txt:脚本与参数说明
下面这个脚本是我常用的转换工具,支持类别索引重映射,适合从公开数据集筛选后做二次标注的场景。
import json import os def coco_to_yolo(coco_json, image_dir, out_dir, label_map): os.makedirs(out_dir, exist_ok=True) with open(coco_json, "r", encoding="utf-8") as f: data = json.load(f) images = {img["id"]: img for img in data["images"]} anns_by_img = {} for ann in data["annotations"]: anns_by_img.setdefault(ann["image_id"], []).append(ann) for img_id, im in images.items(): lines = [] for ann in anns_by_img.get(img_id, []): x, y, w, h = [float(v) for v in ann["bbox"]] if w <= 0 or h <= 0: continue cls = label_map.get(ann["category_id"], -1) if cls < 0: continue # 中心点坐标换算,除以图像宽高完成归一化 cx = (x + w / 2) / im["width"] cy = (y + h / 2) / im["height"] bw = w / im["width"] bh = h / im["height"] # 越界值裁剪到0-1,防止训练增强时出现非法anchor cx = max(0.0, min(1.0, cx)) cy = max(0.0, min(1.0, cy)) lines.append(f"{cls} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}\n") if lines: base = os.path.splitext(im["file_name"])[0] txt_path = os.path.join(out_dir, base + ".txt") with open(txt_path, "w", encoding="utf-8") as f: f.writelines(lines) if __name__ == "__main__": # 原数据集类别id映射到我们的四分类索引,没有映射的一律跳过 label_map = {5: 0, 3: 1, 8: 2, 6: 3} coco_to_yolo("coco.json", "images", "labels", label_map)逻辑说明:代码先按image_id聚合标注,这样每张图只打开一次txt文件。label_map是类别索引的桥梁,公开数据集的类别id几乎不可能和四分类对齐,必须有这张映射表。bbox的w和h已经包含宽高信息,不需要再用右下角坐标减去左上角。
参数说明:中心点坐标做了max/min裁剪,因为标注框偶尔会超出图像边界几个像素,YOLO的增强模块对越界框做缩放时可能生成非法值,fp16下更容易触发NaN。但宽度和高度不裁剪,因为一个小目标部分超出边界是正常情况,裁剪宽度会导致框的面积失真。
4.3 转换后的硬性检查:空标签、类别越界、实例统计
转换完不能直接开训,先用几条命令做硬性检查。我习惯把检查结果输出到日志文件,而不是只扫一眼终端输出。
# 检查空标签:有多少张图片没有对应的txt文件 for img in images/*.jpg; do base=$(basename "$img" .jpg) if [ ! -f labels/$base.txt ]; then echo "MISSING $base" fi done # 检查类别索引越界,四分类合法索引是0-3 grep -E "^[4-9] " labels/*.txt | head -50 # 统计每个类别的实例数,看类不均衡程度 cat labels/*.txt | awk '{print $1}' | sort | uniq -c逻辑说明:空标签不会让训练直接报错,但会把整张图当背景,如果空标签集中在某些特定场景,模型会降低对那个场景的召回率。类别索引越界几乎都是label_map写错,或者旧版本的标签文件混进了新目录。实例数统计是类不均衡的第一道检查,如果某一类占总实例数不足10%,就需要回到采集阶段补数据。
注意:实例统计里有一个容易漏掉的坑,就是同一张图在清洗阶段被移动到blur或dup目录后,图片文件还在,但它的标注文件被留在了原目录,这个“图没了标签还在”的问题会让训练时的图像和标签错位。建议转换前把图片目录和标签目录做一次全量配对比较。
4.4 数据集划分:按场景分组再随机,别让评测集“透题”
数据划分看起来简单,实际上是最容易让验证指标虚高的环节。连续拍摄的照片,同一个垃圾桶在两秒内拍了10张,如果直接全局随机划分,其中5张进训练集、5张进验证集,验证集就“见过”训练的同一场景,测出来的mAP自然漂亮,部署到新场景立刻回落。
我一般会先按文件名前缀提取场景标识,把同一场景的照片绑在一起,再按场景划分到train、val、test:
import os import random from collections import defaultdict groups = defaultdict(list) for f in os.listdir("images"): scene = f.split("_")[0] # 场景前缀,例如 street_001.jpg -> street groups[scene].append(f) random.seed(42) train, val, test = [], [], [] for scene_files in groups.values(): random.shuffle(scene_files) n = len(scene_files) train += scene_files[:int(n * 0.8)] val += scene_files[int(n * 0.8):int(n * 0.9)] test += scene_files[int(n * 0.9):]逻辑说明:按场景分组后,同一个场景的全部照片要么都在训练集,要么都在验证集,不会两边各占一半。这种做法会让验证集mAP比“透题版”低几个点,但那是真实水平。参数上train:val:test按8:1:1切,test从始至终不参与调参,只在最终版本上跑一次。
5. 垃圾四分类数据集常见问题排查:5个高频坑的现象、原因、补救
数据准备好之后,训练过程依然有坑,而且这些坑大多不是调参能解决的,是靠改数据或改评估协议解决。以下5条是我在四分类项目里反复遇到的,按现象、原因、解决三部分来写,方便对照排查。
5.1 标注层的坑:错标传染、杂类不收敛、空标签引发NaN
坑1:错标率超过3%时,mAP开始失灵。现象是训练loss正常下降,验证mAP也不差,但抽查预测结果发现错误集中在同一种物品上,比如模型把烟盒当其他垃圾、把纸巾当厨余垃圾。原因是那一类物品的标注本身不一致,不同标注员对同一物品的判断不同,YOLO在类间语义接近时会学平均特征,等于被噪声拉偏。解决办法是抽检500个框,错标率超过3%就返工,并针对bad case物品做全量复查。前期多花一天返工,后期能少调两周参数。
坑2:“其他垃圾”是杂类,模型会把它当垃圾桶。现象是验证集里“其他垃圾”的误检测集中在所有非标注区域,模型输出大量假阳框。原因是“其他垃圾”的定义是“不属于前三类”,语义上是开放集合,闭合分类器对开放类别天然不友好,模型找不到内部一致的模式。解决办法是做数据时把“其他垃圾”按常见子类拆开标注,比如烟蒂、纸巾、灰土、陶瓷碎片,训练时再合并到一根,让模型至少学会每个子类的视觉特征。
坑3:标注坐标越界导致训练中途出NaN。现象是loss前几个epoch正常,某次保存后突然变成NaN,或者加载预训练权重后一训练就爆。原因是标签里出现了1.0001这种越界坐标,YOLO的增强模块对越界框做缩放时产生非法anchor,fp16下更敏感。解决办法是转换脚本里严格用max/min裁剪所有中心点坐标到0-1,并在训练前检查所有txt文件里是否存在大于1或小于0的值。
5.2 数据分布层的坑:不均衡的“难易度”、同场景泄漏
坑4:类不均衡不只在数量,更在“难易度”。现象是有害垃圾AP只有0.4,其他三类都在0.7以上,即使加了类权重提升也有限。原因是数量少只是表面原因,深层原因是“有害垃圾”出现的位置五花八门,桌面、角落、垃圾袋内都有,每张图的上下文差异巨大,模型很难学到稳定的判别特征。解决办法是不要硬上过采样,而是把一半“干净桌面上的电池”图删掉,换成一米外地面杂物里露出一角电池的难例。实例数和难易度的平衡,比单纯凑数量更重要。
坑5:训练和验证同场景泄漏,本地mAP虚高。现象是本地验证mAP等于0.84,去一个新小区拍了一组真实环境图测试,mAP掉到0.58。原因是划分数据时没有按场景分组,同一个垃圾桶的连续帧同时出现在train和val。解决办法是改用上一章的按场景分组划分法重建训练集,并且在项目上线前,去没见过的现场拍100张图做盲测集,之后每个版本都用同一批盲测图回测。盲测集一旦用于调参,就会慢慢变成第二个验证集,失去盲测意义,所以它必须是只跑不改的。
6. 用回放集和混淆矩阵验收数据集:一个耐用的迭代习惯
数据集不会一次做对,真正让工程质量提升的是验收习惯。我现在的做法是给每个四分类项目维护一个“回放集”,核心规则有两条。第一条,回放集里的图像只来源于真实现场,比如小区门口、垃圾房、清运车,每个季度新增一批,新增的图像标注后全部并入回放集,不并入训练集。因为训练集可以持续变大,但验收集必须保持独立,否则模型只是在考试里见过原题。第二条,每次训练完,不看单一mAP,先看混淆矩阵里哪两个类别互相污染最严重。
四分类场景的混淆矩阵有一个典型规律:“可回收物”和“其他垃圾”互窜的频率远高于其他组合,原因是两者的外观边界模糊,纸箱、包装袋、一次性塑料制品在不同光照下很容易交叉。如果混淆矩阵显示某个类别的召回率明显低于其他类,我一般直接回到数据层面查三件事:实例数是否偏少、难例图是否没有覆盖、标注规则是否在那个类里有分歧。查完这三件事再去调超参数,效率高很多。
除了混淆矩阵,我还会保存每一轮的bad case图。具体做法是训练完后挑出置信度大于0.5但预测错误的框,按预测类别分组,每个组看一遍。这一步虽然耗时间,但能发现很多标注阶段漏掉的问题,比如某个场景里所有“矿泉水瓶”都被标成了“其他垃圾”,这种标注层面的大面积错误,在loss曲线和mAP上几乎看不出来,只有看bad case才能定位。
我现在养成的习惯是:每两周从现场采集100到300张图,人工标注后全部追加到回放集,然后重新训练一轮,把新旧版本的混淆矩阵和bad case对比一遍。这个循环看起来很土,但它帮我省掉了大量“不知道为什么这版变差了”的黑匣子排查时间,也让每个训练版本都有明确的验收标准。如果你正被四分类数据集折磨,不妨从今天开始建一个只属于你自己的回放集,坚持三个版本后再回头看,会感谢这个习惯的。
希望帮到你。
本文还有配套的精品资源,点击获取