ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于Python的智能垃圾分类系统:从模型推理到边缘部署全流程

基于Python的智能垃圾分类系统:从模型推理到边缘部署全流程 简介这份资源是一套基于Python开发的智能垃圾分类系统完整源码与部署指南面向计算机相关专业的毕业设计、期末大作业及课程实践场景适合具备一定Python与深度学习基础的学习者参考。系统通过卷积神经网络与迁移学习实现可回收物、厨余垃圾、有害垃圾及其他垃圾四大类别的智能识别涵盖图像采集、数据预处理、模型推理与可视化界面等模块。压缩包共24个文件约55.76MB以py源码、zip数据集、ui界面文件、zbak备份文件为主另含cpp示例、md说明文档及jpg、png图片结构清晰便于按模块查阅。目前已有57人学习。资源内附环境配置、依赖库清单、模型训练与部署流程说明代码采用模块化设计并配有详尽注释读者可据此快速复现系统、理解迁移学习优化思路并在此基础上进行功能扩展与性能调优。1. 从一张垃圾桶照片到可运行系统智能垃圾分类到底在做什么你拍一张外卖餐盒的照片系统在 300 毫秒内告诉你「这属于其他垃圾请沥干水分后投放」——这件事听起来简单但背后是一条从图像采集、预处理、模型推理到结果映射的完整链路。基于 Python 的智能垃圾分类系统核心就是用 Python 把这条链路串起来用 OpenCV 做图像预处理用 PyTorch 或 TensorFlow 加载训练好的分类模型再通过 Flask 或 FastAPI 暴露一个 HTTP 接口让前端页面或硬件设备调用。它解决的不是「分类」本身而是把分类能力封装成可部署、可调用的服务。适合谁有 Python 基础、想做一个完整 AI 落地项目的学生或初级工程师也适合需要快速验证垃圾分类硬件方案的嵌入式开发者。源码和部署指南的价值在于你不用从零训练模型直接拿现成权重跑通推理再把服务部署到本地或边缘设备上。2. 拆解垃圾分类系统的技术栈为什么选 Python 而不是其他语言2.1 模型推理层PyTorch 与 ONNX 的取舍垃圾分类模型通常基于 ResNet、MobileNet 或 EfficientNet 做迁移学习。训练阶段用 PyTorch 最顺手因为 torchvision 提供了预训练权重和完整的数据增强工具链。但部署阶段要考虑目标设备如果是服务器或 PC直接加载 .pth 文件用 PyTorch 推理没问题如果要在树莓派或 Jetson 上跑建议导出为 ONNX 格式用 onnxruntime 推理内存占用和启动速度都更优。我一般会先在 PyTorch 里把模型跑通确认精度达标后用 torch.onnx.export 导出。导出时注意 opset_version 选 11 或 13太低不支持某些算子太高部分推理引擎不兼容。输入尺寸固定为 224x224 或 320x320动态轴只保留 batch 维度。import torch import torchvision.models as models # 加载预训练 ResNet50替换最后一层为 4 分类可回收、厨余、有害、其他 model models.resnet50(pretrainedTrue) num_features model.fc.in_features model.fc torch.nn.Linear(num_features, 4) model.load_state_dict(torch.load(garbage_resnet50.pth, map_locationcpu)) model.eval() # 导出 ONNX输入固定为 1x3x224x224 dummy_input torch.randn(1, 3, 224, 224) torch.onnx.export( model, dummy_input, garbage_resnet50.onnx, opset_version11, input_names[input], output_names[output], dynamic_axes{input: {0: batch_size}, output: {0: batch_size}} )这段代码的关键参数是 opset_version 和 dynamic_axes。opset_version11 兼容性最好dynamic_axes 让 batch 维度可变方便后续做批量推理。导出后务必用 onnxruntime 加载一次确认输出和 PyTorch 一致误差在 1e-4 以内算正常。2.2 服务层Flask 够用FastAPI 更现代如果只是本地测试或小规模部署Flask 足够代码短、依赖少、上手快。但如果你需要异步处理、自动生成 API 文档、或者并发请求较多FastAPI 更合适。它的 async 支持让图像预处理和模型推理可以并行调度实测在 4 核 CPU 上FastAPI 的 QPS 比 Flask 高 30% 左右。from fastapi import FastAPI, File, UploadFile from PIL import Image import io import numpy as np import onnxruntime as ort app FastAPI() session ort.InferenceSession(garbage_resnet50.onnx) def preprocess(image_bytes): img Image.open(io.BytesIO(image_bytes)).convert(RGB) img img.resize((224, 224)) arr np.array(img).astype(np.float32) / 255.0 mean np.array([0.485, 0.456, 0.406]) std np.array([0.229, 0.224, 0.225]) arr (arr - mean) / std arr arr.transpose(2, 0, 1) # HWC - CHW return arr[np.newaxis, :, :, :].astype(np.float32) app.post(/classify) async def classify(file: UploadFile File(...)): contents await file.read() input_data preprocess(contents) outputs session.run(None, {input: input_data}) pred int(np.argmax(outputs[0])) labels [可回收, 厨余, 有害, 其他] return {label: labels[pred], confidence: float(np.max(outputs[0]))}预处理里的 mean 和 std 必须和训练时一致否则精度会掉 5% 以上。transpose 把 HWC 转成 CHW 是因为 ONNX 模型期望 NCHW 输入。返回的 confidence 用 softmax 后的最大值如果模型输出没加 softmax需要手动补上。2.3 前端与硬件对接三种常见方案第一种是 Web 页面用 HTML5 的 input typefile 或 getUserMedia 调摄像头POST 到 /classify 接口展示结果。第二种是微信小程序通过 wx.uploadFile 上传图片后端返回 JSON。第三种是嵌入式设备比如树莓派加摄像头模块用 Python 脚本定时抓拍并调用本地推理服务。三种方案的后端接口可以完全复用区别只在前端采集和展示方式。3. 从零跑通推理服务环境配置与最小可运行步骤3.1 Python 环境与依赖安装先确认 Python 版本建议 3.8 到 3.10太新的版本部分库还没适配。用 conda 或 venv 创建独立环境避免污染系统 Python。conda create -n garbage python3.9 conda activate garbage pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu pip install onnx onnxruntime fastapi uvicorn pillow numpy opencv-python如果要用 GPU 推理把 torch 换成 CUDA 版本onnxruntime 换成 onnxruntime-gpu。注意 CUDA 版本要和驱动匹配否则会报 libcudart.so 找不到。安装完后用 python -c import torch; print(torch.version) 验证。3.2 模型文件准备与目录结构假设你已经拿到了训练好的 .pth 或 .onnx 文件。推荐目录结构如下garbage-classifier/ ├── models/ │ ├── garbage_resnet50.pth │ └── garbage_resnet50.onnx ├── app/ │ ├── main.py │ └── preprocess.py ├── static/ │ └── index.html ├── requirements.txt └── README.md把模型放在 models 目录代码里用相对路径加载。如果模型文件超过 100MB不建议提交到 Git用 Git LFS 或单独下载。3.3 启动服务与接口测试用 uvicorn 启动 FastAPI 服务uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 2--workers 2 表示启动两个工作进程适合多核 CPU。启动后用 curl 测试curl -X POST http://localhost:8000/classify \ -H accept: application/json \ -F filetest_garbage.jpg返回类似 {label:厨余,confidence:0.92} 就说明链路通了。如果报 500 错误先看服务端日志常见原因是图片格式不支持或模型输入尺寸不匹配。3.4 用 OpenCV 做实时摄像头推理如果想接摄像头做实时分类用 OpenCV 抓帧后直接调用推理函数import cv2 import numpy as np import onnxruntime as ort session ort.InferenceSession(models/garbage_resnet50.onnx) labels [可回收, 厨余, 有害, 其他] def classify_frame(frame): img cv2.resize(frame, (224, 224)) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) arr img.astype(np.float32) / 255.0 mean np.array([0.485, 0.456, 0.406]) std np.array([0.229, 0.224, 0.225]) arr (arr - mean) / std arr arr.transpose(2, 0, 1)[np.newaxis, :, :, :].astype(np.float32) outputs session.run(None, {input: arr}) pred int(np.argmax(outputs[0])) return labels[pred], float(np.max(outputs[0])) cap cv2.VideoCapture(0) while True: ret, frame cap.read() if not ret: break label, conf classify_frame(frame) cv2.putText(frame, f{label} {conf:.2f}, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.imshow(Garbage Classifier, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()注意 OpenCV 读到的帧是 BGR 格式必须转成 RGB 再归一化否则颜色通道错位会导致精度暴跌。cv2.waitKey(1) 里的 1 表示等待 1 毫秒值越大帧率越低。4. 部署到边缘设备与容器化让服务真正跑起来4.1 树莓派部署的四个关键调整树莓派 4B 跑 ONNX 推理是可行的但要做四件事第一把模型换成 MobileNetV3 或 EfficientNet-B0参数量控制在 5M 以内第二用 onnxruntime 的 CPU 执行提供者不要装 GPU 版本第三输入尺寸降到 160x160 或 192x192精度损失约 2% 但速度翻倍第四用 systemd 把服务做成开机自启。# /etc/systemd/system/garbage.service [Unit] DescriptionGarbage Classifier Service Afternetwork.target [Service] Userpi WorkingDirectory/home/pi/garbage-classifier ExecStart/home/pi/miniconda3/envs/garbage/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000 Restartalways [Install] WantedBymulti-user.target写完 service 文件后执行 sudo systemctl daemon-reload sudo systemctl enable garbage sudo systemctl start garbage。用 systemctl status garbage 查看运行状态。4.2 Docker 镜像构建与体积优化容器化部署的好处是环境一致换设备不用重新配。但 PyTorch 镜像动辄 2GB 以上必须优化。用 python:3.9-slim 作为基础镜像只装 onnxruntime 不装 PyTorch模型用 ONNX 格式。FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY models/ ./models/ COPY app/ ./app/ EXPOSE 8000 CMD [uvicorn, app.main:app, --host, 0.0.0.0, --port, 8000]requirements.txt 里只写 onnxruntime、fastapi、uvicorn、pillow、numpy、opencv-python-headless。opencv-python-headless 比完整版小 200MB适合容器环境。构建命令 docker build -t garbage-classifier:v1 .运行 docker run -d -p 8000:8000 garbage-classifier:v1。4.3 模型量化把推理速度再提一倍ONNX Runtime 支持动态量化把 FP32 权重转成 INT8模型体积缩小 4 倍推理速度提升 50% 到 100%精度损失通常在 1% 以内。量化命令from onnxruntime.quantization import quantize_dynamic, QuantType quantize_dynamic( model_inputgarbage_resnet50.onnx, model_outputgarbage_resnet50_int8.onnx, weight_typeQuantType.QUInt8 )量化后的模型在树莓派上单帧推理时间从 120ms 降到 60ms 左右。注意量化只对权重做激活值仍是 FP32所以不需要校准数据集。如果精度掉得厉害改用 QuantType.QInt8 试试。5. 避坑与排查那些让我熬夜的翻车现场5.1 现象接口返回 200 但 label 永远是同一个原因预处理时没有做归一化或者 mean/std 和训练时不一致。模型对输入分布敏感输入值全在 0 到 255 之间时输出会退化成常数。解决打印预处理后的数组确认数值范围在 -2 到 2 之间。检查 mean 和 std 是否和训练脚本一致常见值是 ImageNet 的 [0.485, 0.456, 0.406] 和 [0.229, 0.224, 0.225]。5.2 现象ONNX 推理结果和 PyTorch 差很多原因导出时没有设置 model.eval()BatchNorm 和 Dropout 层还在训练模式。或者输入尺寸和导出时不一致。解决导出前务必调用 model.eval()。用 onnxruntime 加载后用同一张图片分别跑 PyTorch 和 ONNX对比输出。如果误差大于 1e-3检查 opset_version 和算子兼容性。5.3 现象Docker 容器启动后立即退出原因CMD 里的 uvicorn 命令找不到模块或者端口被占用。容器内没有前台进程时 Docker 会自动退出。解决用 docker logs container_id 看报错。如果是 ModuleNotFoundError检查 WORKDIR 和 COPY 路径。如果是端口冲突换一个宿主机端口映射比如 -p 8001:8000。5.4 现象树莓派上推理一分钟后 CPU 降频原因树莓派没有主动散热持续满载会触发温度墙CPU 从 1.5GHz 降到 1GHz 以下。解决加散热片和小风扇或者用 taskset 限制 CPU 亲和性把推理进程绑到两个核心上留两个核心给系统。命令 taskset -c 0,1 uvicorn app.main:app。5.5 现象上传大图时接口超时原因没有限制图片尺寸一张 4000x3000 的图片预处理就要几百毫秒加上推理时间超过网关超时。解决在预处理里加 resize 上限比如先缩到 640x640 再送模型。或者在前端用 canvas 压缩后再上传。FastAPI 里可以设置 max_upload_size但更简单的是在 preprocess 函数里判断 if max(img.size) 1024: img.thumbnail((1024, 1024))。6. 进阶技巧用 ONNX Runtime 的 IO Binding 把延迟压到极限如果你已经把服务跑通想再榨一点性能IO Binding 是最值得试的技巧。默认情况下ONNX Runtime 每次推理都要把输入从 CPU 内存拷贝到推理引擎内部输出再拷贝回来。IO Binding 允许你预先分配输入输出缓冲区绑定到特定设备省掉重复拷贝。在 CPU 上提升不明显但在 GPU 或 NPU 上能减少 20% 到 30% 的延迟。import onnxruntime as ort import numpy as np session ort.InferenceSession(garbage_resnet50.onnx, providers[CUDAExecutionProvider]) io_binding session.io_binding() # 预分配输入输出缓冲区 input_shape (1, 3, 224, 224) input_tensor np.zeros(input_shape, dtypenp.float32) output_tensor np.zeros((1, 4), dtypenp.float32) io_binding.bind_cpu_input(input, input_tensor) io_binding.bind_output(output, cuda) # 输出绑到 GPU # 推理时直接填充 input_tensor然后调用 def infer_with_binding(image_array): input_tensor[:] image_array session.run_with_iobinding(io_binding) return output_tensor关键点是 bind_cpu_input 把输入绑到 CPU 内存bind_output 把输出绑到 GPU。如果你的输入已经在 GPU 上用 bind_input 直接绑 GPU 内存省掉 Host-to-Device 拷贝。注意 IO Binding 不是线程安全的每个线程要创建独立的 session 和 binding。另一个技巧是批处理。如果有多张图片同时到达攒成 batch 再推理GPU 利用率能从 30% 提到 70% 以上。但 batch 太大会增加单次延迟建议 batch_size 设为 4 或 8根据显存调整。验证 IO Binding 是否生效用 session.get_providers() 确认 CUDAExecutionProvider 在列表里再用 nsight 或 nvprof 看拷贝次数。如果拷贝次数没降检查 bind_output 的设备名是否写对CUDA 用 cudaTensorRT 用 tensorrt。我自己的习惯是先用默认推理跑通精度再开 IO Binding 压延迟最后用批处理提吞吐。三步分开做每步都记录基准数据避免玄学调优。这套流程在 Jetson Nano 上把单帧延迟从 85ms 压到了 52ms batch4 时吞吐从 12 FPS 提到 28 FPS。希望帮到你。本文还有配套的精品资源点击获取
返回列表