Ente ML Playground:从模型探索实验到移动端 ONNX 优化产物的完整实践指南
2026/9/12 17:05:40 网站建设 项目流程

Ente ML Playground:从模型探索实验到移动端 ONNX 优化产物的完整实践指南

【免费下载链接】ente💚 End-to-end encrypted cloud for everything.项目地址: https://gitcode.com/GitHub_Trending/en/ente

导读

本文以 Ente 仓库中的 infra/ml/playground/README.md 为骨架,系统讲解 Ente 端到端加密照片库的机器学习基础设施中"模型探索与生产化准备"这一环节:playground目录承载了 MobileCLIP、YOLOv5Face、MobileFaceNet 等模型的探索性 Notebook、实验数据与可复现的模型优化脚本,最终产出被 Android/iOS 移动端直接消费的 ONNX 模型产物。读完本文,你将掌握如何用uv搭建并运行这套 Notebook 环境、理解每个模型在生产前经历了哪些 ONNX 图变换(batch 固定、PReLU/GELU 重写、opset 升级等),以及如何通过model_manifest.json校验优化产物的完整性与可复现性。

目录定位:探索与研究专用,不是运行时组件

playground 目录 在 infra/ml/README.md 中与test/明确分工:playground/仅用于探索性 Notebook 与模型准备工作,而test/承载 ML 索引一致性(parity)测试框架(Python 真值、桌面/移动端 runner、比较器与 CI 入口)。两个子区域使用独立的 Python 工程:playground 使用自己的 pyproject.toml,parity 测试配置保持在infra/ml根目录。

原文档明确了 playground 的四个组成:

目录/文件用途
CLIP/CLIP/mobileclip 相关的 Notebook 与实验
YOLOv5Face/YOLOv5Face Notebook 及相关资源(含pytorch_weights/权重目录)
data/Notebook 使用的本地样例图片(如singapore.jpgman.jpegpeople.jpeg
optimizations/可复现的模型优化脚本及其最终产物

注意这个"探索"定位:Notebook 负责把 PyTorch 模型导出为 ONNX、验证其正确性;而真正进入生产的图变换逻辑收敛在optimizations/下的可复现脚本中,二者解耦,避免实验代码污染生产路径。

环境搭建与运行 Notebook

原文档给出了三步启动流程,结合 pyproject.toml 可还原完整依赖背景。

步骤 1:安装 uv

uv是项目选定的 Python 依赖/虚拟环境管理器(playground 的依赖锁在uv.lock中)。安装方式以 uv 官方文档为准,安装后在仓库任意位置执行uv命令均可。

步骤 2:从仓库根目录同步工程

uv sync --project infra/ml/playground

该命令会按pyproject.toml创建独立虚拟环境并安装全部依赖。核心依赖清单如下(版本约束取自 pyproject.toml):

  • 推理与模型操作onnx>=1.17.0onnxruntime==1.28.0(版本被精确固定)、onnxsim>=0.4.36onnxruntime-extensions>=0.12.0onnxconverter-common>=1.14.0
  • PyTorch 生态torch>=2.4.1torchvision>=0.19.1torchaudio>=2.4.1
  • 图像处理opencv-python>=4.10.0.84pillow>=10.4.0pillow-heif>=0.21.0(HEIF 解码,用于测试 HEIC 样例)
  • 数据与可视化numpy>=2.1.2pandas>=2.2.3scipy>=1.14.1matplotlib>=3.9.2seaborn>=0.13.2
  • 工具类pyyaml>=6.0.2requests>=2.32.3tqdm>=4.66.5thop>=0.1.1(模型 FLOPs 统计)
  • 内核ipykernel>=6.29.5(同时出现在依赖与[tool.uv] dev-dependencies中)

工程要求 Python>=3.12。需要注意 infra/ml/README.md 中的平台提醒:ONNX Runtime 1.28 未发布 Apple x86_64 二进制,因此在 macOS 上,parity 与 playground 的 Python 环境要求 Apple Silicon 且系统为 macOS 14 或更新。

步骤 3:选择内核运行

在 VS Code 或 Jupyter 中选择内核infra/ml/playground/.venv/bin/python即可运行CLIP/YOLOv5Face/下的 Notebook。

Notebook 卫生规范

原文档强调:提交前务必清空 Notebook 输出(Clear notebook outputs),以保持 diff 可读、稳定。这一点对含大体积 base64 图片输出与长日志的模型 Notebook 尤其重要——输出一旦入库,git diff将难以追踪真正的代码变更。

探索阶段:两个 Notebook 的模型准备实验

CLIP/YOLOv5Face/分别对应图像语义向量(CLIP)与人脸检测两个生产能力的模型来源。

MobileCLIP:图像/文本双模型导出

CLIP/mobileclip_onnx.ipynb 以 Apple 的mobileclip_s2为基准(引用论文 arXiv:2311.17049 与官方 ml-mobileclip 仓库),实验内容:

  • 拉取官方仓库与预训练权重(mobileclip_s2.pt等),加载模型与分词器;
  • 使用data/singapore.jpg等样例图验证预处理(MobileCLIP 图像输入为[1, 3, 256, 256])与文本编码(77 token 上限)的输出;
  • 通过EncodeImageWrapper/EncodeTextWrapper分别导出图像编码器(mobileclip_s2_image_float32.onnx)与文本编码器(mobileclip_s2_text_int64.onnx),opset 18(为 Resize 的抗锯齿语义)、开启常量折叠、指定input/output命名;
  • 对导出图做改名等调整(将输入重命名为og_input,为后续可容纳预处理子图的"alter model"预留input名),并用onnx.checker.check_model校验。

YOLOv5Face:人脸检测模型导出

YOLOv5Face/yoloface_onnx.ipynb 基于 deepcam-cn/yolov5-face(论文 arXiv:2105.12931):

  • 要求用户将 PyTorch.pt权重手动放入pytorch_weights/(即 YOLOv5Face/pytorch_weights);
  • 克隆官方仓库并复制models/utils/源码用于导出时的算子实现;
  • img_size=[640, 640]batch_size=1、静态 shape 导出 ONNX。

这两个 Notebook 共同揭示了生产模型的来源链路:PyTorch 权重 → 官方实现 → 静态 ONNX 导出,而进一步的生产化变换则交由optimizations/脚本完成。

生产化阶段:optimizations 目录的可复现优化

如果说 Notebook 是"探索",那么 optimizations/README.md 记录的则是"交付"。它承载 Ente 移动端 ML 模型的生产变换:生成的 CDN 产物写在models/下,ONNX 文件被.gitignore有意排除,而 models/model_manifest.json 记录可复现的输出元数据。

重建命令

从仓库根目录执行(--no-sync避免在纯重建场景重复同步依赖):

uv run --project infra/ml/playground --no-sync python \ infra/ml/playground/optimizations/optimize_models.py \ --source-dir infra/ml/test/.cache/local_model_mirror \ --output-dir infra/ml/playground/optimizations/models

脚本只执行被选中进入生产的变换,下面逐一拆解。

YOLO:batch 固定 + 常量折叠

  • 将 batch 维固定为 1(set_batch_one,见 optimize_models.py 中set_batch_one);
  • 用 ONNX Runtime 的 basic 优化器(ORT_ENABLE_BASIC+CPUExecutionProvider)对 shape 图做常量折叠。

源模型yolov5s_face_640_640_dynamic.onnx需匹配固定的 SHA-256(71a00870...),输出为yolov5s_face_640_640_static_b1.onnx。从 manifest 看,产物输入为[1, 3, 640, 640],输出[1, 25200, 16](640/8、640/16、640/32 三个尺度合计 25200 个候选框,每框 16 维),共 328 个节点,其中 Conv 64 个、Sigmoid 67 个。

MobileFaceNet:面向双端可移植的 PReLU 分解

这是三个模型中变换最密集的一个,核心动机记录在源码注释中(optimize_models.py 的rewrite_prelu_decompositions):

CoreML 不支持Abs,Android 的 WebGPU EP 不支持PRelu,而两者都支持恒等改写:PReLU(x, alpha) = Relu(x) - alpha * Relu(x * -1)

具体变换:

  1. PReLU 分解:将 33 个训练好的 PReLU 激活精确改写为上述Relu/Mul/Relu/Mul/Sub五算子组合(脚本断言必须恰好 33 个,否则抛错),避开 WebGPU 专有 kernel,同时使用 CoreML MLProgram 与 WebGPU 均支持的算子,使同一产物可同时被 Android 与 iOS 共享
  2. batch 固定为 1
  3. 隐式 padding 显式化:给 2 个未声明pads/auto_pad的 Conv 补上[0,0,0,0](CoreML 要求 ONNX 默认零填充显式化,make_implicit_conv_padding_explicit);
  4. 移除冗余的 L2 归一化:末尾的LpNormalization被替换为Identityremove_redundant_output_normalization),因为 Rust 调用方已自行执行输出 L2 归一化。

产物mobilefacenet_portable_static_b1.onnx输入[1, 112, 112, 3](NHWC),输出 192 维人脸嵌入,共 233 个节点,算子表印证了重写结果:66 个 Relu + 33 个 Sub + 66 个 Mul(不含 Conv 的 Mul),恰好对应分解后的正负两支。

MobileCLIP:opset 20 升级 + GELU 折叠

  • onnx.version_converter将图升级到 opset 20;
  • 将 54 处展开的精确 GELU 表达式(x/sqrt(2) -> Erf -> +1 -> *x -> *0.5子图)折叠为Gelu(approximate="none")rewrite_exact_gelu),脚本断言恰好 54 处。这样在保持 FP32/精确 GELU 语义的同时,把融合算子暴露给 CoreML 与 WebGPU。

产物mobileclip_s2_image_gelu_opset20.onnx输入[1, 3, 256, 256],输出 512 维图像嵌入,504 个节点,算子表含 54 个Gelu

产物元数据与校验

build_models阶段对每个源模型做 SHA-256 校验(与脚本头部常量比对,防止镜像被替换),最终写出 model_manifest.json,记录每个产物的:

  • 相对路径与 SHA-256、字节数、节点数;
  • 完整算子清单(按算子类型计数的 Counter,按名称排序);
  • 输入/输出张量的名称与 shape(含维度值或维度参数)。

该清单既是对 CDN 发布内容的机器可读校验依据,也是"ONNX 文件不入库"策略下保证可复现性的关键元数据。

产物验证:两个基准测试报告

optimizations/benchmark_reports/下两份报告验证了上述产物的运行时表现与平台差异,可作为理解"为什么需要这些优化"的实证材料。

移动端 ML 索引基准(2026-07-22)

20260722_FINAL_MOBILE_ML_BENCHMARK_REPORT.md 在 Pixel 8(Android 17,ONNX Runtime WebGPU)与 iPhone 15 Pro(iOS 26.5.2,ONNX Runtime CoreML)上以 14 张 parity 夹具图评测三模型管线:

  • iOS 持久化 CoreML 缓存收益显著:三模型总加载时间从缓存关闭的 8,595.5 ms 降至暖缓存 840.8 ms,降幅 90.2%(10.2 倍加速);冷启动(prime)为 4,306.4 ms;
  • 稳态端到端索引中位数:Pixel 8 为 12,994.3 ms(928.2 ms/图),iPhone 15 Pro 为 5,022.2 ms(358.7 ms/图),iPhone 端到端快 2.59 倍、Rust 管线内快 4.06 倍;
  • 正确性:移动端两机互比 14/14 通过;与 Python 真值对比 13/14,唯一差异是已知的IMG_8905.CR2(RAW 直解码失败后平台回退 JPEG 渲染,与 Python 直接解码 RAW 不同);
  • 报告建议:启用持久化 CoreML 缓存;将 RAW 回退转换列为独立优化目标;Rust 后处理已不再是瓶颈(Pixel 12.7 ms / iPhone 2.3 ms)。

基准日志通过编译期开关ENTE_ML_BENCHMARK_LOGGINGENTE_ML_BENCHMARK_RELEASE_TESTS=1选择性启用,正常构建不产生这些计时,机器可读产物(summary/results/logcat)位于infra/ml/test/out/下。

Android 调度器验证(2026-07-23)

20260723_ANDROID_ML_SCHEDULER_VERIFICATION_REPORT.md 追踪了 07-22 报告中"iPhone 远快于 Pixel"的成因,结论是Android 的 DVFS 与调度行为而非硬件差距:ML 管线每轮 ~240 ms GPU 推理后紧跟的短 CPU 突发,无法累积出足够的每线程利用率来提升频率。将全部管线工作集中到单一大核(变体 D,Cortex-X3,CPU8)后,预处理从 596.7 ms 降到 212.4 ms(2.8 倍),中位频率从 910 MHz 升到 2,363 MHz。报告据此提出生产建议:用单一持久化专用线程运行 ML 管线、将 CPU 预处理与 GPU 推理流水化、采用 ADPF(PerformanceHintManager)、移除fast_image_resize的 rayon 特性,并明确"硬性绑核只是诊断手段,不可直接上线"。

这两份报告共同说明了 playground 产物的下游验证闭环:优化脚本产出的 ONNX 被移动端消费后,其加载、推理与正确性均由基准与 parity 测试持续把关

与 Rust 运行时的衔接

优化产物最终进入 rust/crates/ml/src/assets.rs 维护的模型目录:该文件列出mobileclip_s2_image.onnxyolov5s_face_640_640_dynamic.onnxmobilefacenet_opset15.onnx等源模型(从https://models.ente.com/拉取并按内置目录的 key 与校验和校验),运行时通过selected_indexing_models(run_faces, run_clip, run_pets)按需选择模型资产。这与 playground 的工作形成清晰链路:Notebook 导出 →optimize_models.py生产化 → manifest 校验 → Rust 资产目录消费 → 移动端(WebGPU/CoreML)推理。关于 parity 框架与真值生成,可进一步阅读 infra/ml/test/README.md。

小结与最佳实践

  • 分层明确playground专注探索与模型准备,optimizations收敛可复现的生产变换,test负责一致性与正确性把关,三者各司其职;
  • 可复现性优先:源模型 SHA-256 强校验、固定onnxruntime==1.28.0、ONNX 产物不入库而以 manifest 记录元数据,共同保证"脚本重跑可得相同产物";
  • 跨端可移植是硬约束:MobileFaceNet 的 PReLU 分解与 MobileCLIP 的 GELU 折叠,本质都是为了让同一个 ONNX 文件同时满足 CoreML 与 WebGPU 的算子支持集,减少双端模型维护成本;
  • 基准驱动决策:CoreML 持久缓存、Android 调度行为等结论均来自真实设备基准报告,而非主观推测。

对希望为移动端自建 ML 索引能力的开发者而言,playground 目录提供了一个可复用的模板:探索 Notebook 与生产脚本分离、以 manifest 固化产物元数据、用基准报告驱动优化取舍。

【免费下载链接】ente💚 End-to-end encrypted cloud for everything.项目地址: https://gitcode.com/GitHub_Trending/en/ente

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

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

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

立即咨询