news 2026/9/28 6:17:27

基于CNN的垃圾识别分类系统:Python源码与数据集实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于CNN的垃圾识别分类系统:Python源码与数据集实战

简介:这份资源是面向高校学生与深度学习入门者的垃圾识别分类课程设计完整项目,基于卷积神经网络实现图像分类,可直接用于课程设计或期末大作业,无需二次修改即可运行。压缩包共约2000个文件,以1196张jpg与789张jpeg图像构成训练与测试数据集,另含13个Python源码文件、1个json配置与1个md说明文档,整体约564.69MB,覆盖数据读取、模型搭建、训练与推理等环节。目前已有263人学习下载,说明该方案在同类课设中具备一定参考价值。项目已通过导师指导并获得97分高分,读者可据此掌握CNN图像分类的完整流程,包括数据集组织方式、模型结构设计、训练参数配置与结果评估思路,同时可借助现成权重与脚本快速复现实验,节省从零搭建的时间,适合作为课设模板或深度学习实践练手项目。

1. 垃圾识别分类系统:从一张照片到四个桶,CNN 到底替你做了哪些判断

你拍一张外卖盒的照片,系统告诉你「其他垃圾」;拍一节废电池,它告诉你「有害垃圾」。这件事听起来像是个简单的四分类问题,但真正动手做过的人都知道,难点从来不在模型结构本身,而在数据质量、类别边界和部署时的输入一致性。基于深度学习卷积神经网络实现垃圾识别分类系统,本质上是把「图像分类」这套成熟范式,套进一个类别定义模糊、样本分布极不均衡的真实场景里。Python 源码加数据集的组合,意味着你拿到的不只是一份作业,而是一条从数据预处理、模型搭建、训练调参到推理验证的完整链路。适合谁?正在做课程设计的学生、想跑通第一个 CNN 落地项目的初学者,以及需要一套可复现基线来改造成自己分类任务的工程师。接下来我会按「先立住原理、再动手复现、最后讲坑」的顺序,把这条链路拆开讲清楚。

2. 卷积神经网络凭什么能认出垃圾:结构、选型与数据集的真实边界

2.1 从像素到类别:CNN 在这套系统里到底学了什么

一张垃圾图片送进网络之前,只是一个三维数组,比如 224×224×3。卷积层做的事,是用一组可学习的卷积核在图像上滑动,提取边缘、纹理、局部形状这些低级特征,再通过堆叠的层逐步组合成「瓶盖的圆环」「纸板的褶皱」这类高级语义。池化层负责降维,把特征图缩小但保留关键响应。最后全连接层把特征映射到四个类别的概率分布上,取最大值就是预测结果。

这套机制之所以适合垃圾识别,是因为垃圾类别的区分往往依赖局部纹理和形状,而不是全局布局。一个塑料瓶和一个玻璃瓶,颜色可能接近,但反光纹理和边缘锐度不同,卷积核恰好对这类局部差异敏感。相比之下,如果把图片拉平直接送进全连接网络,参数量会爆炸,而且丢失空间信息,效果通常差很多。

需要提前说清楚一个边界:CNN 不是万能的。如果两类垃圾在视觉上几乎无法区分,比如「污染过的纸巾」到底算其他垃圾还是厨余垃圾,模型只能学到训练集里的标注倾向,换一个城市的标准就可能翻车。所以这套系统的上限,很大程度上由数据集标注的一致性决定,而不是由网络深度决定。

2.2 选 ResNet 还是自己搭:课程设计场景下的模型取舍

课程设计常见的做法有两种:一是自己搭一个 4 到 6 层的简单 CNN,二是用预训练模型做迁移学习。两者没有绝对优劣,取决于你的目标和算力。

自己搭的优点是结构透明,每一层为什么这么设计都能讲清楚,适合答辩时展开。缺点是参数量少、特征提取能力有限,如果数据集只有几千张,准确率可能卡在 70% 上下。常见做法是堆三层卷积加两层全连接,每层后接 ReLU 和最大池化,最后用 Softmax 输出四类。

迁移学习的优点是收敛快、准确率高,通常拿 ResNet18 或 MobileNetV2 在 ImageNet 上的预训练权重,把最后一层全连接改成四输出,冻结前面的层先训几轮,再解冻微调。缺点是如果答辩老师追问「这个网络每一层在干什么」,你需要额外准备。我一般会建议:如果时间紧、想先把系统跑通,用迁移学习;如果重点是展示对 CNN 原理的理解,自己搭。

方案参数量级训练数据需求典型准确率适用场景
自建 4 层 CNN几十万3000+70%~85%课程设计、原理展示
ResNet18 迁移千万级1000+85%~95%快速落地、追求精度
MobileNetV2 迁移百万级1000+85%~93%后续要部署到边缘设备

2.3 数据集长什么样:四个类别、不均衡分布与清洗要点

标题里提到含数据集,这类课程设计数据集通常是按文件夹组织的图像分类格式,每个类别一个子目录。四个类别一般对应可回收物、有害垃圾、厨余垃圾、其他垃圾。实际拿到手后,第一件事不是直接训练,而是统计每个类别的样本数。

常见情况是「其他垃圾」样本最多,「有害垃圾」样本最少,比例可能达到 5:1 甚至更高。这种不均衡会让模型倾向于预测多数类,导致有害垃圾的召回率很低。处理方式有三种:一是对少数类做数据增强,比如随机翻转、旋转、颜色抖动;二是在损失函数里给少数类更高权重;三是过采样少数类。我一般先用增强,简单且不容易引入偏差。

清洗环节要重点检查三类问题:重复图片、标注错误、尺寸异常。重复图片会让训练集和验证集产生泄漏,验证准确率虚高。标注错误在垃圾数据集里很常见,比如把「一次性餐盒」标成可回收,实际应该看材质。尺寸异常指个别图片分辨率远低于其他图片,送进网络前统一 resize 到 224×224 即可,但要注意保持宽高比,避免拉伸变形。

提示:在划分训练集和验证集之前,先按图片内容去重,否则同一张图可能同时出现在两边,验证结果没有参考价值。

3. 用 Python 跑通训练全流程:从目录结构到第一个可用的模型文件

3.1 环境准备与目录结构:把源码和数据放到正确的位置

拿到压缩包后,先解压,观察目录结构。典型布局是源码文件夹、数据集文件夹、模型保存文件夹并列。Python 环境建议用 3.8 到 3.10,太新的版本有时会和旧版 PyTorch 冲突。依赖主要是 torch、torchvision、numpy、Pillow、matplotlib。

# 创建虚拟环境,避免污染系统 Python python -m venv venv # 激活环境,Windows 用 venv\Scripts\activate source venv/bin/activate # 安装核心依赖,torch 版本按自己显卡情况调整 pip install torch torchvision numpy pillow matplotlib

这段命令做三件事:建独立环境、激活、装依赖。参数说明:python -m venv venv里的第二个 venv 是环境目录名,可以改。source在 Windows 下换成对应激活脚本。torch 如果要用 GPU,需要去官网查对应 CUDA 版本的安装命令,不要直接 pip install torch,否则可能装到 CPU 版本。

目录结构建议整理成下面这样,后面代码里的路径才不用反复改:

project/ data/ train/ recyclable/ hazardous/ kitchen/ other/ val/ 同上四个子目录 src/ train.py predict.py dataset.py models/ best_model.pth

3.2 数据加载与增强:用 DataLoader 把图片变成批次张量

数据加载的核心是自定义 Dataset 或直接用 ImageFolder。ImageFolder 要求每个类别一个子目录,正好匹配上面的结构。训练集做增强,验证集只做 resize 和归一化,这一点不能搞反。

import torch from torchvision import datasets, transforms from torch.utils.data import DataLoader # 训练集增强:随机裁剪、翻转、颜色抖动,提升泛化 train_tf = transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomCrop(224), transforms.RandomHorizontalFlip(), transforms.ColorJitter(brightness=0.2, contrast=0.2), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) # 验证集只做确定性变换,保证评估可复现 val_tf = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) train_ds = datasets.ImageFolder('data/train', transform=train_tf) val_ds = datasets.ImageFolder('data/val', transform=val_tf) train_loader = DataLoader(train_ds, batch_size=32, shuffle=True, num_workers=2) val_loader = DataLoader(val_ds, batch_size=32, shuffle=False, num_workers=2) print(train_ds.classes) # 打印类别顺序,后面预测要用

逻辑说明:训练集先 resize 到 256 再随机裁到 224,是为了让每次裁到的区域略有不同,相当于免费扩充数据。Normalize 里的均值方差是 ImageNet 的统计值,用预训练模型时必须一致,自己搭网络也建议沿用。参数说明:batch_size 设 32 是显存和收敛速度的折中,显存不够就降到 16 或 8。num_workers 在 Windows 下有时会报错,改成 0 即可。train_ds.classes打印出来的顺序就是模型输出索引对应的类别,务必记下来。

3.3 搭建与训练模型:损失函数、优化器和训练循环的关键参数

如果自己搭网络,结构可以这样写。三层卷积,每层后接 ReLU 和池化,最后接全连接。

import torch.nn as nn class SimpleCNN(nn.Module): def __init__(self, num_classes=4): super().__init__() self.features = nn.Sequential( nn.Conv2d(3, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(32, 64, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(64, 128, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2), ) self.classifier = nn.Sequential( nn.Flatten(), nn.Linear(128 * 28 * 28, 256), nn.ReLU(), nn.Dropout(0.5), nn.Linear(256, num_classes) ) def forward(self, x): return self.classifier(self.features(x))

逻辑说明:每次池化把特征图边长减半,224 经过三次池化变成 28,通道数从 3 升到 128。Dropout 放在全连接前,防止过拟合。参数说明:卷积核统一用 3×3,padding=1 保证卷积后尺寸不变。如果输入尺寸改了,128 * 28 * 28这个数要跟着改,否则会报维度不匹配。

训练循环里几个关键选择:损失函数用 CrossEntropyLoss,优化器用 Adam,学习率初始 1e-3。如果类别不均衡,给 CrossEntropyLoss 传 weight 参数。

import torch.optim as optim device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = SimpleCNN(num_classes=4).to(device) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=1e-3) for epoch in range(20): model.train() for imgs, labels in train_loader: imgs, labels = imgs.to(device), labels.to(device) optimizer.zero_grad() outputs = model(imgs) loss = criterion(outputs, labels) loss.backward() optimizer.step() # 每个 epoch 后在验证集上评估 model.eval() correct, total = 0, 0 with torch.no_grad(): for imgs, labels in val_loader: imgs, labels = imgs.to(device), labels.to(device) preds = model(imgs).argmax(dim=1) correct += (preds == labels).sum().item() total += labels.size(0) print(f'epoch {epoch+1}, val acc {correct/total:.4f}')

逻辑说明:optimizer.zero_grad()清空上一轮梯度,不能省。loss.backward()反向传播,optimizer.step()更新参数。验证阶段用torch.no_grad()关闭梯度计算,省显存。参数说明:epoch 数 20 是起点,如果验证准确率还在涨就继续加。学习率如果 loss 震荡明显,降到 1e-4。训练完把model.state_dict()保存到 models 目录,后面预测直接加载。

4. 推理、评估与踩坑记录:模型训完之后才是真正的问题开始

4.1 单张图片预测:把模型文件变成可调用的接口

训练保存的模型文件只包含权重,预测时需要重新构建网络结构再加载权重。这一步经常有人忘记,直接 load 会报错。

from PIL import Image # 重建结构并加载权重 model = SimpleCNN(num_classes=4).to(device) model.load_state_dict(torch.load('models/best_model.pth', map_location=device)) model.eval() def predict(img_path): img = Image.open(img_path).convert('RGB') tensor = val_tf(img).unsqueeze(0).to(device) # 增加 batch 维度 with torch.no_grad(): prob = torch.softmax(model(tensor), dim=1) idx = prob.argmax().item() return train_ds.classes[idx], prob[0][idx].item() print(predict('test.jpg'))

逻辑说明:unsqueeze(0)把单张图的维度从 C×H×W 变成 1×C×H×W,因为网络要求有 batch 维度。softmax把输出转成概率,方便看置信度。参数说明:map_location在只有 CPU 的机器上加载 GPU 训练的权重时必须加。val_tf必须和验证集一致,不能用训练集的增强变换,否则结果不稳定。

4.2 评估指标不只看准确率:混淆矩阵暴露的真实问题

准确率在类别不均衡时会骗人。如果其他垃圾占 60%,模型全预测其他垃圾也有 60% 准确率。所以要打印混淆矩阵,看每个类别的召回率。

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

逻辑说明:把验证集所有预测收集起来,一次性算矩阵和报告。classification_report会给出每个类别的 precision、recall、f1。重点看有害垃圾的 recall,如果低于 0.6,说明模型没学好这个少数类,需要回头做增强或加权。参数说明:target_names传入类别名,报告可读性更好。

4.3 避坑记录:五个让课程设计翻车的典型问题

现象一:验证准确率 99%,换一张新图就乱猜。原因通常是训练集和验证集划分时没有去重,同一张图两边都有,模型只是记住了。解决方式是先对全部图片做哈希去重,再按 8:2 划分,确保验证集图片在训练集里没出现过。

现象二:训练 loss 一直不降,卡在 1.38 左右。这是四分类的随机猜测水平,说明模型没学到东西。常见原因是学习率太大导致梯度爆炸,或者数据归一化没做。检查 Normalize 是否加在 ToTensor 之后,学习率降到 1e-4 再试。

现象三:预测时所有图片都输出同一个类别。多半是类别不均衡加上没做处理。先统计训练集各类数量,如果比例超过 3:1,在 CrossEntropyLoss 里加 weight,或者对少数类做 oversample。另一个可能是加载权重时类别顺序和训练时不一致,检查train_ds.classes是否和训练时打印的一样。

现象四:GPU 显存不够,batch_size 降到 1 还是报错。检查是不是在验证阶段忘了加torch.no_grad(),导致梯度图一直累积。另外图片 resize 尺寸如果设成 512,显存占用会翻好几倍,课程设计用 224 足够。

现象五:换一台机器跑源码,报路径找不到。源码里如果写了绝对路径,换机器必挂。把所有路径改成相对路径,或者在代码开头用os.path.dirname(__file__)拼出项目根目录。数据集的目录名如果有中文或空格,也可能导致 ImageFolder 读取异常,建议改成英文。

注意:模型文件.pth只存权重,不存网络结构。换一份源码加载时,必须保证网络定义完全一致,否则load_state_dict会报 key 不匹配。

5. 把准确率再推一截:迁移学习微调与置信度阈值的实战技巧

自己搭的 CNN 跑通之后,如果准确率卡在 80% 左右上不去,最划算的升级路径是换成预训练模型做微调。做法不复杂:把 ResNet18 的最后一层全连接换成四输出,先冻结卷积层只训全连接,几轮之后再解冻最后两个卷积块一起微调。学习率要调小,微调阶段用 1e-4,否则预训练学到的特征会被冲掉。

from torchvision import models model = models.resnet18(pretrained=True) for param in model.parameters(): param.requires_grad = False # 先冻结全部 model.fc = nn.Linear(model.fc.in_features, 4) # 替换分类头 # 第一阶段只训 fc,学习率 1e-3 optimizer = optim.Adam(model.fc.parameters(), lr=1e-3) # 第二阶段解冻 layer4,整体微调,学习率降到 1e-4 for param in model.layer4.parameters(): param.requires_grad = True optimizer = optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=1e-4)

逻辑说明:冻结参数可以防止随机初始化的分类头在训练初期把预训练特征带偏。解冻 layer4 是因为这一层对应高级语义特征,和垃圾类别最相关。参数说明:pretrained=True会下载权重,第一次运行需要联网。如果环境不能联网,需要提前把权重文件放到 torchvision 的缓存目录。

另一个实用技巧是设置置信度阈值。四分类问题里,模型对某些模糊图片的最高概率可能只有 0.4,这时候强行输出类别不如提示「无法确定」。在预测接口里加一个判断:最高概率低于 0.6 就返回待人工确认。这个阈值可以根据验证集上的准确率-覆盖率曲线来定,一般取 0.5 到 0.7 之间。

微调策略可训练参数比例收敛轮数验证准确率参考
只训分类头约 1%5~1082%~88%
解冻 layer4约 30%10~2088%~94%
全部解冻100%20+90%~95%,但易过拟合

最后说一个我自己的习惯:每次改完超参数或数据增强策略,一定把验证集准确率、混淆矩阵和当前配置记在一个文本文件里,和模型文件放在一起。课程设计答辩时被问到「你这个 92% 是怎么来的」,能直接翻出记录,比现场回忆靠谱得多。这套垃圾识别分类系统本身不复杂,真正拉开差距的是数据清洗的细致程度和排错时的耐心。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 6:16:54

Java集成支付宝扫码支付全链路实战:从沙箱到回调验签与幂等

简介:这份资源面向需要在Java应用中接入支付宝支付能力的开发者,尤其适合电商、O2O场景下希望快速跑通扫码支付流程的中级Java工程师。项目围绕支付宝SDK展开,涵盖扫码支付、订单处理、异步回调、appid与密钥配置、前端二维码展示页面以及API…

作者头像 李华
网站建设 2026/9/28 6:16:53

S7-1200 PUT/GET通讯避坑指南:DB块配置与自动连接5大关键点

1. 为什么PUT/GET通讯总在DB块上栽跟头1.1 一个让无数工程师抓狂的现场S7-1200做PUT/GET通讯,连接组态好了,硬件也下载了,一触发读写就报错。错误代码五花八门,有时候是16#05,有时候是16#0A,有时候干脆连接…

作者头像 李华
网站建设 2026/9/28 6:16:22

陀螺匠企业助手:把战略规划从PPT变成落地执行

1. 陀螺匠企业助手:先搞懂它到底解决什么事我第一次拿到“陀螺匠企业助手”这个战略规划工具时,第一反应是这名字怎么这么像养生用品。但真把它跑完一轮,我才意识到它其实是个挺上头的管理框架:把企业战略规划这件事,从…

作者头像 李华
网站建设 2026/9/28 6:16:03

情感戏写作:如何把“信赖”从结果改写成过程

1. 这一章到底在写什么:先把信赖的层次拆清楚写“莹姐的信赖”这个章节之前,我花了整整两天时间想一个问题:信赖到底是一个结果,还是一个过程?很多人写情感戏,习惯把信赖当成一个可以瞬间达成的结果——主角…

作者头像 李华
网站建设 2026/9/28 6:15:58

智慧城市与可持续发展EI会议投稿全攻略:从选题到检索避坑指南

1. 先把这个会议标题拆开看:每个关键词都在传递信号做学术的人看到这种会议宣传,第一反应往往是既心动又警惕。心动的是"EI检索"几个字,警惕的也是这仨字。我在学术圈子里混了十几年,既投过稿也审过稿,对这种…

作者头像 李华
网站建设 2026/9/28 6:15:58

顶级CTO不写代码:如何通过决策与评审决定代码命运

"顶级 CTO 从不写代码"这句话,很多人第一眼看到会觉得反常识:CTO不是技术最高负责人吗?不写代码,技术团队谁带?代码质量谁把关?我在技术管理这条路上走了十多年,见过太多从一线工程师…

作者头像 李华