☰
垃圾分类图像分类数据集实战:从选型清洗到模型训练避坑指南
2026/10/7 19:08:58 网站建设 项目流程

简介:这份深度学习数据集面向图像分类入门与实战练习者,聚焦垃圾分类场景中的瓶子识别任务,可用于训练卷积神经网络完成塑料瓶、玻璃瓶、金属瓶等类别的自动判别,适合课程设计、模型对比实验或迁移学习练手。资源包共约2000个文件,以1998张jpg图像为主体,另附1个py脚本与1个json类别文件,压缩包大小39.65MB,图像按类别目录存放,同类样本归入同一文件夹,便于直接接入ImageFolder等标准加载方式。数据已预先划分为训练集约2200张、验证集约200张、测试集约100张,比例合理,可支撑模型训练、调参与最终评估的完整流程。目前已有269人学习下载。借助清晰的目录结构与类别标注,读者可快速搭建数据管道、复现分类基线,并在此基础上尝试数据增强、类别不均衡处理与模型微调,省去自行采集与清洗图像的繁琐环节。

1. 垃圾分类图像分类数据集:从「能跑通」到「能落地」差在哪

垃圾分类的图像分类数据集,核心就一件事:把一张垃圾照片映射到「可回收物 / 厨余垃圾 / 有害垃圾 / 其他垃圾」这类标签上。听起来像入门练手项目,但真正做过的人都知道,公开数据集和真实场景之间隔着一道鸿沟——背景杂乱、光照突变、类别极度不均衡,模型在验证集上 95% 的准确率,换一批实拍图可能直接掉到 60%。这个方向适合两类人:一是想找一个完整闭环练手图像分类算法的新手,二是要做智能垃圾桶、分拣线视觉模块的工程师。下面我按「数据集怎么选 → 怎么清洗 → 怎么训练 → 怎么避坑」的顺序,把这条链路讲透,参数和命令都能直接抄。

2. 垃圾分类数据集怎么选:三类来源与选型对比

2.1 公开数据集、自采数据、合成数据的取舍

做垃圾分类图像分类,数据来源无非三种,选错了后面全是返工。

公开数据集是最省事的起点。常见的做法是找 TrashNet 这类经典集合,它把垃圾分成玻璃、纸、纸板、塑料、金属、其他六类,图片数量在几千张量级,背景大多是白纸或纯色桌面。优点是干净、标注质量高、拿来就能跑通 baseline;缺点是太「实验室」,模型学到的是「白背景 + 单个物体」,一到真实场景就翻车。另一个常见来源是各类垃圾分类挑战赛放出的数据,类别更贴近国内四分类,但往往需要自己申请或从比赛页面获取,格式不统一。

自采数据是落地的必经之路。用手机或工业相机在目标场景拍,比如小区垃圾桶投放点、分拣线传送带。自采的关键不是拍多少,而是拍得「杂」:不同时段的光照、不同角度、物体堆叠、部分遮挡、背景里有人有车。我一般建议每个类别先拍 300~500 张,四分类就是 1200~2000 张起步,后续再按混淆矩阵补拍。

合成数据适合补长尾类别。比如有害垃圾里的灯管、电池,实拍样本少,可以用抠图 + 背景融合的方式生成。但合成数据只能当补充,不能当主力,否则模型会学到合成痕迹而不是物体本身特征。

数据来源典型规模优点主要风险
公开数据集数千张开箱即用、标注干净场景单一,迁移差
自采数据每类 300+贴合真实场景标注成本高、易漏标
合成数据按需生成补长尾、可控域差异大,需混训

2.2 类别体系怎么定:四分类还是细分类

类别体系直接决定标注成本和模型上限。国内主流是「可回收物、厨余垃圾、有害垃圾、其他垃圾」四分类,但实际落地时经常要再拆。比如可回收物里,塑料瓶和纸箱的回收价值、处理工艺完全不同,如果下游分拣线要分开,那模型就得分。

我的经验是:先按下游动作定类别,而不是按概念定类别。如果分拣线只需要「能回收 / 不能回收」两个动作,就别做四分类,二分类的准确率和鲁棒性会高一大截。反过来,如果要做精细分拣,那类别可能到 10 类以上,这时候数据量必须跟上,每类至少 500 张,否则长尾类别必然拖后腿。

还有一个容易被忽略的点:「其他垃圾」是个筐。很多人把不好归类的全丢进去,导致这个类内部差异极大,模型学不动。正确做法是把「其他」再拆成几个视觉上一致的子类,或者干脆在训练时给它更高的损失权重。

2.3 数据格式与目录结构:让训练脚本直接能读

不管数据从哪来,最后都要整理成训练框架能直接读的结构。图像分类最通用的是按类别分文件夹:

dataset/ ├── train/ │ ├── recyclable/ │ ├── kitchen/ │ ├── hazardous/ │ └── other/ ├── val/ │ ├── recyclable/ │ ├── kitchen/ │ ├── hazardous/ │ └── other/ └── test/ └── ...

这种结构 PyTorch 的ImageFolder、TensorFlow 的image_dataset_from_directory都能直接读,不用写自定义 Dataset。划分比例我一般用 7:1.5:1.5,验证集和测试集都要保证每个类别都有样本,不能出现某个类在验证集里一张都没有的情况。

提示:划分前先做一次全量去重。用感知哈希(pHash)或简单的文件 MD5,把重复图片删掉。重复图会导致验证集泄漏,准确率虚高,这是最常见的翻车点之一。

3. 数据清洗与增强:把「脏数据」挡在训练之前

3.1 用脚本做去重、坏图检测和尺寸统计

拿到数据第一件事不是训练,是体检。下面这段脚本做三件事:检测损坏图片、按感知哈希去重、统计尺寸分布。

import os from PIL import Image import imagehash from collections import Counter def audit_dataset(root): bad_files, hashes, sizes = [], {}, [] for cls in os.listdir(root): cls_dir = os.path.join(root, cls) if not os.path.isdir(cls_dir): continue for fname in os.listdir(cls_dir): fpath = os.path.join(cls_dir, fname) try: img = Image.open(fpath).convert("RGB") except Exception as e: bad_files.append((fpath, str(e))) continue # 感知哈希,容忍轻微压缩差异 h = str(imagehash.phash(img)) if h in hashes: print(f"重复: {fpath} <-> {hashes[h]}") else: hashes[h] = fpath sizes.append(img.size) print("损坏文件:", bad_files) print("尺寸分布 top5:", Counter(sizes).most_common(5)) audit_dataset("dataset/train")

逻辑说明:Image.open加convert("RGB")能同时暴露截断文件和四通道 PNG;phash对缩放、轻微压缩不敏感,适合找「同一张图存了多次」的情况。参数上,如果发现大量图片尺寸差异极大(比如有的 200×200,有的 4000×3000),不要急着统一缩放,先看是不是混入了不同来源的数据,必要时按来源分批处理。

3.2 增强策略:别把瓶子「增强」成别的类别

图像分类的增强里,翻转、裁剪、颜色抖动是标配,但垃圾分类有几个特殊坑。

水平翻转要慎用。瓶子、纸箱翻转后还是瓶子纸箱,没问题;但有些类别翻转后语义会变,比如带文字的包装。如果类别里有依赖文字或方向的样本,翻转前要确认。

颜色抖动幅度别太大。垃圾分类里颜色是重要线索——厨余垃圾偏暗黄绿,可回收塑料瓶颜色鲜艳。如果ColorJitter的hue给到 0.5,模型可能把绿色瓶子认成厨余。我一般把brightness=0.2, contrast=0.2, saturation=0.2, hue=0.05作为起点。

RandomResizedCrop 的 scale 下限别太低。如果scale=(0.08, 1.0),一张瓶子图可能被裁得只剩瓶盖,模型学到的就是局部纹理。垃圾分类建议scale=(0.5, 1.0),保证主体基本完整。

from torchvision import transforms train_tf = transforms.Compose([ transforms.RandomResizedCrop(224, scale=(0.5, 1.0)), transforms.RandomHorizontalFlip(p=0.5), transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.05), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ])

Normalize用的是 ImageNet 统计量,如果你从零训练可以用自己数据算的均值方差,但用预训练权重时保持一致更稳。

3.3 类别不均衡:加权采样还是损失加权

垃圾分类数据几乎必然不均衡——其他垃圾一大堆,有害垃圾没几张。两种处理方式:

加权采样(WeightedRandomSampler)让每个 batch 里各类别比例接近,适合类别差距在 10 倍以内的场景。损失加权(CrossEntropyLoss(weight=...))给少数类更高惩罚,适合差距极大的场景。我一般先用加权采样,如果少数类召回还是上不去,再叠加损失加权。

import numpy as np from torch.utils.data import WeightedRandomSampler # labels 是训练集每个样本的类别索引 class_counts = np.bincount(labels) class_weights = 1.0 / class_counts sample_weights = class_weights[labels] sampler = WeightedRandomSampler( weights=sample_weights, num_samples=len(sample_weights), replacement=True )

replacement=True表示有放回采样,少数类会被反复抽到。注意num_samples设成训练集大小,这样每个 epoch 的步数和普通训练一致,方便对比。

4. 训练垃圾分类模型:从 baseline 到调参的完整命令

4.1 选 backbone:ResNet 还是更新的图像分类模型

2024 年之后做图像分类,backbone 选择比几年前多得多。但垃圾分类这个任务,数据量通常不大(几千到几万张),别一上来就上 ViT 大模型。我的建议:

  • 数据 < 5000 张:ResNet-18 / EfficientNet-B0,用 ImageNet 预训练,冻结前几层微调。
  • 数据 5000~50000 张:ResNet-50 / ConvNeXt-Tiny / EfficientNet-B2。
  • 数据 > 50000 张且有 GPU 预算:可以试 Swin-T 或 ViT-B/16,但要注意小数据集上 ViT 容易过拟合。

最新的图像分类模型不一定适合你的数据规模。我见过有人拿 ViT-H 在 3000 张垃圾图上训,结果验证集准确率还不如 ResNet-18,原因就是参数量和数据量不匹配。

4.2 一个能直接跑的训练脚本

下面用 PyTorch 写一个最小可跑的训练循环,包含加权采样、余弦退火和验证。

import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision import datasets, models, transforms device = torch.device("cuda" if torch.cuda.is_available() else "cpu") train_ds = datasets.ImageFolder("dataset/train", transform=train_tf) val_ds = datasets.ImageFolder("dataset/val", transform=val_tf) # 加权采样 labels = [s[1] for s in train_ds.samples] class_counts = np.bincount(labels) sample_weights = (1.0 / class_counts)[labels] sampler = WeightedRandomSampler(sample_weights, len(sample_weights), True) train_loader = DataLoader(train_ds, batch_size=32, sampler=sampler, num_workers=4) val_loader = DataLoader(val_ds, batch_size=32, shuffle=False, num_workers=4) model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) model.fc = nn.Linear(model.fc.in_features, len(train_ds.classes)) model = model.to(device) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4, weight_decay=1e-4) scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=30) for epoch in range(30): model.train() for x, y in train_loader: x, y = x.to(device), y.to(device) optimizer.zero_grad() loss = criterion(model(x), y) loss.backward() optimizer.step() scheduler.step() model.eval() correct = total = 0 with torch.no_grad(): for x, y in val_loader: x, y = x.to(device), y.to(device) pred = model(x).argmax(1) correct += (pred == y).sum().item() total += y.size(0) print(f"epoch {epoch}, val_acc {correct/total:.4f}")

关键参数说明:lr=3e-4是微调预训练模型的常用起点,如果 loss 震荡就降到 1e-4;weight_decay=1e-4抑制过拟合;T_max=30和总 epoch 一致,让学习率在一个训练周期内从峰值降到接近 0。batch_size=32在 8GB 显存上跑 ResNet-18 的 224 输入基本够用,显存不够就降到 16 并同步调小学习率。

4.3 训练过程中该盯哪些指标

别只看准确率。垃圾分类必须看混淆矩阵和每类召回。常见情况是整体准确率 90%,但有害垃圾召回只有 40%——因为样本太少,模型干脆全预测成其他类。

from sklearn.metrics import confusion_matrix, classification_report all_preds, all_labels = [], [] model.eval() with torch.no_grad(): for x, y in val_loader: x = x.to(device) all_preds.extend(model(x).argmax(1).cpu().numpy()) all_labels.extend(y.numpy()) print(classification_report(all_labels, all_preds, target_names=train_ds.classes)) print(confusion_matrix(all_labels, all_preds))

如果发现某两类互相混淆严重(比如塑料瓶和玻璃瓶),说明特征区分度不够,要么补这两类的难例,要么在增强里加入更强的形变让模型关注材质纹理而不是颜色。

5. 垃圾分类图像分类的避坑与排查清单

5.1 验证集准确率很高,实拍图一塌糊涂

现象:验证集 95%,拿手机在垃圾桶前拍几张,模型乱猜。

原因:训练数据和真实场景存在域差异。公开数据集多是白背景、单物体、正面拍摄,实拍图背景杂乱、有遮挡、角度随意。

解决:在训练集里混入实拍图,哪怕每类只有几十张,也能显著拉近域距离。另一个办法是在增强里加入随机背景替换或 RandAugment,让模型不过度依赖背景。

5.2 某个类别召回率始终上不去

现象:有害垃圾召回 30%,其他类都 90%+。

原因:样本太少,加权采样后每个 batch 里该类样本仍然有限,模型没学够。

解决:先确认该类标注没有错标漏标;然后叠加损失加权,把该类权重设为其他类的 3~5 倍;如果还不够,用合成数据或从公开数据集里找同类样本补充。注意补数据后要重新划分验证集,别把补充数据混进验证集。

5.3 训练 loss 不降或震荡

现象:loss 在前几个 epoch 上下跳,准确率不涨。

原因:学习率太大、batch size 太小导致梯度噪声大,或者数据标注有大量错误。

解决:先把学习率降到 1e-4 试;如果还震荡,检查标注——随机抽 50 张看标签对不对。垃圾分类标注最容易错的是「其他垃圾」和「可回收物」的边界,比如沾了油的纸盒到底算哪类,标注前要统一规则。

5.4 模型把「背景」当特征

现象:把垃圾桶放在桌上拍能识别,放在地上就认不出。

原因:训练图里某个类别总是出现在特定背景(比如厨余垃圾都在厨房拍),模型学到了背景而不是物体。

解决:做背景多样性增强,或者用 Grad-CAM 可视化模型关注区域,确认它看的是物体还是背景。如果确实在看背景,就要补拍不同背景下的同类样本。

5.5 推理速度不达标

现象:模型准确率够,但部署到边缘设备上帧率只有个位数。

原因:backbone 太大,或者输入分辨率太高。

解决:先降输入分辨率,224 降到 160 通常准确率掉 1~2 个点但速度翻倍;再考虑换轻量 backbone(MobileNetV3、EfficientNet-Lite);最后才是量化。量化对垃圾分类这种颜色敏感任务要小心,INT8 量化后颜色特征可能损失,建议量化后在验证集上重新评估。

6. 进阶技巧:用混淆矩阵驱动数据迭代

训练完一轮不是结束,而是下一轮数据采集的开始。我习惯把混淆矩阵当成「数据采集指南」:矩阵里非对角线的值,就是模型分不清的类别对,针对这些类别去补难例,比盲目加数据有效得多。

具体做法:先跑一遍验证集得到混淆矩阵,找出 top3 混淆对,比如「塑料瓶 vs 玻璃瓶」「纸盒 vs 其他垃圾」。然后针对每一对,去真实场景里专门拍这两类容易混的样本——比如透明塑料瓶和透明玻璃瓶放在一起拍,沾油纸盒和干净纸盒对比拍。每类补 50~100 张,重新训练,看混淆矩阵对应位置是否下降。

import seaborn as sns import matplotlib.pyplot as plt cm = confusion_matrix(all_labels, all_preds) sns.heatmap(cm, annot=True, fmt="d", xticklabels=train_ds.classes, yticklabels=train_ds.classes) plt.xlabel("pred") plt.ylabel("true") plt.savefig("confusion.png", dpi=150)

这个循环我一般跑 3 轮。第一轮用公开数据 + 少量实拍,第二轮按混淆矩阵补难例,第三轮做类别平衡和增强微调。三轮下来,实拍场景的准确率通常能从 60% 提到 85% 以上。

还有一个验证技巧:留出一个「野生测试集」。从真实场景随机拍 100 张,不参与任何训练和调参,只在最后评估。这个集合的准确率才是你能对外说的数字。我吃过亏——早期只看验证集,上线后被现实打脸,后来养成习惯,任何模型上线前必须过野生测试集这一关。

最后说个习惯:每次实验都记录数据版本、增强参数、学习率和混淆矩阵截图。垃圾分类这个任务,数据改动对结果的影响远大于调参,没有记录的话,两周后你根本想不起来哪版数据对应哪个准确率。希望帮到你。

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

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

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

立即咨询