在 Android 与 iOS 上完成 ONNX 模型部署的完整指南
【免费下载链接】onnxruntimeONNX Runtime: cross-platform, high performance ML inferencing and training accelerator项目地址: https://gitcode.com/GitHub_Trending/on/onnxruntime
给相机 App 加人脸检测、给笔记 App 加文字识别,这类端侧推理需求现在大多绕不开 ONNX Runtime 移动端部署。它是微软开源的跨平台机器学习推理引擎:你导出一份标准 ONNX 模型,Android 和 iOS 各接一次,推理部分共用同一套模型资产。本文按"备模型 → 落平台 → 压测排障"的顺序走一遍,照着做完即可上线。
选型速览:为什么用 ONNX Runtime
本节回答一个问题:相比自己写推理循环或直接绑原生框架,ONNX Runtime 多了什么。
| 方案 | 模型迁移成本 | 硬件加速 | 算子覆盖 |
|---|---|---|---|
| 纯 CPU 自实现推理 | 每种框架各写一套 | 无 | 自己兜底 |
| Core ML / NNAPI 原生框架 | 模型需转换,双端各写一套 | 有,但绑定单一生态 | 受平台算子列表限制 |
| ONNX Runtime | 一份 ONNX 模型全端通用 | Execution Provider 按需切换 | 持续扩充,不支持算子自动回退 |
上图是它的核心机制:训练侧框架统一导出 ONNX 格式,部署侧通过Execution Provider(执行器,决定模型算子跑在哪种硬件上的执行后端)把图分发到 CPU、GPU、NPU。端侧对应的就是 Android 的 NNAPI 执行器和 iOS 的 Core ML 执行器,切换执行器不改动模型代码。
模型就绪之后,剩下的问题是"怎么喂给两端"。
第一步:模型准备与 ONNX 模型量化
本节解决三件事:从主流框架导出 ONNX、做 INT8 量化压缩、用官方工具校验可用性。
导出以 PyTorch 为例,torch.onnx.export指定 opset 后直接产出.onnx文件;TensorFlow/Keras 走tf2onnx或keras-onnx转换器。移动端建议导出时固定 batch 维,静态 shape 对 Core ML 执行器更友好。
量化是降低体积与内存占用的关键步骤,仓库内置了量化工具链,核心代码在 onnxruntime/python/tools/quantization 目录。静态量化的最小用法:
from onnxruntime.quantization import quantize_static, QuantType quantize_static( "mobilenetv2.onnx", "mobilenetv2_int8.onnx", calibration_table, per_channel=True, weight_type=QuantType.QInt8, )INT8 权重的代价是精度可能轻微下降,量化后务必用原模型的校验集对比精度,再决定是否采用。导出与量化都完成后,用官方校验工具确认模型能被当前 ONNX Runtime 解析:
python -m onnxruntime.tools.check_onnx_model mobilenetv2.onnx模型过了校验,接下来把它落到 Android 工程里。
Android 集成与 NNAPI 加速
本节解决 Android 端的依赖、模型装载与硬件加速开关。
依赖配置。在app/build.gradle中声明 onnxruntime-android 制品,版本号以仓库根目录的 VERSION_NUMBER 和 Maven Central 上的实际发布为准:
dependencies { implementation "com.microsoft.onnxruntime:onnxruntime-android:+:arm64" }模型放置。把mobilenetv2.onnx放进src/main/assets随 APK 打包;大模型(几十 MB 以上)建议改为首次启动时从 assets 解到应用私有目录,避免每次从压缩区读取。
核心推理代码。会话选项与推理的完整链路如下(Java,Kotlin 写法一致):
OrtEnvironment env = OrtEnvironment.getEnvironment(); OrtSession.SessionOptions opts = new OrtSession.SessionOptions(); opts.addNnapi(EnumSet.of(NNAPIFlags.USE_FP16)); // 开启 NNAPI 加速 OrtSession session = env.createSession(modelPath, opts); float[] input = preprocess(bitmap); // 预处理:缩放、归一化 long[] shape = {1, 3, 224, 224}; OnnxTensor inputTensor = OnnxTensor.createTensor(env, input, shape); try (OrtSession.Result result = session.run(Map.of("input", inputTensor))) { float[] scores = (float[]) result.get(0).getValue(); }NNAPI 是 Android 的神经网络 API,由系统把算子派发到设备的 NPU/DSP。注意它是"尽量使用"语义:设备不支持的算子会自动回退到 CPU,而不是加载失败。若某个调试场景必须确认算子全部落在 NNAPI 上,可以显式传入NNAPIFlags.CPU_DISABLED,此时有任何 CPU 实现参与的算子,模型加载会直接报错,方便定位。线程数用opts.setIntraOpNumThreads(n)按设备核心数调节,具体建议见 docs/Android_testing.md。
Android 跑通后,iOS 侧的接入路径类似。
iOS 集成与 Core ML 执行器
本节解决 iOS 端的依赖接入、Swift 推理代码与 Core ML 执行器的版本适配。
依赖接入。用 CocoaPods 在Podfile写pod 'ONNXRuntime'后执行pod install;偏好 SPM 的项目直接在 Xcode 中添加 package 依赖即可,版本号以官方发布页为准。模型文件拖进 Xcode 工程并勾选 "Copy items if needed",运行时通过Bundle.main.url(forResource:withExtension:)拿到路径。
核心推理代码。Objective-C 层封装见 objectivec/ort_session.mm,Swift 侧这样使用:
let env = try ORTEnv(loggingLevel: .warning) let opts = try ORTSessionOptions() let mlOpts = ORTCoreMLExecutionProviderOptions() mlOpts.useCPUOnly = false try opts.appendCoreMLExecutionProvider(with: mlOpts) guard let url = Bundle.main.url(forResource: "mobilenetv2", withExtension: "onnx") else { fatalError("model not found") } let session = try ORTSession(env: env, modelURL: url, sessionOptions: opts)Core ML 执行器与 iOS 版本映射。Core ML 是苹果的系统级机器学习框架,执行器底层会把它编译为 Core ML 模型后交给 GPU/ANE 执行。版本对应关系以官方文档为准:Core ML 3 对应 iOS 13,Core ML 4 对应 iOS 14,Core ML 5 对应 iOS 15。createMLProgram选项需要 Core ML 5(iOS 15+),低版本设备加载会失败,所以建议用onlyEnableForDevicesWithANE控制只在 ANE 设备上启用,或按#available做运行时分支。选项定义可见 objectivec/include/ort_coreml_execution_provider.h。
两端代码都就位,最后一关是压测与排障。
双端压测与指标监控
本节给你一套可复制的压测方法和一张"优化前/优化后"对照表。
Android 端可以直接用仓库自带的onnx_test_runner在设备上跑 ONNX 测试集,把模型推到/data/local/tmp后执行,具体流程参考 docs/Android_testing.md:
adb push onnx_test_runner /data/local/tmp/ adb push mobilenetv2_int8.onnx /data/local/tmp/ adb shell "/data/local/tmp/onnx_test_runner /data/local/tmp/mobilenetv2_int8.onnx"iOS 端用 Instruments 组合三张表:Time Profiler 看单帧耗时,Allocations 看峰值内存,Energy Log 看能耗。日常监控抓三个指标就够:
- 延迟:推理前后各记一次时间戳,统计 P50/P95;
- 内存:Android 用
Debug.getNativeHeapAllocatedSize(),iOS 用 Allocations; - 功耗:Android Studio Energy Profiler / Instruments Energy Log。
优化时逐项记录,避免凭感觉调参:
| 优化项 | 预期影响 | 记录方式 |
|---|---|---|
| INT8 量化(ONNX 模型量化) | 模型体积、内存下降,延迟通常下降 | 量化前后各测一轮 P50/P95 |
调整setIntraOpNumThreads | 小 batch 下延迟敏感 | 扫 1/2/4/8 取最优 |
| 开启 memory arbitrator | 内存占用与碎片下降 | 对照 Allocations 峰值 |
具体数值取决于模型与机型,建议以实测数据回填这张表,内存优化细节可查 docs/Memory_Optimizer.md。
高频问题排障表
上线前把这几类高频问题过一遍,能省掉大量现场排查时间。
| 现象 | 原因与处理 |
|---|---|
| 模型加载 OOM / 崩溃 | 检查设备内存水位;确认 memory arbitrator 状态(docs/Memory_Optimizer.md);大模型考虑解包到磁盘再加载 |
| NNAPI 上个别算子异常缓慢 | 默认会自动回退 CPU,属正常行为;要定位具体算子可传NNAPIFlags.CPU_DISABLED强制报错 |
老设备/Core ML 版本低时appendCoreMLExecutionProvider失败 | 用#available(iOS 13.0, *)做运行时分支,失败时留 CPU 执行器兜底 |
| 动态 shape 输入下 Core ML 端性能波动 | 给onlyAllowStaticInputShapes置 true 强制静态,或导出时固定 batch 维 |
| 与第三方库链接符号冲突 | 用静态库集成时注意-force_load与符号前缀配置,详见 objectivec 目录 ReadMe |
排障表兜不住的问题,优先查仓库的 FAQ 文档,其中覆盖了大部分环境类疑难。
趋势与下一步
端侧推理的走向可以概括为三点:更多 NPU 执行器落地,模型一次导出、任意芯片加速;低代码路径成熟,训练框架直接产出可部署模型;动态 shape 支持增强,适配视频流等变分辨率场景。
模型上线只是起点。后续文章我们拆一遍 ONNX 模型量化的完整参数:校准集怎么选、per-channel 与对称量化的取舍、以及量化后精度掉点时怎么定位到具体算子。
【免费下载链接】onnxruntimeONNX Runtime: cross-platform, high performance ML inferencing and training accelerator项目地址: https://gitcode.com/GitHub_Trending/on/onnxruntime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考