☰
垃圾目标检测数据集处理与YOLO训练避坑指南
2026/9/27 23:26:08 网站建设 项目流程

简介:垃圾目标检测数据集基于真实场景拍摄构建,面向生态环境与工业智能应用开发者,共包含245张图片(训练集231张、验证集14张),统一标注为Trash单类别目标,采用标准YOLO格式边界框与类别标签,可直接用于YOLO系列、Faster R-CNN等主流检测框架的训练和评估。压缩包共492个文件,含245张jpg原图、245个txt标注文件、1个yaml配置文件及1份docx说明文档,整体约33.05MB,数据组织清晰,便于直接导入工程使用。数据集覆盖多样光照、角度和复杂背景条件,适用于智能垃圾分类系统、清洁机器人视觉定位、环保违规倾倒监测及计算机视觉教学等场景,可为中高级算法工程师和研究者提供贴近真实任务的训练样本,助力模型泛化能力验证与环保科技产品落地。目前已有195人学习下载,是开展单类别目标检测实验和环保应用研究的实用基础数据。

1. 垃圾目标检测数据集.zip:一个压缩包背后是整条训练流水线

下载到一个“垃圾目标检测数据集.zip”是目标检测项目里最常见也最容易劝退新手的一步:你拿到的往往不是一个可以直接开工的项目,而是一堆图片和标注文件,甚至标注格式混着YOLO、VOC和COCO三种风格。压缩包的价值不在包本身,而在你能不能把里面的内容清洗、切分、转换成一个干净的数据集,并跑通一版可评估的垃圾检测模型。下面从解压开始,一直讲到训练调试和避坑,适合正在做垃圾分类识别,或需要快速评估一份陌生数据集能不能用的工程师。

2. 解压后的第一课:先看懂数据集的“户籍”再谈训练

看到“垃圾目标检测数据集.zip”,很多人第一反应是用解压软件双击,然后拖进训练脚本。这个流程在干净的开箱数据上没问题,但陌生数据集十有八九需要先做户籍核对:图片和标注是否一一对应、标注格式是什么、类别编号是否连续、有没有空标签和损坏图片。这些信息不核对完,后面的训练翻车基本就是白浪费时间。

2.1 压缩包内常见的三种目录结构(images/labels、JPEGImages/Annotations、COCO json)

建议不要在图形界面里乱点,先到终端里把zip解开:

mkdir -p garbage_dataset unzip 垃圾目标检测数据集.zip -d garbage_dataset cd garbage_dataset tree -L 2 .

这段命令会创建一个干净的目录并展示两层文件结构。如果系统没有tree,用ls -R或find . -maxdepth 2 -type d也能看目录骨架。解压后你大概率见到下面三种布局中的一种。

结构图片目录标注目录/文件常见标注格式
YOLO风格images/labels/*.txt每行class_id x_center y_center width height,坐标相对图片宽高归一化
VOC风格JPEGImages/Annotations/*.xml每个对象一个<bndbox>,坐标是像素绝对值
COCO风格images/annotations/*.json图片数组 + 标注数组,bbox是[x, y, width, height]像素值

判断方法很简单:看有没有labels、Annotations或instance_*.json。有的zip会用train/、val/分好目录,里面再放images和labels;有的则只有一个大目录,需要你自己切分。更多时候是“包装”不统一:图片叫IMG_20230101_123456.jpg,标签却叫0a1b2c3d.txt,靠前缀根本对不上。这种时候先别急着改,把两边的文件名列表导出,用前缀匹配或下标重建映射,先搞清楚对应关系再动工。

还要注意目录下可能有一个classes.txt或data.yaml,这是数据集的“身份证”。classes.txt按行排列类别名,行号就是类别id;data.yaml则用names列表定义类别。把它和标签文件里的数字对照一下,能直接看出类别id是否从0开始、命名是否有中文和空格。

2.2 用命令列文件清单与统计:find、file、du

第一次拿到数据集,我一般按下面顺序跑三条命令:

find . -type f \( -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" \) | wc -l find . -name "*.txt" | wc -l du -sh .

第一行统计图片总数,第二行统计TXT总数,第三行看整个数据集体积。如果图片数和TXT数明显相差,说明有很多图片没有标签,或者很多标签找不到对应图片。差值不清理,训练时YOLO会直接跳过无标签图片,你实际用到的样本数会和你以为的不一样。

接下来用file检查前几十个文件的实际格式:

find images -type f | head -20 | xargs file

有的“jpg”其实是PNG或WebP,有的文件头已经被压坏,file会直接识别成data。目标检测训练框架通常能容忍部分图片格式混乱,但一旦遇到截断的JPEG,cv2.imread返回None,程序可能报错,也可能静默丢弃。与其等到训练中期报错,不如现在就把坏文件挑出来。

检查完文件,统计单类别的数量分布。垃圾检测数据集的标签里一般会有cardboard、glass、metal、paper、plastic、trash这些类别,不同数据集的类别命名和编号并不一致。统计命令是:

cat labels/*.txt | awk '{print $1}' | sort | uniq -c | sort -nr

awk取出每行第一个字段作为类别id,sort | uniq -c计数,最后的sort -nr让数量多的类别排前面。结果里最值得关注的是两类:一类是数量特别高的常见类别,另一类是数量只有个位数的稀有类别。垃圾检测里最常见的类别不平衡是塑料瓶、易拉罐这类单件目标很多,而玻璃碎片、织物、烟头这类目标很少。

还要统计空标签文件的数量:

find labels -type f -empty | wc -l

空TXT代表图片上没有任何标注目标。这类图片能不能当负样本用,取决于你的场景:如果图片里确实没有垃圾,可以作为背景负样本;如果图片里其实有垃圾但漏标了,那就是脏数据,必须删掉或补标。

2.3 快速核查类别标签,定位类别不平衡

光看数量还不够,要把标签画到图片上才能发现“坐标到底对不对”。这里推荐用现成的可视化脚本而不是人工翻图。目标检测常用标注工具LabelImg、CVAT、X-AnyLabeling都内置了图片和标注的预览;如果你不想装工具,也可以用OpenCV写个画框脚本,挑几十张图检查。重点看三处:框是不是落在目标上、框是不是超出图片边界、类别名和图片内容是否对得上。

这一步还能发现“标注粒度”问题。比如有的数据集把“可回收物”作为一个大类,有的则细分成塑料瓶、纸箱、玻璃瓶;有的把“垃圾”框一个外接大框,把多个小目标全包进去。大框对“是否有人乱扔垃圾”够用,对“回收机器人精确抓取单个瓶子”完全不够。你把类别统计和可视化结合起来,就能判断这份数据集的粒度适不适合你的任务。

同时建议用下面这段Python脚本统计每张图片里的目标数量,找出异常大或异常小的图片:

import os counts = [] for label_file in os.listdir("labels"): with open(os.path.join("labels", label_file)) as f: n = sum(1 for line in f if line.strip()) counts.append((label_file, n)) counts.sort(key=lambda x: x[1]) print("目标数最少的前10个:", counts[:10]) print("目标数最多的前10个:", counts[-10:])

目标数最多的图片往往是“垃圾堆大合影”,十几甚至几十个目标挤在一张图里,训练时loss会被这些图主导;目标数只有1个的图片则容易让模型学到“出现垃圾就是那一类”。这些统计结果,就是你后续清洗、切分、重采样的依据。

3. 数据清洗与切分:把原始标注整理成能直接喂给YOLO的训练集

3.1 格式盘点:VOC、YOLO、COCO,以及互相转换的原则

旧数据集里最常见的情况是三种格式混杂。要转换,不能只看后缀,要把每一条标注都拆成“类别 + 左上角 + 右下角 + 图片宽高”五要素,再输出成目标格式。这个思路比硬背转换公式要实用得多。

格式文件载体坐标含义关键字段
YOLO.txt归一化中心坐标class x_center y_center width height
VOC.xml像素左上角+右下角object.name, bndbox.xmin/ymin/xmax/ymax
COCO.json像素左上角+宽高images[i].width/height, annotations[i].bbox

YOLO转像素框的公式:

x1 = (x_center - width / 2) * image_width y1 = (y_center - height / 2) * image_height x2 = (x_center + width / 2) * image_width y2 = (y_center + height / 2) * image_height

反过来,像素框转YOLO:

x_center = (x1 + x2) / 2 / image_width y_center = (y1 + y2) / 2 / image_height box_w = (x2 - x1) / image_width box_h = (y2 - y1) / image_height

这四行公式是转换的核心。VOC的<size>节点里有图片宽高;COCO的images数组里也有width和height;唯独YOLO格式的txt不自带宽高,所以转成YOLO之前必须先读图片尺寸。如果你在转换脚本里没读宽高,看到一堆0~1之间的数就以为没问题,实际上很可能把归一化坐标当成了像素坐标,埋下一颗雷。

3.2 清洗脏标注:图片损坏、空标签、越界框的自动化检查

建议直接用一个Python脚本把问题样本筛出来。下面这段假设你已经把数据统一成了YOLO风格,也就是每张图片对应一个同名txt。

import os import cv2 images_dir = "images" labels_dir = "labels" backup_dir = "cleaned_backup" os.makedirs(backup_dir, exist_ok=True) bad_images = [] bad_labels = [] empty_labels = [] for img_name in os.listdir(images_dir): if not img_name.lower().endswith((".jpg", ".jpeg", ".png")): continue stem = os.path.splitext(img_name)[0] img_path = os.path.join(images_dir, img_name) label_path = os.path.join(labels_dir, stem + ".txt") if not os.path.exists(label_path): bad_labels.append(img_name) continue img = cv2.imread(img_path) if img is None: bad_images.append(img_name) continue h, w = img.shape[:2] if os.path.getsize(label_path) == 0: empty_labels.append(img_name) continue with open(label_path) as f: lines = f.readlines() valid_lines = [] for line in lines: parts = line.strip().split() if len(parts) != 5: continue cls_id, xc, yc, bw, bh = parts xc, yc, bw, bh = float(xc), float(yc), float(bw), float(bh) x1 = (xc - bw / 2) * w y1 = (yc - bh / 2) * h x2 = (xc + bw / 2) * w y2 = (yc + bh / 2) * h if x1 < -0.01 or y1 < -0.01 or x2 > w + 0.01 or y2 > h + 0.01: continue valid_lines.append(line) if not valid_lines: empty_labels.append(img_name) else: with open(label_path, "w") as f: f.writelines(valid_lines) print("缺失标签的图片数:", len(bad_labels)) print("损坏图片数:", len(bad_images)) print("空标签或清洗后无目标的图片数:", len(empty_labels))

这个脚本做了四件事:检查标签是否存在、检查图片能否被OpenCV读取、检查TXT是否为空、检查归一化坐标换算出的像素框是否越界。越界容差给0.01是因为浮点运算会有极小的误差,不必因为0.0001的偏差删掉一个有效标注。

如果你的数据是VOC或COCO格式,清洗逻辑完全一样:先解析出所有框,再换算坐标判断边界,最后重写。区别只在于解析方式不同。脚本输出如果显示缺失标签上百张,说明这份zip里大量图片没有标注,需要确认是负样本还是漏标;如果是漏标,后面训练会高估背景的错误率。

3.3 按类别分层切分train/val,给出脚本

很多“垃圾目标检测数据集.zip”只给了一份图片集,没有划分train和val。你随便切一个80/20,可能造成稀有类别全部落在训练集,验证集的mAP波动很大。这里给出一个先保稀有、再随机的切分思路。

import os import random from collections import defaultdict random.seed(42) images = [f for f in os.listdir("images") if f.lower().endswith((".jpg", ".jpeg", ".png"))] # 先建立图片到类别集合的映射 image_to_classes = {} for img_name in images: stem = os.path.splitext(img_name)[0] label_path = os.path.join("labels", stem + ".txt") with open(label_path) as f: classes = {line.split()[0] for line in f if line.strip()} image_to_classes[img_name] = classes # 收集每个类别下的图片 class_to_images = defaultdict(list) for img_name, classes in image_to_classes.items(): for c in classes: class_to_images[c].append(img_name) val_images = set() # 每个类别至少保证有1张进验证集,稀有类别优先 for c, imgs in class_to_images.items(): if len(imgs) < 20: # 稀有类,放20%进验证集 need = max(1, int(len(imgs) * 0.2)) selected = imgs[:need] val_images.update(selected) # 剩余空间用随机补齐到20% target_val = int(len(images) * 0.2) random.shuffle(images) for img in images: if len(val_images) >= target_val: break if img not in val_images: val_images.add(img) train_images = [img for img in images if img not in val_images] print(f"训练集 {len(train_images)} 张,验证集 {len(val_images)} 张")

切分完成后,还要写一段检查代码,验证每个类别在train和val里的占比不要偏差太大。比例超过3倍就要调整seed或改成按类别比例切分。对垃圾检测这种类别极不平衡的数据集,我一般会把切分后的类别分布打印出来,亲眼看一遍再继续。

另外要特别注意:图片移动后,标签要跟着移动。上面脚本只生成了文件名集合,具体搬移可以用shutil.move,但一定要保持stem一致。如果你在脚本里改了文件名,后面的标签同步改了,训练时就会报“no labels found”。

4. 在YOLO上训练垃圾检测模型:最小可跑通路与参数选择

4.1 准备data.yaml并解释参数

数据洗干净并切分完后,进入训练阶段。以最常见的YOLOv8训练路径为例,先准备一份garbage.yaml:

path: /workspace/garbage_dataset train: images/train val: images/val nc: 6 names: 0: cardboard 1: glass 2: metal 3: paper 4: plastic 5: trash

path最好写成绝对路径,否则ultralytics会基于当前工作目录做相对路径拼接,换目录后经常找不到训练图片。train和val是相对于path的目录路径,不是绝对路径;nc是类别总数,names的顺序就是标签文件里的类别id顺序。这里最容易错的地方是把names写错位:比如你把plastic放在第4位,但标签文件里plastic的id是5,那模型会把塑料学成trash。

类别名建议全用英文小写。中文类别名在训练日志里会显示乱码,虽然不影响训练,但后续导出模型、部署时容易踩编码坑。

4.2 训练命令与关键超参

最小可跑命令是:

yolo detect train \ data=garbage.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ patience=20 \ project=output \ name=garbage

model=yolov8n.pt:用小模型验证流水线。垃圾检测数据集通常不大,直接用s或m模型容易过拟合。数据集类别和COCO重叠度低,几千张以内我更推荐用yolov8n.pt预训练权重,因为COCO上的通用特征对瓶子、纸箱这类人造物体是有帮助的。如果你选择从头训练,可以改成model=yolov8n.yaml,但需要更多epochs和数据。

epochs=100:对几百到几千张图片的数据集够用。如果数据量上万,可以降到50~80,主要看验证集loss是否还在下降。imgsz=640是默认输入尺寸,小目标多就调到960或1280,但显存占用会变大。batch=16受显存限制,报OOM就降到8或4。patience=20表示连续20轮验证集mAP没有提升就早停,实际训练经常会提前停在50~70轮。

如果想提速,可以加workers=8和cache=True。cache=True会把数据集加载进内存,几百MB的数据集能明显提速,但要确认内存足够,否则会OOM。垃圾检测zip解压后如果原始图片很大,建议先做一次缩放,把长边压到1600以内,训练时再交给imgsz处理,能省很多显存。

4.3 从训练日志判断过拟合与欠拟合

训练过程中不要只盯最后一个epoch的loss,要看output/garbage/results.png里的曲线。这里列几个我常用的判断口径:

日志现象判断对策
train box_loss持续下降,val_cls_loss后段抬升过拟合停训或回退到val最低点,增加mosaic、hsv增强,减少epochs
前20轮loss就降到接近0,但mAP50很低类别id错位或标签大量为空回到第3章的统计,可视化一批预测框
val loss和mAP曲线大幅震荡验证集太小或切分不当增大验证集比例,固定随机种子重新切分
所有指标稳定但mAP50-95明显低于mAP50大目标有学习,小目标差调大imgsz,或对小目标图片做切片增强

如果你发现“目标检测模型微调崩了”,也就是用了预训练权重后loss不降反升,先检查一件事:是不是用了过大的学习率。lr0默认是0.01,对于垃圾检测这种和ImageNet差异很大的场景,我一般会降到0.005,或者把模型改成更大的预训练底子来微调。不要一上来就给自己加戏,先跑通最小配置再调。

训练时best.pt和last.pt会同时保存,判定崩没崩的标准是看验证集mAP,而不是训练集loss。还可以打开output/garbage/results.csv,用Excel或pandas看每个epoch的val/map50和val/map50-95。如果这两个指标在最后10个epoch还在缓慢上升,说明epoch没跑够;如果已经连续20个epoch不涨,早停就是合理的。

5. 避坑:垃圾检测数据集常见的翻车现场与排查手册

下面四条是我处理垃圾检测数据集时最常撞见的坑,每一条都按现象、原因、解决展开,可以直接对照排查。

5.1 训练loss崩到0,验证mAP也是0:类别id没对齐

现象:训练集loss从第1轮就开始下降,到第20轮已经降到0.3以下,但验证集mAP50一直是0或0.0几,预测框一个都对不上。

原因:数据集里标签类别id是从1开始编号的,而你data.yaml的names是从0开始。转换标签时少做了一步减1,导致所有类别整体错位。更隐蔽的情况是classes.txt里类别顺序和你的names顺序不一致,比如原数据集把glass放在第3位,你放在第1位,模型学到的class 0其实是glass,标签里的class 0却是cardboard。

解决:先跑一下标签id统计,确认最大id值:

cat labels/*.txt | awk '{print $1}' | sort -n | uniq

如果出现0和1混在一起、且最大id恰好等于nc,基本就是从1开始的。把每个人的标签文件里所有id减1即可。

import glob for path in glob.glob("labels/**/*.txt", recursive=True): rows = [] with open(path) as f: for line in f: parts = line.strip().split() if not parts: continue new_id = int(parts[0]) - 1 if new_id < 0: raise ValueError("found id=0, check the label") rows.append(f"{new_id} " + " ".join(parts[1:])) with open(path, "w") as f: f.write("\n".join(rows))

改完后重新统计一遍,确认id范围是0 ~ nc-1再训练。这类问题最气人的地方在于loss曲线完全正常,因为模型确实把“类别空间”学出来了,只是编号整体平移了一位。

5.2 手机和相机照片的EXIF旋转让标签整体漂移

现象:训练时loss正常,单独看验证集也能画框,但把图片放大看会发现框的位置总是往右上或左下偏,尤其在竖拍照片上更明显。

原因:手机、相机拍摄的JPEG会写入EXIF Orientation信息,指示查看器应该旋转多少度。OpenCV读取时通常按原始像素读,PIL的ImageOps.exif_transpose会按EXIF转正。如果标注软件画框时基于转正的图,你训练时却用未转正的图,坐标自然错位。

解决:先统一图片方向,再同步转换标签。最简单的清洗方式是用PIL把所有图转正:

from PIL import Image, ImageOps import os for img_name in os.listdir("images"): path = os.path.join("images", img_name) im = Image.open(path) im = ImageOps.exif_transpose(im) im.save(path)

转正后,如果原图被旋转了90度,像素坐标也要做对应变换。90度旋转时坐标(x, y)变成(h - y, x),180度时变成(w - x, h - y)。最简单的方法是转正图片后,再做一次越界检查和可视化,看到框严重偏离就说明坐标还没同步。

5.3 验证集高mAP,实拍却乱检:背景负样本不足

现象:在验证集上mAP50有0.85,但把模型放到小区、街边实拍,电线杆、斑马线、下水道盖都被框成垃圾。

原因:数据集里每张图片都至少有一个目标,模型从来没有见过“完全没有垃圾”的场景,于是把背景纹理里和垃圾边缘相似的区域都激活成了目标。这是目标检测里典型的假正例爆炸。

解决:收集一批没有垃圾的负样本图片,放到images/train里,空标签文件也放一份到labels/train。类别列表里不要加“背景类”,负样本直接以空标签形式存在,YOLO会把它们当成背景硬负样本参与训练。如果不好手工收集,可以用“开放词汇目标检测”的方式辅助:先用CLIP或开放词汇检测模型扫一遍现场,把置信度不稳定的框导出来,人工确认是误检后再加入负样本集。这个循环多跑几轮,实拍误检率会显著下降。

5.4 小目标类别AP常年为0:既要缩图也要切片

现象:cardboard、glass这种大目标训练得不错,塑料瓶盖、烟头这种小目标AP却一直是0,验证集PR曲线在小目标区间几乎贴着x轴。

原因:垃圾检测数据集中小目标占比极高,但标注框可能在归一化后只有0.01的量级。这个尺度经过8倍下采样后只剩下几个像素,模型根本学不出有效特征。另一个原因是该类样本本身数量不够,统计出来只有几十张,再怎么调参也难有起色。

解决:先在标签文件里统计框面积占比,看看到底小到多严重:

awk '{printf "%d %.6f\n", $1, $4 * $5}' labels/*.txt | sort -k2 -n | head -20

如果大量框面积占比小于0.001,就把imgsz从640调到960或1280,同时把batch降到8。显存不够时,选择切片而不是盲目放缩:把图片切成2x2或3x3的子图,丢弃没有目标的子图,用子图训练。这个思路和遥感图像目标检测里的滑窗策略一致,本质是把小目标放大成常规目标。推理阶段也可以用同样的滑窗,虽然耗时会增加,但小目标召回率提升非常明显。

6. 验证技巧与下一步:把ZIP变成持续可用的资产

6.1 用混淆矩阵和PR曲线验证垃圾检测结果

训练结束后,验证不能只打印一个mAP数字。我通常先用model.val()拿到完整指标:

from ultralytics import YOLO model = YOLO("output/garbage/weights/best.pt") metrics = model.val(data="garbage.yaml", split="val") print("mAP50:", metrics.box.map50) print("mAP50-95:", metrics.box.map)

拿到数字后,打开output/garbage/confusion_matrix.png和PR_curve.png看两个重点:混淆矩阵里如果某两类互相误判严重,例如plastic和paper经常混在一起,说明视觉特征本身相似,需要补充更多对比样本,或在损失函数里加大这两类的权重;PR曲线里如果recall在0.5前曲线快速掉头,说明存在大量低置信度正样本,推理时把conf阈值降低到0.25往往能救回一部分小目标。

6.2 下一步:把一次性ZIP变成持续可用的训练资产

不要训练完就当结束了。我会从训练集里挑出错检和漏检的图片,交给CVAT或X-AnyLabeling做增量标注,形成“训练—评估—挑错—补标—再训练”的闭环。数据量少时,还可以把多张垃圾图片拼接成马赛克图来做简单数据增强。之后如果想走开放词汇目标检测或多模态目标检测路线,这份数据集还能作为固定的正样本基础,把硬编码类别改成自然语言类别描述,扩大可检测范围。

我现在拿到任何一份目标检测数据集,第一件事永远是先做统计而不是直接训练。这个习惯帮我在垃圾检测项目里少走了很多弯路。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询