如何使用 Ultralytics 导出非 YOLO PyTorch 模型#
Ultralytics 提供独立的导出实用程序,位于 ultralytics.utils.export,这些实用程序将多个后端封装在一个统一的接口后方。你可以将任何 torch.nn.Module(包括 timm 图像模型、torchvision 分类器和检测器,或你自己的自定义架构)导出到 ONNX、TorchScript、OpenVINO、CoreML、NCNN、PaddlePaddle、MNN、ExecuTorch、Core AI、TensorFlow SavedModel 和 TensorFlow Frozen Graph,而无需分别学习每个后端。
将 PyTorch 模型部署到生产环境通常意味着要为每个目标处理不同的导出器:用于 ONNX 的 torch.onnx.export、用于 Apple 设备的 coremltools、用于 TensorFlow 的 onnx2tf、用于 NCNN 的 pnnx,等等。每个工具都有自己的 API、依赖项特性和输出约定。这些工具将它们统一为一种调用模式。
为什么使用 Ultralytics 导出非 YOLO 模型?#
- **横跨 11 种格式的单一 API:**只需学习一种调用约定,而不必学习十几种。
- 统一的工具接口: 导出辅助函数位于
ultralytics.utils.export下,因此安装后端软件包后,你可以在不同格式之间保持相同的调用模式。 - 与 YOLO 导出使用相同的代码路径: 所有 Ultralytics YOLO 导出都由相同的辅助函数提供支持。
- 内置 FP16 和 INT8 量化(适用于支持这些量化的格式:OpenVINO、CoreML 和 MNN;NCNN 和 Core AI 仅支持 FP16)。
- **可在 CPU 上运行:**导出步骤本身不需要 GPU,因此你可以在笔记本电脑上本地运行导出步骤;Windows 不支持 CoreML 导出,并且 Core AI 导出需要 Apple 芯片上的 macOS 26 或更高版本。
快速开始#
最快的方式是用两行代码将模型导出为 ONNX,无需 YOLO 代码,除了 pip install ultralytics onnx timm 外也无需其他设置:
import timm
import torch
from ultralytics.utils.export import torch2onnx
model = timm.create_model("resnet18", pretrained=True).eval()
torch2onnx(model, torch.randn(1, 3, 224, 224), output_file="resnet18.onnx")支持的导出格式#
torch2* 函数接收标准的 torch.nn.Module 和一个示例输入张量。MNN、TF SavedModel 和 TF Frozen Graph 会经过中间的 ONNX 或 Keras 工件。两种情况下都不需要 YOLO 特有的属性。
| 格式 | 函数 | 安装 | 输出 |
|---|---|---|---|
| ONNX | torch2onnx() | pip install onnx | .onnx 文件 |
| TorchScript | torch2torchscript() | 随 PyTorch 提供 | .torchscript 文件 |
| OpenVINO | torch2openvino() | pip install openvino | _openvino_model/ 目录 |
| CoreML | torch2coreml() | pip install coremltools | .mlpackage |
| TF SavedModel | onnx2saved_model() | 查看下面的详细要求 | _saved_model/ 目录 |
| TF Frozen Graph | keras2pb() | 查看下面的详细要求 | .pb 文件 |
| NCNN | torch2ncnn() | pip install ncnn pnnx | _ncnn_model/ 目录 |
| MNN | onnx2mnn() | pip install MNN | .mnn 文件 |
| PaddlePaddle | torch2paddle() | pip install paddlepaddle x2paddle | _paddle_model/ 目录 |
| ExecuTorch | torch2executorch() | pip install executorch | _executorch_model/ 目录 |
| Core AI | torch2coreai() | pip install coreai-torch(Apple 芯片上的 macOS 26+) | .aimodel 目录 |
MNN、TF SavedModel 和 TF Frozen Graph 导出会将 ONNX 作为中间步骤。先导出为 ONNX,然后再进行转换。
多个导出函数接受可选的 metadata 字典(例如 torch2torchscript(..., metadata={"author": "me"})),并会在格式支持的情况下将自定义键值对嵌入导出的工件。
分步示例#
下面的每个示例都使用相同的设置:处于评估模式的预训练 timm ResNet-18:
import timm
import torch
model = timm.create_model("resnet18", pretrained=True).eval()
im = torch.randn(1, 3, 224, 224)Dropout、批归一化和其他仅用于训练的层在推理期间的行为不同。跳过 .eval() 会生成输出不正确的导出模型。
导出为 ONNX#
from ultralytics.utils.export import torch2onnx
torch2onnx(model, im, output_file="resnet18.onnx")如需动态批大小,请传入一个 dynamic 字典:
torch2onnx(model, im, output_file="resnet18_dyn.onnx", dynamic={"images": {0: "batch_size"}})默认 opset 为 14,默认输入名称为 "images"。你可以使用 opset、input_names 或 output_names 参数进行覆盖。
导出为 TorchScript#
无需额外依赖。底层使用 torch.jit.trace。
from ultralytics.utils.export import torch2torchscript
torch2torchscript(model, im, output_file="resnet18.torchscript")导出为 OpenVINO#
from ultralytics.utils.export import torch2openvino
ov_model = torch2openvino(model, im, output_dir="resnet18_openvino_model")该目录包含一对固定名称的 model.xml 和 model.bin 文件:
resnet18_openvino_model/
├── model.xml
└── model.bin传入 dynamic=True 可使用动态输入形状,传入 quantize=16 可使用 FP16,或传入 quantize=8 进行 INT8 量化。INT8 还需要 calibration_dataset 参数。
需要 openvino>=2024.0.0(macOS 15.4 及更高版本上需要 >=2025.2.0)和 torch>=2.1。
导出为 CoreML#
import coremltools as ct
from ultralytics.utils.export import torch2coreml
inputs = [ct.TensorType("input", shape=(1, 3, 224, 224))]
ct_model = torch2coreml(model, inputs, im, classifier_names=None, output_file="resnet18.mlpackage")对于分类模型,将类别名称列表传入 classifier_names,即可向 CoreML 模型添加分类头。
需要 coremltools>=9.0、torch>=1.11 和 numpy<=2.3.5。Windows 不支持。
coremltools>=9.0 为 macOS 和 Linux 提供 Python 3.10–3.13 的 wheel。在更新的 Python 版本中,原生 C 扩展无法加载。请使用 Python 3.10–3.13 导出 CoreML。
导出为 TensorFlow SavedModel#
TF SavedModel 导出会经过 ONNX 这一中间步骤:
from ultralytics.utils.export import onnx2saved_model, torch2onnx
torch2onnx(model, im, output_file="resnet18.onnx")
keras_model = onnx2saved_model("resnet18.onnx", output_dir="resnet18_saved_model")该函数返回一个 Keras 模型,并在输出目录中生成 FP32 和 FP16 LiteRT 文件(.tflite):
resnet18_saved_model/
├── saved_model.pb
├── variables/
├── assets/
├── fingerprint.pb
├── resnet18_float32.tflite
└── resnet18_float16.tflite传入 quantize=8 可在这些文件旁边添加 INT8 .tflite。
要求:
tensorflow>=2.0.0,<=2.19.0onnx2tf>=1.26.3,<1.29.0tf_keras<=2.19.0sng4onnx>=1.0.1onnx_graphsurgeon>=0.3.26- macOS 上使用
ai-edge-litert>=1.2.0,<1.4.0(其他平台上使用ai-edge-litert>=1.2.0) onnxslim>=0.1.82onnx>=1.12.0,<2.0.0protobuf>=5
导出为 TensorFlow Frozen Graph#
接着上面的 SavedModel 导出,将返回的 keras_model 转换为冻结的 .pb 图:
from pathlib import Path
from ultralytics.utils.export import keras2pb
keras2pb(keras_model, output_file=Path("resnet18_saved_model/resnet18.pb"))导出为 NCNN#
from ultralytics.utils.export import torch2ncnn
torch2ncnn(model, im, output_dir="resnet18_ncnn_model")该目录包含固定名称的 param 和 bin 文件,以及一个 Python 包装器:
resnet18_ncnn_model/
├── model.ncnn.param
├── model.ncnn.bin
└── model_ncnn.pytorch2ncnn() 首次使用时会检查 ncnn 和 pnnx。
导出为 MNN#
MNN 导出需要一个 ONNX 文件作为输入。先导出为 ONNX,然后再转换:
from ultralytics.utils.export import onnx2mnn, torch2onnx
torch2onnx(model, im, output_file="resnet18.onnx")
onnx2mnn("resnet18.onnx", output_file="resnet18.mnn")支持使用 quantize=16 进行 FP16,使用 quantize=8 进行 INT8 量化。需要 MNN>=2.9.6 和 torch>=1.10。
导出为 PaddlePaddle#
from ultralytics.utils.export import torch2paddle
torch2paddle(model, im, output_dir="resnet18_paddle_model")该目录包含 PaddlePaddle 模型和参数文件:
resnet18_paddle_model/
├── model.pdmodel
└── model.pdiparams需要 x2paddle,以及适用于你平台的正确 PaddlePaddle 发行版:
- CUDA 上的
paddlepaddle-gpu>=3.0.0,<3.3.0 - ARM64 CPU 上的
paddlepaddle==3.0.0 - 其他 CPU 上的
paddlepaddle>=3.0.0,<3.3.0
NVIDIA Jetson 不支持。
导出为 ExecuTorch#
from ultralytics.utils.export import torch2executorch
torch2executorch(model, im, output_dir="resnet18_executorch_model")导出的 .pte 文件会保存在输出目录中:
resnet18_executorch_model/
└── model.pte需要 torch>=2.9.0 和匹配的 ExecuTorch 运行时(pip install executorch)。有关运行时用法,请参阅 ExecuTorch 集成。
导出到 Core AI#
from ultralytics.utils.export import torch2coreai
torch2coreai(model, im, output_file="resnet18.aimodel").aimodel 资产是一个目录:
resnet18.aimodel/
├── main.mlirb
├── main.hash
└── metadata.json导出运行在 Apple 芯片上的 macOS 26 或更高版本(pip install coreai-torch)上,且 quantize=16 写入一个接受 float16 输入的 FP16 资产;该资产运行在 iOS 27 和 macOS 27 上。请参阅 Core AI integration,包括其中关于加载时中止的 FP16 资产的说明。
验证导出的模型#
导出后,在发布前验证其与原始 PyTorch 模型的数值一致性。使用来自 ultralytics.nn.backends 的 ONNXBackend 进行快速冒烟测试,可以比较输出,并及早发现跟踪或量化错误:
import numpy as np
import timm
import torch
from ultralytics.nn.backends import ONNXBackend
model = timm.create_model("resnet18", pretrained=True).eval()
im = torch.randn(1, 3, 224, 224)
with torch.no_grad():
pytorch_output = model(im).numpy()
onnx_model = ONNXBackend("resnet18.onnx", device=torch.device("cpu"))
onnx_output = onnx_model(im)[0]
diff = np.abs(pytorch_output - onnx_output).max()
print(f"Max difference: {diff:.6f}") # ~1e-6 for an FP32 ONNX export容差按格式分别设置,而不是全局统一设置。对于 ResNet-18,FP32 导出在 ONNX、TF SavedModel 和 LiteRT 中接近 1e-6,而 TorchScript 中正好为 0。NCNN 是例外,约为 1e-2:其 CPU 运行时默认启用 FP16 打包和运算,因此 FP32 导出仍会以半精度运行。远高于该格式自身基线的差异,通常表示存在不支持的算子、输入形状错误,或模型未处于评估模式。FP16 和 INT8 导出的容差更宽松。请使用真实数据而不是随机张量进行验证。
对于其他运行时,输入张量名称可能不同。例如,OpenVINO 使用模型的 forward 参数名称(通用模型通常为 x),而 torch2onnx 默认为 "images"。
运行导出的模型#
导出的非 YOLO 模型可以通过常规的 YOLO() API 重新加载。上面的导出不包含 Ultralytics 任务或输入尺寸元数据,因此请显式传入 task,并将 imgsz 设置为导出时所用示例张量对应的值:
from ultralytics import YOLO
results = YOLO("resnet18.onnx", task="classify")("path/to/image.jpg", imgsz=224)
print(results[0].probs.top1)当导出模型具有固定输入形状时,imgsz 很重要:上面的 ONNX 和 TF SavedModel 导出会拒绝默认值 640。上面的 TorchScript 和 NCNN 导出确实接受其他尺寸,但两个导出器都不保证这一点:它们都基于示例张量进行跟踪,因此将输入展平为 Linear 层的模型会保持固定形状。请检查你自己的导出模型。
随后,该值会向上取整为模型步幅的倍数;没有元数据时,模型步幅为 32。因此,200x200 的固定形状导出模型会接收 224x224 的输入并被拒绝,即使 imgsz=200 与其匹配。对于不是 32 的倍数的输入尺寸,请直接调用后端。
直接调用后端#
对于不使用 Ultralytics 预处理和后处理的原始张量,请使用 ultralytics.nn.backends 中按格式提供的类,就像上面的验证示例一样。每个类都接收导出的工件和设备,并且可以直接调用:
| 格式 | 后端 | 输入布局 |
|---|---|---|
| ONNX | ONNXBackend | BCHW |
| TorchScript | TorchScriptBackend | BCHW |
| OpenVINO | OpenVINOBackend | BCHW |
| CoreML | CoreMLBackend | BHWC |
| TF SavedModel、Frozen Graph | TensorFlowBackend | BHWC |
| LiteRT | LiteRTBackend | BCHW |
| NCNN | NCNNBackend | BCHW |
| PaddlePaddle | PaddleBackend | BCHW |
| MNN | MNNBackend | BCHW |
| ExecuTorch | ExecuTorchBackend | BCHW |
| Core AI | CoreAIBackend | BCHW |
TensorFlowBackend 支持两种格式,默认使用 format="saved_model",因此对于冻结图,请传入 format="pb"。
YOLO() 路径会为你处理、而直接调用不会处理的三件事:
- 输入布局:
CoreMLBackend和TensorFlowBackend需要 BHWC。请先使用im.permute(0, 2, 3, 1)进行转置;BCHW 张量会引发形状不匹配。 - Autograd:将调用封装在
torch.inference_mode()中。TorchScriptBackend返回的张量仍然携带梯度图。 - 后处理:没有元数据时,后端会将
task保留为None,并将names保持为空。LiteRTBackend仍会按照图像尺寸对任何 3-D 输出进行反归一化,假设其中包含 YOLO 框;对于具有 3-D 输出的非 YOLO 模型,这一假设是错误的。分类器 logits 等二维输出不受影响。
已知限制#
- 多输入支持不均衡:
torch2onnx和torch2openvino接受用于具有多个输入的模型的示例张量的元组或列表。torch2torchscript、torch2coreml、torch2ncnn、torch2paddle、torch2executorch和torch2coreai假定单个输入张量。 - ExecuTorch 需要
flatc: ExecuTorch 运行时需要 FlatBuffers 编译器。在 macOS 上使用brew install flatbuffers安装,在 Ubuntu 上使用apt install flatbuffers-compiler安装。 - 不嵌入元数据: 上面的导出不包含 Ultralytics 任务或输入尺寸元数据,因此
YOLO()无法推断这两者,需要显式传入。请参阅运行导出的模型。 - 仅限 YOLO 的格式: Axelera 和 Sony IMX500 导出需要 YOLO 特有的模型属性,通用模型无法使用。
- 平台特定格式: TensorRT 需要 NVIDIA GPU。RKNN 需要
rknn-toolkit2SDK(仅限 Linux)。Edge TPU 需要edgetpu_compiler二进制文件(仅限 Linux)。
结论#
这些工具可以通过统一的 API,将任意 PyTorch 模型从普通的 torch.nn.Module 转换为可部署的 ONNX、OpenVINO、CoreML、TensorFlow 或移动运行时工件。选择与你的目标硬件匹配的格式,验证与原始模型的数值一致性,然后按照匹配的集成指南执行特定运行时的部署步骤。
常见问题#
任意
torch.nn.Module。这包括来自 timm、torchvision 或任何自定义 PyTorch 模型的模型。导出前,模型必须处于评估模式(model.eval())。ONNX 和 OpenVINO 还接受多输入模型的示例张量元组。所有支持的格式(TorchScript、ONNX、OpenVINO、CoreML、TF SavedModel、TF Frozen Graph、NCNN、PaddlePaddle、MNN、ExecuTorch、Core AI)都可以在 CPU 上导出。导出过程本身不需要 GPU。TensorRT 是唯一需要 NVIDIA GPU 的格式。
使用 Ultralytics
>=8.4.38,其中包含ultralytics.utils.export模块以及标准化的output_file/output_dir参数。可以。torchvision 的分类、检测和分割模型可通过
torch2coreml导出到.mlpackage。对于图像分类模型,将类别名称列表传递给classifier_names,即可内置分类头。在 macOS 或 Linux 上运行导出。Windows 不支持 CoreML。请参阅 CoreML 集成 了解 iOS 部署详情。可以,支持多种格式。导出到 OpenVINO、CoreML 或 MNN 时,传递
quantize=16表示 FP16 或传递quantize=8表示 INT8;NCNN 和 Core AI 默认导出 FP32,采用quantize=16表示 FP16,并且没有 INT8 路径。OpenVINO 中的 INT8 另外需要一个calibration_dataset参数用于后训练量化。请参阅各个格式的集成页面了解量化权衡。在相同输入上运行原始 PyTorch 模型和导出的模型,然后比较输出。使用匹配的后端加载导出的文件(例如,ONNX 使用
ONNXBackend),并检查最大绝对差值。根据该格式自身的基线评估差异。对于上面的 ResNet-18 示例,FP32 ONNX、TF SavedModel 和 LiteRT 的数值接近1e-6,TorchScript 为0,NCNN 接近1e-2,因为其 CPU 运行时默认使用 FP16。差异大得多通常意味着存在不支持的算子、输入形状错误,或模型未处于 eval 模式。请参阅验证导出的模型查看可运行示例。