☰
ML-For-Beginners 实战扩展:用 ONNX Runtime 构建你的首个菜品推荐 Web 应用
2026/10/8 21:26:47 网站建设 项目流程

ML-For-Beginners 实战扩展:用 ONNX Runtime 构建你的首个菜品推荐 Web 应用

【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners

读完本章节的练习(分类模型训练 + ONNX 模型转换 + JavaScript 推理),你将掌握如何把一个 Scikit-learn 分类模型转化为浏览器可运行的推荐应用。本指南将引导你完成一次"从零到能跑"的课后作业——从复现训练流程、导出model.onnx,到编写index.html调用 ONNX Runtime Web 完成本地推理,最终做出一套可自定义扩展的菜品推荐器,并验证当前仓库中给出的参考实现的完整运行链路。

本作业要求你在已经掌握的技能之上进一步"迁移创造":把"用 ONNX Runtime + 转换后的 ONNX 模型构建 JavaScript 网页应用"这一套路,应用到新的推荐场景上(可以是宠物推荐、心情配乐推荐等)。仓库中的课程正文给出了完整的参考流程,本文将以"完成作业"为目标,把其中的每个环节拆解成可以照做的清单。

作业目标与评价标准

课程给出的作业原文(见 assignment.md)核心只有两条:

  1. 动手迁移:基于本课练习,你已经能用 Onnx Runtime 和转换后的 Onnx 模型搭建 JavaScript 网页应用。请尝试用本课程的数据集或其他来源的数据(记得注明来源)构建一个新的推荐系统。
  2. 发挥创意:例如,可以做一个根据性格属性推荐宠物的应用,或根据情绪推荐音乐类型的应用。

评分标准(Rubric)则直接决定了交付物形态:

标准优秀(Exemplary)合格(Adequate)需改进(Needs Improvement)
交付物网页应用与 notebook 均已呈现、文档清晰、可正常运行两者中缺一项或有缺陷两者缺失或有缺陷

换句话说,一份合格的作业 = 一个训练/转换模型的 notebook + 一个能跑通推理的网页应用 + 清晰的文档。本文接下来把"示例参考方案"完整复现一遍,作为你完成自定义推荐器的模板。

参考方案的整体架构

为了让"推荐器"可落地,课程选择了一条"轻前端"路线:模型在 Python 端训练并导出,推理全部发生在浏览器端。

  • 训练端:用 Scikit-learn 的 SVC 对清洗后的菜品数据训练一个多分类模型(区分 chinese/indian/japanese/korean/thai 五种菜系)。
  • 转换端:通过skl2onnx把模型转成 ONNX 格式,得到一个无 Python 依赖、跨平台的模型文件model.onnx。
  • 推理端:在浏览器里用 CDN 引入 onnxruntime-web,通过ort.InferenceSession加载模型,把用户勾选的食材编码成 380 维特征向量喂给模型,用返回的菜系标签完成"推荐"。

这种架构与课程前面的 UFO 回归 Web 应用(Flask + pickle 的全栈 Python 方案)形成对照:当你的技术栈要求是纯 JavaScript 应用时,ONNX 让你同样可以离线、本地地使用模型做推理。

课程把"食材 → 菜系"这种分类判断包装成"推荐"场景:当你看着冰箱里的剩菜、不知道该做什么菜系时,勾选手头食材,模型会告诉你这些组合更接近哪种菜系。下面逐环节复现。

环节一:训练并导出 ONNX 分类模型

1. 准备数据

复用本系列一直使用的清洗后菜品数据集 cleaned_cuisines.csv。先确认结构:该 CSV 第一列是无名索引列、第二列是cuisine标签列,随后是各食材列。仓库中的文件表头共 382 列,扣除前两列后正好剩下380 个食材特征列——这个数字在后面转换模型和编写前端时会反复用到。

notebook(见 notebook.ipynb)中,训练的第一步是引入skl2onnx,并读取数据:

!pip install skl2onnx import pandas as pd data = pd.read_csv('../data/cleaned_cuisines.csv') data.head()

skl2onnx的作用是把 Scikit-learn 模型序列化为 ONNX 格式。接着拆分特征与标签:

X = data.iloc[:, 2:] # 去掉索引列与 cuisine 标签列,得到 380 个特征 y = data[['cuisine']] # 标签列

2. 训练 SVC 模型

课程选用SVC(支持向量分类器),并指出它在此数据集上准确率表现良好。导入所需库并划分训练/测试集:

from 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 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

注意这里刻意设置了probability=True,这是为了让模型能输出概率(虽然本应用的推理只用到了最终标签)。构建并拟合模型:

model = SVC(kernel='linear', C=10, probability=True, random_state=0) model.fit(X_train, y_train.values.ravel())

其中kernel='linear'表示线性核、C=10是正则化强度、random_state=0保证结果可复现;y_train.values.ravel()用于把 DataFrame 标签展平成符合 Scikit-learn 输入要求的一维数组。

3. 验证模型质量

预测并打印分类报告:

y_pred = model.predict(X_test) print(classification_report(y_test, y_pred))

课程记录的一份输出大致如下,说明在五种菜系上整体准确率约为 0.79,宏平均与加权平均均约 0.79:

precision 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

注意:分类报告数值随随机切分浮动。如果你做自定义推荐器,可以在 notebook 中把这份报告作为"模型质量"的证据写进文档——这正好对应 Rubric 中"notebook 文档清晰、可运行"的要求。

4. 转换为 ONNX 模型

转换时必须声明正确的张量形状。本数据集有380 个食材特征,因此在FloatTensorType中标注[None, 380](None 表示样本数可变,运行时实际为 1):

from 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}} onx = convert_sklearn(model, initial_types=initial_type, options=options) with open("./model.onnx", "wb") as f: f.write(onx.SerializeToString())

这里的两个选项值得展开说明(skl2onnx的转换参数化机制):

  • zipmap: False:对分类模型,默认会附带一个 ZipMap 算子,把概率输出包装成"字典列表"结构。对本应用而言并不需要这种结构,关闭它可以让输出更简洁。
  • nocl: True:nocl指是否把类别信息包含进模型。将其设为True(不包含类别列表)可以显著减小模型体积。

运行整个 notebook 后,当前目录下会生成model.onnx,供下一步的网页应用调用。仓库中已保留了这份转换产物 model.onnx,可直接用于对照测试。

环节二:用 Netron 检查模型结构

ONNX 是二进制模型格式,在编辑器里无法直观阅读。课程推荐使用Netron这类模型可视化工具打开model.onnx,检查模型是否构建正确。你应当能看到:输入节点名为float_input、形状为 380 维,以及模型末尾的分类器/输出节点。

这一步在开发中很重要,因为它能帮你确认前端代码里要用到的输入张量名与形状——参考实现中feeds = { float_input: input }里的float_input正是训练阶段定义的名字,可以用 Netron 验证核对。如果是自建模型,务必用同样的方式确认输出节点名(例如label)与输入节点名,避免推理时报错。

环节三:编写浏览器端推理应用

1. 页面骨架与食材勾选区

在与model.onnx同一目录下创建index.html。先搭好 HTML 骨架:

<!DOCTYPE html> <html> <header> <title>Cuisine Matcher</title> </header> <body> ... </body> </html>

然后在body中放置一组代表食材的复选框。每个 checkbox 的value是该食材在 380 个特征中的索引下标,例如 apple 在按字母排序的食材列表中位于第 5 列(从 0 计数的下标 4)。仓库中的 ingredient_indexes.csv(共 380 个字段)正是用来查询"食材名 → 下标"的索引表:

<h1>Check your refrigerator. What can you create?</h1> <div id="wrapper"> <div class="boxCont"> <input type="checkbox" value="4" class="checkbox"> <label>apple</label> </div> <div class="boxCont"> <input type="checkbox" value="247" class="checkbox"> <label>pear</label> </div> <div class="boxCont"> <input type="checkbox" value="77" class="checkbox"> <label>cherry</label> </div> <div class="boxCont"> <input type="checkbox" value="126" class="checkbox"> <label>fenugreek</label> </div> <div class="boxCont"> <input type="checkbox" value="302" class="checkbox"> <label>sake</label> </div> <div class="boxCont"> <input type="checkbox" value="327" class="checkbox"> <label>soy sauce</label> </div> <div class="boxCont"> <input type="checkbox" value="112" class="checkbox"> <label>cumin</label> </div> </div> <div style="padding-top:10px"> <button onClick="startInference()">What kind of cuisine can you make?</button> </div>

这些示例食材取自参考实现 index.html,可与 ingredient_indexes.csv 中的字段逐一对应验证(例如第 4 个字段即apple)。

2. 引入 ONNX Runtime Web

在最后一个闭合</div>之后,通过 CDN 引入 onnxruntime-web,得到全局对象ort:

<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web@1.9.0/dist/ort.min.js"></script>

ONNX Runtime 让同一个 ONNX 模型可以运行在广泛的硬件平台上,并提供统一的调用 API;在浏览器场景中,它会在 WebAssembly 等后端上执行模型推理。

3. 编码特征与执行推理

接下来编写推理脚本。整体逻辑分为:初始化 380 维特征数组 → 监听复选框变化实时改写特征 → 校验是否至少勾选一项 → 加载模型并推理:

<script> const ingredients = Array(380).fill(0); const checks = [...document.querySelectorAll('.checkbox')]; checks.forEach(check => { check.addEventListener('change', function() { // 依据复选框勾选状态,把该食材下标处的特征置 1 或 0 ingredients[check.value] = check.checked ? 1 : 0; }); }); function testCheckboxes() { // 校验是否至少勾选了一个复选框 return checks.some(check => check.checked); } async function startInference() { let atLeastOneChecked = testCheckboxes() if (!atLeastOneChecked) { alert('Please select at least one ingredient.'); return; } try { // 创建推理会话并加载模型 const session = await ort.InferenceSession.create('./model.onnx'); const input = new ort.Tensor(new Float32Array(ingredients), [1, 380]); const feeds = { float_input: input }; // 喂入输入并运行 const results = await session.run(feeds); // 读取结果 alert('You can enjoy ' + results.label.data[0] + ' cuisine today!') } catch (e) { console.log(`failed to inference ONNX model`); console.error(e); } } </script>

这段代码在完成五件事,拆开来看:

  1. 构建特征向量:创建长度为 380、初始全 0 的数组。当某个食材复选框被勾选时,把该下标对应的位置改成 1;取消勾选则改回 0。这就是模型期望的"此样本是否含某食材"的多热编码。
  2. 监听复选框:把页面上所有.checkbox元素收集起来,逐一绑定change事件,实时同步ingredients数组。
  3. 输入校验:testCheckboxes()用Array.some()判断是否至少有一项被勾选;没有则弹窗提示并终止,避免把全 0 向量发给模型。
  4. 异步推理入口:点击按钮触发startInference(),勾选校验通过后进入 try 块。
  5. 推理例行程序:
    • 用ort.InferenceSession.create('./model.onnx')异步加载模型(./model.onnx需与页面同目录);
    • 用new ort.Tensor(new Float32Array(ingredients), [1, 380])构造形状为[1, 380]的张量;
    • 构造feeds = { float_input: input },键名必须与训练时定义的输入名一致;
    • session.run(feeds)执行推理,从results.label.data[0]取回预测的菜系标签并弹出提示。

环节四:本地运行与结果验证

在存放index.html的目录打开终端。确保全局安装了http-server,随后启动:

http-server

浏览器会自动打开 localhost 页面。勾选若干食材点击按钮,即可看到推荐的菜系结果。如果你希望立刻验证,可以参照仓库中的参考实现与现成的 model.onnx 启动同款应用。

运行前提:index.html与model.onnx必须处于同一目录,且推理脚本中的相对路径./model.onnx与输入名float_input需与实际模型一致。仓库中的 model.onnx 由训练阶段FloatTensorType([None, 380])生成,因此页面张量形状固定为[1, 380]。

作业扩展方向与完成清单

完成"示例复刻"后,作业要求你把它改造成属于你自己的推荐系统。方向提示:

  • 宠物推荐器:用"性格属性"做特征列、宠物品种做标签列,训练分类模型后同样转换 ONNX。
  • 音乐类型推荐器:用人情绪或音乐特征做输入、音乐流派做输出。
  • 也可以继续深挖食材主题,使用 ingredient_indexes.csv 的全部 380 个食材及其下标,探索"哪些风味组合能拼出某道国民菜"。

无论选哪个方向,对照 Rubric 自检交付物:

  1. notebook 完整可运行:包含数据来源说明、训练、分类报告、ONNX 转换与导出代码;
  2. 网页应用可用:index.html与model.onnx同目录,复选框值对应新数据集的真实特征下标,输入名/形状与新模型匹配;
  3. 两者都有清晰的文档:最好用项目 README 的写作风格说明运行方式与设计思路。

课程还建议继续阅读推荐系统领域的相关资料进行自我拓展。把上述三个环节完整走通、并把模型与页面参数一一对应检查,你就交付了一份符合"优秀"标准的作业,同时也掌握了"Python 训练 + ONNX 转换 + 浏览器推理"这一可复用到多种应用场景的完整链路。

【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询