ARTICLE DETAIL

资讯详情

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

基于迁移学习的智能垃圾分类系统:从模型训练到Web部署全流程

基于迁移学习的智能垃圾分类系统:从模型训练到Web部署全流程 简介这份资源是面向人工智能初学者与高校学生的智能垃圾分类系统完整源码包适用于毕业设计、课程作业及AI实验场景帮助读者理解图像识别与深度学习在环保分类中的落地方式。包内共52个文件以28个Python脚本为核心涵盖数据预处理、模型构建、训练与预测等模块另有13个pyc编译文件、6张运行截图及gif演示、rst与md说明文档、json配置等压缩包约1.15MB结构清晰便于按模块查阅。项目围绕卷积神经网络展开涉及ResNet、CBAM等模型实现并包含训练、评估、可视化与预测脚本可帮助读者掌握数据增强、模型调优与结果分析的完整流程。目前已有141人学习适合希望积累AI项目实战经验、提升编程与算法调优能力的学习者参考借鉴。1. 从一份「智能垃圾分类系统」压缩包说起它到底能帮你交掉什么如果你正在翻毕设选题或者刚被课程作业的 deadline 追着跑看到「用于人工智能学习所实验的智能垃圾分类系统.zip」这种命名第一反应大概是这玩意儿能不能直接跑起来、能不能改、能不能撑起一篇论文的工作量。我先把结论摆前面——这类项目本质是一个「图像分类 Web 演示」的完整闭环核心是拿一个预训练卷积网络在垃圾图像数据集上做迁移学习再套一层前后端把识别结果可视化出来。它解决的不是算法创新问题而是让你在有限时间里把「数据—训练—推理—展示」这条链路走通并且每一步都有东西可写、可截图、可答辩。适合谁三类人一是计算机、物联网、人工智能相关专业的本科毕设选手需要一个能落地、有界面、有指标的题目二是选了「人工智能导论」「深度学习」这类课、期末要交大作业的同学三是想入门计算机视觉但不知道从哪下手的新手拿它当第一个端到端项目练手。不适合谁想发论文、想做算法对比实验的人——这类项目的模型结构基本是现成的创新点得你自己往上加。下面我按「先跑通、再拆解、再改造」的顺序讲中间会给出可直接抄的命令和参数也会说清楚哪些地方最容易翻车。2. 智能垃圾分类系统的技术骨架从一张图片到分类结果中间发生了什么2.1 为什么是「分类」而不是「检测」选型理由先讲清很多人一上来就想做目标检测觉得能框出垃圾位置更高级。但你要先想清楚场景用户拍一张照片里面通常只有一件垃圾或者主体非常明确。这种情况下分类网络ResNet、MobileNet、EfficientNet 这类就够了标注成本低、训练快、部署轻。检测网络YOLO 系列需要边界框标注标注工作量是分类的 5 到 10 倍而且毕设周期内你未必能标完一个像样的数据集。我一般会这样定如果数据集是「一图一物」的公开垃圾数据集比如 TrashNet 那种 6 分类、每类几百张的规模直接上分类如果非要检测就用现成 YOLO 权重做微调别从零训。分类方案的另一个好处是后端接口极其简单——输入一张图输出一个类别和置信度前端拿去做动画、做音效、做积分都好接。选主干网络时看两个指标参数量和 Top-1 准确率。毕设场景我推荐 MobileNetV3 或 ResNet18前者适合要部署到边缘设备比如树莓派、Jetson Nano的选题后者适合纯服务器推理、追求稳定收敛的选题。EfficientNet-B0 是折中选项但它的预处理归一化参数和普通 ImageNet 不一样容易踩坑后面避坑章节会讲。2.2 数据集的获取、清洗与划分别让脏数据毁掉整个训练公开垃圾数据集常见的有 TrashNet6 类纸板、玻璃、金属、纸、塑料、其他垃圾、Kaggle 上的垃圾分类数据集通常 12 类或 30 类。下载下来第一件事不是直接训练而是做三件事去重、去模糊、检查类别平衡。去重用感知哈希pHash几行代码就能筛掉大量重复图。去模糊用拉普拉斯方差阈值一般设在 100 左右低于这个值的图大概率是糊的。类别平衡看直方图如果某一类只有几十张而其他类有几百张要么做数据增强补要么在损失函数里加类别权重。import cv2 import numpy as np from PIL import Image import imagehash def is_blurry(image_path, threshold100): img cv2.imread(image_path) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) variance cv2.Laplacian(gray, cv2.CV_64F).var() return variance threshold def get_phash(image_path): return str(imagehash.phash(Image.open(image_path))) # 遍历数据集目录筛掉模糊图和重复图 import os seen_hashes set() clean_list [] for root, _, files in os.walk(dataset_raw): for f in files: if not f.lower().endswith((.jpg, .png, .jpeg)): continue path os.path.join(root, f) if is_blurry(path): continue h get_phash(path) if h in seen_hashes: continue seen_hashes.add(h) clean_list.append(path) print(f清洗后剩余 {len(clean_list)} 张)这段代码的逻辑是先算拉普拉斯方差判断模糊度再用感知哈希做去重。threshold100是经验值你可以根据自己数据集的实际清晰度调整调太低会漏掉模糊图调太高会误删正常图。phash对轻微缩放和亮度变化不敏感适合做近似去重。划分数据集时训练集、验证集、测试集按 7:1.5:1.5 或 8:1:1 分。注意要按类别分层抽样别随机分完发现某一类全进了训练集。用sklearn.model_selection.train_test_split的stratify参数就能做到。2.3 迁移学习训练脚本冻结哪些层、学习率怎么设拿到清洗后的数据下一步是训练。毕设场景不建议从零训直接用 ImageNet 预训练权重做迁移学习。具体策略先冻结主干网络的所有层只训练最后的全连接分类头跑 5 到 10 个 epoch然后解冻最后两个 stage对于 ResNet 就是 layer3 和 layer4用更小的学习率做微调。import torch import torch.nn as nn from torchvision import models, transforms, datasets from torch.utils.data import DataLoader # 数据增强与归一化 train_tf transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.RandomRotation(15), transforms.ColorJitter(brightness0.2, contrast0.2), 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]) ]) train_ds datasets.ImageFolder(dataset/train, transformtrain_tf) val_ds datasets.ImageFolder(dataset/val, transformval_tf) train_loader DataLoader(train_ds, batch_size32, shuffleTrue, num_workers4) val_loader DataLoader(val_ds, batch_size32, shuffleFalse, num_workers4) # 加载预训练 ResNet18 model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) num_classes len(train_ds.classes) model.fc nn.Linear(model.fc.in_features, num_classes) # 第一阶段冻结主干只训分类头 for name, param in model.named_parameters(): if fc not in name: param.requires_grad False criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr1e-3) scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size5, gamma0.5) device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) for epoch in range(10): model.train() running_loss 0.0 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() running_loss loss.item() scheduler.step() print(fEpoch {epoch1}, Loss: {running_loss/len(train_loader):.4f})关键参数说明batch_size32是 8GB 显存下的安全值显存小就降到 16 或 8lr1e-3是只训分类头时的常用学习率解冻主干后要降到1e-4甚至1e-5StepLR每 5 个 epoch 把学习率乘 0.5防止后期震荡。num_workers4在 Windows 上有时会报错改成 0 即可。第二阶段微调的代码结构一样只是把requires_grad全部设为True然后换一个更小的学习率重新建优化器。注意解冻后不要用同一个优化器直接接着训因为 Adam 里存了之前的状态学习率突变会导致 loss 飞掉。新建一个 Adam学习率设1e-4再跑 10 到 15 个 epoch。2.4 推理接口与前后端对接从模型文件到页面上的一个按钮训练完保存模型权重后端用 Flask 或 FastAPI 包一个推理接口。前端用 Vue 或原生 HTML 都行核心就是一个上传图片的按钮加一个结果展示区域。from flask import Flask, request, jsonify from PIL import Image import torch from torchvision import transforms app Flask(__name__) model torch.load(garbage_model.pth, map_locationcpu) model.eval() class_names [cardboard, glass, metal, paper, plastic, trash] 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]) ]) app.route(/predict, methods[POST]) def predict(): file request.files[image] img Image.open(file.stream).convert(RGB) tensor tf(img).unsqueeze(0) with torch.no_grad(): output model(tensor) prob torch.softmax(output, dim1) conf, pred torch.max(prob, dim1) return jsonify({ class: class_names[pred.item()], confidence: round(conf.item(), 4) }) if __name__ __main__: app.run(host0.0.0.0, port5000)这段代码里map_locationcpu是为了在没有 GPU 的机器上也能加载unsqueeze(0)是把单张图变成 batch 维度为 1 的张量torch.no_grad()关闭梯度计算推理时省显存。前端拿到 JSON 后直接渲染即可置信度低于 0.6 时可以提示「请重新拍摄」这个阈值根据你的验证集表现来定。3. 把系统跑起来的完整路径环境、命令与第一次推理验证3.1 环境搭建Python 版本、CUDA 与依赖锁定这类项目最常见的翻车点不是模型本身而是环境。Python 版本建议 3.8 到 3.10太新了有些包还没适配。PyTorch 版本要和 CUDA 驱动匹配如果你机器上没有 NVIDIA 显卡直接装 CPU 版就行训练慢但能跑通。# 创建虚拟环境 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 安装 PyTorchCPU 版示例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装其他依赖 pip install flask pillow numpy opencv-python scikit-learn imagehash装完后跑一句python -c import torch; print(torch.__version__)确认没报错。如果报DLL load failed大概率是 Visual C Redistributable 没装去微软官网下最新的装上。3.2 训练与验证看 loss 曲线判断有没有训崩训练时重点盯两个信号训练 loss 是否稳定下降、验证准确率是否跟着涨。如果训练 loss 降但验证准确率不动说明过拟合了加数据增强或加 Dropout。如果训练 loss 震荡剧烈把学习率降一半。如果 loss 变成 NaN检查输入归一化是不是漏了或者学习率太大。验证脚本单独写一个加载保存的权重在测试集上跑一遍输出混淆矩阵和每类准确率。混淆矩阵能告诉你哪些类容易混——比如「塑料」和「其他垃圾」经常分不清这时候要么补数据要么在业务逻辑里做后处理。from sklearn.metrics import classification_report, confusion_matrix import seaborn as sns import matplotlib.pyplot as plt model.eval() all_preds, all_labels [], [] with torch.no_grad(): for imgs, labels in test_loader: outputs model(imgs.to(device)) _, preds torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.numpy()) print(classification_report(all_labels, all_preds, target_namesclass_names)) cm confusion_matrix(all_labels, all_preds) sns.heatmap(cm, annotTrue, fmtd, xticklabelsclass_names, yticklabelsclass_names) plt.savefig(confusion_matrix.png)classification_report会输出每类的 precision、recall、f1-score答辩时直接截图放论文里。混淆矩阵热力图也是加分项能体现你对结果的分析深度。3.3 前端联调上传、预览、结果展示的最小闭环前端不需要多复杂一个input typefile加一个img预览再用fetch把图片 POST 到后端接口。注意跨域问题开发阶段在后端加 CORS 头或者用 Nginx 做反向代理。async function predict() { const fileInput document.getElementById(fileInput); const file fileInput.files[0]; if (!file) return alert(请先选择图片); const formData new FormData(); formData.append(image, file); const res await fetch(http://localhost:5000/predict, { method: POST, body: formData }); const data await res.json(); document.getElementById(result).innerText 识别结果${data.class}置信度${data.confidence}; }这段代码的核心是FormData构造和fetch调用。data.class和data.confidence对应后端返回的 JSON 字段。如果前端显示undefined打开浏览器开发者工具的 Network 面板看响应体大概率是后端字段名写错了。4. 避坑与排查这类项目最容易翻车的 5 个地方4.1 现象训练准确率 99%测试准确率 40%原因数据集划分时没有按类别分层或者训练集和测试集有重复图片。另一个常见原因是数据增强只加在了训练集但验证集的归一化参数和训练集不一致。解决用stratify参数重新划分跑一遍去重脚本确认没有跨集重复。检查验证集的transforms是否和训练集用了同一套Normalize参数。4.2 现象推理时所有图片都预测成同一类原因模型加载时没有调用model.eval()Dropout 和 BatchNorm 还在训练模式。或者保存模型时保存的是整个模型对象而不是state_dict加载时结构对不上。解决推理前必须model.eval()并用torch.no_grad()包住。保存时用torch.save(model.state_dict(), path)加载时先实例化模型再load_state_dict。4.3 现象Flask 接口在本地能跑部署到服务器后 502原因Flask 默认单线程并发一高就卡死。或者服务器上的 Python 环境没装flask-cors跨域请求被拦。解决生产环境用gunicorn或uwsgi起多进程命令是gunicorn -w 4 -b 0.0.0.0:5000 app:app。跨域在 Flask 里加CORS(app)一行搞定。4.4 现象EfficientNet 训练 loss 一直不降原因EfficientNet 的预处理归一化参数和 ResNet 不一样如果你直接套用了 ImageNet 的mean[0.485, 0.456, 0.406]输入分布对不上。解决查你用的那版 EfficientNet 的官方预处理参数通常mean[0.485, 0.456, 0.406]和std[0.229, 0.224, 0.225]也能用但有些实现要求mean[0.5, 0.5, 0.5]。最稳妥的办法是看torchvision里对应模型的weights.transforms()输出。4.5 现象GPU 显存够但训练速度极慢原因num_workers设成了 0数据加载在主进程里串行执行。或者没有开pin_memoryCPU 到 GPU 的传输成了瓶颈。解决DataLoader里设num_workers4Linux或num_workers0Windows 兼容加pin_memoryTrue。如果还是慢检查是不是在 CPU 上训练——torch.cuda.is_available()返回False的话装 CUDA 版 PyTorch。5. 从「能跑」到「能写进论文」三个让答辩加分的小改造5.1 加一个置信度阈值与拒识逻辑分类模型有个天然缺陷你给它一张完全不属于任何类别的图它也会强行输出一个类别和置信度。在垃圾分类场景里用户可能拍了一只手、一个键盘、甚至一片空白。加一个拒识逻辑当最大置信度低于阈值比如 0.55时返回「无法识别请重新拍摄」。conf, pred torch.max(prob, dim1) if conf.item() 0.55: return jsonify({class: unknown, confidence: round(conf.item(), 4)})这个改动很小但在论文里可以写一节「基于置信度的拒识机制」答辩时老师问「如果拍的不是垃圾怎么办」你就有东西答。5.2 用 Grad-CAM 做可视化解释Grad-CAM 能生成一张热力图标出模型在图片上看哪些区域做出的判断。对于垃圾分类这种场景热力图能直观展示模型是不是真的在看垃圾主体而不是在看背景。实现上就是用pytorch-grad-cam库几行代码就能出图。from pytorch_grad_cam import GradCAM from pytorch_grad_cam.utils.image import show_cam_on_image target_layers [model.layer4[-1]] cam GradCAM(modelmodel, target_layerstarget_layers) grayscale_cam cam(input_tensortensor) visualization show_cam_on_image(img_array, grayscale_cam[0], use_rgbTrue)把原图和热力图并排放在论文里比单纯放准确率数字有说服力得多。老师看到这个基本不会再质疑「你是不是只是调了个包」。5.3 做一个简单的模型对比实验毕设如果只跑一个模型工作量显得单薄。用同一份数据、同一套训练策略分别跑 ResNet18、MobileNetV3、EfficientNet-B0记录准确率、参数量、单张推理耗时。表格一列结论自然就有了MobileNetV3 参数量最小但准确率略低ResNet18 准确率最高但模型最大EfficientNet-B0 居中。模型参数量(M)测试准确率单张推理耗时(ms)ResNet1811.292.3%18MobileNetV3-Small2.589.7%9EfficientNet-B05.391.1%14这张表的数据你自己跑出来填别抄。跑三组实验大概多花两三个小时但论文里多了一整节对比分析性价比很高。最后说个我自己的习惯每次改完代码先在一个小批量比如 20 张图上跑一遍确认输入输出形状对得上再上全量。这个习惯帮我省了无数次「训了半天发现标签映射错了」的后悔药。希望帮到你。本文还有配套的精品资源点击获取
返回列表