4. 主流深度学习IR介绍
在自动驾驶模型部署工程中,中间表示(Intermediate Representation,IR)是连接训练框架与车载推理引擎的“通用语言”。不同 IR 的设计目标差异很大:有的强调跨框架互操作,有的强调图级优化,有的强调多后端代码生成,有的则试图成为编译器基础设施。理解它们的抽象层级、算子表达能力和部署链路位置,是做出正确技术选型的前提。
一、核心原理:五种IR的抽象层级与定位
ONNX 是一种开放的模型交换格式,以 protobuf 描述计算图,节点为算子,边为张量,强调“一次导出、多引擎加载”。TorchScript 是 PyTorch 的序列化图表示,通过 trace 或 script 将动态图固化为可独立执行的 IR,天然贴近 PyTorch 语义。TensorFlow GraphDef 是 TensorFlow 1.x 的计算图序列化格式,节点与属性描述完整,但生态绑定较强。TVM Relay 是 TVM 的高层函数式 IR,面向张量计算与自动调度,强调从图到硬件代码的端到端编译。MLIR 则是可扩展的编译器基础设施,通过多层 dialect 表达从高层算子到硬件指令的渐进下降。
关键判断:ONNX 解决“交换”,TorchScript 解决“PyTorch 固化”,GraphDef 解决“TF 图持久化”,Relay 解决“图到硬件的编译”,MLIR 解决“多层抽象的统一编译框架”。
二、方法流程:从训练框架到车载推理引擎
以典型的自动驾驶感知模型为例,部署流程通常包含导出、优化、量化、编译和运行五个阶段。不同 IR 在流程中的位置不同,决定了它们的适用边界。
- 导出阶段:PyTorch 模型通过
torch.onnx.export生成 ONNX,或通过torch.jit.trace生成 TorchScript;TensorFlow 模型通过freeze_graph生成 GraphDef。 - 图优化阶段:ONNX 可借助 onnx-simplifier 做常量折叠与算子简化;GraphDef 使用 Grappler 做布局优化与算子融合;Relay 使用 FoldConstant、FuseOps 等 Pass。
- 量化阶段:ONNX 支持 QDQ 格式量化;TorchScript 支持
torch.quantization;Relay 支持relay.quantize;MLIR 通过量化 dialect 表达。 - 编译阶段:Relay 通过 AutoTVM/Ansor 生成调度并编译为 CUDA 或 ARM 代码;MLIR 通过 Pass Pipeline 逐步下降至 LLVM IR。
- 运行阶段:ONNX 由 TensorRT、OpenVINO 或 ONNX Runtime 加载;TorchScript 由 LibTorch 或 Torch-TensorRT 执行;GraphDef 由 TF-TRT 或 TFLite 执行。
# ONNX 导出示例
torch.onnx.export(
model, dummy_input, "perception.onnx",
opset_version=13,
input_names=["camera"], output_names=["boxes", "scores"],
dynamic_axes={"camera": {0: "batch"}}
)
# TorchScript 导出示例
scripted = torch.jit.script(model)
scripted.save("perception.pt")
# Relay 加载 ONNX 并编译
mod, params = relay.frontend.from_onnx(onnx_model, shape_dict)
with tvm.transform.PassContext(opt_level=3):
lib = relay.build(mod, target="cuda", params=params)
三、案例/场景:自动驾驶中的典型选型
在量产自动驾驶项目中,选型往往由芯片工具链决定。若目标芯片是 NVIDIA Orin,TensorRT 对 ONNX 的支持最成熟,因此 ONNX 常作为首选交换格式;若团队以 PyTorch 为主且需要保留动态控制流,TorchScript 更合适;若使用 TensorFlow 训练且部署到 TFLite,GraphDef 是自然选择;若需要针对自研 NPU 做深度算子调度,TVM Relay 提供了从图到硬件的完整编译能力;若芯片厂商提供 MLIR 后端,则 MLIR 可作为统一接入层。
场景提示:多传感器融合模型常包含动态 shape 和自定义算子。ONNX 对动态 shape 支持较好,但自定义算子需要注册;TorchScript 对控制流支持更自然;Relay 和 MLIR 则允许通过自定义 dialect 或 intrinsic 表达专有算子。
四、对比清单:五种IR的适用性对比
| IR | 抽象层级 | 核心优势 | 主要局限 | 自动驾驶适用场景 |
|---|---|---|---|---|
| ONNX | 算子图 | 跨框架交换、生态广 | 自定义算子支持弱、opset 版本碎片化 | TensorRT/OpenVINO 部署、多框架模型统一 |
| TorchScript | 框架图 | 保留 PyTorch 语义、支持控制流 | 跨框架能力弱、优化空间有限 | PyTorch 原生部署、Torch-TensorRT 链路 |
| GraphDef | 框架图 | TF 生态完整、Grappler 优化成熟 | 生态绑定强、新框架支持弱 | TF-TRT、TFLite 车载部署 |
| TVM Relay | 函数式张量IR | 自动调度、多后端代码生成 | 算子覆盖需补齐、调试门槛高 | 自研 NPU、异构芯片深度优化 |
| MLIR | 多层 dialect | 可扩展、渐进下降、统一编译框架 | 学习曲线陡、生态仍在演进 | 芯片厂商编译器、统一接入层 |
五、误区与复盘
常见误区一:认为 ONNX 是“万能格式”。实际上 ONNX 的算子集由 opset 决定,不同推理引擎对同一 opset 的支持程度不同,导出后必须做算子兼容性校验。
常见误区二:忽视 TorchScript 的 trace 与 script 差异。trace 会丢失数据依赖的控制流,script 才能保留 if/for 等动态逻辑,自动驾驶中的后处理逻辑应优先使用 script。
常见误区三:把 Relay 和 MLIR 当作同类工具。Relay 是 TVM 的高层 IR,面向张量计算与调度;MLIR 是编译器基础设施,Relay 本身也可以作为 MLIR 的一个 dialect 存在,二者是不同层面的抽象。
复盘来看,IR 选型应遵循“芯片工具链优先、算子覆盖其次、团队熟悉度再次”的原则。ONNX 适合作为跨团队交换格式,TorchScript 适合 PyTorch 原生链路,GraphDef 适合 TF 存量项目,Relay 适合需要深度调度的自研芯片,MLIR 适合作为长期编译器基础设施。理解每种 IR 的抽象层级和部署链路位置,才能在自动驾驶多芯片、多框架的工程现实中做出稳健决策。