拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于CNN的垃圾识别分类系统源码解析:从数据集到模型部署

基于CNN的垃圾识别分类系统源码解析:从数据集到模型部署 简介这份资源是面向高校学生与深度学习入门者的垃圾识别分类课程设计完整项目基于卷积神经网络实现图像分类可直接用于期末大作业或课程设计答辩。压缩包共约2000个文件以1196张jpg与789张jpeg图像构成训练与测试数据集另有13个Python源码文件负责模型搭建、训练与推理配合json配置与md说明文档整体约564.69MB目录结构清晰便于按模块查阅与复现。项目已获导师指导并通过据描述取得97分高分下载后无需修改即可运行省去自行收集数据与调试环境的时间。内容预览可见电池、塑料瓶等多类别样本覆盖数据标注、模型训练到分类预测的完整流程适合希望快速掌握CNN图像分类实践、完成课程任务或作为入门练手项目的读者参考。目前已有263人学习关注。1. 从一张img_电池_452.jpeg说起这套 CNN 垃圾识别源码到底能跑出什么你拿到手的这个压缩包解压后第一眼看到的不是train.py而是一堆命名规整的图片img_电池_452.jpeg、img_塑料瓶_244.jpeg、img_塑料瓶_67.jpeg、img_电池_541.jpeg……文件名里直接带了类别标签和序号。这不是随手拍的数据集而是已经按「电池 / 塑料瓶」等类别做过粗分拣的原始素材文件名本身就是弱监督信号。整套资源是一份基于卷积神经网络的垃圾识别分类系统 Python 源码附带数据集和训练好的模型定位很明确课程设计、期末大作业下载解压后能直接跑通推理也能自己重训。它解决的核心问题不是「从零教你 CNN」而是把数据组织、模型定义、训练循环、推理脚本、模型保存这一整条链路打包好让你不用在环境配置和目录结构上反复翻车。适合两类人一类是赶课程设计 deadline、需要一份结构完整且能演示的项目另一类是刚学完卷积神经网络原理想找一个真实小数据集把Conv2d、MaxPool2d、CrossEntropyLoss这些概念落到代码里的人。下面我按「资源结构 → 数据管线 → 模型与训练 → 推理与验证 → 避坑 → 进阶」的顺序拆每一步都给出可抄的参数和命令。2. 拆包看结构数据集、模型文件与训练脚本怎么摆2.1 先确认目录布局别急着装环境拿到压缩包后我一般先不碰 Python而是用tree或文件管理器把层级看清楚。这类课程设计项目的典型结构是一个dataset目录按类别分子文件夹一个model目录放.pth权重根目录下若干.py脚本。图片文件名里的img_电池_452.jpeg这种格式说明数据在采集阶段就按类别命名但真正喂给网络时靠的是它所在的父文件夹名而不是文件名本身。# 查看解压后的目录层级重点看 dataset 和 model 两个目录 unzip 垃圾识别分类系统.zip -d trash_cnn cd trash_cnn find . -maxdepth 3 -type d | sort # 统计每个类别下的图片数量确认有没有空文件夹或数量严重不均 for d in dataset/*/; do echo -n $d: ; ls $d | wc -l; done逻辑说明find -maxdepth 3只展开三层避免数据集图片太多刷屏第二个循环按类别统计数量是为了提前发现类别不平衡。参数上maxdepth根据你的实际层级调整如果类别文件夹在dataset/train/下就改成 4。这一步不做后面训练时 loss 不下降你都不知道是模型问题还是某个类别只有三张图。2.2 数据集划分与文件名标签的取舍文件名里的中文类别电池、塑料瓶在 Windows 下没问题但跨平台传到 Linux 或某些 Python 库读取时中文路径是高频翻车点。常见做法是保留原始图片但在生成训练列表时用父目录名做标签而不是解析文件名。这样即使文件名编码乱了只要文件夹名是英文或拼音管线就稳。目录/文件作用是否可改dataset/电池/电池类原始图片可增删需保持类别名一致dataset/塑料瓶/塑料瓶类原始图片同上model/best.pth训练好的权重可替换需匹配网络结构train.py训练入口可调超参predict.py单张推理改图片路径即可提示如果解压后类别文件夹是中文名先别重命名跑一遍python -c import os; print(os.listdir(dataset))确认 Python 能正常读出再决定是否改成拼音。3. 数据管线与预处理把img_电池_452.jpeg变成张量3.1 用ImageFolder还是手写Dataset这类项目数据组织成「一个类别一个文件夹」最省事的是torchvision.datasets.ImageFolder它会自动按文件夹名生成class_to_idx。但课程设计里经常需要自定义增强或处理文件名标签所以源码里也可能手写Dataset。两种都能用区别在于ImageFolder要求目录结构严格手写Dataset灵活但容易在__getitem__里写错返回顺序。import os from PIL import Image from torch.utils.data import Dataset, DataLoader from torchvision import transforms class TrashDataset(Dataset): def __init__(self, root, transformNone): self.samples [] self.transform transform # 遍历每个类别文件夹用文件夹名作为标签 self.classes sorted(os.listdir(root)) self.class_to_idx {c: i for i, c in enumerate(self.classes)} for c in self.classes: c_dir os.path.join(root, c) if not os.path.isdir(c_dir): continue for fname in os.listdir(c_dir): if fname.lower().endswith((.jpg, .jpeg, .png)): self.samples.append((os.path.join(c_dir, fname), self.class_to_idx[c])) def __len__(self): return len(self.samples) def __getitem__(self, idx): path, label self.samples[idx] img Image.open(path).convert(RGB) # 统一转 RGB防止灰度图报错 if self.transform: img self.transform(img) return img, label # 训练增强随机裁剪翻转验证只做缩放和归一化 train_tf transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.RandomRotation(10), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) val_tf transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])逻辑说明__getitem__里convert(RGB)是关键数据集里如果有单通道图或带 alpha 通道的 PNG不转会在ToTensor后维度对不上。Normalize用的均值方差是 ImageNet 统计值因为后面大概率用预训练权重保持一致能少调一个参数。Resize((224, 224))对应 ResNet 系列输入如果你换自定义小网络可以降到 64 或 128但别低于 32否则池化几次特征图就没了。3.2DataLoader的 batch 与 shuffle 怎么定train_ds TrashDataset(dataset/train, transformtrain_tf) val_ds TrashDataset(dataset/val, transformval_tf) train_loader DataLoader(train_ds, batch_size32, shuffleTrue, num_workers2) val_loader DataLoader(val_ds, batch_size32, shuffleFalse, num_workers2)参数说明batch_size32是课程设计里比较稳的值显存不够就降到 16 或 8shuffleTrue只在训练集开验证集必须 False否则评估指标会随顺序波动num_workers在 Windows 下有时会卡死设 0 最稳Linux 下可以设 2 到 4。如果数据集图片总数只有几百张batch_size别超过 64否则一个 epoch 只有几步loss 曲线会很难看。4. 卷积网络搭起来从Conv2d到CrossEntropyLoss4.1 自定义 CNN 还是迁移学习课程设计常见两种写法一种是从头搭一个 3 到 5 层的 CNN另一种是加载resnet18预训练权重改最后一层。前者代码短、依赖少适合展示对卷积神经网络结构的理解后者精度高、收敛快适合数据量少的情况。如果数据集只有电池和塑料瓶两类且每类几百张自定义 CNN 完全够用如果类别多、图片杂迁移学习更省事。import torch.nn as nn import torchvision.models as models # 方案一自定义轻量 CNN class SimpleCNN(nn.Module): def __init__(self, num_classes2): super().__init__() self.features nn.Sequential( nn.Conv2d(3, 32, 3, padding1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(32, 64, 3, padding1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(64, 128, 3, padding1), nn.ReLU(), nn.MaxPool2d(2), ) self.classifier nn.Sequential( nn.AdaptiveAvgPool2d(1), # 自适应池化避免全连接维度算错 nn.Flatten(), nn.Linear(128, num_classes) ) def forward(self, x): return self.classifier(self.features(x)) # 方案二迁移学习改 resnet18 最后一层 def build_resnet(num_classes2): model models.resnet18(weightsmodels.ResNet18_Weights.DEFAULT) model.fc nn.Linear(model.fc.in_features, num_classes) return model逻辑说明AdaptiveAvgPool2d(1)是新手最容易忽略的一步它把任意尺寸的特征图压成 1x1这样输入图片尺寸变了也不用改全连接层。padding1配合kernel_size3保持特征图尺寸只在池化时减半。迁移学习方案里weightsDEFAULT会自动下载预训练权重如果环境不能联网需要提前把权重文件放到缓存目录否则会卡在下载。4.2 训练循环与学习率设置import torch from torch import optim device torch.device(cuda if torch.cuda.is_available() else cpu) model SimpleCNN(num_classeslen(train_ds.classes)).to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr1e-3) for epoch in range(20): model.train() total_loss 0 for imgs, labels in train_loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() out model(imgs) loss criterion(out, labels) loss.backward() optimizer.step() total_loss loss.item() print(fepoch {epoch1}, loss {total_loss/len(train_loader):.4f})参数说明lr1e-3是 Adam 的常用起点如果 loss 震荡就降到 1e-4epoch20对几百张图的小数据集通常够但要看验证集准确率是否还在涨。CrossEntropyLoss内部已经包含 softmax所以模型最后一层不要加Softmax否则等于做了两次梯度会出问题。训练时如果显存不够把batch_size减半或者把输入尺寸从 224 降到 128。5. 推理、验证与模型保存别让best.pth变成黑匣子5.1 单张图片推理脚本from PIL import Image import torch def predict(image_path, model, class_names, transform, device): model.eval() img Image.open(image_path).convert(RGB) tensor transform(img).unsqueeze(0).to(device) # 增加 batch 维度 with torch.no_grad(): out model(tensor) prob torch.softmax(out, dim1) pred prob.argmax(dim1).item() return class_names[pred], prob[0][pred].item() # 用法 model.load_state_dict(torch.load(model/best.pth, map_locationdevice)) name, score predict(img_电池_452.jpeg, model, train_ds.classes, val_tf, device) print(name, score)逻辑说明unsqueeze(0)把单张图的[C,H,W]变成[1,C,H,W]因为模型 forward 默认按 batch 处理。torch.no_grad()关闭梯度计算省显存也提速。map_locationdevice保证在 CPU 上也能加载 GPU 训练的权重这是跨设备推理的后悔药。val_tf不能带随机增强否则同一张图每次预测结果可能不同。5.2 验证集评估与混淆矩阵from sklearn.metrics import confusion_matrix, classification_report model.eval() all_preds, all_labels [], [] with torch.no_grad(): for imgs, labels in val_loader: imgs imgs.to(device) out model(imgs) preds out.argmax(dim1).cpu().numpy() all_preds.extend(preds) all_labels.extend(labels.numpy()) print(classification_report(all_labels, all_preds, target_namestrain_ds.classes)) print(confusion_matrix(all_labels, all_preds))参数说明classification_report会给出每个类别的 precision、recall、f1比只看总体准确率有用。如果某一类 recall 特别低说明该类样本被大量误判常见原因是样本太少或增强过度。混淆矩阵能直接看出「电池被认成塑料瓶」还是「塑料瓶被认成电池」对应去补数据或调权重。6. 避坑与排查这几处翻车我替你踩过了6.1 中文路径导致Image.open报错现象在 Windows 上跑得好好的换到 Linux 或打包成 exe 后Image.open抛FileNotFoundError或乱码。原因文件系统编码不一致中文文件夹名在某些 Python 版本下解析失败。解决把类别文件夹改成拼音或英文或者在代码里用os.fsencode/os.fsdecode包一层最稳的是统一改成英文目录名。6.2 训练 loss 不降准确率卡在 50%现象二分类任务loss 一直在 0.69 附近准确率等于瞎猜。原因标签和输出维度对不上或者CrossEntropyLoss前又加了Softmax。解决检查num_classes是否等于类别数检查模型最后一层输出维度去掉多余的Softmax。另外确认DataLoader的shuffle在训练集为 True。6.3 验证集准确率远低于训练集现象训练集 99%验证集 60%。原因数据量太小、增强过猛或者训练集和验证集有重复图片。解决先检查两个集合有没有同名文件再降低增强强度比如去掉RandomRotation必要时用迁移学习替代从头训练。6.4num_workers在 Windows 下卡死现象程序启动后不动CPU 占用为 0。原因Windows 下多进程 DataLoader 和if __name__ __main__保护不兼容。解决把num_workers设为 0或者把训练代码包进if __name__ __main__:里。6.5 模型保存后加载报Missing key(s)现象load_state_dict报缺少键或多余键。原因保存时用了torch.save(model, path)整个模型加载时又用load_state_dict或者网络结构改过。解决统一用torch.save(model.state_dict(), path)保存权重加载前先实例化同结构模型再load_state_dict。7. 进阶技巧把 97 分项目改成能演示的完整系统课程设计交完不是终点这套源码稍微改改就能当作品集里的演示项目。我一般会做三件事第一把predict.py包一层argparse支持命令行传图片路径和模型路径这样演示时不用改代码第二加一个requirements.txt把torch、torchvision、Pillow、scikit-learn的版本钉死避免换机器后环境崩掉第三用torch.jit.trace把模型导出成 TorchScript推理时不再依赖源码里的类定义。import argparse import torch if __name__ __main__: parser argparse.ArgumentParser() parser.add_argument(--img, typestr, requiredTrue, help待识别图片路径) parser.add_argument(--model, typestr, defaultmodel/best.pth) args parser.parse_args() # 加载模型并推理输出类别和置信度 # 具体加载逻辑复用第 5 章 predict 函数参数说明requiredTrue强制传图片路径避免空跑default给模型路径一个默认值演示时少敲几个字。导出 TorchScript 的常见做法是example torch.rand(1, 3, 224, 224).to(device) traced torch.jit.trace(model, example) traced.save(model/traced_model.pt)这样部署时只需要torch.jit.load不用再带.py文件。验证方法也简单拿一张训练时没见过的img_塑料瓶_67.jpeg分别用原始模型和 TorchScript 模型跑一遍看输出类别和置信度是否一致差太多说明 trace 时漏了动态分支。从那以后我每次拿到这类课程设计源码都强制先跑一遍find统计类别数量再拿一张图走完推理确认best.pth和网络结构对得上才敢动训练脚本。希望帮到你。本文还有配套的精品资源点击获取
返回列表