Ultralytics YOLO27:

如何使用 Ultralytics 导出非 YOLO PyTorch 模型#

Ultralytics 在 ultralytics.utils.export 下提供独立的导出工具,可通过统一的接口封装多个后端。你可以将任意 torch.nn.Module 导出为 TorchScript、ONNX、OpenVINO、CoreML、Core AI、TensorFlow SavedModel、TensorFlow Frozen Graph、PaddlePaddle、MNN、NCNN 和 ExecuTorch,包括 timm 图像模型、torchvision 分类器和检测器,以及你自己的自定义架构,无需分别学习每个后端。

将 PyTorch 模型部署到生产环境,通常意味着要针对每个目标来回切换不同的导出工具:torch.onnx.export 用于 ONNX,coremltools 用于 Apple 设备,onnx2tf 用于 TensorFlow,pnnx 用于 NCNN,等等。每种工具都有自己的 API、依赖项细节和输出约定。这些工具将这一切统一为一种调用方式。

为什么使用 Ultralytics 导出非 YOLO 模型?#

  • **一种 API 支持 11 种格式:**只需掌握一种调用约定,无需学习十几种。
  • **与 YOLO 导出使用相同的代码路径:**所有 Ultralytics YOLO 导出都由同一套辅助函数支持。
  • 对于支持的格式,只需通过一个 quantize 参数即可进行 FP16 和 INT8 量化。
  • **可在 CPU 上运行:**导出步骤本身不需要 GPU,因此你可以在笔记本电脑上本地运行;Windows 不支持 CoreML 导出,而 Core AI 导出需要在 Apple 芯片设备上运行 macOS 26 或更高版本,或在运行 glibc 2.34 或更高版本的 x86_64 Linux 上使用 Python 3.11 到 3.14。

快速入门#

最快的方法是用两行代码导出为 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 专用属性。

格式函数安装输出
TorchScripttorch2torchscript()PyTorch 内置.torchscript 文件
ONNXtorch2onnx()pip install onnx.onnx 文件
OpenVINOtorch2openvino()pip install openvino_openvino_model/ 目录
CoreMLtorch2coreml()pip install coremltools.mlpackage
Core AItorch2coreai()pip install coreai-torch(Apple 芯片 macOS 26+、x86_64 Linux glibc 2.34+;Python 3.11-3.14).aimodel 目录
TF SavedModelonnx2saved_model()查看下方的详细要求_saved_model/ 目录
TF Frozen Graphkeras2pb()查看下方的详细要求.pb 文件
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddle_paddle_model/ 目录
MNNonnx2mnn()pip install MNN.mnn 文件
NCNNtorch2ncnn()pip install ncnn pnnx_ncnn_model/ 目录
ExecuTorchtorch2executorch()pip install executorch_executorch_model/ 目录
嵌入元数据

多个导出函数接受可选的 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)
导出前务必调用 `model.eval()`

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 不支持。

`BlobWriter not loaded` 错误

coremltools>=9.0 为 macOS 和 Linux 提供 Python 3.10–3.13 的 wheel。在更新的 Python 版本中,原生 C 扩展会加载失败。请使用 Python 3.10–3.13 导出 CoreML。

导出为 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 或更高版本的系统上进行,也可在运行 glibc 2.34 或更高版本的 x86_64 Linux 上使用 Python 3.11 到 3.14 进行(pip install coreai-torch);quantize=16 会生成一个接收 float16 输入的 FP16 资源文件,该资源文件可在 iOS 27 和 macOS 27 上运行。请参阅 Core AI 集成,其中还说明了会在加载时中止的 FP16 资源文件。

导出为 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 导出无法在 Python 3.13 或更高版本的 macOS 上运行;请在 macOS 上使用 Python 3.12 或更早版本,或改用 Linux。

Python 3.12 或更早版本的要求(Python 3.13 或更高版本的导出则需要 tensorflow>2.19.0、tf_keras>2.19.0、 onnx2tf>=2.3.0,<2.3.16 和 protobuf>=6.31.1,<7.0.0):

  • tensorflow>=2.0.0,<=2.19.0
  • onnx2tf>=1.26.3,<1.29.0
  • tf_keras<=2.19.0
  • sng4onnx>=1.0.1
  • onnx_graphsurgeon>=0.3.26
  • macOS 上的 ai-edge-litert>=1.2.0,<1.4.0(其他平台上的 ai-edge-litert>=1.2.0)
  • onnxslim>=0.1.82
  • onnx>=1.12.0,<2.0.0
  • protobuf>=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.py

导出为 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/
├── inference_model/
│   ├── model.json
│   └── model.pdiparams
├── model.pdparams
└── x2paddle_code.py

需要 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 集成指南。

验证导出的模型#

导出后,在发布前先验证其与原始 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 导出的结果与 PyTorch 相差约 1e-5 以内,而 TorchScript 的结果完全一致。不过,三种运行时仍可能以较低精度计算 FP32 导出模型,并产生接近 1e-2 到 1e-1 的结果:NCNN 会在支持 FP16 的 CPU 上启用 FP16 运算;MNNBackend 使用 precision="low" 加载模型;OpenVINO CPU 插件则会在某些硬件(例如 Apple 芯片)上,按照默认的 PERFORMANCE 执行模式自动以 FP16 运行。误差远高于该格式自身的基准值,可能意味着存在不受支持的算子、输入形状错误,或模型未处于评估模式。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 中各格式对应的类,正如上面的验证示例所示。每个类都接收导出的工件和设备,并可直接调用:

格式后端输入布局
TorchScriptTorchScriptBackendBCHW
ONNXONNXBackendBCHW
OpenVINOOpenVINOBackendBCHW
CoreMLCoreMLBackendBHWC
Core AICoreAIBackendBCHW
TF SavedModel、Frozen GraphTensorFlowBackendBHWC
LiteRTLiteRTBackendBCHW
PaddlePaddlePaddleBackendBCHW
MNNMNNBackendBCHW
NCNNNCNNBackendBCHW
ExecuTorchExecuTorchBackendBCHW

TensorFlowBackend 支持两种格式,默认使用 format="saved_model",因此对于冻结图,请传入 format="pb"。

通过 YOLO() 路径调用时,会自动为你处理以下三件事,而直接调用则不会:

  • 输入布局:CoreMLBackend 和 TensorFlowBackend 需要 BHWC 格式。请先使用 im.permute(0, 2, 3, 1) 进行转置;BCHW 张量会导致形状不匹配。
  • 自动微分:请将调用放在 torch.inference_mode() 中。TorchScriptBackend 返回的张量仍会携带梯度图。
  • 后处理:没有元数据时,后端会将 task 保留为 None,并将 names 设为空。LiteRTBackend 仍会假定任何 3-D 输出都包含 YOLO 边界框,并按图像尺寸对其进行反归一化;对于输出为 3-D 的非 YOLO 模型,这种处理是错误的。分类器 logits 等二维输出不受影响。

已知限制#

  • 多输入支持情况不一:torch2onnx、torch2openvino 和 torch2torchscript 可接受示例张量元组,用于多输入模型。torch2coreml、torch2coreai、torch2ncnn、torch2paddle 和 torch2executorch 则假定模型只有一个输入张量。
  • 仅限 YOLO 的格式:Axelera 和 Sony IMX500 导出需要 YOLO 专用模型属性,通用模型无法使用。
  • 平台专属格式:TensorRT 需要 NVIDIA GPU。RKNN 需要 rknn-toolkit2 SDK(仅限 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 还接受示例张量元组,用于多输入模型。

  • 所有受支持的格式(TorchScript、ONNX、OpenVINO、CoreML、Core AI、TF SavedModel、TF Frozen Graph、PaddlePaddle、MNN、NCNN、ExecuTorch)都可以在 CPU 上导出。导出过程本身不需要 GPU。TensorRT 是唯一需要 NVIDIA GPU 的格式。

  • 请使用最新版本。quantize 参数需要 Ultralytics >=8.4.81,Core AI 导出则需要 >=8.4.131(Linux 上或使用 coreai-torch>=0.4.3 时为 >=8.4.163)。

  • 可以。torchvision 分类、检测和分割模型都可以通过 torch2coreml 导出为 .mlpackage。对于图像分类模型,向 classifier_names 传入类别名称列表,即可将分类头嵌入模型中。请在 macOS 或 Linux 上运行导出。CoreML 不支持 Windows。有关 iOS 部署的详细信息,请参阅 CoreML 集成。

  • 是,若干格式支持量化。导出到 OpenVINO、CoreML 或 MNN 时,传入 quantize=16 可使用 FP16,传入 quantize=8 可使用 INT8;onnx2saved_model 接受 quantize=8,以生成 INT8 LiteRT 文件;NCNN 和 Core AI 默认导出 FP32,传入 quantize=16 可使用 FP16,但不支持 INT8。OpenVINO 中的 INT8 还需要 calibration_dataset 参数才能进行训练后量化。请参阅各格式的集成页面,了解量化的权衡。

  • 使用相同输入运行原始 PyTorch 模型和导出模型,然后比较输出。使用对应的后端加载导出文件(例如,用于 ONNX 的 ONNXBackend),并检查最大绝对差值。请根据该格式自身的基准值来判断差异:NCNN、MNNBackend 以及某些 CPU 上的 OpenVINO,可能会以较低精度运行 FP32 导出模型,结果接近 1e-2 到 1e-1;而大多数其他格式的结果则接近 1e-5。差异大得多,可能意味着存在不受支持的算子、输入形状错误,或模型未处于评估模式。请参阅验证导出的模型,查看可运行示例。

评论