
简介这份垃圾目标检测数据集面向从事计算机视觉与环保科技应用的开发者、研究者及学生用于构建垃圾识别与分类模型可服务于智能垃圾桶、回收站自动分拣等场景。数据集共1499张真实场景图片按训练集1349张、验证集120张、测试集30张划分覆盖垃圾袋、玻璃、金属、纸张、塑料、泡沫塑料及一般垃圾共7个类别标注采用YOLO格式包含边界框与类别标签可直接用于目标检测任务并兼容主流深度学习框架。资源包共2000个文件以1499个txt标注文件、499张jpg图片为主另含1个yaml配置文件与1份docx说明文档压缩包约153.44MB目录结构清晰便于检索。目前已有159人学习下载。借助精准标注与真实场景多样性读者可快速开展模型训练与验证也可将其扩展至实例分割等视觉任务为废物回收与环境保护研究提供数据支撑。1. 垃圾目标检测数据集从压缩包到可训练模型的落地路径你拿到一个名为垃圾目标检测数据集_20251118_181557.zip的压缩包第一反应大概率是解压看看里面有什么。但真正决定这个数据集能不能用的不是文件数量而是标注格式、类别定义和图像质量这三件事。垃圾目标检测这个方向落地场景非常明确社区垃圾分类督导、环卫巡检、回收站分拣线、甚至智能垃圾桶的满溢识别。它和通用目标检测最大的区别在于——类别边界模糊、遮挡严重、小目标密集而且“垃圾”这个词本身就带着极强的场景依赖性。同一个塑料瓶在厨房场景和建筑工地场景里标注策略可能完全不同。这个数据集适合谁适合想快速验证垃圾检测方案可行性的算法工程师、做智慧环卫产品原型的开发者以及需要本地化数据集做课程设计或论文实验的学生。但前提是你得先搞清楚它到底给了你什么而不是急着往 YOLO 里塞。2. 拆开压缩包先看什么目录结构、标注格式与类别体系2.1 解压后的第一轮体检用三条命令摸清家底拿到压缩包后不要直接双击解压到桌面。我一般会在 Linux 或 WSL 下操作因为后续路径处理和批量脚本会更顺。先建一个工作目录把压缩包放进去然后执行下面这套“体检命令”。# 创建工作目录并解压 mkdir -p ~/workspace/trash_det cd ~/workspace/trash_det unzip 垃圾目标检测数据集_20251118_181557.zip -d raw_data # 查看顶层目录结构只看两层 find raw_data -maxdepth 2 -type d | head -50 # 统计图像文件数量和格式分布 find raw_data -type f \( -iname *.jpg -o -iname *.jpeg -o -iname *.png \) | wc -l find raw_data -type f \( -iname *.jpg -o -iname *.jpeg -o -iname *.png \) | sed s/.*\.// | sort | uniq -c # 统计标注文件数量和格式 find raw_data -type f \( -iname *.xml -o -iname *.json -o -iname *.txt \) | wc -l find raw_data -type f \( -iname *.xml -o -iname *.json -o -iname *.txt \) | sed s/.*\.// | sort | uniq -c这三组命令分别回答三个问题目录怎么组织的、图像有多少张且是什么格式、标注是什么格式且有多少份。如果图像数量和标注文件数量对不上比如图像 5000 张但 xml 只有 4800 个那说明有 200 张图是负样本或者漏标了。这个信息直接决定你后面要不要做数据清洗。参数说明-maxdepth 2控制目录展开深度避免数据集嵌套太深时输出爆炸sed s/.*\.//提取扩展名uniq -c统计每种格式的数量。如果标注是.txt且和图像同名同目录那大概率是 YOLO 格式如果是.xml那就是 VOC 格式如果是单个.json可能是 COCO 格式。三种格式的转换路径完全不同先确认再动手。2.2 标注格式判定与类别分布统计确认格式后下一步是看类别体系。垃圾检测数据集的类别设计直接决定模型能不能落地。常见的有两种粗粒度可回收物、厨余垃圾、有害垃圾、其他垃圾和细粒度塑料瓶、纸箱、玻璃瓶、电池、烟头等。粗粒度适合做四分类桶边督导细粒度适合做分拣线机械臂抓取。你需要从标注文件里把类别名和实例数抽出来。如果标注是 VOC 格式用下面这段 Python 脚本统计import os import xml.etree.ElementTree as ET from collections import Counter def count_voc_classes(xml_dir): class_counter Counter() file_count 0 for fname in os.listdir(xml_dir): if not fname.endswith(.xml): continue file_count 1 tree ET.parse(os.path.join(xml_dir, fname)) root tree.getroot() for obj in root.findall(object): name obj.find(name).text.strip() class_counter[name] 1 print(f标注文件总数: {file_count}) for cls, cnt in class_counter.most_common(): print(f{cls}: {cnt}) return class_counter # 替换成你的实际标注目录 count_voc_classes(raw_data/annotations)逻辑说明遍历所有 xml 文件解析每个object下的name字段用 Counter 累加。输出结果里你会看到每个类别的实例数。如果某个类别实例数少于 50基本可以判定为长尾类别训练时要么做重采样要么直接合并到相近类别。如果类别名出现拼写不一致比如 “plastic_bottle” 和 “plastic bottle” 同时存在那必须做名称归一化否则模型会把它们当成两个类。参数说明obj.find(name).text.strip()里的strip()不能省有些标注工具会在类别名前后留空格。most_common()按实例数降序排列方便你一眼看出哪些类别是主力、哪些是尾巴。如果是 YOLO 格式的.txt标注类别是数字索引你需要额外找classes.txt或data.yaml来映射名称。如果找不到就得从图像里反推——挑几张图可视化一下看看索引 0 对应的是什么。2.3 图像质量抽检分辨率、亮度与遮挡比例类别统计完之后别急着转格式。先抽 30 到 50 张图做可视化检查。我一般用 Python 的 PIL 和 matplotlib 快速拼一张网格图看三件事分辨率是否统一、亮度是否差异过大、目标遮挡是否严重。import os import random from PIL import Image import matplotlib.pyplot as plt img_dir raw_data/images img_files [f for f in os.listdir(img_dir) if f.endswith((.jpg, .png))] sample random.sample(img_files, min(36, len(img_files))) fig, axes plt.subplots(6, 6, figsize(15, 15)) for ax, fname in zip(axes.flatten(), sample): img Image.open(os.path.join(img_dir, fname)) ax.imshow(img) ax.set_title(f{img.size[0]}x{img.size[1]}, fontsize8) ax.axis(off) plt.tight_layout() plt.savefig(sample_grid.jpg, dpi100) print(抽样网格图已保存)逻辑说明随机抽 36 张图拼成 6x6 网格每张图标题显示分辨率。如果分辨率从 320x240 到 4000x3000 都有那训练前必须统一 resize 或做多尺度训练。如果大量图像亮度偏低夜间或阴影场景要考虑做直方图均衡化或者引入亮度增强。遮挡比例这个只能靠肉眼判断如果超过一半的目标被遮挡超过 50%那这个数据集对模型的挑战就很大需要优先考虑用 Copy-Paste 增强或者 Mosaic 增强来提升鲁棒性。参数说明random.sample保证每次抽的图不一样多跑几次能覆盖更多样本。figsize(15,15)保证每张图有足够显示面积。dpi100控制输出分辨率太高会导致文件过大。3. 从原始标注到 YOLO 训练格式转换脚本与四个边界坑3.1 VOC 转 YOLO坐标归一化与类别映射假设你确认了标注是 VOC 格式图像和 xml 在同一目录或分目录存放。转成 YOLO 格式需要做两件事把xmin, ymin, xmax, ymax转成x_center, y_center, width, height并归一化到 0 到 1 之间把类别名映射成从 0 开始的整数索引。import os import xml.etree.ElementTree as ET from PIL import Image def voc_to_yolo(xml_path, img_path, class_list, output_label_dir): tree ET.parse(xml_path) root tree.getroot() img Image.open(img_path) img_w, img_h img.size lines [] for obj in root.findall(object): cls_name obj.find(name).text.strip() if cls_name not in class_list: continue cls_id class_list.index(cls_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, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) if xmax xmin or ymax ymin: continue # 跳过无效框 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if lines: base os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(output_label_dir, base .txt), w) as f: f.write(\n.join(lines)) return len(lines) # 使用示例 class_list [plastic_bottle, paper_box, glass_bottle, battery, cigarette_butt] os.makedirs(yolo_labels, exist_okTrue) # 遍历你的 xml 文件逐个调用逻辑说明核心是坐标归一化公式。x_center (xmin xmax) / 2 / img_ww (xmax - xmin) / img_wy 方向同理。边界裁剪那四行是血泪经验——有些标注工具会导出超出图像范围的坐标如果不裁剪YOLO 训练时虽然不会报错但会引入噪声框导致模型学到错误的边界。if xmax xmin or ymax ymin这行是跳过无效框比如宽高为负或为零的情况。参数说明class_list的顺序就是类别索引的顺序必须和后续data.yaml里的names完全一致。:.6f保留六位小数YOLO 官方推荐精度太少会导致小目标坐标偏移太多没必要。3.2 四个边界坑坐标越界、类别名空格、图像损坏、空标注第一个坑是坐标越界。上面代码里已经做了裁剪但你要知道为什么。VOC 标注里xmin可能小于 0xmax可能大于图像宽度这在使用 LabelImg 时如果拖拽超出边界就会发生。不裁剪的话归一化后坐标会小于 0 或大于 1YOLO 虽然能读但损失计算时会出现梯度异常。第二个坑是类别名前后空格。obj.find(name).text拿到的字符串可能带\n或空格必须.strip()。更隐蔽的是类别名中间有多个连续空格比如 “plastic bottle”这种要用 .join(name.split())归一化。第三个坑是图像损坏。有些 jpg 文件下载不完整PIL 打开时会抛UnidentifiedImageError。转换前加一层 try-except把损坏图像记录下来直接跳过不要让它中断整个转换流程。第四个坑是空标注。有些图像没有任何目标框对应的 xml 里object数量为零。这种图在 YOLO 里可以作为负样本使用但需要生成一个空的.txt文件而不是不生成。否则训练时 dataloader 找不到对应标签会报错。3.3 生成 data.yaml 与训练集验证集划分转换完成后需要生成 YOLO 训练所需的data.yaml并划分训练集和验证集。我一般按 8:2 划分如果数据量少于 2000 张按 7:3 划分保证验证集有足够样本。import os import random import shutil def split_dataset(img_dir, label_dir, output_dir, val_ratio0.2): img_files [f for f in os.listdir(img_dir) if f.endswith((.jpg, .png))] random.shuffle(img_files) val_count int(len(img_files) * val_ratio) val_files img_files[:val_count] train_files img_files[val_count:] for split, files in [(train, train_files), (val, val_files)]: os.makedirs(os.path.join(output_dir, images, split), exist_okTrue) os.makedirs(os.path.join(output_dir, labels, split), exist_okTrue) for f in files: shutil.copy(os.path.join(img_dir, f), os.path.join(output_dir, images, split, f)) label_name os.path.splitext(f)[0] .txt src_label os.path.join(label_dir, label_name) dst_label os.path.join(output_dir, labels, split, label_name) if os.path.exists(src_label): shutil.copy(src_label, dst_label) else: open(dst_label, w).close() # 空标注文件 print(f训练集: {len(train_files)} 张, 验证集: {len(val_files)} 张) split_dataset(raw_data/images, yolo_labels, dataset, val_ratio0.2)逻辑说明先打乱文件列表再按比例切分。复制图像的同时复制对应标签如果标签不存在就创建一个空文件。这样保证训练时每张图都能找到对应标签路径。参数说明val_ratio0.2是经验值数据量少时可以调到 0.3。random.shuffle前建议设随机种子保证每次划分一致方便复现。对应的data.yaml内容如下path: ./dataset train: images/train val: images/val nc: 5 names: [plastic_bottle, paper_box, glass_bottle, battery, cigarette_butt]nc是类别数names顺序必须和转换时的class_list一致。路径可以用相对路径但建议用绝对路径避免训练时找不到文件。4. 训练参数怎么设垃圾检测场景下的超参选择与增强策略4.1 模型选型YOLOv8n 还是 YOLOv8s垃圾检测的落地场景通常对推理速度有要求尤其是边缘设备部署。YOLOv8n 参数量约 3.2M在 RTX 3060 上推理一张 640x640 图像大约 2ms适合 Jetson Nano 或树莓派加加速棒。YOLOv8s 参数量约 11.2M精度通常比 n 高 3 到 5 个 mAP 点适合服务器端或工控机。我的建议是如果数据集类别数少于 10 且图像分辨率不超过 1280先用 YOLOv8n 跑 baseline看 mAP0.5 能不能到 0.7 以上。如果不到再换 YOLOv8s 对比。不要一上来就上 YOLOv8x垃圾检测的瓶颈通常在数据质量而不是模型容量。4.2 关键训练参数imgsz、batch、lr0 与 epochs用 Ultralytics 的 YOLOv8 训练时下面这组参数是我在垃圾检测任务上反复调过的起点yolo detect train \ datadataset/data.yaml \ modelyolov8n.pt \ imgsz640 \ batch16 \ epochs100 \ lr00.01 \ lrf0.01 \ momentum0.937 \ weight_decay0.0005 \ warmup_epochs3.0 \ cos_lrTrue \ close_mosaic10 \ patience20 \ device0参数说明imgsz640是 YOLOv8 的默认值但如果你的数据集里小目标多比如烟头、瓶盖可以提到 1280代价是显存翻倍。batch16在 8GB 显存下跑 640 分辨率基本安全如果 OOM 就降到 8。lr00.01是 SGD 的初始学习率如果换 AdamW 要降到 0.001。lrf0.01是最终学习率因子配合cos_lrTrue做余弦退火。close_mosaic10表示最后 10 个 epoch 关闭 Mosaic 增强让模型在真实分布上收敛。patience20是早停耐心值验证集 mAP 连续 20 轮不提升就停。4.3 垃圾检测专属增强Mosaic、Copy-Paste 与 HSV 扰动垃圾检测的增强策略和通用检测有区别。Mosaic 增强把四张图拼成一张对小目标密集场景非常有效但要注意如果数据集里大目标多Mosaic 会切碎目标导致标注框不完整。Copy-Paste 增强适合长尾类别——把稀有类别实例抠出来贴到其他图上能显著提升召回。HSV 扰动里hsv_h0.015、hsv_s0.7、hsv_v0.4是默认值但垃圾检测场景下光照变化大我一般把hsv_v提到 0.5让模型对暗光更鲁棒。# 在 data.yaml 同级目录下创建 custom_aug.yaml 覆盖默认增强 # 然后在训练命令里加 augments 参数或直接修改 default.yaml # 更简单的做法是在 train 参数里直接指定实际上 Ultralytics 支持在命令行直接覆盖增强参数yolo detect train \ datadataset/data.yaml \ modelyolov8n.pt \ imgsz640 \ batch16 \ epochs100 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.5 \ degrees10.0 \ translate0.1 \ scale0.5 \ mosaic1.0 \ mixup0.1 \ copy_paste0.1degrees10.0允许随机旋转正负 10 度垃圾在自然场景中很少完全水平。scale0.5允许缩放 50%增加尺度多样性。mixup0.1和copy_paste0.1概率不要设太高否则会引入太多合成噪声。5. 避坑与排查训练不收敛、mAP 虚高、部署掉点的真实原因5.1 现象loss 震荡不下降mAP 卡在 0.2 以下原因通常有三个学习率太大、标注格式有误、类别不平衡太严重。先检查data.yaml里的nc和names数量是否一致不一致会直接导致类别索引错乱。然后看训练日志里的cls_loss如果它一直是nan说明标注文件里有非法字符或坐标越界。解决方法是重新跑一遍转换脚本加上边界裁剪和空文件检查。如果类别不平衡比如某个类实例数只有 20 个用copy_paste增强或者在该类上加大损失权重。5.2 现象验证集 mAP 很高但实际测试图检测效果差这是典型的过拟合或者验证集泄漏。检查训练集和验证集是否有同一场景的连续帧——如果有随机划分会导致验证集里出现训练集的近似重复图。解决方法是按场景或按视频序列划分而不是随机划分。另一个原因是验证集图像和训练集图像来自同一批次亮度、角度分布一致模型只是记住了分布而不是学到了特征。这时候要把验证集换成完全独立采集的数据。5.3 现象模型导出 ONNX 后推理结果和 PyTorch 不一致常见原因是预处理不一致。PyTorch 训练时用了 Ultralytics 的 letterbox 填充导出 ONNX 后如果部署端直接 resize 而不做 letterbox坐标就会偏移。解决方法是导出时加dynamicTrue或者固定输入尺寸部署端严格复现 letterbox 逻辑。另一个坑是归一化参数YOLOv8 默认除以 255如果部署端忘了这一步输出会完全乱掉。5.4 现象训练时显存溢出batch 降到 1 还是 OOM检查imgsz是不是设太大了。640 分辨率下 YOLOv8n 的 batch16 大约占 4GB 显存如果设到 1280显存直接翻四倍。另外检查workers参数Windows 下workers设大于 0 容易出问题设成 0 用主进程加载数据。如果还是 OOM用ampTrue开启混合精度训练能省大约 30% 显存。5.5 现象某些类别完全检测不到召回率为零先看这个类别的实例数是不是少于 50。如果太少模型根本学不到有效特征。解决方法是在data.yaml里把这个类别合并到语义相近的父类或者用过采样把包含该类别的图像复制多份。另一个原因是该类别目标尺寸太小比如烟头在 640 分辨率下可能只有 10x10 像素YOLOv8 的 P3 特征图 stride 是 810 像素的目标在特征图上只剩 1 个像素根本没法检测。这时候要么提高输入分辨率到 1280要么换用带 P2 层的模型结构。6. 用 50 张图快速验证数据集可用性的最小闭环不要等全部数据清洗完再开始训练。我一般会先抽 50 张图做一次“最小闭环”验证转换格式、生成 yaml、跑 20 个 epoch、看 mAP 和实际检测效果。如果 50 张图都跑不出合理结果那全量数据大概率也有问题。下面这个脚本帮你从原始数据里抽 50 张图并生成对应的最小数据集。import os import random import shutil def build_mini_dataset(img_dir, label_dir, output_dir, sample_size50): img_files [f for f in os.listdir(img_dir) if f.endswith((.jpg, .png))] sample random.sample(img_files, min(sample_size, len(img_files))) for split in [train, val]: os.makedirs(os.path.join(output_dir, images, split), exist_okTrue) os.makedirs(os.path.join(output_dir, labels, split), exist_okTrue) # 前 40 张训练后 10 张验证 for i, f in enumerate(sample): split train if i 40 else val shutil.copy(os.path.join(img_dir, f), os.path.join(output_dir, images, split, f)) label_name os.path.splitext(f)[0] .txt src_label os.path.join(label_dir, label_name) dst_label os.path.join(output_dir, labels, split, label_name) if os.path.exists(src_label): shutil.copy(src_label, dst_label) else: open(dst_label, w).close() print(f最小数据集已生成: 训练 40 张, 验证 10 张) build_mini_dataset(raw_data/images, yolo_labels, mini_dataset)逻辑说明从全量数据里随机抽 50 张前 40 张做训练后 10 张做验证。这个比例不是用来评估模型精度的而是用来验证数据管道是否通畅。如果 40 张训练图能让模型在 10 张验证图上过拟合到 mAP0.5 大于 0.9说明标注格式和训练流程没问题。如果过拟合都做不到那一定是格式或路径有问题。参数说明sample_size50是经验值太少模型学不到东西太多失去快速验证的意义。random.sample前建议固定种子方便复现。跑完最小闭环后看三个指标cls_loss是否稳定下降、验证集 mAP0.5 是否在 20 个 epoch 内超过 0.5、实际推理几张测试图看框是否准确。如果都通过再上全量数据。全量训练时把epochs提到 100 到 300patience设 20 到 30imgsz根据小目标比例决定是否提到 1280。最后提醒一句垃圾检测数据集的类别定义一定要和落地场景对齐我在一个社区项目里见过把“外卖餐盒”标成“其他垃圾”而把“塑料瓶”标成“可回收物”结果模型在桶边督导场景里把餐盒误判成可回收物现场直接翻车。标注规范比模型选型重要十倍。希望帮到你。本文还有配套的精品资源点击获取