ML-For-Beginners 实战课:用 ONNX 把菜品分类模型做成「冰箱食材推荐」Web 应用 ML-For-Beginners 实战课用 ONNX 把菜品分类模型做成「冰箱食材推荐」Web 应用【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners本教程对应开源课程 ML-For-Beginners「4-Classification」模块的第 4 课4-Applied核心目标是用此前分类课程中学到的技术训练一个菜系分类模型将其导出为标准 ONNX 模型再用纯 JavaScript 构建一个「查看冰箱剩余食材、推荐可做菜系」的轻量 Web 应用。读完本文你将掌握如何用skl2onnx把 Scikit-learn 模型转换为 ONNX、如何用 Netron 可视化校验模型结构、以及如何用onnxruntime-web在浏览器中完成端到端推理。为什么要构建「应用型」机器学习系统将模型投入真实业务场景是机器学习落地中不可或缺的一环。ONNXOpen Neural Network Exchange提供了一种开放、跨框架的模型格式其 Web 运行时使得模型可以被直接放进浏览器执行——这也意味着应用可以在本地甚至离线环境中运行。课程在此前 3-Web-App/1-Web-App/README.md 一课中曾训练过一个关于 UFO 目击的回归模型将其用pickle序列化后在 Flask 应用中使用。但那套方案本质是一个全栈 Python 服务如果业务要求是纯 JavaScript 前端例如静态站点或移动端 WebView就需要一条模型训练一次、到处推理的新路径。本课正是这条路径的最小可行实现在 Python 中用清洗后的菜系数据训练一个分类模型用skl2onnx把它转换为model.onnx在浏览器中用onnxruntime-web加载模型做推理。预备知识训练数据的形态本课沿用整个分类系列使用的菜系数据集。核心数据文件位于 4-Classification/data/cleaned_cuisines.csv其结构为第 1 列行索引无实际意义第 2 列cuisine标签列取值为chinese/indian/japanese/korean/thai其余共380 列每一列是一种食材almond、apple、soy_sauce……单元格值为 0/1表示该菜谱是否用到该食材。也就是说每个样本是一个 380 维的 0/1 向量任务是根据使用了哪些食材预测所属菜系。这里380是后续 ONNX 转换与前端代码中反复出现的关键常数务必记住它的由来。训练一个菜系分类模型第 1 步读取数据并拆分特征/标签在 Jupyter 笔记本中逐步执行完整代码见 4-Classification/4-Applied/solution/notebook.ipynb。首先安装并导入依赖!pip install skl2onnx import pandas as pdskl2onnx用于把 Scikit-learn 模型转换为 ONNX 格式。随后读取 CSV并剔除前两列索引列与标签列得到特征矩阵Xdata pd.read_csv(../data/cleaned_cuisines.csv) data.head() X data.iloc[:,2:] X.head()将cuisine列单独取出作为标签yy data[[cuisine]] y.head()第 2 步进入训练流程选用此前课程验证过、精度良好的支持向量机分类器SVCfrom sklearn.model_selection import train_test_split from sklearn.svm import SVC from sklearn.model_selection import cross_val_score from sklearn.metrics import accuracy_score,precision_score,confusion_matrix,classification_report按 7:3 划分训练集与测试集X_train, X_test, y_train, y_test train_test_split(X,y,test_size0.3)创建并拟合模型注意三个关键超参数kernellinear线性核、正则化强度C10、probabilityTrue使模型可输出概率这为后续保留概率输出节点做准备random_state0保证结果可复现model SVC(kernellinear, C10, probabilityTrue,random_state0) model.fit(X_train,y_train.values.ravel())其中y_train.values.ravel()把 DataFrame 形状的标签压平为一维数组以满足fit的输入要求。调用predict()并输出分类报告评估模型质量y_pred model.predict(X_test) print(classification_report(y_test,y_pred))参考输出如下整体准确率约 0.79precision recall f1-score support chinese 0.72 0.69 0.70 257 indian 0.91 0.87 0.89 243 japanese 0.79 0.77 0.78 239 korean 0.83 0.79 0.81 236 thai 0.72 0.84 0.78 224 accuracy 0.79 1199 macro avg 0.79 0.79 0.79 1199 weighted avg 0.79 0.79 0.79 1199将 Scikit-learn 模型转换为 ONNX转换的关键是声明正确的输入张量形状。数据集中共有 380 种食材因此特征维度为 380from skl2onnx import convert_sklearn from skl2onnx.common.data_types import FloatTensorType initial_type [(float_input, FloatTensorType([None, 380]))] options {id(model): {nocl: True, zipmap: False}}各参数含义float_input为模型输入节点起的名字必须记住——稍后在前端feeds中要以同名键喂入数据FloatTensorType([None, 380])形状为[batch, 380]的浮点张量None表示批大小可变推理时每批只有一行[1, 380]options以id(model)为键的转换选项字典。zipmapFalse表示不生成 ZipMap 输出分类模型默认会附带一个把类别映射到概率的字典列表浏览器端并不需要移除可简化输出结构noclTrue表示不把类别信息内嵌进模型从而显著缩小模型体积——这正是前端results.label直接以字符串类别出现的替代方案的前提。执行转换并写入文件model.onnxonx convert_sklearn(model, initial_typesinitial_type, optionsoptions) with open(./model.onnx, wb) as f: f.write(onx.SerializeToString())运行完整个笔记本当前目录下即生成model.onnx。仓库中的成品示例保存在 4-Classification/4-Applied/solution/model.onnx。用 Netron 可视化校验模型ONNX 模型本质是二进制 protobuf在 VS Code 中无法直接看到其结构。行业研究者常用免费工具 Netron 打开model.onnx直观确认模型是否构建正确。从上方截图可以清晰看到本模型的完整计算图输入节点float_input维度标注?×380中间黑色核心节点SVMClassifier两个输出label预测的菜系类别与probabilities维度?×5对应 5 个菜系的概率分布。Netron 的价值不止于看一眼转换后输入节点名、输出节点名、张量形状都会在此如实呈现你可以据此核对前端代码中feeds的键名应为float_input以及输出字段label、probabilities。模型就绪后就可以用它构建一个实用性场景打开冰箱、勾选剩余食材让模型告诉你这些食材最可能做出哪个国家的菜。构建浏览器端的推荐 Web 应用在保存model.onnx的同一目录下新建index.html仓库内成品见 4-Classification/4-Applied/solution/index.html。整套页面只有一个文件不依赖任何构建工具。页面骨架与食材复选框先搭建 HTML 骨架!DOCTYPE html html header titleCuisine Matcher/title /header body ... /body /html在body中加入标题、一组食材复选框与触发按钮h1Check your refrigerator. What can you create?/h1 div idwrapper div classboxCont input typecheckbox value4 classcheckbox labelapple/label /div div classboxCont input typecheckbox value247 classcheckbox labelpear/label /div div classboxCont input typecheckbox value77 classcheckbox labelcherry/label /div div classboxCont input typecheckbox value126 classcheckbox labelfenugreek/label /div div classboxCont input typecheckbox value302 classcheckbox labelsake/label /div div classboxCont input typecheckbox value327 classcheckbox labelsoy sauce/label /div div classboxCont input typecheckbox value112 classcheckbox labelcumin/label /div /div div stylepadding-top:10px button onClickstartInference()What kind of cuisine can you make?/button /div复选框 value 的含义每个value是食材在数据集中对应列索引从 0 开始计数。以apple为例它在按字母排序的食材列表中位于第 5 列因此值是4对照 ingredient_indexes.csv 可以查到文件首行为按字母序排列的 380 种食材第二行即依次为0,1,2,…,379的索引。例如从上面输出可确认apple索引为 4、cherry为 77、cumin为 112、fenugreek为 126、pear为 247、sake为 302、soy_sauce为 327——与 HTML 中写死的 value 完全一致。想扩充更多食材时只需从该 CSV 查索引并复制一组 checkbox 即可。引入 onnxruntime-web在最后一个闭合/div之后加入脚本块。首先从 CDN 引入 Onnx Runtimescript srchttps://cdn.jsdelivr.net/npm/onnxruntime-web1.9.0/dist/ort.min.js/scriptOnnx Runtime 用于跨各类硬件平台运行 ONNX 模型内置推理优化并提供统一的调用 API。浏览器端加载该脚本后全局对象ort即可用。编写推理逻辑script const ingredients Array(380).fill(0); const checks [...document.querySelectorAll(.checkbox)]; checks.forEach(check { check.addEventListener(change, function() { // toggle the state of the ingredient // based on the checkboxs value (1 or 0) ingredients[check.value] check.checked ? 1 : 0; }); }); function testCheckboxes() { // validate if at least one checkbox is checked return checks.some(check check.checked); } async function startInference() { let atLeastOneChecked testCheckboxes() if (!atLeastOneChecked) { alert(Please select at least one ingredient.); return; } try { // create a new session and load the model. const session await ort.InferenceSession.create(./model.onnx); const input new ort.Tensor(new Float32Array(ingredients), [1, 380]); const feeds { float_input: input }; // feed inputs and run const results await session.run(feeds); // read from results alert(You can enjoy results.label.data[0] cuisine today!) } catch (e) { console.log(failed to inference ONNX model); console.error(e); } } /script逐段拆解这段代码发生了什么事构造 380 维输入向量const ingredients Array(380).fill(0);先建立长度为 380 的全 0 数组每个复选框监听change事件勾选时把ingredients[check.value]置 1取消勾选时置回 0即把我有哪些食材编码成一个 0/1 稀疏向量语义上与训练数据中每行 380 个 0/1 特征完全对齐。收集复选框并判断勾选状态[...document.querySelectorAll(.checkbox)]用展开运算符把 NodeList 转为数组testCheckboxes()通过checks.some(check check.checked)校验是否至少勾选了一项未勾选时弹出提示并提前返回。触发推理点击按钮调用async function startInference()。推理例程分为四步await ort.InferenceSession.create(./model.onnx)异步创建推理会话并加载模型new ort.Tensor(new Float32Array(ingredients), [1, 380])把普通数组包装成形状[1, 380]的Float32Array张量——1 表示批大小为 1const feeds { float_input: input };构造 feeds键名float_input必须与训练阶段initial_type中声明的输入名完全一致可用 Netron 复核此名称const results await session.run(feeds);把 feeds 送入模型并等待输出。读取结果results.label.data[0]取出预测类别并弹窗展示results.probabilities则为各菜系概率5 维可供需要置信度展示的进阶版本使用。提示由于推理时需要从同目录加载model.onnx涉及异步 fetch 与 WebAssembly 资源请勿直接双击file://打开页面务必通过本地 HTTP 服务访问以避免跨域限制导致加载失败。本地运行与效果验证在存放index.html的目录打开终端确保全局安装过 http-server 后执行http-server终端会输出本地地址形如http://127.0.0.1:8080浏览器打开即可看到应用。勾选若干食材例如fenugreek与cumin并点击 What kind of cuisine can you make?模型会返回最匹配的菜系至此你已经完成了一个真实可用的推荐 Web 应用模型训练、格式转换、结构校验、浏览器端推理全链路打通。挑战把系统扩展成真正的推荐器当前应用的食材复选框仍很有限。可继续基于 ingredient_indexes.csv 扩充更多食材及其索引探索哪些食材组合会触发特定国家菜系的推荐。更进一步的扩展方向包括展示probabilities输出给出 Top-3 候选菜系及置信度按国家/地区食材特征分组建复选框做成筛选器参考同一课程的 assignment.md用本课掌握的 训练 → 转 ONNX → onnxruntime-web 推理 方法论改造出宠物推荐器、按心情推荐音乐流派等全新推荐器注意评价标准要求同时交付带注释的 Web 应用与 notebook二者缺一不可。延伸阅读本课只是触及了食材推荐系统的皮毛推荐系统是 ML 应用中范例极其丰富的领域。可进一步阅读推荐引擎的构建方法如协同过滤、基于内容的推荐、混合推荐等经典主题深入理解其数据形态、相似度度量与冷启动等核心问题。关联资源一览均位于当前仓库内可直接打开对照本课英文原始文档4-Classification/4-Applied/README.md完整可运行笔记本4-Classification/4-Applied/solution/notebook.ipynb前端成品页面4-Classification/4-Applied/solution/index.html已导出的 ONNX 模型4-Classification/4-Applied/solution/model.onnx训练数据4-Classification/data/cleaned_cuisines.csv食材-索引对照表4-Classification/data/ingredient_indexes.csv前置课Flask pickle 回归应用3-Web-App/1-Web-App/README.md【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考