第三章 模型格式与转换: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,兼容性最好。
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 的效果。
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 模型转换的通用流程
我总结了一套通用的转换流程,不管用什么工具,基本都适用:
- 导出中间格式:优先转成 ONNX,兼容性最好
- 简化模型:用 onnxsim 或 polygraphy 去掉冗余节点
- 精度验证:转换前后跑一遍推理,对比输出差异
- 性能调优:根据目标硬件调整精度和批量大小
- 集成测试:在目标设备上跑完整流程
你想想看,如果跳过精度验证这一步,模型转换后输出全是 NaN,你根本不知道是哪里出了问题。我曾经在一个语音识别项目里,就因为漏了这一步,排查了整整两天。
3.5 常见问题与避坑指南
最后分享几个我踩过的坑,希望能帮你省点时间。
- 算子不支持:转换时报错 "Unsupported op"。解决办法是查文档,看目标框架是否支持该算子。如果不支持,要么替换成等效算子,要么自定义实现。
- 精度下降:转换后模型输出和原来不一样。我建议先检查输入数据的预处理是否一致,比如归一化方式、通道顺序。很多时候问题出在这里。
- 动态形状:ONNX 导出时如果用了动态 batch,某些推理框架可能不支持。我的习惯是固定 batch size,除非明确需要动态输入。
- 版本兼容:不同版本的转换工具和推理库之间,经常有兼容性问题。我建议用 Docker 或者虚拟环境隔离,避免污染系统环境。
好了,模型格式和转换就讲到这里。记住一句话:没有万能的格式,只有合适的工具。选对工具,转换就成功了一半。