YOLO垃圾四分类数据集构建与训练踩坑指南:从标注到部署
简介YOLO垃圾四分类数据集包含可回收、有害、厨余、其他垃圾四类目标检测标注数据面向计算机视觉开发者与智能环保应用设计者可用于训练和评估YOLO系列等目标检测模型。包内共2000个文件主体为1999个txt格式的YOLO标注文件另有1个yaml类别配置文件压缩包整体约540.75MB标注信息规范清晰下载解压后即可用于模型训练与验证。目前已有2088人学习浏览适合算法工程师、高校学生以及环保项目团队使用。数据涵盖塑料、玻璃、纸张、金属、电池、灯泡、果皮、食物残渣、烟蒂、一次性餐具等常见废弃物类别定义明确能帮助模型充分学习不同垃圾的外观差异为自动分类设备提供准确的目标定位与类别信息。借助该数据集可构建智能垃圾分类系统支撑可回收物分拣、有害垃圾预警、厨余垃圾资源化处理等场景从而有效提升垃圾分类的准确性和处理效率。1. 垃圾四分类为什么看着简单却总在真实场景翻车第一次接到“用 YOLO 做垃圾四分类”这个需求时我以为是送分题类别少、目标大、背景简单拿现成数据集训练一轮指标应该不会难看。真做完才发现四分类的难点不在“分类”而在“边界”。厨余垃圾里混着的塑料袋、可回收物里沾了油污的纸盒、有害垃圾桶里套着垃圾袋的电池——这些样本在公开数据集里几乎不会成对出现而真实场景里全部挤在同一张图里。这篇文章要讲的是整个落地路径怎么定义四分类的类别体系、怎么构建一个不让模型“自我欺骗”的数据集、怎么把标注干净地转成 YOLO 格式、训练参数怎么定、以及我踩过的几个典型翻车现场。适合正在自建数据集、或者拿了公开数据但训练效果不理想的从业者。先说结论YOLO垃圾四分类数据集的核心不是“图片够多”而是“边界样本够多、类别噪声够少”。2. 构建四分类数据集类别定义、采样策略与脏数据清洗2.1 四分类的类别边界先统一命名再谈采集垃圾四分类在国内城市管理里通常指可回收物、厨余垃圾、有害垃圾、其他垃圾。但在公开数据集里“可回收物”经常被拆成玻璃、金属、塑料、纸类“有害垃圾”则可能只覆盖电池和灯泡。如果你直接下载别人整理好的数据集第一件事不是训练而是检查类别体系是否一致否则模型学到的边界和你部署时的边界根本对不上。我一般会先把类别映射固定成一个四行的 CSV禁止在标注过程中口头修改类别名称。字段就两列source原始类别名和target统一后的四分类 ID。比如原始数据里既有plastic_bottle又有bottle映射后都归入可回收物既有food_waste又有leftover统一归入厨余垃圾。这张表是整个数据集的“宪法”后续清洗、转换、训练都依赖它。import pandas as pd mapping pd.DataFrame([ {source: plastic_bottle, target: 0}, # 可回收物 {source: bottle, target: 0}, {source: glass, target: 0}, {source: metal_can, target: 0}, {source: food_waste, target: 1}, # 厨余垃圾 {source: leftover, target: 1}, {source: battery, target: 2}, # 有害垃圾 {source: lamp, target: 2}, {source: napkin, target: 3}, # 其他垃圾 {source: dust, target: 3}, ]) mapping.to_csv(category_mapping.csv, indexFalse)这段代码的逻辑很简单把原始数据里的所有类别名映射到四分类 ID。参数说明里最值得注意是target的编号顺序——它必须和训练时的data.yaml里的类别顺序完全一致否则会出现“模型训练时认为是可回收物推理时 ID 0 变成了厨余垃圾”这种鬼故事。我习惯把映射表提交进 Git 仓库每次新增原始类别只改 CSV 不改代码。2.2 采样策略按场景、光照、拍摄角度分层采样很多从业者犯的第一个错误是“按类别总数均衡采样”。垃圾桶场景的难点从来不是“每类够不够多”而是“同一类在不同光照、不同角度下长什么样”。我用过的一个模拟项目X里最初训练集 80% 是白天平视拍摄的干净样本模型 mAP 做到 0.92一上实地测试直接掉到 0.61——因为实地摄像头是俯视角度而且下午四点阳光斜射导致色温完全变了。正确的做法是按场景分层采样住宅小区垃圾桶、商业区路边果壳箱、环卫收运站流水线、夜晚灯光环境每类场景至少占总样本的 20%。角度上俯视摄像头安装在桶上方和 45 度斜视各要一批。光照方面白天自然光、傍晚混合光、夜间灯光三种各留一批作为验证集。采集时把现场条件记录进文件名例如residential_day_overlook_bottle_001.jpg方便后续排查模型为什么对某类样本失效。2.3 脏数据清洗统计分布、查损坏图、盯紧标注质量拿到数据后的第一件事不是看标注长什么样而是先跑一个清洗脚本把坏图、重复图、明显越界的标注过滤掉。这个步骤我在某图像处理Demo上吃过亏一个从网络爬来的数据包里混着几百张 0 字节图片和大量 GIF 转 JPG 后残留的透明通道图训练时 YOLO 完全不报错但验证集指标忽高忽低玄学得很。我常用的清洗脚本分三步第一步检查图片文件是否损坏第二步统计每类目标数量和图片尺寸分布第三步输出标注框面积占比过滤掉面积过小的目标比如归一化后宽高小于 0.01 的框基本是标注员误标的噪声。find . -type f \( -name *.jpg -o -name *.png \) -size -1k -deletefrom PIL import Image import os import glob img_paths glob.glob(raw_images/*.jpg) valid_exts {.jpg, .jpeg, .png} for p in img_paths: ext os.path.splitext(p)[1].lower() if ext not in valid_exts: print(fskip: {p} (ext: {ext})) continue try: img Image.open(p) img.verify() except Exception: print(fcorrupt: {p}) os.rename(p, p .bad) # 不直接删除保留现场参数说明img.verify()只是校验图片头部和完整性不会真正解码像素数据速度很快适合全量扫描。我一般把坏图改名而不是删除等确认无误后统一清理这算是一种“后悔药”——万一是脚本误判还能找回来。-size -1k会在 bash 层过滤掉小于 1KB 的文件这类文件几乎不可能是有效 JPG。3. 把标注转成 YOLO 格式脚本、类别映射与四个边界坑3.1 用 JSON/XML 转 YOLO 的统一脚本无论你用的是 LabelImg输出 XML、LabelStudio输出 JSON还是某在线标注平台导出的 COCO 格式最终都要落到 YOLO 的 txt 格式每张图片对应一个同名 txt每一行是class_id x_center y_center width height四个坐标值都归一化到 [0,1]。我见过太多人手工转换后漏了几百个文件最后训练时静默跳过导致某些类别数量虚标。下面这段脚本是我处理 COCO JSON 转 YOLO 的通用版本。核心逻辑就是读取标注文件拿着图片 ID 去查 key逐框转换坐标系最后写 txt。import json import os def coco_to_yolo(json_path, img_dir, out_dir): with open(json_path, encodingutf-8) as f: coco json.load(f) # 建立图片ID到文件名的索引 img_id_to_name {im[id]: im[file_name] for im in coco[images]} # 建立类别ID到统一四分类ID的映射从CSV读入这里简化 cat_id_to_target {1: 0, 2: 0, 3: 1, 4: 2, 5: 3} for ann in coco[annotations]: img_id ann[image_id] img_name img_id_to_name[img_id] base os.path.splitext(img_name)[0] txt_path os.path.join(out_dir, base .txt) cat_id ann[category_id] target cat_id_to_target[cat_id] x, y, w, h ann[bbox] # COCO格式: [x左上, y左上, w, h] # 转YOLO中心点格式并归一化 with open(os.path.join(img_dir, img_name), rb) as img_f: img Image.open(img_f) img_w, img_h img.size x_center (x w / 2) / img_w y_center (y h / 2) / img_h nw w / img_w nh h / img_h # 关键一步裁剪到有效范围 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) nw min(max(nw, 0.0), 1.0 - x_center) nh min(max(nh, 0.0), 1.0 - y_center) with open(txt_path, a) as out: out.write(f{target} {x_center:.6f} {y_center:.6f} {nw:.6f} {nh:.6f}\n)逻辑说明先把 COCO 的category_id通过映射表转成你定义的统一四分类 ID再把左上角宽高的 bbox 转成中心点宽高最后做一次 clip。注意最后对宽高的 clip 不能只裁到 [0,1]还要保证x_center width/2不越过 1.0否则会出现“标框在画面里但中心点偏移导致训练报错”的边界问题。这个脚本跑完后结果目录里几百个 txt 的类别分布可能和原始 COCO 不一致一定要再跑一次统计脚本核对。3.2 四个边界坑图片名对不上、多标签目标、通道干扰、类别顺序错位第一个坑是图片名对不上。有的数据集文件名叫IMG_001.JPG但标注 JSON 里写的是img_001.jpg大小写不一致有的平台导出的文件名带了_mask后缀但原图没这个后缀。转换脚本里img_id_to_name直接按文件名精确查找一旦有差异就会 KeyError。我一般会在读取 JSON 时就把文件名全部lower()同时把磁盘上的文件列表也统一小写再查索引这样能消除绝大多数对不上问题。第二个坑是一张图里有“垃圾桶套着垃圾袋”这种多标签叠加。某些标注平台会把“垃圾袋”和“袋内饮料瓶”各标一个框两个框重叠 95% 以上。转成 YOLO 格式后一个 anchor 可能同时负责两个高度重叠的目标训练时 loss 震荡明显。我的处理是加一个“重叠过滤”逻辑两个同类别框 IoU 大于 0.9 时只保留面积大的那个。def filter_overlap(boxes, iou_thresh0.9): # boxes: list of (x_center, y_center, w, h) keep [] for box in boxes: dup False for kept in keep: if iou(box, kept) iou_thresh: dup True break if not dup: keep.append(box) return keep第三个坑是图片格式干扰。手机拍的 HEIC、网页上的 WebP、截图存的 PNG 带透明通道这些图片在标注平台里能看但转成训练集后要么读不出来要么通道数不对。我在清洗脚本里会统一转成 RGB 三通道 JPG顺便重新压缩一下避免某些超大尺寸图片拖慢训练数据加载。第四个坑是类别顺序错位。data.yaml里的类别列表顺序必须和转换脚本里的cat_id_to_target定义的 ID 一致。有人会在标注平台里用中文类别名导出转脚本时 ID 写对了但训练配置文件里类别顺序改了结果整个模型的输出全部错位。最稳妥的方法是转换脚本读category_mapping.csv生成一个classes.txtdata.yaml的names从同一个文件读取不要手写。这样两边永远保持一致。3.3 训练集 / 验证集 / 测试集怎么分才不算作弊分割数据集不是随手shuffle一下就行。YOLO 训练时默认在训练集上计算 loss验证集用来做早停和超参调整如果你把同一批次拍摄的图片同时放进训练集和验证集模型实际上“见过”验证集的背景信息mAP 会虚高。更隐蔽的是同一个“垃圾袋里多个瓶子”的场景被连续拍了好几张模型记住的是场景而不是瓶子。我习惯按“场景目录”分割所有来自同一个小区、同一个摄像头角度、同一时间段抓取的图片放进同一个桶然后按桶分配而不是按单张图片分配。这样验证集的 mAP 才真正反映泛化能力。分割比例上四分类这种类别少、场景多的任务我一般用 65/20/15——测试集不参与任何超参决策只在最终部署前验证用。4. 用 YOLO 训练垃圾四分类的最小可跑配置从 YAML 到命令行4.1 写 data.yaml路径、类别名、类权重训练的第一步不是敲训练命令而是写data.yaml。这个文件经常被忽略但它决定了训练的整个数据视野。我用得最多的是 YOLOv8 的命令行体系YOLOv5 的坑和它类似配置文件长这样train: /data/waste/train/images val: /data/waste/val/images test: /data/waste/test/images nc: 4 names: 0: recyclable 1: kitchen_waste 2: hazardous 3: other # 可选如果类别不平衡这里手动指定损失权重 # loss_weights: # 0: 0.8 # 1: 1.2 # 2: 1.5 # 3: 1.0关键参数说明train和val指向的是图片目录YOLO 会自动在同级目录下找对应的 txt 标签文件names的顺序就是模型输出的类别顺序一旦训练开始后不要再改否则模型权重和输出 ID 就对不上了。如果你的数据集里厨余垃圾样本特别多、有害垃圾特别少可以在配置里给每个类别加 loss 权重我一般不直接改 YAML而是在训练命令里用--class_weights参数传入一个权重向量方便跑对比实验。4.2 选择模型尺寸和预训练权重的根据“直接上 YOLOv8x”往往是最差的起点。垃圾四分类只有 4 个类别目标的形状和纹理差异远小于 COCO 的 80 类因此不需要超大容量模型反而是小模型更容易收敛、更容易部署到边缘设备。我一般先在 YOLOv8s 上跑通全流程确认数据没问题后再对比 YOLOv8m 和 YOLOv8n 的精度差异。预训练权重的选择有一个原则优先选在 COCO 上训练过的版本而不是 random weights。虽然垃圾图片和 COCO 的 80 类差异大但预训练模型的底层特征边缘、纹理、颜色分布是通用的能显著缩短收敛时间。某次实验里我从随机初始化开始训练epoch 100 的 mAP 才和预训练版本 epoch 50 打平。yolo detect train \ datawaste.yaml \ modelyolov8s.pt \ epochs300 \ imgsz640 \ batch16 \ patience30 \ optimizerAdamW \ lr00.001 \ augmentTrue \ cacheTrue \ device0参数说明epochs300配patience30的意思是模型如果连续 30 个 epoch 在验证集上 mAP 没有提升就自动早停。batch16是 640×640 输入下 24GB 显存常见的起点如果你的卡是 12GB建议降到 8 并加cacheTrue让数据全部进显存否则磁盘读取会成为瓶颈。lr0是初始学习率我习惯用 0.001 而不是默认的 0.01——四分类数据集通常只有几千张图学习率太大会震荡。4.3 数据增强怎么开、怎么关、怎么调YOLO 默认开启的增强包括马赛克mosaic、随机翻转、缩放、颜色扰动。对于垃圾场景马赛克增强是双刃剑它把四张图拼接成一张模型被迫学习“小目标”的特征但如果你训练集里目标普遍偏小马赛克会让目标变得更小反而难收敛。我的经验是目标占画面面积大于 10% 时开马赛克小于 5%关掉马赛克。具体的调整方式是在训练命令里关闭单个增强项比如设mosaic0.0或mixup0.0。还有一种常见情况是夜间场景不足我一般不开额外的hsv_h之类的随机颜色参数而是使用degrees45让标注框随图像旋转——这能模拟摄像头安装角度不一致的实地情况。对垃圾四分类垂直翻转不要开因为“倒置的垃圾桶”在实地几乎不存在开了反而增加误检。4.4 训练过程监控看什么、信什么、不信什么训练过程中大家最容易盯着 loss 曲线看但 loss 下降不必然代表 mAP 上升。我更关注验证集 mAP50 和 mAP50-95 这两个指标以及它们在不同类别上的差异。YOLO 训练日志里每个 epoch 结束会输出各类别的 AP如果某一个类别的 AP 明显低于其他类第一时间不是加数据而是去看验证集预测结果图确认是标注问题还是特征本身太相似。训练到一半时我习惯停一次用当前权重在验证集上跑一批推理把预测框画出来直接看。YOLO 有个内置参数save_period10每 10 个 epoch 保存一次权重配合projectwaste_exp指定输出目录这样中途检查模型状态和做对比实验都很方便。这里要提醒一句不要用训练集 loss 判断模型好坏你真正该看的是验证集 mAP 的曲线是否稳定上升、最终是否收敛到一个合理区间。5. 训练与推理中的典型翻车现场五条踩坑排查记录5.1 现象一mAP 高达 0.94但视频流里瓶子检测框乱跳这不是模型“坏了”而是过拟合了训练集的背景信息。我遇到的具体情况是训练集 80% 图片都带“绿色垃圾桶”背景模型不知不觉把绿色当成了可回收物的共现特征一旦部署摄像头对准水泥地面瓶子还是能检出但置信度普遍低于 0.5导致框忽隐忽现。原因训练集和验证集同源背景多样性太低。解决把这些背景一致的图片全部打进同一个按场景划分的桶重新分配训练集同时加一组“无目标背景图”负样本放进数据集让模型学会“没有垃圾桶和垃圾时不该检出”。负样本不需要标注YOLO 会把它当作全背景图处理。5.2 现象二厨余垃圾袋和可回收塑料瓶互相误判这是四分类里最容易出现的语义混淆。透明的、揉皱的塑料袋和揉皱的塑料瓶都属于可回收物但塑料袋包着剩菜时模型倾向于判成厨余垃圾——因为训练集里大量厨余垃圾样本都“装塑料袋”。逻辑上人能区分“塑料袋是干垃圾”和“它里面的食物是湿垃圾”但模型没有这个推理链。原因标注类别时没有区分“垃圾袋”和“袋内物品”。解决重新审视标注规则一个目标有遮挡时按露出面积最大的物体标注。如果既有袋子又有食物要么只标食物要么新增一个“厨余垃圾带包装”的特殊类。我在模拟项目X里用了第二种方案误判率直接降了一半。5.3 现象三损失函数降了但 mAP 一直波动上不去训练日志里 box_loss 和 cls_loss 都在降mAP50 却像过山车一样在 0.6-0.8 之间来回跳。我排查时先看学习率是否过大导致参数空间震荡再一看验证集图片数量只有 80 张单张图里的目标种类又不均匀某一类一抖mAP 就跟着抖。原因验证集太小、类别分布不稳定。解决把验证集扩到至少 300 张并保证每个类别在验证集里的目标数量大致均衡。此外把batch调大或者在显存不够时把imgsz从 640 降到 512减少每个 batch 内统计量的方差mAP 曲线会平稳很多。5.4 现象四训练时报 “All labels are empty in ...”这个报错我遇到过三次每次原因都不同。第一次是标注转换脚本跑出的 txt 和图片没有放在同一级目录YOLO 搜不到第二次是data.yaml里train指向了图片目录但txt在另一个子目录第三次是清洗时误删了所有“该类目标数量过少”的标注导致整个训练集里某类标签为空。原因本质都是数据路径或标注文件没被正确找到。解决先单独写一段校验脚本遍历data.yaml里train目录下的每张图检查同名 txt 是否存在以及是否至少有一行有效标注。把校验脚本挂在训练命令之前运行能避免训练到一半才发现问题。python check_labels.py --img_dir /data/waste/train/images --label_dir /data/waste/train/labels5.5 现象五显存只有 8GB训练直接 OOM显存不够是入门者最常见的“劝退点”。我最早跑 YOLOv8s 640 batch 16在 8GB 卡上一步也跑不动以为必须换卡。其实先别急着加预算可以分三步降显存imgsz降到 512、batch降到 4、cacheFalse不缓存数据到显存。这三步一般能让 8GB 卡跑通训练只是速度慢一些。原因显存占用主要来自三块——输入批次的图像张量、特征图、梯度。解决在 YOLO 命令行里加--batch 4 --imgsz 512 --cache False同时打开--device 0确保代码没用 CPU 计算。如果这一步之后还 OOM再考虑换更大显存的卡而不是一开始就上 24GB。训练速度慢可以用--workers 4提高数据加载线程数来补偿。6. 验证模型不是看 mAP 就够用混淆矩阵和边缘部署评估真效果训练完成不等于项目完成。我在实际交付里吃过“验证集 0.93 的 mAP部署到 RK3588 上变成 0.71”的亏让我知道自己缺的不是模型而是一整套基于部署视角的验证方法。第一件事是看confusion_matrix.png混淆矩阵图。YOLO 训练完成后输出目录里有带背景类的混淆矩阵这张图能告诉你“误判到底发生在哪两个类别之间”。如果背景那一列出现高亮说明模型产生了大量的 false positive——背景图上检出了垃圾。如果矩阵对角之外有非零格子你需要判断是类别边界定义不清还是标注本身有错。我的习惯是每次训练完第一眼看混淆矩阵第二眼才看 PR 曲线。yolo detect val \ datawaste.yaml \ modelbest.pt \ splitval \ conf0.5 \ iou0.45 \ projectwaste_exp \ nameval_check参数说明conf0.5是推理置信度阈值调低会提高召回但增加误检iou0.45是 NMS 的 IoU 阈值调高能抑制重叠框但过高的iou0.7会让两个靠得近的真目标被合并成一个框。我在垃圾桶场景里习惯用conf0.4、iou0.5作为基线再根据实地测试微调。第二件事是做一次“通道对齐”的部署验证。训练时图片是随机缩放到 640但部署端的摄像头可能输出 1920×1080我见过太多人忘记在推理代码里把图片 resize 到训练尺寸导致小目标完全检不出。正确的做法是在导出模型前用一张和部署现场分辨率相同的图片跑一次推理确认预处理逻辑和训练一致。import cv2 import torch def preprocess(img_path, input_size640): img cv2.imread(img_path) # BGR, HWC img cv2.resize(img, (input_size, input_size)) img img[:, :, ::-1] # BGR - RGB img img.astype(float32) / 255.0 img torch.from_numpy(img).permute(2, 0, 1).unsqueeze(0) return img这段代码暴露了最容易被忽略的两个点resize是否用cv2.INTER_LINEAR、颜色通道顺序是不是和训练时一致。YOLO 训练时默认用letterbox保持宽高比加灰边而不是直接拉伸许多手写推理代码会把这两者搞混导致部署后精度异常。如果你的部署端必须直接拉伸就要在训练时把rectTrue关掉并用拉伸预处理对齐。最后一件事是量化验证。边缘设备上跑 FP16 还是 INT8 差别很大INT8 量化后 mAP 一般会掉 1-3 个百分点。我在实际项目里测试过垃圾四分类对颜色纹理敏感度较高INT8 量化后“厨余垃圾”的 AP 掉了 5% 以上后来换用 TensorRT 的 FP16精度损失在 1% 以内速度已经足够达到实时。除非你的设备算力极度紧张否则不要盲目上 INT8——先查量化的每层精度损失分布再决定是否做混合精度。我现在每次训练完都坚持跑一遍“混淆矩阵 现场测试视频 边缘设备帧率”三连查缺一项都等于没有真正验证模型。这个习惯帮我提前发现过太多潜在返工点——熬夜加班的那种。网格搜参也好、数据增强也好最终都要落到部署现场才作数。希望这篇文章能把你的 YOLO垃圾四分类数据集的坑提前填上一些真正少走点弯路。本文还有配套的精品资源点击获取