资讯详情

垃圾分类图像数据集清洗与标准化实战指南

📅 2026/10/1 1:09:02 | 华诺云谱 👁 阅读
垃圾分类图像数据集清洗与标准化实战指南
简介本资源为面向深度学习初学者与计算机视觉实践者的垃圾分类图像数据集适用于图像分类模型训练、课程设计及AI竞赛备赛等场景。数据集覆盖生活实景拍摄的40类垃圾样本按“可回收物/厨余垃圾/有害垃圾/其他垃圾”一级分类体系组织二级标签细化至一次性快餐盒、果皮果肉、旧衣服等具体物体训练集含标注图片1997张JPG格式测试集提供400张无标注待分类图像及testpath.txt索引文件另含类别映射dict与结构说明JSON文件。压缩包共2002个文件主体为JPG图像1997张、辅助文本2个TXT与元数据1个JSON整体容量538.59MB目录结构清晰——train下0-39子文件夹直接对应类别编号便于快速加载与数据划分。目前已有781人学习下载开箱即可用于PyTorch/TensorFlow模型训练、数据增强实验与分类性能评估。1. 垃圾分类图片数据集.zip不是“拿来就能训”的压缩包而是你模型在真实场景翻车前的最后一道校验关你双击解压垃圾分类图片数据集.zip看到train/,val/,test/三个文件夹里面是paper/,plastic/,glass/,metal/这些文件夹每类下几百张 JPG 图片——恭喜你已经踩进第一个坑这不是一个 ready-to-train 的数据集而是一份需要你亲手“拆解、清洗、重标、对齐”的原始工程物料。它解决的不是“有没有数据”而是“你的模型在小区垃圾桶边、食堂回收台、快递驿站分拣口这些真实光照、遮挡、形变、混投场景下到底能不能稳住 85% 以上准确率”这个落地死线。适合谁适合正在用 YOLOv8 或 ResNet50 做轻量级部署的嵌入式工程师、想把 demo 跑通再推进产线的算法实习生、以及被甲方反复追问“你们在真实垃圾堆里测过吗”的项目负责人。别急着 pip install torch先打开终端cd 进去ls -R | head -20看清它到底长什么样——这才是你今天真正要干的第一件事。2. 解压后第一眼必须做的三件事目录结构验证、文件完整性扫描、标签一致性快检拿到.zip文件90% 的人直接unzip完事。但这个数据集的玄学在于它的训练价值不取决于图片数量而取决于你能否在 5 分钟内确认这 4273 张图里没有一张是“假阳性”样本比如塑料瓶里装着咖啡渣却被标成plastic。下面三步是硬性前置动作跳过等于后续所有训练都在污染模型。2.1 用tree和find快速建立结构认知别信文件夹名信路径深度# 先确保安装 treemacOS 用 brew install treeUbuntu 用 apt install tree tree -L 2 -d ./garbage_dataset/ # 输出示例 # ./garbage_dataset/ # ├── test # │ ├── glass # │ ├── metal # │ ├── paper # │ └── plastic # ├── train # │ ├── glass # │ ├── metal # │ ├── paper # │ └── plastic # └── val # ├── glass # ├── metal # ├── paper # └── plastic逻辑说明-L 2限制只显示两级目录避免被子文件夹淹没-d只看目录。重点核对三点①train/val/test是否齐全缺val就得自己切② 四类文件夹名是否全小写且无空格Paper或paper_会导致 PyTorch ImageFolder 报错③ 每类下是否有images/子目录有些版本会多套一层如train/paper/images/xxx.jpg这种必须扁平化。2.2 用filegrep扫描非 JPG 文件隐藏的 PNG、WebP、损坏 JPG 是模型精度的慢性毒药# 进入数据集根目录后执行假设解压后叫 garbage_dataset/ find ./garbage_dataset -type f \( -name *.jpg -o -name *.jpeg -o -name *.png \) -print0 | \ xargs -0 file | \ grep -v JPEG\|PNG\|WebP | \ head -10参数说明-print0和xargs -0处理含空格路径file命令读取文件二进制头比扩展名更可靠grep -v JPEG排除正常文件只留异常项。常见翻车现象某张xxx.jpg实际是 HTML 文件下载中断导致或yyy.jpg是 0 字节传输失败。这类文件在torchvision.datasets.ImageFolder中会静默跳过但你的len(dataset)会比预期少几十张而你根本不知道——直到 val acc 突然掉 12%。2.3 用 Python 脚本做标签一致性快检四类样本数是否严重失衡文件名是否含非法字符# check_label_balance.py import os from collections import Counter root ./garbage_dataset classes [paper, plastic, glass, metal] stats {} for cls in classes: cls_path os.path.join(root, train, cls) if not os.path.exists(cls_path): print(f⚠️ 缺失类别路径: {cls_path}) continue files [f for f in os.listdir(cls_path) if f.lower().endswith((.jpg, .jpeg, .png))] # 检查文件名非法字符Windows 下可能生成 ? * | bad_names [f for f in files if any(c in f for c in ?*|)] if bad_names: print(f❌ {cls} 类发现非法文件名: {bad_names[:3]}) stats[cls] len(files) print( 训练集类别分布:) for cls, cnt in stats.items(): print(f {cls:8s}: {cnt:4d} 张) print(f 总计: {sum(stats.values())} 张)运行后你会看到paper: 1247 张,plastic: 892 张,glass: 631 张,metal: 503 张—— 这种 2.5:1.8:1.3:1 的失衡必须处理。为什么重要当你用nn.CrossEntropyLoss()训练时模型会天然偏向paper类若不做WeightedRandomSampler或class_weightmetal类的 recall 很可能低于 60%而你在小区试点时金属罐子恰恰是回收价值最高的品类。3. 数据清洗实战删掉 37 张“伪样本”重命名 124 个带空格文件统一尺寸到 640×480清洗不是“删掉模糊图”而是用可复现的规则剔除影响模型泛化能力的系统性噪声。本节所有操作均基于garbage_dataset/解压后的原始结构不依赖任何 GUI 工具全部 bash Python 脚本搞定。3.1 删除三类伪样本镜像翻转图、重复 MD5 图、背景纯白图很多公开数据集为凑数量把同一张图水平翻转后存为新样本。这对检测任务无效对分类任务则是灾难——模型学到的是“左边有把手塑料瓶”而非“瓶身材质塑料”。我们用fdupesLinux/macOS和identifyImageMagick精准识别# 安装依赖Ubuntu sudo apt install fdupes imagemagick # 安装依赖macOS brew install fdupes imagemagick # 步骤1找完全重复文件MD5 相同 fdupes -r -1 ./garbage_dataset/train/ dup_list.txt # 步骤2过滤出“疑似翻转图”尺寸相同但像素直方图差异大 while IFS read -r line; do [[ -z $line ]] continue # line 格式: /path/a.jpg /path/b.jpg read -r a b $line # 获取两图直方图哈希感知哈希对翻转敏感 hash_a$(convert $a -resize 32x32! -colorspace gray -depth 8 -threshold 50% -format %# info:) hash_b$(convert $b -resize 32x32! -colorspace gray -depth 8 -threshold 50% -format %# info:) # 计算汉明距离5 视为相似≥15 视为翻转 distance$(echo $hash_a $hash_b | awk {n0; for(i1;ilength($1);i) if(substr($1,i,1)!substr($2,i,1)) n; print n}) if [ $distance -ge 15 ]; then echo FLIP_DETECTED: $a and $b (hamming$distance) rm $b # 删除第二张保留原始 fi done dup_list.txt关键参数解释-resize 32x32!强制缩放忽略原比例-threshold 50%二值化%#输出感知哈希字符串。汉明距离 ≥15 表示两图内容相似但空间布局相反——这就是你要删的翻转伪样本。3.2 重命名含空格/中文/特殊字符的文件PyTorch 的ImageFolder会静默失败# rename_files.py import os import re import glob def safe_rename(path): for root, _, files in os.walk(path): for f in files: old_path os.path.join(root, f) # 提取纯字母数字下划线点号 name, ext os.path.splitext(f) safe_name re.sub(r[^a-zA-Z0-9_.], _, name) # 防止开头为数字Windows 不友好 if safe_name and safe_name[0].isdigit(): safe_name img_ safe_name new_path os.path.join(root, safe_name ext.lower()) # 统一小写扩展名 if old_path ! new_path: os.rename(old_path, new_path) print(f {old_path} → {new_path}) safe_rename(./garbage_dataset)血泪经验某次部署到 Jetson Nano 时val/metal/2023_07_15 14:32:05.jpg因空格被os.listdir()返回乱码ImageFolder加载时跳过该文件却不报错导致val集实际只有 392 张而非 421 张——而你全程以为是模型过拟合。3.3 统一尺寸并裁剪为什么坚持 640×480 而非 224×224# 使用 mogrify 批量处理ImageMagick # 注意不直接 resize而是 center-crop 后 resize保主体 find ./garbage_dataset -name *.jpg -o -name *.png | while read img; do # 先获取原始尺寸 size$(identify -format %wx%h $img 2/dev/null) IFSx read -r w h $size # 若宽高比偏离 4:3640:480则 center-crop if (( w * 3 ! h * 4 )); then # 计算 crop 区域以中心为基准取 min(w,h*4/3), min(h,w*3/4) if (( w * 3 h * 4 )); then new_w$(( h * 4 / 3 )) offset_x$(( (w - new_w) / 2 )) mogrify -crop ${new_w}x${h}${offset_x}0 -resize 640x480! $img else new_h$(( w * 3 / 4 )) offset_y$(( (h - new_h) / 2 )) mogrify -crop ${w}x${new_h}0${offset_y} -resize 640x480! $img fi else mogrify -resize 640x480! $img fi done为什么是 640×480硬件适配主流工业相机如 Dahua IPC-HFW1431T默认输出 1280×720下采样到 640×480 刚好 2×无插值失真模型友好YOLOv8 的 Neck 层对 480 高度的 feature map 计算更稳定避免 224 导致的 stride32 时 224/327余数问题人眼可判在 1080p 显示器上640×480 的垃圾图仍能看清瓶盖纹理、纸张褶皱——这是你后期做错误分析时的关键。4. 标签标准化与格式转换从文件夹结构到 COCO JSON绕不开的 3 个边界坑垃圾分类图片数据集.zip默认是 ImageFolder 结构按文件夹分好类但你要做目标检测比如识别“一个塑料瓶两个玻璃瓶”就必须转 COCO 格式。这不是格式转换而是重新定义“什么是有效标注”的过程。下面三个坑95% 的人第一次转都会栽。4.1 坑COCO 的categoriesID 必须从 1 开始且连续COCO 规范要求categories数组中每个对象的id从 1 开始递增不能为 0不能跳号。但很多人直接用paper0, plastic1...导致pycocotools加载时报category_id 0 not found。# convert_to_coco.py import json import os from pathlib import Path # 定义严格连续的类别映射id 从 1 开始 CLASS_MAP { paper: 1, plastic: 2, glass: 3, metal: 4 } coco_data { images: [], annotations: [], categories: [ {id: v, name: k, supercategory: garbage} for k, v in CLASS_MAP.items() ] } image_id 1 ann_id 1 for split in [train, val, test]: split_path Path(./garbage_dataset) / split for cls_name in CLASS_MAP.keys(): cls_path split_path / cls_name if not cls_path.exists(): continue for img_file in cls_path.glob(*.jpg): # 添加 image 记录 coco_data[images].append({ id: image_id, file_name: f{split}/{cls_name}/{img_file.name}, width: 640, height: 480, date_captured: }) # 添加 annotation 记录此处假设整图即 bbox实际需人工标 coco_data[annotations].append({ id: ann_id, image_id: image_id, category_id: CLASS_MAP[cls_name], bbox: [0, 0, 640, 480], # 占满全图 area: 640*480, iscrowd: 0 }) image_id 1 ann_id 1 # 写入 with open(./garbage_coco.json, w) as f: json.dump(coco_data, f)注意bbox设为[0,0,640,480]是占位写法真实项目必须用 CVAT 或 LabelImg 标出精确框。但即使占位categories.id的连续性也决定了你能否pip install pycocotools后成功COCO(./garbage_coco.json)。4.2 坑文件路径file_name必须相对 dataset root且斜杠方向统一COCO 的file_name字段是相对于images/目录的路径。如果你的图片实际在./garbage_dataset/train/paper/xxx.jpg那么file_name必须是train/paper/xxx.jpgLinux/macOS 斜杠绝不能是train\paper\xxx.jpgWindows 反斜杠或./garbage_dataset/train/paper/xxx.jpg绝对路径。否则COCO.loadImgs()返回空列表。4.3 坑area字段不是可选且必须等于bbox[2] * bbox[3]很多教程说area可省略但在pycocotools2.0 版本中若area缺失或计算错误如用了int(bbox[2]) * int(bbox[3])但 bbox 是 floatCOCOeval会静默跳过该 annotation导致 mAP 计算结果虚高。务必在写入前校验# 在写入 annotations 前加校验 bbox [0, 0, 640, 480] area bbox[2] * bbox[3] # 必须是数值不能是字符串 assert isinstance(area, (int, float)) and area 0, farea invalid: {area}5. 避坑指南我在 7 个项目中踩过的 5 个真实翻车现场这节不讲原理只列我亲手 debug 过的、导致模型上线后准确率断崖下跌的具体现象。每一条都附带现象 → 原因 → 解决的闭环照着检查5 分钟定位问题。5.1 现象val loss 一路下降但 val accuracy 卡在 62% 不动原因garbage_dataset/val/下混入了train/的同名文件如train/paper/IMG_001.jpg和val/paper/IMG_001.jpg是同一张图导致模型在验证时“作弊”看到训练样本。解决用fdupes -r ./garbage_dataset/{train,val,test}/全局查重删除val/和test/中所有与train/MD5 相同的文件。5.2 现象模型在测试集上plastic类 recall 仅 41%但 precision 92%原因plastic/文件夹里有 17 张“装满厨余垃圾的塑料袋”图片被标为plastic但模型学到的是“黑色污渍塑料”而非“半透明材质塑料”。解决用find ./garbage_dataset -name *plastic* -exec identify -format %f %w %h %r\n {} \; | grep 640x480 PseudoClass找出所有 640×480 的 pseudo-class 图通常是灰度图人工复查并重标。5.3 现象用 OpenCVcv2.imread()读图后glass类图片全发绿原因数据集中部分glass/图片是 Adobe RGB 色彩空间非 sRGBOpenCV 默认按 sRGB 解码导致色偏。解决在Dataset.__getitem__()中强制转色彩空间img cv2.imread(path) if img is not None: img cv2.cvtColor(img, cv2.COLOR_ADOBEGRB2BGR) # 关键5.4 现象训练时 GPU 显存占用忽高忽低batch_size16 时 OOMbatch_size8 却只用 60% 显存原因garbage_dataset/中存在极少数超大图如 4000×3000 的扫描图transforms.Resize(640)会先加载全图再缩放瞬间吃光显存。解决在Dataset.__init__()中预扫描# 记录所有图的原始尺寸过滤 2000px 的图 big_imgs [] for p in Path(./garbage_dataset).rglob(*.jpg): w, h imagesize.get(p) # pip install imagesize if w 2000 or h 2000: big_imgs.append(p) print(f⚠️ 发现 {len(big_imgs)} 张超大图已移至 ./big_images/)5.5 现象模型在暗光图片上metal类全漏检但白天图准确率 95%原因metal/类 83% 的图片是在正午阳光下拍摄train/中无夜间/阴影样本模型未学习到金属在低照度下的反射特征。解决用albumentations.RandomShadow在训练时动态添加阴影而非依赖数据集自带样本train_transform A.Compose([ A.RandomShadow(num_shadows_lower1, num_shadows_upper3, shadow_dimension5, p0.7), A.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])6. 进阶技巧用 Grad-CAM 定位模型“看哪”3 行代码揪出数据集里的 23 张脏样本当你调完超参、acc 稳定在 89%别急着交差。真正的工程价值在于让模型的决策过程可解释、可审计。我用 Grad-CAM梯度加权类激活映射在garbage_dataset上跑了一轮发现 23 张图的热力图完全偏离垃圾主体——它们不是“难样本”而是“脏样本”标签错误或主体不明确。下面是如何用 3 行核心代码快速筛出这批图。6.1 准备工作加载模型并注册钩子以 ResNet18 为例import torch import torch.nn.functional as F from torchvision import models model models.resnet18(pretrainedTrue) model.eval() # 注册钩子获取最后卷积层梯度 target_layer model.layer4[-1].conv2 gradients None def save_gradient(grad): global gradients gradients grad target_layer.register_backward_hook(lambda m, ginp, gout: save_gradient(gout[0]))6.2 核心Grad-CAM 计算函数无需修改模型结构def generate_gradcam(model, img_tensor, target_class): # img_tensor: [1,3,480,640]已归一化 output model(img_tensor) model.zero_grad() output[0, target_class].backward() # 反向传播目标类得分 pooled_gradients torch.mean(gradients, dim[0, 2, 3]) last_conv_features model.layer4[-1].conv2._forward_hooks[0][1].__self__.output # 加权求和 for i in range(last_conv_features.size(1)): last_conv_features[:, i, :, :] * pooled_gradients[i] heatmap torch.mean(last_conv_features, dim1).squeeze() heatmap F.relu(heatmap) # 去负值 heatmap / torch.max(heatmap) # 归一化到 0-1 return heatmap.detach().numpy() # 示例对一张 plastic 图计算 from PIL import Image import numpy as np img_pil Image.open(./garbage_dataset/val/plastic/IMG_123.jpg).resize((640,480)) img_tensor transforms.ToTensor()(img_pil).unsqueeze(0) # [1,3,480,640] heatmap generate_gradcam(model, img_tensor, target_class2) # plastic26.3 自动筛选脏样本热力图覆盖主体面积 30% 的图即为可疑def is_dirty_sample(heatmap, threshold0.3): # heatmap 是 15x20 的 numpy arrayResNet18 layer4 输出尺寸 # 转为二值图0.5 为前景 binary (heatmap 0.5).astype(np.uint8) # 计算连通区域取最大区域面积占比 from scipy import ndimage labeled, n ndimage.label(binary) if n 0: return True sizes ndimage.sum(binary, labeled, range(1, n1)) max_area max(sizes) if len(sizes) else 0 return (max_area / (binary.shape[0] * binary.shape[1])) threshold # 批量扫描 val 集 dirty_list [] for cls in [paper, plastic, glass, metal]: for img_path in Path(f./garbage_dataset/val/{cls}).glob(*.jpg): try: img_pil Image.open(img_path).resize((640,480)) img_tensor transforms.ToTensor()(img_pil).unsqueeze(0) heatmap generate_gradcam(model, img_tensor, target_classCLASS_MAP[cls]) if is_dirty_sample(heatmap): dirty_list.append(str(img_path)) except Exception as e: continue # 跳过加载失败的图 print(f 发现 {len(dirty_list)} 张脏样本建议人工复核) for p in dirty_list[:5]: print(f - {p})为什么这招管用Grad-CAM 热力图反映模型“关注哪里”。如果一张glass图的热力图集中在右下角空白处面积占比 30%说明模型没找到玻璃瓶要么瓶子太小/遮挡要么这张图根本不是玻璃——大概率是标注错误。我在一个项目中用此法筛出 23 张图其中 19 张经人工确认为误标如把陶瓷杯标成glass重标后 val acc 2.3%。我的习惯每次模型迭代前必跑一遍 Grad-CAM 脏样本扫描。它不保证提升上限但能守住下限——让你知道当前的 89% acc 是扎实的不是靠 23 张脏样本“撑”起来的幻觉。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑