第三章 模型格式与转换:ONNX、TensorRT、Caffe、MindSpore Lite

模型格式这件事,说白了就是不同框架之间的「方言」。你想想看,PyTorch 训练出来的模型,总不能直接塞进华为的昇腾芯片里跑吧?中间必须有个翻译官。这个翻译的过程,就是模型转换。

我个人习惯把模型格式分成两类:一类是训练框架的原生格式,比如 PyTorch 的 .pth、TensorFlow 的 .pb;另一类是中间表示格式,比如 ONNX。真正做芯片适配时,我们打交道最多的其实是后者。

3.1 主流模型格式速览

先快速过一遍常见的格式,我挑几个最常用的讲。

格式 来源框架 特点 适用场景
ONNX 微软、Facebook 联合推出 开放标准,框架间互转首选 跨平台部署、硬件适配
TensorRT NVIDIA 专为 GPU 优化,支持 INT8/FP16 NVIDIA 显卡推理加速
Caffe 伯克利视觉中心 老牌框架,结构清晰 安防、人脸识别等传统场景
MindSpore Lite 华为 端侧推理,支持昇腾芯片 华为手机、IoT 设备

嗯,这里要注意:ONNX 虽然叫「开放神经网络交换格式」,但它并不是万能的。我在项目中遇到过好几次 ONNX 算子不支持的情况,尤其是自定义算子,转换时直接报错。

3.2 ONNX:模型转换的「通用语」

ONNX 为什么重要?因为它打通了训练和推理之间的壁垒。你拿 PyTorch 训练,导出 ONNX,然后就能转到 TensorRT、OpenVINO、NCNN 等各种推理后端。

导出 ONNX 其实很简单,核心就几行代码:

import torch
import torch.onnx

# 假设 model 是你的 PyTorch 模型
dummy_input = torch.randn(1, 3, 224, 224)
torch.onnx.export(
    model,
    dummy_input,
    "model.onnx",
    opset_version=11,
    input_names=["input"],
    output_names=["output"]
)

这里有个坑——opset_version 的选择。我刚开始做转换时,习惯用最新的版本,结果发现某些老芯片的驱动根本不支持。后来我学乖了,一般选 11 或 13,兼容性最好。

小技巧:导出 ONNX 后,记得用 onnxsim 工具简化一下模型结构。它能去掉一些冗余的节点,减少转换时的出错概率。

3.3 模型转换工具实战

接下来是重头戏。我挑三个最常用的转换工具,结合真实项目经验来讲。

3.3.1 onnx2trt:ONNX 转 TensorRT

TensorRT 是 NVIDIA 的推理优化引擎。说白了,它能把模型里的算子融合、量化、内存复用,让推理速度翻倍。

转换命令很简单:

# 基本用法
onnx2trt model.onnx -o model.trt

# 指定精度和批量大小
onnx2trt model.onnx -o model_fp16.trt --fp16 --maxBatchSize=8

我曾经在一个目标检测项目里,用 FP16 精度把推理延迟从 12ms 降到了 4ms。但要注意,不是所有模型都适合 FP16。有些模型对精度敏感,量化后 mAP 掉得厉害。我的建议是:先跑一遍 FP32 的基线,再对比 FP16 的效果。

避坑指南:我曾经遇到过 TensorRT 版本不兼容的问题。onnx2trt 工具和 TensorRT 库的版本必须严格对应,否则转换出来的模型跑起来直接崩。建议用 Docker 环境,省心很多。

3.3.2 atc:昇腾芯片的转换工具

ATC(Ascend Tensor Compiler)是华为昇腾芯片的模型转换工具。它支持将 Caffe、TensorFlow、ONNX 等格式转换为昇腾的 .om 格式。

# ONNX 转 OM
atc --model=model.onnx \
    --framework=5 \
    --output=model \
    --input_shape="input:1,3,224,224" \
    --soc_version=Ascend310

# Caffe 转 OM
atc --model=deploy.prototxt \
    --weight=model.caffemodel \
    --framework=0 \
    --output=model \
    --soc_version=Ascend310

这里 framework 参数的值要记清楚:0 是 Caffe,3 是 TensorFlow,5 是 ONNX。我刚开始用的时候,经常搞混这个数字,转换失败还找不到原因。

还有一个关键参数是 soc_version。不同昇腾芯片的指令集不一样,比如 Ascend310 和 Ascend910 的优化策略就不同。选错了,模型虽然能转,但推理效率会打折扣。

3.3.3 caffe2ncnn:老牌框架的移动端适配

NCNN 是腾讯开源的端侧推理框架,专门针对手机 CPU 优化。Caffe 模型转 NCNN 是很多安防项目的标配流程。

# 先转成 NCNN 格式
caffe2ncnn deploy.prototxt model.caffemodel model.param model.bin

# 然后可以进一步优化
ncnnoptimize model.param model.bin model_opt.param model_opt.bin 0

嗯,这里要注意:Caffe 的 prototxt 文件里,有些层 NCNN 不支持。比如 Deconvolution 层,NCNN 早期版本就没有。我遇到过这种情况,最后只能手动改写网络结构,用其他层替代。

核心要点:模型转换不是简单的格式翻译,而是算子映射和优化的过程。每个工具都有自己的算子支持列表,转换前一定要先查文档。

3.4 模型转换的通用流程

我总结了一套通用的转换流程,不管用什么工具,基本都适用:

  1. 导出中间格式:优先转成 ONNX,兼容性最好
  2. 简化模型:用 onnxsim 或 polygraphy 去掉冗余节点
  3. 精度验证:转换前后跑一遍推理,对比输出差异
  4. 性能调优:根据目标硬件调整精度和批量大小
  5. 集成测试:在目标设备上跑完整流程

你想想看,如果跳过精度验证这一步,模型转换后输出全是 NaN,你根本不知道是哪里出了问题。我曾经在一个语音识别项目里,就因为漏了这一步,排查了整整两天。

模型转换通用流程 导出中间格式 简化模型 精度验证 性能调优 集成测试 失败回退,重新调整 核心原则:先验证精度,再优化性能 ONNX / Caffe onnxsim / polygraphy 对比输出差异 FP16 / INT8 量化 目标设备上跑通

3.5 常见问题与避坑指南

最后分享几个我踩过的坑,希望能帮你省点时间。

  • 算子不支持:转换时报错 "Unsupported op"。解决办法是查文档,看目标框架是否支持该算子。如果不支持,要么替换成等效算子,要么自定义实现。
  • 精度下降:转换后模型输出和原来不一样。我建议先检查输入数据的预处理是否一致,比如归一化方式、通道顺序。很多时候问题出在这里。
  • 动态形状:ONNX 导出时如果用了动态 batch,某些推理框架可能不支持。我的习惯是固定 batch size,除非明确需要动态输入。
  • 版本兼容:不同版本的转换工具和推理库之间,经常有兼容性问题。我建议用 Docker 或者虚拟环境隔离,避免污染系统环境。
我的经验:模型转换这件事,80% 的时间花在调试上,20% 的时间花在转换上。别指望一次成功,多试几次,积累自己的「踩坑笔记」。

好了,模型格式和转换就讲到这里。记住一句话:没有万能的格式,只有合适的工具。选对工具,转换就成功了一半。

可可精品资料库