ONNX Runtime:3步在Android和iOS跑通AI模型推理的实操指南
【免费下载链接】onnxruntimeONNX Runtime: cross-platform, high performance ML inferencing and training accelerator项目地址: https://gitcode.com/GitHub_Trending/on/onnxruntime
假设我们要给一个拍照应用加上图片识别能力,但又不想让用户等200ms以上。这里要用到的工具是ONNX Runtime:模型训练好之后导出成ONNX格式,一次导出,Android和iOS两端原生跑通。这篇文章记录我们完整走一遍移动部署的流程——装依赖、建会话、出推理结果。
它到底解决什么问题
一句话:ONNX Runtime就像一个多语言翻译官,把PyTorch/TensorFlow导出的模型统一"翻译"成手机硬件能直接执行的指令。它内部的核心概念是执行提供器(Execution Provider,简称EP):每种硬件加速通道对应一个EP,比如Android上的NNAPI、iOS上的Core ML、以及通用的XNNPACK(针对移动CPU优化过的内核库)。创建会话时,运行时会自动挑选支持模型算子的EP来执行,我们只需要声明"想用哪个",剩下的交给框架。
下图是官方文档里的执行提供器架构图,可以看到EP是挂在推理运行时下面的一层,同一套API对上层完全透明:
动手前准备 📦
先确认三样东西,都很轻:
- 模型文件:
.onnx格式的模型,比如MobileNetV2这类分类网络,大小通常几MB到几十MB。没有现成的话,用torch.onnx.export从PyTorch导出即可,详见 docs/FAQ.md。 - 开发环境:Android端用Android Studio(Java 11及以上),iOS端用Xcode + CocoaPods,ORT版本与仓库根目录的
VERSION_NUMBER(当前为1.30.0)保持一致。 - 依赖安装:
// app/build.gradle,Android端依赖 implementation 'com.microsoft.onnxruntime:onnxruntime-android:1.30.0'# Podfile,iOS端依赖 pod 'ONNXRuntime'门槛到这里就算过了,接下来是最核心的代码部分。
3步跑通第一个demo
步骤1:创建环境与SessionOptions
做什么:初始化全局环境,配置会话参数。
// 环境与会话选项(建议放在App启动时一次性完成) OrtEnvironment env = OrtEnvironment.getEnvironment(); // 全局单例 OrtSession.SessionOptions options = new OrtSession.SessionOptions(); options.addExecutionProvider( List.of(new OrtEpDevice(OrtProvider.NNAPI)), Map.of()); // 关键:声明优先用NNAPI加速怎么验证:env.getAvailableProviders()返回的列表里包含NnapiExecutionProvider,说明这个EP在当前构建里可用。
步骤2:加载模型,构建输入张量
做什么:把ONNX文件加载成会话,把预处理好的图像数据包成输入张量。
// 从assets读模型并创建会话 InputStream in = getAssets().open("mobilenet_v2.onnx"); byte[] model = in.readAllBytes(); OrtSession session = env.createSession(model, options); // 图像预处理成float数组(224x224x3转成NCHW布局) float[] pixels = preprocessImage(bitmap); // ... 省略归一化等细节 long[] shape = {1, 3, 224, 224}; OnnxTensor input = OnnxTensor.createTensor(env, pixels, shape);怎么验证:session创建成功且不抛OrtException,说明模型图解析、算子匹配都通过了。
步骤3:执行推理并读结果
做什么:按名字传入输入张量,拿到输出并解析。
Map<String, OnnxTensor> inputs = Map.of("input", input); // key必须是模型定义的输入名 OrtSession.Result outputs = session.run(inputs); float[] scores = (float[]) outputs.get(0).getValue(); outputs.close(); int topClass = argmax(scores); // 找到概率最高的类别怎么验证:topClass对着一张猫的照片应该稳定落在"猫"对应的类别号上,这就是最小闭环跑通了。
两端API同构,关键差异用这张表就够了:
| 对比项 | Android | iOS |
|---|---|---|
| 硬件加速EP | OrtProvider.NNAPI | Core ML(通过provider options配置) |
| 模型放置 | src/main/assets打包进APK | 拖进Xcode工程,走Bundle |
| 推理接口 | OrtSession.run(Map) | ObjCORTSession的runWithInputs:(Swift直接可用) |
| 底层实现 | java/src/main/java/ai/onnxruntime/OrtSession.java | objectivec/include/ort_session.h |
iOS侧大致长这样:
// 创建会话并启用Core ML(options里带coreml版本等配置) NSError *error = nil; ORTSession *session = [[ORTSession alloc] initWithEnvironment:env options:options handle:&error]; // runWithInputs: 传入ORTValueArray,返回输出,逻辑与Java版run对应让它跑得快 ⚙️
环境这块跑通了,下面是收益最明显的几个调优点:
- 算子内线程数:推理慢且CPU利用率低时,把
setIntraOpNumThreads设到接近物理核心数(大模型时留1-2核给系统)。
// 通常设为CPU核心数或略少 options.setIntraOpNumThreads(4);- 确认EP真的生效:创建会话后打印日志,确认子图被分配到NNAPI/Core ML而不是全部回落CPU;回落了就先检查算子覆盖率。
- 量化模型:CPU推理瓶颈明显时,在PC端离线做INT8量化,模型体积和内存占用都会降下来:
# 仓库自带工具,生成QDQ格式量化模型 python -m onnxruntime.quantize \ --input mobilenet_v2.onnx \ --output mobilenet_v2_int8.onnx \ --quant_format QDQ --per_channel- 图优化保持默认最高档:
setOptimizationLevel默认就是全部优化开启,别误关。
下图展示了图优化阶段做的算子融合与常量折叠,这就是"同一个模型跑得快"的主要来源:
踩坑记录 🐛
症状:创建NNAPI会话失败或大量算子回落CPU。原因:NNAPI支持的算子集合有限,模型里有未覆盖的算子。解法:用NnapiFlags相关的provider options调整CPU回落策略,配置细节见 onnxruntime/core/providers/nnapi/。
症状:
session.run抛异常,提示输入找不到。原因:传给run的key和模型定义的输入名对不上。解法:先通过会话的输入信息(ValueInfo/OnnxModelMetadata)打印真实名字,别手写。症状:第一帧推理特别慢,后面正常。原因:首次执行包含图优化、算子编译和内存分配。解法:会话创建和首次预热放在App启动阶段做,不要放在用户点击识别之后。
症状:加载大模型时OOM。原因:权重本身大,加上中间张量峰值内存。解法:优先上量化模型,内存相关选项参考 docs/Memory_Optimizer.md。
接下来可以学什么
- 真机测试方法:怎么把benchmark推到手机上跑,见 docs/Android_testing.md。
- 移动端CPU内核:XNNPACK背后的MLAS库做了大量手工优化,源码在 onnxruntime/core/mlas/,适合想抠极限性能的人。
- EP算子支持范围:想知道某个EP到底支持哪些算子,翻 docs/ContribOperators.md 和providers目录。
到这里,一个能跑的最小闭环就搭完了,剩下的就是按自己的模型和业务把线程数、量化格式、EP配置这几个旋钮调到位。
【免费下载链接】onnxruntimeONNX Runtime: cross-platform, high performance ML inferencing and training accelerator项目地址: https://gitcode.com/GitHub_Trending/on/onnxruntime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考