垃圾目标检测数据集全攻略:从标注选型到小目标性能优化 📅 发布时间:2026/9/15 5:54:27 👁 浏览次数: 简介面向计算机视觉与环保应用开发者的一份可直接用于训练的目标检测数据集覆盖电池、纸团、一次性杯子、塑料瓶和积木五类常见垃圾。所有图片均按COCO格式标注提供详细边界框信息每类垃圾约350张样本数量适合训练与验证也便于学生微调YOLO、SSD、Faster R-CNN等主流检测模型快速搭建垃圾分类识别实验。资源包共1753个文件以1743张JPG图片为主体另有8张PNG图片和2个JSON标注文件整体压缩包约20.19MB数据量适中便于下载与部署。目前已有2199人学习或下载过这份数据集在相关社区中有一定认可度。使用这份数据可以省去自行采集与标注图像的繁琐流程直接进入模型训练、验证和调优阶段适合在环保监控、智能回收等场景下开展算法研究与应用落地。1. 垃圾目标检测数据集问题不在“下载”在标注决策垃圾目标检测和常规物体检测最大的区别在于垃圾不是一个“类别”而是一组“状态”被压扁的易拉罐、浸水发黑的纸巾、半埋在土里的玻璃瓶、被树叶盖住的烟头。同样的物体在城市道路、海岸线、河道两岸和分类仓里呈现完全不同的外观导致很多在 COCO 上表现不错的模型一到真实环卫场景就掉点。做这个任务的数据集核心不是“下载一个现成的 zip”而是先回答三个问题类别粒度定多细、小目标占比怎么保证、负样本干净路面、落叶堆怎么处理。这篇文章面向正在做智慧环卫、河道巡查、岸线垃圾监测和分类机器人抓取的算法工程师讲清楚公开集选型、自建标注流程以及小目标与背景误检的专项优化最后落到可复现的命令和参数上。2. 垃圾目标检测数据集选型公开集、COCO 子类与场景划分2.1 主流公开数据集清单和适用边界做垃圾目标检测大多数团队不会从零开始采集先看手头有没有能直接用的公开集。这里列几个我实际用过的按“能不能直接进训练管线”排序而不是按下载量排序。数据集拍摄场景标注格式类别粒度典型问题TACO野外开放环境COCO JSON数十类覆盖塑料袋、瓶罐、烟头遮挡和截断严重部分类样本极少TrashNet实验室白底拍摄单类标签6 类glass, paper, cardboard, plastic, metal, trash背景干净域迁移能力弱MJU-Waste室内外混合框级标注多类附带 RGB-D 双模态需要深度图预处理管线重AquaTrash水下场景COCO 风格水下生活垃圾水体模糊、颜色偏移大Drinking Waste饮料瓶细分框级标注按颜色和材质分瓶类类别单一只适合做瓶类分支以 TACO 为例它是野外垃圾场景里类别覆盖最全的一个但直接拿来训练会踩两个坑一是长尾严重瓶类和塑料袋占了大头灯泡、废旧电池这类样本少到几乎不参与训练二是同一地点连续拍摄的帧高度相似如果随机切 train/val验证集会“背下”训练集的背景mAP 虚高得离谱。TrashNet 则相反白底单物体标注让模型学不到“垃圾在场景里是什么样”只能用来做类别语义的预热身。我一般会把公开集拆成两部分用TACO 这类野外数据负责学“遮挡、形变、环境光”TrashNet 这类干净数据负责学“类别之间的边界”两者比例按目标场景来调。如果场景是河道岸线TACO 的权重就大如果场景是分类传送带TrashNet 更接近部署域。公开集的价值不是替代自采而是让模型在自采数据到位前先有一版可用的基线。2.2 为什么不能随机划分数据场景相关性垃圾检测数据的划分粒度直接影响模型评估的可信度。很多项目组拿 TACO 或自采数据后直接train_test_split按图片随机分结果在验证集上 mAP0.5 到 0.9一上实地就崩。原因很简单垃圾检测的采集往往是固定点位或连续视频抽帧同一视角下相邻帧的背景、光照、物体位置几乎没有变化随机划分等于把同一场景的副本同时放进训练集和验证集模型记住的是“这块地砖长什么样”而不是“垃圾长什么样”。正确做法是按场景或拍摄点位划分保证同一视角的所有帧只出现在一个集合里。下面这段代码按文件名前缀假设了拍摄点位标识实战中对应视频文件、采集任务的编号或 GPS 坐标前缀import os import random random.seed(42) images [f for f in os.listdir(images) if f.endswith(.jpg)] scenes {} for img in images: scene img.split(_)[0] scenes.setdefault(scene, []).append(img) val_scenes random.sample(list(scenes.keys()), kmax(1, int(len(scenes) * 0.2))) val_imgs [img for sc in val_scenes for img in scenes[sc]] train_imgs [img for img in images if img not in val_imgs] print(ftrain: {len(train_imgs)}, val: {len(val_imgs)}, scenes: {len(scenes)})这段代码先把图片按点位分组再整体抽取 20% 的点位作为验证场景。关键在于val_scenes是“场景序号”而不是“图片序号”这样同一视角的帧不会跨集合。逻辑上垃圾检测模型要泛化的是“没去过的新点位”所以验证集模拟的也应该是新点位。如果你手里的数据没有点位 ID退而求其次用视频片段编号切分也比逐帧随机切分可靠。2.3 用 COCO 子类补充域能做但别贪多公开集不够时一个常见的做法是从 COCO2017 数据集结构里抽出 bottle、cup、fork、knife 等子类当作垃圾检测的补充数据。COCO2017 的标注结构是标准 COCO JSON类别 ID 在 40 到 60 之间用 pycocotools 或直接读 JSON 都能按 category_id 过滤。但这里有一个域偏移问题COCO 里的瓶子多数摆在室内餐桌上背景干净、形态完整而垃圾场景里的瓶子被压扁、沾满泥、半埋在土里。直接把 COCO 子类混进训练集模型会倾向于把“完整的瓶子”当成正样本对“变形的瓶子”反而漏检。我的做法是给 COCO 子类单独一个类别映射不合并进垃圾主类别然后限制它在每个 batch 中的占比。不论怎么补统计现有数据集的类别分布是第一步。下面这段代码用纯标准库解析任意 COCO JSON不依赖 pycocotools 也能跑import json from collections import Counter with open(annotations.json, r, encodingutf-8) as f: data json.load(f) cat_id2name {c[id]: c[name] for c in data[categories]} counter Counter(cat_id2name[a[category_id]] for a in data[annotations]) for name, cnt in counter.most_common(30): print(f{name}: {cnt})这段代码把每个类别名及出现次数打印出来。Counter直接统计每张图里每个类别的标注数量注意这里统计的是标注框数量而不是图片数量一张图里出现 5 个烟头会算作 5 次。如果你发现头部类别占了全部标注的 70% 以上说明长尾已经严重到需要专项处理了这个结论直接决定下一章训练策略怎么定。3. 从标注到训练垃圾目标检测数据集的格式转换与长尾增强3.1 标注工具选型与 COCO JSON 转 YOLO 格式自建垃圾目标检测数据集标注工具的选择会影响后续转换成本。常见做法是 labelImg 直接标 YOLO 格式或者用 X-AnyLabeling 借助 SAM 类模型做半自动分割再转框。我的建议是如果团队有 2 人以上参与标注直接用 CVAT 搭 Web 服务便于审核和多人并行如果只是个人调试X-AnyLabeling 的半自动能力能把标注速度提升一倍。工具半自动能力输出格式适合规模labelImg无纯手标VOC XML / YOLO txt千张以内X-AnyLabelingSAM 分割辅助COCO JSON / YOLO txt千到万张CVAT模型辅助、自动追踪COCO / VOC / YOLO万张以上多人在线不管用哪个工具最终进 YOLO 训练管线都需要统一成 txt 格式一行一个目标cls cx cy w h其中 cx、cy、w、h 都除以图片宽高做归一化。很多公开集是 COCO JSON 格式转成 YOLO 格式是避不开的一步。下面这段脚本接收 COCO 标注文件路径和输出目录直接产出每个图片对应的 txtimport json import os def convert_coco(coco_path, label_dir, cls_mapNone): os.makedirs(label_dir, exist_okTrue) with open(coco_path, r, encodingutf-8) as f: data json.load(f) img_info {img[id]: img for img in data[images]} cat_id { c[id]: (cls_map[c[name]] if cls_map and c[name] in cls_map else c[id]) for c in data[categories] } anns_by_img {} for ann in data[annotations]: anns_by_img.setdefault(ann[image_id], []).append(ann) for img_id, anns in anns_by_img.items(): img img_info[img_id] ih, iw img[height], img[width] lines [] for ann in anns: x, y, w, h ann[bbox] cx (x w / 2) / iw cy (y h / 2) / ih nw w / iw nh h / ih if nw 1e-4 or nh 1e-4: continue lines.append(f{cat_id[ann[category_id]]} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}) stem os.path.splitext(os.path.basename(img[file_name]))[0] with open(os.path.join(label_dir, f{stem}.txt), w) as out: out.write(\n.join(lines)) convert_coco(train.json, labels)代码里cat_id的映射是做类别合并的关键。垃圾场景里同一物体在不同公开集里名字可能不同比如塑料瓶在 A 数据集叫plastic_bottle在 B 数据集叫bottle转换前用cls_map统一名称。nw 1e-4的过滤条件是防脏数据的标注工具偶尔会生成宽或高为 0 的异常框这类框不过滤会导致训练时 loss 计算出现 NaN。转换完成后检查一下 labels 目录下的 txt 内容三五行即可确认归一化后数值都在 0 到 1 之间且类别 ID 连续从 0 开始。YOLO 训练时类别 ID 必须从 0 开始编号如果 COCO JSON 里的类别 ID 是从 1 开始的没有重映射会直接漏掉第一个类别。3.2 类不平衡处理过采样与复制粘贴增强垃圾数据的标注分布几乎注定是长尾的。烟头、纸巾、塑料瓶这类高频类别可能有几千个框而灯泡、废旧电池、渔网这类低频类别可能只有几十张图。直接训练模型对低频类别的召回率会低到不可用因为前景梯度完全被高频类别主导。最简单有效的做法是低频类别过采样按类别统计框数把低频类别的图片在数据加载时多复制几份进训练集。但过采样只能解决“看到过几遍”的问题解决不了“目标太小、特征太少”的问题。更强的做法是复制粘贴增强从原图中把低频目标抠出来随机贴到干净背景图上。下面是一个简化版的贴图逻辑import cv2 import random def paste(obj_img, obj_box, bg_img): x, y, w, h [int(v) for v in obj_box] roi obj_img[y:y h, x:x w] hh, ww bg_img.shape[:2] scale random.uniform(0.8, 1.2) new_h, new_w int(h * scale), int(w * scale) if new_h hh or new_w ww: return None roi cv2.resize(roi, (new_w, new_h)) bx random.randint(0, ww - new_w) by random.randint(0, hh - new_h) bg_img[by:by new_h, bx:bx new_w] roi cx (bx new_w / 2) / ww cy (by new_h / 2) / hh nw new_w / ww nh new_h / hh return bg_img, cx, cy, nw, nh这个函数把原图中框出的目标区域裁剪出来随机缩放后贴到背景图的随机位置并返回新的 YOLO 格式坐标。随机缩放 0.8 到 1.2 是为了让模型不要只见过固定大小的目标。注意这个简化版本没有做交叠检查生产环境里需要对新框和已有框算 IoU超过阈值就重新选位置否则会出现两个目标叠在一起的情况。贴图用的背景图最好来自负样本池也就是第 4 章会讲到的、完全没有任何垃圾的场景图。3.3 用 YOLOv8 训练自己的数据集参数怎么定数据准备好之后训练命令本身并不复杂。以 YOLOv8 为例一条最常用的命令yolo detect train \ modelyolov8m.pt \ datatrash.yaml \ epochs120 \ imgsz1280 \ batch16 \ close_mosaic10 \ patience20 \ device0data 文件 trash.yaml 内容如下path: trash_data train: train/images val: val/images names: 0: cigarette 1: plastic_bottle 2: can这里几个参数对垃圾场景特别关键。imgsz1280是首选垃圾目标烟头、瓶盖在原图中往往小于 32 像素YOLO 默认 640 输入下这些小目标只占几个特征点1280 能把它们放大到可学习的尺寸范围代价是显存占用和推理耗时。close_mosaic10表示最后 10 个 epoch 关闭 Mosaic 增强Mosaic 会把四张图拼在一起小目标可能被切掉一半最后 10 个 epoch 关掉它让模型在真实分布上稳住。patience20是在验证集指标连续 20 轮不提升时提前停止防止长尾数据下过拟合。模型选择上yolov8n训练快但小目标检测能力明显偏弱yolov8m是精度和速度的平衡点部署时如果要压帧率再换yolov8s蒸馏。3.4 训练完先看每类 AP而不是只看总 mAP训练结束后跑一次验证把每个类别的 AP 单独打出来。总 mAP 会掩盖长尾类别的问题20 个类里 2 个类 AP 是 0总 mAP 可能只掉 3 个点看起来还行但业务上那 2 个类就是失效的。下面这段代码用 ultralytics 的接口跑验证并逐类输出 APfrom ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) res model.val(datatrash.yaml, imgsz1280, conf0.25, iou0.5) for i, ap in enumerate(res.box.ap50): print(f{res.names[i]}: mAP0.5 {ap:.4f})res.box.ap50是一个数组索引和 names 里的类别 ID 一一对应。跑完这一步你会直观看到哪几个类拖后腿。如果一个类的 AP 比其他类低 0.3 以上不要急着调损失权重先去看这个类的样本量。样本量少于 100 框的类别模型基本学不到什么稳定特征优先补数据而不是调参。4. 小目标漏检与背景误检垃圾目标检测数据集的专项调优4.1 先判断瓶颈在哪垃圾检测精度上不去通常是两类错误小目标漏检和背景误检。它们在外观上很像但诊断方法完全不同。看两个指标如果 mAP0.5 差强人意而 mAP0.5:0.95 掉得厉害说明模型对目标的定位和尺度不够精确小目标占大头如果训练集损失收敛但验证集 mAP 波动大且混淆矩阵里背景那一列的值偏高说明模型把路面裂纹、落叶、积水反光当成了垃圾。背景误检在垃圾场景里格外严重因为垃圾本身没有刚性形状一块皱着的塑料袋和一片落叶在纹理上极其相似。遇到这种情况很多人会去调置信度阈值但这只是掩盖问题正确做法是给模型喂负样本。4.2 负样本与难例挖掘让模型见过“不是垃圾”的东西目标检测模型训练时不标注的图片区域都默认是背景。如果你的训练集里每张图都有垃圾模型学到的背景分布就只是“垃圾周围的背景”一旦部署到干净的广场或刚扫过的马路模型会因为没见过这种背景而制造大量假阳性。做法是往训练集里加 200 到 500 张完全没有垃圾的场景图例如雨后积水的地面、落叶堆、井盖、树影斑驳的人行道、夜间灯光反射的路面。这些图直接放进train/images不生成任何标签文件。ultralytics 会把找不到标签文件的图片当作背景样本参与训练不会额外输出一个背景类别也不会报错。训练完成后用验证集跑一轮导出 confusion matrixfrom ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) res model.val(datatrash.yaml, imgsz1280) res.confusion_matrix.plot(save_dirruns/detect/train/, namesmodel.names)生成的confusion_matrix.png里看最右侧背景列。如果某一类对应的背景列数值偏高说明该类经常和背景混淆。把对应图片捞出来人工看一遍确认是标注漏框还是背景过像。这个循环重复两轮之后背景误检通常能降一半以上。4.3 切图推理用 SAHI 处理烟头级别的目标小目标漏检的另一个硬解法是切图推理。把原图切成若干瓦片每个瓦片单独过模型再把结果映射回原图坐标。这个思路对垃圾检测尤其有效因为垃圾目标的绝对像素往往小于 32但切成 512 瓦片后它在瓦片中占比显著提升特征提取自然更充分。推荐用 SAHI 库做切图推理它对 YOLO 系列模型有现成适配pip install sahi推理脚本from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction model AutoDetectionModel.from_pretrained( model_typeyolov8, model_pathruns/detect/train/weights/best.pt, confidence_threshold0.25, image_size1280 ) res get_sliced_prediction( street.jpg, model, slice_height512, slice_width512, overlap_height_ratio0.2, overlap_width_ratio0.2 ) res.export_visuals(export_diroutput/)几个参数说明slice_height512和slice_width512是瓦片尺寸不建议用 640垃圾目标太小瓦片越大放大效果越弱overlap_height_ratio0.2和overlap_width_ratio0.2是重叠率保证跨瓦片的目标至少在某个瓦片里是完整的如果目标正好被切在边界上没有重叠就会漏检。切图推理只建议在推理阶段用训练阶段不需要切图因为 Mosaic 增强已经做了类似的事训练时再切会破坏目标的上下文信息。4.4 小目标增强Mosaic 和复制粘贴的配合训练阶段的小目标处理主要靠增强策略。YOLOv8 默认开启的 Mosaic 增强对中等目标效果好但小目标在四图拼接后会被进一步缩小。配合第 3 章的复制粘贴增强把小目标类别单独放大贴到背景图上效果更可控。一个经验值烟头这类小于 16 像素的目标复制粘贴时把目标放大 1.5 到 2 倍再贴让模型先学会“烟头在场景中的上下文是什么样”推理时再通过 SAHI 还原小目标检测能力。5. 验证口径与数据集迭代垃圾场景还要盯住哪些指标5.1 mAP0.5 和 mAP0.5:0.95 分开看垃圾目标检测的业务指标通常选 mAP0.5因为环卫侧关心的是“这里有没有垃圾”而不是“框有多准”。定位不精确的框比如框得比实际目标大一圈在 mAP0.5 下可能仍然算正确检测但在 mAP0.5:0.95 下就掉分了。如果你的模型 mAP0.5 在 0.85 但 mAP0.5:0.95 只有 0.5说明定位质量不足优先做回归分支的调优而不是继续堆数据。顺带提醒如果往多模态路线走把可见光和热红外图像做融合检测评价口径不要混用。红外小目标检测里的评价参数比如基于信杂比和局部对比度的指标针对的是弱目标增强效果和可见光目标检测的 AP 体系不是一回事混用会导致团队对模型能力产生误判。5.2 按场景拆验证集别只报一个总数垃圾分类的部署场景往往横跨白天、夜间、雨后、逆光四个状态同一模型在不同光照下的表现差异可能超过 20 个点。建议把测试集按目录拆开yolo detect val modelbest.pt datatrash.yaml sourceval/day yolo detect val modelbest.pt datatrash.yaml sourceval/night yolo detect val modelbest.pt datatrash.yaml sourceval/rainsource参数会覆盖 data 里的 val 路径。分别看三个场景的 mAP你会很容易发现夜间场景的 AP 明显偏低那就针对性补夜间数据而不是整体加数据。这个拆分逻辑同样适用于点位类型河道、路面、景区步道分开评每个场景的负样本池也要分开建。5.3 数据质量闭环标注一致性是上限当 mAP 连续两轮不再上升先别急着换网络回到标注规范上去查一致性。垃圾目标检测最容易出现的问题有压扁的物体边框怎么定、多个物体挨在一起时要不要分开标、半遮挡物体算正样本还是忽略。三个人标同一张图同一目标的类别和边框如果差异超过 5%模型的回归上限就会卡在标注噪声上。另一个常见问题是重复利用公开集时产生的伪标签错误——自动生成的框没有人工复核直接进数据集这类错误对 AP 的侵蚀比数据量不足更隐蔽。把每轮验证集里置信度在 0.3 到 0.7 之间的样本定期抽出来复核这部分的标注错误率基本就是模型还能再涨几个点的余量。本文还有配套的精品资源点击获取