HuggingFace Transformers 模型导出 ONNX 完整指南:从 Optimum CLI 到内置 OnnxExporter 的源码级实践
在 Transformers 中,模型训练完成后的关键一步是序列化与部署:把 PyTorch(或 TensorFlow)模型导出为跨框架、可被专用硬件与运行时高效加载执行的中间格式。本文以 Transformers 官方文档《تصدير إلى ONNX(导出到 ONNX)》为核心,完整覆盖 ONNX 导出的两种官方路径——🤗 Optimum CLI 命令行导出与 optimum.onnxruntime 编程式导出,并结合当前仓库 src/transformers/exporters/ 目录中内置的 OnnxExporter 实现,深入讲解导出配置参数、验证流程与底层导出流水线的五阶段机制。读完后你将能够:用 CLI 一键导出 Hub 上的模型检查点、导出本地模型并指定任务头、用 ONNX Runtime 加载推理,并理解 OnnxConfig 各参数在源码中如何影响 torch.onnx.export 的行为。
一、为什么选择 ONNX:跨框架部署的中间表示
ONNX(Open Neural Network Exchange)是一个开放标准,它定义了一组通用的算子(op)和共享的文件格式,用于在 PyTorch、TensorFlow 等众多框架之间表示深度学习模型。当一个模型被导出为 ONNX 后,这些标准算子会被组织成一张计算图(通常称为中间表示,IR),该图描述了数据在神经网络中的完整流动路径。
由于 ONNX 图由标准算子与标准数据类型构成,框架之间的切换成本被大幅降低:例如,一个在 PyTorch 中训练的模型可以先导出为 ONNX,再导入 TensorFlow(反之亦然),而无需重写模型代码。
导出到 ONNX 之后,模型可以获得以下三类能力:
- 推理优化:通过图优化与量化等技术降低延迟与内存占用;
- ONNX Runtime 推理:通过
ORTModelForXXX系列类加载,它们遵循与 Transformers 中熟悉的AutoModel相同的 API 风格; - 优化的推理管道:使用与 Transformers
pipeline函数相同 API 的推理管线,直接替换推理链路。
🤗 Optimum 是 Transformers 的扩展框架,它的 exporters 模块提供了基于**配置对象(config object)**的 ONNX 导出能力:这些配置对象已为一批模型架构预置就绪,并且设计上可以方便地扩展到其他架构。完整的预置配置与任务清单可查阅 Optimum 官方文档。
版本适用前提:当前仓库中的内置导出器声明了测试版本
torch 2.12.0、onnx 1.21.0、onnxscript 0.7.0,且要求依赖torch、onnx、onnxscript三个包(见 OnnxExporter)。因此使用仓库内置导出路径时请确认环境版本。
二、用 Optimum CLI 导出 ONNX
2.1 安装与帮助
首先安装附加依赖:
pip install optimum-onnx
查看所有可用参数的两种方式:查阅 Optimum 文档,或在命令行直接查看帮助:
optimum-cli export onnx --help
2.2 从 Hub 导出模型检查点
要导出 Hub 上的一个检查点(例如 distilbert/distilbert-base-uncased-distilled-squad),运行:
optimum-cli export onnx --model distilbert/distilbert-base-uncased-distilled-squad distilbert_base_uncased_squad_onnx/
命令执行过程中会打印进度日志,最终显示生成的 model.onnx 文件保存位置,例如:
Validating ONNX model distilbert_base_uncased_squad_onnx/model.onnx...
-[✓] ONNX model output names match reference model (start_logits, end_logits)
- Validating ONNX Model output "start_logits":
-[✓] (2, 16) matches (2, 16)
-[✓] all values close (atol: 0.0001)
- Validating ONNX Model output "end_logits":
-[✓] (2, 16) matches (2, 16)
-[✓] all values close (atol: 0.0001)
The ONNX export succeeded and the exported model was saved at: distilbert_base_uncased_squad_onnx
这段日志值得逐行理解:导出后会自动执行数值一致性校验——ONNX 模型的输出名必须与参考模型(原始 PyTorch 模型)的输出名匹配(此处为 start_logits、end_logits),每个输出的张量形状必须一致((2, 16)),且所有数值在绝对误差容差 atol: 0.0001 内接近。这一步等价于对导出过程做了一次回归测试,确保 ONNX 图与原模型行为一致。
2.3 导出本地模型
当导出的是本地模型而非 Hub 检查点时,需要遵守两条约定:
- 先把模型权重文件与分词器文件保存在同一目录(记为
local_path); - 在 CLI 中把
local_path传给model参数(替代 Hub 检查点名),并显式提供--task参数。
optimum-cli export onnx --model local_path --task question-answering distilbert_base_uncased_squad_onnx/
如果不提供 task 参数,系统会自动推断任务,缺省为不带任何任务特定头的纯模型架构(例如不带问答头的 DistilBertModel)。支持的任务列表以 Optimum 文档中的任务管理器说明为准。
2.4 导出 TensorFlow 检查点
对 Hub 上的 TensorFlow 检查点,流程完全相同。例如导出来自 Keras 组织的纯 TensorFlow 检查点:
optimum-cli export onnx --model keras-io/transformers-qa distilbert_base_cased_squad_onnx/
2.5 用 ONNX Runtime 加载与推理
导出完成后,model.onnx 可以在任何支持 ONNX 标准的运行时/加速器上运行。以 ONNX Runtime 为例:
>>> from transformers import AutoTokenizer
>>> from optimum.onnxruntime import ORTModelForQuestionAnswering
>>> tokenizer = AutoTokenizer.from_pretrained("distilbert_base_uncased_squad_onnx")
>>> model = ORTModelForQuestionAnswering.from_pretrained("distilbert_base_uncased_squad_onnx")
>>> inputs = tokenizer("What am I using?", "Using DistilBERT with ONNX Runtime!", return_tensors="pt")
>>> outputs = model(**inputs)
注意 ORTModelForQuestionAnswering 遵循与 AutoModelForQuestionAnswering 一致的调用约定:分词后直接 model(**inputs),输出为包含 start_logits/end_logits 的标准输出对象。不同任务对应不同的 ORTModelForXXX 类(序列分类、因果语言模型等),选择依据就是导出时指定的 --task。
三、用 optimum.onnxruntime 编程式导出
如果不想走 CLI,也可以纯脚本方式完成导出。核心技巧是在 ORTModelForXXX.from_pretrained 中传入 export=True——此时 Optimum 会在内存中完成 PyTorch 模型到 ONNX 的转换,再调用 save_pretrained 落盘:
>>> from optimum.onnxruntime import ORTModelForSequenceClassification
>>> from transformers import AutoTokenizer
>>> model_checkpoint = "distilbert_base_uncased_squad"
>>> save_directory = "onnx/"
>>> # 加载 transformers 模型并导出为 ONNX
>>> ort_model = ORTModelForSequenceClassification.from_pretrained(model_checkpoint, export=True)
>>> tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
>>> # 保存 ONNX 模型与分词器
>>> ort_model.save_pretrained(save_directory)
>>> tokenizer.save_pretrained(save_directory)
该方式的好处是:导出逻辑可以嵌入训练流水线末尾、CI 脚本或模型发布流程中,与 Hub 上传步骤无缝衔接。保存目录中会包含 model.onnx(大权重模型还会伴随 .onnx_data 外部数据文件)以及分词器文件,后续部署只需拷贝整个目录。
四、深入仓库源码:内置 exporters 模块的 ONNX 导出机制
从源码结构看,当前仓库已将导出能力内置到 src/transformers/exporters/ 包中,提供 dynamo(torch.export 产物)、onnx、executorch 三种格式导出器,通过 AutoHfExporter 工厂统一分发(见 auto.py 中的 AUTO_EXPORTER_MAPPING)。这与文档中 Optimum"基于配置对象、可按架构扩展"的设计理念一脉相承,并且把扩展机制显式化:外部框架可以通过 register_exporter / register_export_config 注册自己的格式(见 auto.py)。
4.1 OnnxConfig:导出配置参数详解
OnnxConfig 是控制 ONNX 导出行为的配置类,定义在 configs.py,它继承自 DynamoConfig(torch.export 相关参数),完整参数及默认值如下表:
| 参数 | 类型 / 默认值 | 说明 |
|---|---|---|
output_path |
str / None |
.onnx 文件的输出路径。为 None 时导出的模型仅以内存中的 ONNXProgram 存在,不写磁盘 |
dynamic |
bool / False |
是否以动态(符号化)形状导出。为 True 且未显式指定 dynamic_shapes 时,所有张量维度自动标记为 Dim.AUTO |
strict |
bool / False |
是否启用 torch.export 严格模式:执行完整符号化追踪、捕获更多错误,但更慢且复杂模型更易失败 |
dynamic_shapes |
dict / None |
显式的逐输入动态形状规格,优先级高于 dynamic |
opset_version |
int / None |
目标 ONNX opset 版本;缺省时使用已安装 onnxscript 支持的最新 opset |
external_data |
bool / True |
大型权重张量是否存入独立的 .onnx_data 伴随文件而非内嵌 protobuf——权重超过 2GB protobuf 限制时必须启用 |
optimize |
bool / True |
导出后是否运行 onnxscript 优化通道(常量折叠、死代码消除等);命中上游优化器 bug 时可关闭 |
export_params |
bool / True |
是否把模型权重内嵌进 ONNX 图;设为 False 可导出无权重图(权重需运行时提供) |
keep_initializers_as_inputs |
bool / False |
是否把权重 initializer 暴露为显式图输入,部分 opset < 9 的旧运行时需要 |
OnnxConfig 还支持 to_dict() / from_dict() 往返序列化(见 ExportConfigMixin),export_format 字段保证从字典反序列化时能正确定位到 OnnxConfig 子类,这让"导出配方"可以被持久化、随模型一起分发。
4.2 导出流水线:五阶段转换机制
OnnxExporter(exporter_onnx.py)继承自 DynamoExporter,先经 torch.export 得到 ExportedProgram,再通过 torch.onnx.export 转为 ONNX。文件头部的模块 docstring 完整描述了 ONNX 专属的五阶段转换机制:
- Torch 补丁:通过
apply_patches("onnx")可逆地 monkey-patchtorch算子(如torch.where、torch.histc、torch.roll等数十处,见 exporter_onnx.py),让追踪期产生可被 ONNX 下放的算子模式;补丁在上下文退出时自动还原; - ONNX 内部钩子:钩住
torch.onnx私有入口_prepare_exported_program_for_export,确保第 3 阶段的 FX 节点修复在run_decompositions之后再次执行(注意源码中以警告标注:该钩子针对私有 API,PyTorch 升级后可能需要迁移钩点,见 exporter_onnx.py); - FX 节点修复:对
GraphModule做逐节点原地重写,剔除或替换 ONNX 无法下放的节点(别名节点、原地操作、死比较、_assert_*断言等,见 exporter_onnx.py); - ONNX 自定义翻译表:以 onnxscript 函数覆盖 torchlib 默认 lowering 中缺失或有 bug 的算子,当前覆盖
bincount、index_put、_grouped_mm、repeat_interleave、masked_fill、整型floordiv等(见_ONNX_TRANSLATION_TABLE); - ONNX IR 修复:导出后对
ONNXProgramIR 做原地修复以保证 ORT 兼容性,例如给TopK节点强制sorted=1(ORT CUDA EP 的要求,见 exporter_onnx.py)。
父类 DynamoExporter 的流水线(见 exporter_dynamo.py)还包含五个前置环节:扁平化 forward 签名、模型级可导出补丁、Cache 对象的 Pytree 注册、Dim.AUTO 动态形状自动推断、以及模型状态清理(防止 eager forward 的状态泄漏进追踪)。
OnnxExporter.export 的调用链可以概括为:
# 来自 OnnxExporter.export 的核心调用(src/transformers/exporters/exporter_onnx.py 第 116-134 行)
with patch_model_outputs(model) as (inputs_names, outputs_names), apply_patches("onnx"):
exported_program = super().export(model, sample_inputs, config=config) # torch.export
inputs_names, outputs_names = disambiguate_io_names(inputs_names, outputs_names)
apply_fx_node_fixes("onnx", exported_program.graph_module)
onnx_program = torch.onnx.export(
exported_program, args=(), f=config.output_path,
input_names=inputs_names, output_names=outputs_names,
kwargs=copy.deepcopy(dict(sample_inputs)),
custom_translation_table=_ONNX_TRANSLATION_TABLE,
opset_version=config.opset_version,
external_data=config.external_data,
export_params=config.export_params,
optimize=config.optimize,
)
apply_onnx_ir_fixes(onnx_program)
可以看到,OnnxConfig 中的 opset_version、external_data、export_params、optimize 等参数正是在这里逐项传入 torch.onnx.export,配置对象与底层行为是一一对应的。此外 patch_model_outputs 会把模型 forward 包装为返回扁平字典的形式并采集输入/输出张量名,disambiguate_io_names 则在输入输出重名时自动加 input. / output. 前缀消歧(见 exporter_onnx.py)。
官方用法示例(来自 OnnxExporter 类 docstring):
>>> from transformers.exporters.exporter_onnx import OnnxExporter
>>> from transformers.exporters import OnnxConfig
>>> exporter = OnnxExporter()
>>> onnx_program = exporter.export(model, inputs, config=OnnxConfig(dynamic=True))
>>> outputs = onnx_program(**inputs) # 内存中直接运行
>>> exporter.export(model, inputs, config=OnnxConfig(output_path="model.onnx")) # 保存到磁盘
相关的导出行为由 tests/exporters/test_export.py 与 tests/exporters/test_runtime.py 中的测试用例覆盖,可以作为验证导出正确性的参照。
五、导出未支持架构的模型:如何贡献
如果你希望为当前无法导出的模型添加支持:先确认目标架构是否已在导出器(Optimum 的 optimum.exporters.onnx,或本仓库 transformers.exporters 的注册表)中有配置对象;如果没有,可以直接向 Optimum 仓库贡献。从本仓库源码看,扩展点相当清晰:
- 新格式:继承
HfExporter后用register_exporter(name)注册到AUTO_EXPORTER_MAPPING; - 新配置:继承
ExportConfigMixin后用register_export_config(name)注册(见 auto.py); - ONNX 算子兼容问题:按需添加
@register_patch("onnx", ...)补丁、@register_fx_node_fix("onnx")节点修复,或向_ONNX_TRANSLATION_TABLE增加自定义翻译函数。
AutoHfExporter.from_pretrained(加载"随模型发布的导出配方")目前尚未实现,源码中明确标注为占位接口,预留了模型所有者在 Hub 上发布 export_config.json 的工作流(见 auto.py),可以推断这一机制将让导出配置像 config.json 一样随检查点分发。
六、总结:选择哪种导出路径
| 场景 | 推荐路径 | 要点 |
|---|---|---|
| 快速把 Hub 模型转成可部署的 ONNX | optimum-cli export onnx |
自动完成导出 + 数值校验;本地模型需同目录放权重与分词器,并用 --task 指定任务头 |
| 嵌入训练/发布流水线 | ORTModelForXXX.from_pretrained(..., export=True) |
脚本化,save_pretrained 落盘 |
| 需要精细控制 opset / 动态形状 / 大权重外置 | 仓库内置 OnnxExporter + OnnxConfig |
参数与 torch.onnx.export 一一对应,external_data=True 规避 2GB 限制 |
三条路径的共同出口是同一个产物:一个与参考模型数值一致(atol: 0.0001 校验通过)的 model.onnx(大模型伴生 .onnx_data),可被 ONNX Runtime 等任何 ONNX 运行时加载,并进一步做图优化与量化。文档中指向的 TFLite 导出流程属于同一 exporters 体系下的另一格式,可按相同思路查阅对应指南。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00