垃圾分类模型ONNX部署实战:从PyTorch导出到onnxruntime推理
简介这份资源是一套面向深度学习入门者与环保AI应用开发者的垃圾分类识别项目源码核心解决如何用卷积神经网络自动区分不同类别垃圾的问题适合具备Python基础、希望了解模型训练与跨平台部署流程的学习者。压缩包共6个文件以2个Python脚本和3个CSV数据文件为主另含1个编译缓存文件整体约12KB体量轻便便于快速阅读与二次开发。其中脚本承担模型构建、训练与推理逻辑CSV文件则用于存放类别标签、用户信息与历史记录等配置数据。项目采用ONNX格式导入模型体现了跨框架互操作与灵活部署的思路读者可借此掌握数据预处理、网络结构定义、损失函数与优化器设定、训练循环及模型评估的完整链路并理解模型保存加载与实时图像分类的衔接方式。目前已有116人学习适合作为课程设计或环保类AI项目的参考起点。1. 垃圾分类模型用 ONNX 导入为什么训练完只是走了一半训练脚本跑完best.pt躺在runs/train/exp/weights/里mAP0.5看着挺漂亮很多人到这一步就觉得项目结束了。真到要交付、要嵌进桌面端、要丢给一个只有 CPU 的工控机时才发现 PyTorch 那套环境根本装不进去——CUDA 版本对不上、torch 包 2GB 起步、目标机器连 conda 都没有。这时候 ONNX 就成了那个「后悔药」把模型从训练框架里剥出来变成一个跟框架无关的.onnx文件用 onnxruntime 就能推理。这个标题讲的就是这条链路一个基于深度学习的垃圾分类系统模型不走 PyTorch 原生部署而是导出成 ONNX 再导入运行。它解决的是「训练环境」和「部署环境」解耦的问题适合做毕设、做边缘盒子、做桌面端小工具的人。下面按「先立住原理、再动手复现、最后讲坑」的顺序拆开讲代码都能直接抄。2. 从 PyTorch 到 ONNX垃圾分类模型的导出与结构确认2.1 为什么垃圾分类适合走 ONNX 这条路垃圾分类本质是一个图像分类或检测任务输入是固定尺寸的图片输出是类别概率或检测框。这类模型结构规整、算子常规几乎没有自定义算子是 ONNX 导出最友好的场景之一。常见的做法是用 ResNet、MobileNet、EfficientNet 这类骨干网络做迁移学习或者用 YOLO 系列做检测式分类。选 ONNX 而不是直接上 TensorRT 或 NCNN理由很实际ONNX 是中间格式一次导出可以喂给多个后端。今天用 onnxruntime 在 CPU 上跑明天想换 GPU、换 NCNN、换 RKNN都能从同一个.onnx转过去。热搜里常出现的「onnx转ncnn在线网站」「onnx转rknn」前提都是你手里先有一个干净的 ONNX。所以导出这一步是整个部署链的地基地基歪了后面全歪。需要先明确一件事onnxruntime 和 onnx 不是一回事。onnx 是格式标准定义文件长什么样onnxruntime 是执行引擎负责把文件跑起来。导出用torch.onnx.export推理用onnxruntime.InferenceSession两者别混。2.2 导出前的三个准备动作动手之前先确认三件事否则导出报错会很难查。第一确认模型处于 eval 模式。训练时的 BatchNorm 和 Dropout 在推理时行为不同忘了.eval()会导致导出后的输出和训练时对不上这种玄学问题最耗时间。第二准备一个 dummy input。ONNX 导出需要走一遍前向dummy input 的 shape 必须和真实推理时一致。垃圾分类常见输入是1x3x224x224分类或1x3x640x640检测。第三确认类别数和标签映射。导出只搬模型权重不搬classes.txt。标签顺序要单独存下来推理时按索引查顺序错了模型再准也白搭。import torch import torchvision.models as models # 1. 加载训练好的权重num_classes 换成你自己的类别数 num_classes 6 # 例如可回收物、厨余、有害、其他等 model models.resnet18(weightsNone) model.fc torch.nn.Linear(model.fc.in_features, num_classes) state torch.load(best.pt, map_locationcpu) # 兼容直接存 state_dict 和存 checkpoint 两种情况 state state.get(model, state) if isinstance(state, dict) else state model.load_state_dict(state, strictFalse) # 2. 关键切到推理模式冻结 BN/Dropout model.eval() # 3. dummy input 的 shape 必须和部署时一致 dummy torch.randn(1, 3, 224, 224) torch.onnx.export( model, dummy, garbage_cls.onnx, input_names[input], output_names[logits], opset_version12, # 12 兼容性好别盲目追高 dynamic_axes{input: {0: batch}, logits: {0: batch}}, do_constant_foldingTrue, # 常量折叠减小体积 ) print(export done)这段代码的逻辑先重建网络结构结构必须和训练时一致否则load_state_dict会报 key 不匹配再加载权重切 eval然后用一个假输入触发导出。参数说明几个关键点opset_version12是兼容性比较稳的选择opset 太高有些推理后端不认dynamic_axes把 batch 维设成动态这样推理时 batch 可以是 1 也可以是 8不写死do_constant_folding会把能提前算的常量算掉模型更小。strictFalse是给自己留的后路遇到分类头名字对不上时不至于直接崩但导出后一定要验证精度别让它悄悄漏权重。2.3 导出后先验证别急着部署导出成功不等于导出正确。最常见的翻车是ONNX 能跑但输出和 PyTorch 对不上分类结果全乱。所以导出后第一件事是做数值对齐。import numpy as np import onnxruntime as ort # PyTorch 侧输出 with torch.no_grad(): torch_out model(dummy).numpy() # ONNX 侧输出 sess ort.InferenceSession(garbage_cls.onnx, providers[CPUExecutionProvider]) onnx_out sess.run(None, {input: dummy.numpy()})[0] # 比较最大绝对误差一般应小于 1e-4 diff np.max(np.abs(torch_out - onnx_out)) print(max diff:, diff) assert diff 1e-3, 导出误差过大检查 eval() 和 opset逻辑很直白同一个输入分别喂给 PyTorch 和 onnxruntime比输出差。参数上providers指定执行后端CPU 上用CPUExecutionProvider有 GPU 且装了 onnxruntime-gpu 可以换成CUDAExecutionProvider。误差阈值我一般卡1e-4超过1e-3基本说明有问题优先查是不是忘了eval()其次查 opset 是否和某些算子不兼容。这一步过了才说明这个.onnx是可信的。3. 用 onnxruntime 把垃圾分类跑起来预处理、推理、后处理3.1 预处理必须和训练时一模一样模型精度掉得莫名其妙十有八九是预处理不一致。训练时用了什么 resize、什么归一化均值方差推理时必须一字不差。垃圾分类训练常用 ImageNet 的均值和方差mean[0.485,0.456,0.406]std[0.229,0.224,0.225]。如果你训练时用的是自定义的就换成你自己的。from PIL import Image import numpy as np def preprocess(img_path, size224): img Image.open(img_path).convert(RGB) img img.resize((size, size)) # 和训练时的 resize 方式保持一致 x np.asarray(img, dtypenp.float32) / 255.0 mean np.array([0.485, 0.456, 0.406], dtypenp.float32) std np.array([0.229, 0.224, 0.225], dtypenp.float32) x (x - mean) / std x x.transpose(2, 0, 1) # HWC - CHW x np.expand_dims(x, axis0) # 加 batch 维 return np.ascontiguousarray(x, dtypenp.float32)逻辑说明PIL 读进来是 HWC 排列PyTorch/ONNX 要 CHW所以transpose(2,0,1)。np.ascontiguousarray是为了保证内存连续onnxruntime 对非连续数组有时会报奇怪的错。参数上size必须和导出时的 dummy input 对齐导出用 224 推理就得 224除非你导出时把宽高也设成了动态轴。3.2 推理与后处理拿到类别和置信度import onnxruntime as ort import numpy as np CLASSES [可回收物, 厨余垃圾, 有害垃圾, 其他垃圾, 纸张, 塑料] sess ort.InferenceSession(garbage_cls.onnx, providers[CPUExecutionProvider]) input_name sess.get_inputs()[0].name def predict(img_path): x preprocess(img_path) logits sess.run(None, {input_name: x})[0] # shape: [1, num_classes] prob softmax(logits[0]) idx int(np.argmax(prob)) return CLASSES[idx], float(prob[idx]) def softmax(v): v v - np.max(v) # 减最大值防溢出 e np.exp(v) return e / e.sum()逻辑说明sess.get_inputs()[0].name拿到输入节点名导出时我们命名成input但用代码取更稳避免手写错。sess.run返回的是一个列表取[0]是第一个输出。后处理做 softmax 得到概率argmax取最大类别。参数上CLASSES的顺序必须和训练时数据集文件夹的字母序或你定义的映射一致这是最容易错的地方——模型输出索引 3你CLASSES[3]写错结果就全错。3.3 批量推理和耗时测量单张跑通后实际系统往往要处理一批图。ONNX 支持动态 batch前提是导出时设了dynamic_axes。import time def predict_batch(img_paths): xs np.concatenate([preprocess(p) for p in img_paths], axis0) t0 time.time() logits sess.run(None, {input_name: xs})[0] cost (time.time() - t0) / len(img_paths) results [] for row in logits: prob softmax(row) idx int(np.argmax(prob)) results.append((CLASSES[idx], float(prob[idx]))) return results, cost res, per predict_batch([a.jpg, b.jpg, c.jpg]) print(res, f{per*1000:.1f} ms/img)逻辑说明把多张图在 batch 维拼起来一次推理比循环单张快很多因为摊薄了调用开销。per是单张平均耗时用来判断能不能满足实时性要求。参数上batch 不是越大越好CPU 上 batch 太大反而因为内存带宽瓶颈变慢一般 4 到 8 比较合适具体要实测。如果这里报 shape 不匹配说明导出时没设动态 batch回去重导。4. 部署垃圾分类 ONNX 模型时最容易踩的坑4.1 现象ONNX 输出和 PyTorch 差很多分类全乱原因最常见是导出时忘了model.eval()BatchNorm 还在用 batch 统计量其次是 opset 版本和某个算子不兼容导出时被静默替换成了近似实现。解决导出前强制model.eval()导出后立刻做 2.3 节的数值对齐误差超1e-3就换 opset12 不行试 11 或 13。别跳过对齐直接部署那是给自己埋雷。4.2 现象推理时报输入 shape 不匹配原因导出时 dummy input 写死了1x3x224x224且没设dynamic_axes推理时传了别的尺寸或 batch。解决要么推理时严格按导出尺寸来要么重新导出并加上dynamic_axes。注意动态轴只对你声明的那一维生效宽高要动态就得把 2、3 维也写进去。4.3 现象CPU 上单张要几百毫秒根本没法用原因用了 ResNet50 这种大骨干或者没开图优化或者线程数没配。解决换 MobileNetV3、ShuffleNet 这类轻量骨干重训创建 session 时配sess_options把intra_op_num_threads设成物理核数有条件就上onnxruntime-gpu。热搜里的「.onnx量化int8」也是条路量化后体积和耗时都能降但精度会掉一点要重新验证。so ort.SessionOptions() so.intra_op_num_threads 4 # 按物理核数设 so.graph_optimization_level ort.GraphOptimizationLevel.ORT_ENABLE_ALL sess ort.InferenceSession(garbage_cls.onnx, so, providers[CPUExecutionProvider])4.4 现象换台机器结果就变了原因预处理里的均值方差、resize 插值方式、甚至 PIL 版本不同导致的解码差异都会让输入像素值有微小偏移累积到分类边界样本上就翻类。解决把预处理参数写进配置文件和模型一起交付对边界样本置信度 0.5 附近做二次确认或直接判为「不确定」别硬给一个类别。4.5 现象类别索引对不上A 类被判成 B 类原因训练时ImageFolder按文件夹名字母序生成索引部署时CLASSES列表手写顺序不一致。解决训练完立刻把dataset.classes存成labels.json部署时读这个文件永远不要手敲类别顺序。这是血泪经验手敲迟早出错。5. 让垃圾分类 ONNX 更实用量化、验证与一个交付习惯模型能跑之后下一步通常是压体积、提速度。INT8 量化是最直接的手段onnxruntime 自带动态量化几行代码就能把模型砍掉一半以上。from onnxruntime.quantization import quantize_dynamic, QuantType quantize_dynamic( model_inputgarbage_cls.onnx, model_outputgarbage_cls_int8.onnx, weight_typeQuantType.QInt8, ) print(quantized)逻辑说明动态量化只量化权重激活值在推理时动态量化不需要校准数据集最省事。参数上weight_typeQInt8是 8 位整型体积约为 FP32 的四分之一。代价是精度可能掉 1 到 3 个百分点所以量化后必须重跑验证集对比量化前后的准确率。如果掉太多就改用静态量化并准备校准集或者只量化部分层。量化完别只看文件大小要拿一批真实图片跑一遍统计准确率和单张耗时做个前后对比表版本体积单张耗时(CPU)验证集准确率FP32 ONNX基准基准基准INT8 动态量化约 1/4通常降 30%~50%可能降 1~3 个点这张表要自己实测填别抄别人的数字硬件不同差异很大。我一般会留一个benchmark.py每次换模型或换量化策略都跑一遍数据说话。最后说个交付习惯把.onnx、labels.json、预处理参数、一个最小推理示例脚本打包在一起写清楚输入尺寸和均值方差。我吃过亏——模型交付出去对方预处理用了[0,0,0]均值结果准确率惨不忍睹来回扯皮好几天。从那以后凡是交付 ONNX我一定附一个能直接跑的infer.py让对方先跑通再谈集成。垃圾分类这种项目模型只是中间产物能稳定跑起来、结果可复现才算真正做完。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Redis分布式锁从原理到实战:手写实现与Redisson最佳实践

Redis分布式锁从原理到实战:手写实现与Redisson最佳实践

上上周刚给团队排查完一个库存超卖的事故,最后定位在分布式锁上。其实这类问题很常见:两个服务实例同时扣库存,读-改-写不是原子操作,锁又没锁住关键代码,数据就乱了。这篇文章我想把 Redis 分布式锁这条路完整走一遍&…

2026/10/3 3:03:59 阅读更多 →
Python图像处理实战:水色图像水质评价与分类模型

Python图像处理实战:水色图像水质评价与分类模型

简介:这份资源面向环保监测、计算机视觉方向的初学者与工程实践者,围绕“如何用Python从水色图像中自动评价水质”这一实际问题展开。内容涉及OpenCV与PIL图像读写、灰度化、直方图均衡化、高斯去噪等预处理手段,并延伸到RGB转HSV色彩空间分析…

2026/10/3 3:03:59 阅读更多 →
奶茶饮品DIY+订单管理系统:毕业设计全流程实战解析

奶茶饮品DIY+订单管理系统:毕业设计全流程实战解析

做计算机毕业设计选到这个方向,说明你眼光不错。奶茶饮品DIY 订单管理,这种系统放在就业市场和毕业答辩里都很能打,因为它是典型的“电商交易闭环”代表,涉及用户端、管理端、订单流转、商品定制、库存联动,一套走下来…

2026/10/3 3:03:59 阅读更多 →

最新新闻

Flask-JWT-Extended 实战:从 Token 签发到黑名单管理的完整指南

Flask-JWT-Extended 实战:从 Token 签发到黑名单管理的完整指南

做后端接口开发,绕不开认证授权这件事。早期做 Flask 项目,大家习惯用 session 加 cookie,后来前端分离、移动端兴起,token 变成了主流。而 Flask-JWT-Extended 这个库,几乎是我见过在 Flask 生态里把 JWT 做得最省心的…

2026/10/3 4:23:20 阅读更多 →
对率回归决策树:原理、手写实现与常见坑

对率回归决策树:原理、手写实现与常见坑

简介:面向机器学习初学者的对率回归决策树Python实现,以经典西瓜数据集3.0为实验对象,调用sklearn.linear_model中的LogisticRegression完成模型训练。程序先将字符串型属性数值化,对连续属性进行离散化,再以分类正确率…

2026/10/3 4:23:20 阅读更多 →
Go复合数据类型详解:数组、切片、映射的底层原理与实战

Go复合数据类型详解:数组、切片、映射的底层原理与实战

写Go基础系列已经到第四篇了。前几篇我们把变量、常量、基本数据类型、控制流都过了一遍,今天开始进入真正的“数据组织”环节:复合数据类型。这篇要讲透三个东西——数组、切片、映射,对应Go里的array、slice、map。很多人初学的时候都会有个…

2026/10/3 4:23:20 阅读更多 →
Claude Opus5.5 与 Claude Code 实战:从安装配置到本地模型接入的完整指南

Claude Opus5.5 与 Claude Code 实战:从安装配置到本地模型接入的完整指南

1. 从热搜词里读出的真实需求:大家到底在关心什么先把话说在前头,这篇不是官方发布稿的搬运,也不是把参数表念一遍。我拿到这个标题的时候,第一反应是去看那串热搜词——那才是真实用户在用脚投票的地方。你会发现一个很有意思的现…

2026/10/3 4:23:20 阅读更多 →
AD9694高速ADC调试实战:JESD204B链路与FPGA配置要点

AD9694高速ADC调试实战:JESD204B链路与FPGA配置要点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 4:23:19 阅读更多 →
100 kt/a丙烷制环氧丙烷工艺包:从Aspen Plus模拟到装配图与布置图全流程

100 kt/a丙烷制环氧丙烷工艺包:从Aspen Plus模拟到装配图与布置图全流程

简介:本资源为大学生化工竞赛国赛「东华科技杯」100kt/a丙烷制环氧丙烷项目的完整设计成果包,面向化工类参赛选手、指导教师及工艺设计学习者,可用于赛题复盘、工艺方案参考与工程制图学习。包内共473个文件,涵盖pdf、doc、edr、f…

2026/10/3 4:22:19 阅读更多 →

日新闻

把回忆蒸馏成 AI 的浪漫实验:为什么你需要前任.skill 完整指南

把回忆蒸馏成 AI 的浪漫实验:为什么你需要前任.skill 完整指南

把回忆蒸馏成 AI 的浪漫实验:为什么你需要前任.skill 完整指南 【免费下载链接】ex-skill 前任 skill 项目地址: https://gitcode.com/gh_mirrors/exsk/ex-skill 前任.skill 是一个运行在 Claude Code 上的开源 Skill:导入微信、iMessage、短信、…

2026/10/3 0:00:27 阅读更多 →
45个经典Linux面试题:从命令到网络排障的完整考点解析

45个经典Linux面试题:从命令到网络排障的完整考点解析

刚开始带应届生的时候,我最头疼的就是他们拿着一摞Linux面试题背得滚瓜烂熟,一上机全露馅。后来自己从被面的人变成面别人的人,才慢慢摸清楚:Linux面试题考的根本不是答案本身,而是你面对一个不确定的系统问题时&#…

2026/10/3 0:01:28 阅读更多 →
SAP生产预留实战指南:MB21/MB23/MB25协同与MRP集成

SAP生产预留实战指南:MB21/MB23/MB25协同与MRP集成

简介:本资源是一份面向SAP ABAP开发人员、生产计划专员及ERP实施顾问的实操型操作指南,聚焦SAP生产预留核心业务场景,系统解决物料预留创建、查询、校验与批量处理等高频问题。文档以结构化方式覆盖预留背景原理、OMC2编码规则、工厂级参数配…

2026/10/3 0:01:28 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 19:40:48 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/1 19:41:40 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/10/1 20:05:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 5:26:06 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 6:09:11 阅读更多 →