4、模型转换入门:使用trtexec工具转换ONNX模型、TensorRT的精度模式(FP32/FP16/INT8)

好,咱们今天聊聊模型转换。说实话,这是很多刚接触TensorRT的同学最容易卡住的一步。你想想看,辛辛苦苦训练好的模型,怎么才能让它跑在嵌入式设备上?答案就是——转成TensorRT能吃的格式。

我个人习惯用trtexec这个工具。它虽然是个命令行工具,但功能非常强大。说白了,它就是TensorRT官方给我们的一把瑞士军刀,既能做模型转换,又能做性能测试。

4.1 准备工作:拿到你的ONNX模型

在开始之前,你得先有个ONNX模型。我建议你从PyTorch或者TensorFlow导出ONNX时,注意以下几点:

  • 固定输入尺寸:虽然ONNX支持动态尺寸,但TensorRT对固定尺寸的优化更好。我在项目中遇到过,动态尺寸的模型在Jetson Nano上推理速度慢了将近一倍。
  • 算子兼容性:不是所有ONNX算子TensorRT都支持。嗯,这里要注意,像ResizeScatterND这些算子,在旧版TensorRT里可能有问题。
  • 简化模型:用onnx-simplifier把模型过一遍,去掉那些冗余的节点。
小技巧:导出ONNX时,把opset_version设为11或13,这两个版本在TensorRT里兼容性最好。

4.2 初识trtexec:一行命令搞定转换

trtexec的使用方式非常简单。最基本的命令长这样:

trtexec --onnx=model.onnx --saveEngine=model.engine

就这么一行,就能把ONNX模型转成TensorRT的引擎文件。但实际项目中,我们很少用这么简单的命令。为什么?因为你需要指定精度模式。

4.3 精度模式:FP32、FP16、INT8怎么选?

这是面试时经常被问到的问题。我直接给你一张表,一目了然:

精度模式 存储大小 推理速度 精度损失 适用场景
FP32 基准(100%) 基准(1x) 精度要求极高的场景
FP16 约50% 约1.5~2x 几乎无感 大多数嵌入式场景
INT8 约25% 约2~4x 1%~3% 对带宽敏感、精度容忍度高的场景

你可能会问:「那我是不是直接上INT8就完事了?」别急,听我说完。

4.4 FP32模式:最稳妥的选择

FP32模式不需要任何额外参数。直接用:

trtexec --onnx=model.onnx --saveEngine=model_fp32.engine --fp32

其实--fp32是默认的,不写也行。但我个人习惯还是加上,显得专业嘛。

FP32的好处是零精度损失。如果你的模型在GPU上跑FP32精度是99.2%,转成TensorRT后还是99.2%。我在做医疗影像项目时,客户要求精度不能掉0.1%,那就只能用FP32。

4.5 FP16模式:嵌入式平台的黄金选择

FP16是我用得最多的模式。命令也很简单:

trtexec --onnx=model.onnx --saveEngine=model_fp16.engine --fp16

你想想看,在Jetson Nano上,FP16比FP32快了将近一倍,但精度几乎看不出差别。我做过一个分类模型,FP32精度是92.3%,FP16是92.1%,只掉了0.2%。这点损失换来的速度提升,太值了。

重点:FP16模式下,TensorRT会自动把能转成FP16的层都转过去。但有些层(比如某些激活函数)必须用FP32,TensorRT会保留它们的精度。

4.6 INT8模式:性能怪兽,但需要校准

INT8模式就有点讲究了。它需要你提供校准数据:

trtexec --onnx=model.onnx --saveEngine=model_int8.engine --int8 --calib=calibration_data

校准数据是什么?说白了,就是一小批有代表性的输入数据。我建议你用验证集里的几百张图片就够了。注意,校准数据要覆盖模型可能遇到的各种情况,不能全是同一类图片。

我曾经踩过一个坑:用全黑图片做校准,结果模型推理结果全乱了。后来才发现,校准数据分布太偏,导致量化参数算错了。

警告:INT8模式不是所有模型都适合。如果你的模型很小(比如MobileNet),INT8带来的加速可能不明显。另外,某些层(如Sigmoid、Tanh)在INT8下精度损失较大,建议先用FP16试试。

4.7 实战:三步完成模型转换

好,咱们来走一遍完整的流程。假设你有一个resnet50.onnx模型:

  1. 第一步:检查模型
trtexec --onnx=resnet50.onnx --shapes=input:1x3x224x224 --verbose

这个命令会检查ONNX模型能不能被TensorRT解析。如果报错,别慌,看日志里提示哪个算子不支持。

  1. 第二步:生成FP16引擎
trtexec --onnx=resnet50.onnx --saveEngine=resnet50_fp16.engine --fp16 --workspace=1024

--workspace参数指定了TensorRT在构建引擎时能用的最大显存,单位是MB。我一般设1024,够用。

  1. 第三步:验证引擎
trtexec --loadEngine=resnet50_fp16.engine --shapes=input:1x3x224x224

这个命令会加载引擎并跑一次推理,看看有没有报错。同时它还会输出推理延迟,你可以看看是不是达到了预期。

4.8 常见问题与避坑指南

我整理了几个新手最容易遇到的问题:

  • 「明明转成功了,但推理结果全是错的」:这通常是输入输出的数据排布问题。检查一下你的输入是NCHW还是NHWC,TensorRT默认用NCHW。
  • 「转换时提示显存不足」:增大--workspace参数,或者换个更大的GPU来转。转完的引擎文件可以在小显存设备上跑。
  • 「INT8模式精度掉得厉害」:试试用更多的校准数据,或者改用--int8 --fp16混合模式,让TensorRT自己决定哪些层用INT8。
我的经验:如果你不确定用哪种精度,先跑FP16。它是最平衡的选择。等模型跑通了,再尝试INT8看看能不能接受精度损失。

好了,模型转换这块就聊到这儿。下一章咱们会深入讲讲如何用C++ API加载和运行这些引擎文件。到时候你会发现,trtexec只是开胃菜,真正的硬核内容还在后面。