垃圾分类图像分类实战:从数据清洗到模型部署全流程

发布时间:2026/9/23 18:11:19
垃圾分类图像分类实战:从数据清洗到模型部署全流程 简介一份面向图像分类入门与垃圾分类实战的完整项目资源基于TensorFlow与OpenCV适合希望自主搭建神经网络模型并完成数据训练与预测的开发者。项目以干垃圾、湿垃圾、可回收垃圾、有害垃圾四分类为任务覆盖数据集制作、模型构建、训练和预测全流程其中训练脚本用于训练数据集预测脚本可在图片上以中文形式输出所属类别整体设计简洁可当作图像分类任务的基础模板使用。整个压缩包共包含一千零四十六个文件内容以一千零四十一张图片为主另有两个Python脚本、一个H5模型文件、一个说明文档和一段演示视频包体约为八百二十五兆字节便于学习数据标注、模型保存和推理展示等细节。读者可依据脚本快速复现训练过程并替换数据集以适配其他分类场景。目前该项目已有1331人学习浏览既能帮助初学者快速搭建图像分类流程也为进阶者提供了网络设计与调参的实用参考。1. 垃圾分类项目为什么不是“把图扔进模型”这么简单一张外卖盒、一个矿泉水瓶、一袋厨余垃圾——把这三样东西扔进同一个摄像头底下要分得又快又准并不只是“拿个分类模型跑一下”的事。做图像分类的人都知道模型吃的是干净、规整、有代表性的输入而垃圾恰恰是最不规整的反光、遮挡、袋子半透明、油污、光线忽明忽暗。就这一条就决定了“图像处理 图像分类 垃圾分类”这个组合里图像处理不是可有可无的前戏而是决定模型上限的关卡。这个方向最适合两类人一类是要交图像处理大作业或者做毕设的学生需要的是一条能跑通、能答辩、能展示的完整链路另一类是刚入门图像分类、手上没有现成数据集的开发者想用垃圾分类这个场景练手从数据清洗到部署验证把所有环节走一遍。文章会用一套可复现的方案讲清楚每个环节为什么这么做、参数怎么调、坑在哪让新手能跟着做熟手能直接借框架改。2. 垃圾图像数据从哪里来收集、清洗与类目平衡2.1 数据集比模型更可能成为项目的天花板做垃圾分类的第一件事不是选模型是搞清楚手上有什么数据。常见做法是拿公开数据集起步比如华为的垃圾分类数据集、TrashNet、或者一些机构开源的街拍垃圾数据。公开数据集的优点是标签规范、类别成体系适合先跑通流程缺点是场景相对单一多是白底拍摄或固定角度到了你的摄像头底下分布就变了。另一个路线是自己拍。这个看起来土但实际效果往往比直接下数据集好因为你拍的图片才代表真实部署时的光照、角度和遮挡情况。我自己一般会把两种数据混在一起用公开数据做主体自己补充场景数据比例大概在 7:3 左右。数据量上单类别别低于 500 张否则分类器容易把个别样本的噪声当特征。2.2 清洗阶段必须做的三件事数据收集回来不是直接训先过一遍清洗。第一是去重用感知哈希或者直接用 imagehash 库跑一遍重复样本会放大模型对特定背景的记忆测试集上虚高。第二是去错标签人眼过一遍必不可少特别容易出错的是“可回收物”和“有害垃圾”里的小件物品比如电池和充电线很容易混。第三是检查坏图有些下载来的图是损坏的、全黑的、或者带着大面积水印这类图留着只会教模型学坏。import os from PIL import Image import imagehash hash_dict {} dup_list [] sample_dir ./garbage_raw for fname in os.listdir(sample_dir): path os.path.join(sample_dir, fname) if fname.lower().endswith((.jpg, .jpeg, .png)): try: img Image.open(path) h imagehash.phash(img) for existing_h, existing_name in hash_dict.items(): if h - existing_h 5: dup_list.append(fname) break else: hash_dict[h] fname except Exception as e: print(坏图跳过:, fname, e) dup_list.append(fname) print(重复或损坏样本数量:, len(dup_list))代码里用感知哈希的汉明距离判断相似度阈值取 5小于 5 视为近似重复这个值在大多数垃圾图片上表现合理背景相似的会让距离偏小可以适当放宽到 8 再看看。坏图用异常捕获直接筛掉不要手工去遍历。做完这一步数据集规模通常会缩水 10% 到 20%这是正常的。2.3 类目平衡与一次常见的翻车现场清洗完要检查类别分布。最典型的翻车是数据集中“纸板箱”有 3000 张“塑料瓶”有 300 张模型训出来纸板箱的准确率很高但塑料瓶直接崩了因为网络把大部分容量拿去拟合多类样本的特征了。解决办法有两条路欠采样大类别或者复制增广小类别。我一般会先做复制增广也就是对小类别做随机裁剪、翻转、调亮度的组合用五到十倍的量补上去效果比手动删大类别数据稳。类别本身的定义也要提前统一。垃圾分类按国内标准通常分四类可回收物、有害垃圾、厨余垃圾、其他垃圾按细类可以分到几十上百种。项目起步先定在 10 到 15 个细类比如塑料瓶、玻璃瓶、易拉罐、纸箱、电池、灯泡、果皮、剩菜等。这类目定义一定要在最开始写死不然训到一半发现数据组织和标签不一致后面的工作全白做。3. 图像处理在前段能做什么背景分离、形态学与颜色线索3.1 用颜色空间和背景分离降低分类难度图像分类模型并不“看”物体它是在找像素统计规律。垃圾图片里背景占了很大面积尤其是街拍或摄像头拍到的画面桌面、地面、手掌都在里面。如果直接扔给分类网络网络会拿大量参数去拟合背景差异导致换一个环境就没法用。所以第一步图像处理通常是背景分离。常见做法是用 OpenCV 做颜色空间转换后提取目标掩膜。垃圾里塑料瓶、易拉罐、玻璃瓶各有明显颜色特征先在 HSV 空间里做颜色阈值分离然后配合轮廓分析拿到目标区域。HSV 比 RGB 更适合做这件事因为 H 通道对光照变化相对不那么敏感。import cv2 import numpy as np img cv2.imread(sample_bottle.jpg) hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 蓝色塑料瓶的HSV范围, 根据实际瓶子颜色需要调整 lower_blue np.array([100, 80, 80]) upper_blue np.array([130, 255, 255]) mask cv2.inRange(hsv, lower_blue, upper_blue) kernel cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations2) contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if contours: c max(contours, keycv2.contourArea) x, y, w, h cv2.boundingRect(c) roi img[y:yh, x:xw] cv2.imwrite(bottle_cropped.jpg, roi)这段代码做了三件事颜色阈值分割、闭运算填充目标内部孔洞、按最大轮廓裁剪。闭运算的核大小取 5x5、迭代两次对瓶身上的高光区域和标签文字产生的细小断裂有很好的修复效果。如果你检测的是易拉罐或者透明玻璃瓶同样的逻辑适用只要把 HSV 范围调一下就行。透明物体的颜色分割效果很差这种场景要用轮廓检测和纹理特征去补。3.2 形态学操作里容易被忽略的边界问题OpenCV 的膨胀、腐蚀、开运算、闭运算是垃圾图像处理里用得最多的四个操作。膨胀填小洞腐蚀去毛刺开运算先腐蚀再膨胀用于去噪闭运算反过来用于填充。但在垃圾分类场景里形态学操作有个隐藏边界它改变的是目标边界的位置而不是单纯“修图”。如果你把形态学处理后的掩膜拿去算面积、周长、形状特征那么核的大小直接影响特征数值。核取 3x3 和 7x7同一张图算出来的圆形度差别能到 10%这会连带影响后续分类结果。所以形态学参数一旦定下来整个数据集要用同一套不要在训练数据和测试数据上用不同的核。另一个坑是膨胀和腐蚀在二值掩膜上操作如果掩膜本身边界是渐变的比如边缘有很多半透明像素直接二值化会引入锯齿。先做高斯模糊再做二值化能缓解高斯核一般取 5x5 就行。3.3 形态学之外值得保留的手工特征把目标和背景分离之后垃圾类别之间的区别并不全靠深度网络去学有一些手工特征在传统图像处理阶段就能拿来用而且能直接辅助分类。形状特征是其中一类易拉罐的宽高比接近 1:2塑料瓶细长纸箱多接近矩形。颜色特征也常能用上金属表面反光厉害、玻璃透光、纸类表面粗糙。这些特征在深度学习之前是主流现在则可以和分类网络的结果做后融合。我一般会把宽高比、面积占比、HSV 均值做成一个低维向量和网络的 softmax 输出拼起来过一个线性分类器能小幅提升结果尤其是对易拉罐这种形状特征极强的类别。4. 图像分类模型选型与训练轻量网络到 Transformer 怎么取舍4.1 模型选型先看部署条件再看精度垃圾分类项目的模型选型和你有多少算力、部署在什么设备上直接相关。如果是实验室里跑跑不限显存和推理速度ResNet50 起步、EfficientNet 或 ConvNeXt 都可以如果是要部署到树莓派、Jetson Nano 或者手机端轻量网络 MobileNetV3 和 ShuffleNetV2 才是现实选择。近两年最新的图像分类模型方向里MobileViT 和 EdgeNeXt 这类混合结构在精度和速度之间折中得比较好适合边缘设备。从分类任务本身来说垃圾分类的类别数量有限一般不超过几十类底层视觉特征差距相对明显——瓶子和电池、纸箱和果皮在颜色纹理上有本质差别属于“容易分”的任务。这类任务没必要上特别深的网络泛化风险反而更大。一个常见做法是先用 ResNet18 或 MobileNetV3 跑通流程获得一个基准准确率再视情况换成更大的模型去刷分。ResNet34 和 ResNet50 的差距在垃圾分类上通常只有 1% 到 2%但推理时间和显存占用却上涨将近一倍。4.2 用 ImageNet 预训练做迁移学习冻结策略与参数建议迁移学习是必须的不是可选项。垃圾分类的公开数据集规模撑死几万张和 ImageNet 的百万级数据完全不在一个量级。直接随机初始化训练模型学不到足够通用的边缘和纹理特征准确率会低五到十个点。实操时用 PyTorch 里带预训练权重的 ResNet18替换最后的全连接层然后先冻结前面的 BN 层和卷积层只训练最后的分类头和新增层。import torchvision.models as models import torch.nn as nn model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) for name, param in model.named_parameters(): if name.startswith(layer4) or name.startswith(fc): param.requires_grad True else: param.requires_grad False num_ftrs model.fc.in_features model.fc nn.Sequential( nn.Dropout(0.3), nn.Linear(num_ftrs, 12) )这里只解冻了 layer4 和最后的 fc 层因为垃圾图片与 ImageNet 场景差异不太大冻住前几层让它们保留通用边缘纹理特征能有效防止小数据过拟合。Dropout 取值 0.3 在 12 分类、样本量几千张的场景下是合理起点如果发现训练集准确率远高于验证集往 0.5 调。训练时优化器用 Adam学习率初始 1e-4批次大小 32 或 48。第一个 epoch 结束后如果不收敛先把学习率降一个数量级常见问题是预训练模型的学习率不需要太大。4.3 数据增广策略垃圾分类里真正有效的变换数据增广是垃圾分类训练里最值得花时间的地方。垃圾图的特殊性在于同一瓶矿泉水在室内光、室外强光、夜晚路灯下的表现差别很大瓶子是立着还是倒着背景是桌面还是草地都影响模型判断。所以增广策略要围绕“场景鲁棒性”设计。from torchvision import transforms train_transform transforms.Compose([ transforms.RandomResizedCrop(224, scale(0.6, 1.0)), transforms.RandomHorizontalFlip(p0.5), transforms.ColorJitter(brightness0.4, contrast0.4, saturation0.4, hue0.1), transforms.RandomRotation(degrees15), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])RandomResizedCrop 的 scale 下限设成 0.6垃圾目标经常在画面里占比不大缩得太狠会丢掉关键纹理。ColorJitter 里的 brightness 和 contrast 都取 0.4实际效果是模拟不同光线环境这对垃圾分类太重要了——你测试时很可能是在完全不同的灯光下拍的照片。RandomRotation 只取 15 度垃圾图片的方向是随机的但转太多会把瓶子竖着的长条形变成斜的反而增加学习难度。如果不确定哪个变换有效就在验证集上多测几轮不需要一次全上。4.4 Transformer 图像分类模型在垃圾场景下的真实表现最新的图像分类模型方向里Vision Transformer 和它的变体是绕不开的。ViT 在小数据集上表现往往不如同规模 CNN因为它缺乏 CNN 自带的空间归纳偏置需要更多数据去学。垃圾分类数据量不多直接用 ViT 容易过拟合。但用 Swin Transformer 或经过蒸馏的轻量 Transformer 变体效果会好很多尤其是 Swin 的窗口注意力机制让它在中等规模数据上能打。我的建议是如果数据量在单类 1000 张以上可以尝试 Swin Tiny数据量不够还是 MobileNetV3 或 ResNet18 稳。另一个可行的路是用 Transformer 做教师模型蒸馏给 CNN 学生模型这样既拿到了 Transformer 的表达能力又保留了 CNN 的推理速度和部署友好性。5. 垃圾分类避坑指南5 个高频踩坑现场5.1 反光导致类别整体误判现象塑料瓶和玻璃瓶在强光下大量被识别成金属罐准确率下降明显。原因塑料和玻璃表面的镜面反光在 RGB 图像里形成高亮区域和金属表面的视觉特征高度相似模型学到的是亮斑而不是材质。这是垃圾分类里最经典的视觉陷阱。解决训练数据里加入不同程度的反光增广用高斯模糊降低镜面高光的锐度或者在高光区域加入随机噪声。另外在前处理阶段可以对高光区域做掩膜剔除把反光部分直接置灰再送进模型能显著减少误判。5.2 袋装垃圾拆不开现象一个黑色垃圾袋里装着厨余垃圾模型直接判成其他垃圾哪怕里面透出果皮颜色。原因半透明或不透明的袋子完全遮挡了内部物体的视觉特征分类器只能看到袋子的颜色和纹理。这是物理遮挡问题不是模型问题。解决采集数据时专门增加“袋装垃圾”一类把这种状态当作独立的类别去训练而不是期望模型穿透袋子识别内容。在真实场景中袋装垃圾本身具有独立的管理意义单独分类反而更合理。5.3 训练集准确率奇高、验证集崩溃现象训练集准确率 99%验证集只有 70%差距随时间越来越大。原因标准过拟合。垃圾分类数据集背景多样但数量不多网络把背景特征也学进去了尤其是你自己拍摄的那部分数据如果背景单一会加速过拟合。解决先检查增广策略有没有在训练集和验证集上保持一致RandomResizedCrop 这类增广不要应用到验证集。如果增广已经做了还是过拟合明显加大 Dropout、减少解冻层数、把全连接层改成更小的维度。最后一个有效手段是收集更多带不同背景的数据这是根治法。5.4 类别不平衡导致评估指标虚高现象总准确率有 93%但打开混淆矩阵发现占样本量 50% 的“其他垃圾”类别全对而“有害垃圾”类别全错。原因用 accuracy 做主要评估指标在大类别主导的数据集上会掩盖小类别的高错误率。垃圾分类里“有害垃圾”样本量天然偏少但错误代价高。解决评估时同时打印混淆矩阵和每类别召回率重点盯样本量最小的那三类。训练时用类别权重或者 Focal Loss让模型更重视小类别。5.5 部署环境与原训练环境的光照分布差异现象实验阶段测试集准确率 95%部署到垃圾桶旁边的摄像头之后降到 80%而且集中在傍晚时段恶化。原因训练数据中缺少部署场景的光照分布。这属于典型的域偏移问题——数据分布不一致而不是算法坏了。解决部署前采集目标场景半小时到一小时的真实视频流数据抽帧后加入训练集做 finetune。这是最粗暴也最有效的办法。如果连这些数据都拿不到至少在前处理里做一次白平衡归一化消除光源色温的差异。6. 把方案变成可演示的系统模型导出与视频流验证走到这一步模型训练和调参已经跑通剩下的是把模型变成能给别人演示、能接入摄像头实时工作的系统。这里分享一个我常用来做视频流验证的最小方案。先不要把模型部署到嵌入式设备上先在电脑上用摄像头采集一段视频流按帧推理看模型在连续画面上的表现。这一步能暴露出很多静态测试发现不了的问题比如画面抖动、误检闪烁、目标太小导致漏检。import cv2 import torch from torchvision import transforms from PIL import Image device torch.device(cuda if torch.cuda.is_available() else cpu) model torch.load(garbage_model.pth, map_locationdevice) model.eval() cap cv2.VideoCapture(0) preprocess transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) while True: ret, frame cap.read() if not ret: break rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) pil_img Image.fromarray(rgb) input_tensor preprocess(pil_img).unsqueeze(0).to(device) with torch.no_grad(): output model(input_tensor) pred output.argmax(dim1).item() cv2.putText(frame, fclass: {pred}, (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.imshow(garbage classification, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这套逻辑的核心价值在于你可以肉眼看到模型在真实画面上的失败模式把那些连续判错或者闪烁的片段录下来回去和训练集对比找出是动作模糊的问题、反光的问题还是背景干扰的问题。我自己的习惯是给模型喂一大段 20 分钟的视频然后只统计连续 30 帧以上的错误段用这个方法找出来的问题比盯着一堆验证集图片找要快得多。另外一个值得在演示阶段用的技巧是给模型加一个置信度阈值低于阈值的画面输出“未识别请靠近拍摄”。模型在模糊或者是异常角度下的 softmax 输出通常比较分散降不下来的置信度是一个信号做人机交互时应该把这一步当作拒绝选项而不是硬分类。带 10 到 15 个细类做垃圾分类方向从数据清洗到视频流演示一整套流程走下来并不算长核心精力大多花在数据整理和场景适配这些“看不见”的环节上。我踩过内存不足的坑也踩过类别不平衡的坑给到的这些参数和调整逻辑都是从这些翻车现场攒出来的希望你用的时候能少走几步弯路。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询