ONNX 移动端侧模型实践:从转换到部署

随着移动端 AI 能力的不断提升,越来越多的模型开始从云端下沉到端侧运行。ONNX(Open Neural Network Exchange)作为业界主流的模型交换格式,为跨框架、跨平台的模型部署提供了便利。本文将分享 ONNX 模型在移动端部署的完整实践路径。

为什么选择 ONNX

在实际项目中,我们可能使用 PyTorch、TensorFlow、MXNet 等不同框架训练模型,但移动端推理引擎往往只支持特定格式。ONNX 的价值在于:

模型转换实战

PyTorch 导出 ONNX

以一个简单的图像分类模型为例:

import torch
import torchvision.models as models

# 加载预训练模型
model = models.mobilenet_v3_small(pretrained=True)
model.eval()

# 创建示例输入
dummy_input = torch.randn(1, 3, 224, 224)

# 导出 ONNX
torch.onnx.export(
    model,
    dummy_input,
    "mobilenet_v3.onnx",
    input_names=['input'],
    output_names=['output'],
    dynamic_axes={
        'input': {0: 'batch_size'},
        'output': {0: 'batch_size'}
    },
    opset_version=14,
    do_constant_folding=True
)
💡 关键参数说明:
  • opset_version:建议使用 12+,支持更多算子
  • dynamic_axes:支持动态 batch size,提高灵活性
  • do_constant_folding:常量折叠优化,减小模型体积

模型验证

导出后务必验证模型正确性:

import onnx
import onnxruntime as ort
import numpy as np

# 检查模型格式
onnx_model = onnx.load("mobilenet_v3.onnx")
onnx.checker.check_model(onnx_model)

# 对比推理结果
session = ort.InferenceSession("mobilenet_v3.onnx")
onnx_output = session.run(None, {"input": dummy_input.numpy()})

# PyTorch 原始输出
with torch.no_grad():
    torch_output = model(dummy_input)

# 验证数值一致性
np.testing.assert_allclose(
    torch_output.numpy(), 
    onnx_output[0], 
    rtol=1e-3, 
    atol=1e-5
)
print("✅ 模型验证通过!")

模型优化与量化

移动端资源有限,模型优化是必须的环节。

1. 模型简化

使用 onnx-simplifier 消除冗余算子:

# 安装
pip install onnx-simplifier

# 简化模型
onnxsim mobilenet_v3.onnx mobilenet_v3_sim.onnx

2. 量化压缩

INT8 量化可以显著降低模型体积和推理延迟:

from onnxruntime.quantization import quantize_dynamic, QuantType

# 动态量化(无需校准数据)
quantize_dynamic(
    "mobilenet_v3_sim.onnx",
    "mobilenet_v3_int8.onnx",
    weight_type=QuantType.QUInt8
)

量化效果对比:

模型版本 体积 精度损失 推理加速
FP32 原始 12.8 MB - 1.0x
FP32 简化 11.2 MB ~0% 1.1x
INT8 动态量化 3.2 MB ~0.5% 1.8x
INT8 静态量化 3.0 MB ~0.8% 2.5x

移动端推理框架选型

主流的移动端推理框架对比:

框架 平台支持 ONNX 支持 特点
ONNX Runtime Mobile Android / iOS ⭐⭐⭐⭐⭐ 官方支持,算子全,体积较大
NCNN Android / iOS ⭐⭐⭐⭐ 腾讯开源,轻量,无依赖
MNN Android / iOS ⭐⭐⭐⭐ 阿里开源,性能优秀
TFLite Android / iOS ⭐⭐⭐ Google 官方,生态完善
Paddle Lite Android / iOS ⭐⭐⭐ 百度开源,国产化友好

我的选择建议

Android 集成实战

1. 添加依赖

// build.gradle
dependencies {
    implementation 'com.microsoft.onnxruntime:onnxruntime-android:1.16.0'
}

2. 加载与推理

// Kotlin
class ONNXModel(private val context: Context) {
    private var session: OrtSession? = null
    
    fun init() {
        val env = OrtEnvironment.getEnvironment()
        val modelBytes = context.assets.open("mobilenet_v3_int8.onnx")
            .use { it.readBytes() }
        session = env.createSession(modelBytes)
    }
    
    fun infer(input: FloatArray): FloatArray {
        val inputTensor = OnnxTensor.createTensor(
            OrtEnvironment.getEnvironment(),
            input.reshape(intArrayOf(1, 3, 224, 224))
        )
        
        val output = session?.run(mapOf("input" to inputTensor))
        return output?.get(0)?.value as FloatArray
    }
    
    fun release() {
        session?.close()
    }
}

3. 性能优化技巧

🚀 实测有效的优化手段:
  • 使用 nnapi 后端加速(Android 8.1+)
  • 开启 CoreML 后端(iOS)
  • 输入图片使用 Bitmap 直接转 Tensor,避免多次拷贝
  • 推理线程绑定大核,提高调度优先级
  • 批处理多条数据,摊薄启动开销

常见问题与解决

Q1: 算子不支持怎么办?

部分自定义算子可能不被推理引擎支持,解决方案:

Q2: 量化后精度下降明显?

某些模型对量化敏感(如检测、分割),建议:

Q3: 首次推理慢?

推理引擎首次运行需要编译优化图:

总结

ONNX 为移动端 AI 部署提供了标准化的桥梁。实践中需要注意:

  1. 模型设计阶段就考虑端侧限制,避免复杂算子
  2. 量化要趁早,FP32 和 INT8 模型差异需要及时验证
  3. 推理框架选型要综合考虑包体积、性能、维护成本
  4. 持续监控端侧推理性能,不同设备差异很大

随着 NPU 的普及,端侧 AI 的潜力才刚刚开始释放。希望这篇实践总结能帮助你在移动端 AI 落地之路上少走弯路。