Ultralytics YOLO27: Запишись в список ожидания.
Get Started

Как экспортировать модели PyTorch, не относящиеся к YOLO, с помощью Ultralytics#

Ultralytics предоставляет автономные утилиты экспорта в пакете ultralytics.utils.export, которые объединяют несколько бэкендов за единым интерфейсом. Ты можешь экспортировать любые torch.nn.Module, включая модели изображений timm, классификаторы и детекторы torchvision или собственные архитектуры, в форматы TorchScript, ONNX, OpenVINO, CoreML, Core AI, TensorFlow SavedModel, TensorFlow Frozen Graph, PaddlePaddle, MNN, NCNN и ExecuTorch, не изучая каждый бэкенд отдельно.

Для развёртывания моделей PyTorch в промышленной среде обычно приходится использовать отдельный экспортёр для каждой целевой платформы: torch.onnx.export для ONNX, coremltools для устройств Apple, onnx2tf для TensorFlow, pnnx для NCNN и так далее. У каждого инструмента свои API, особенности зависимостей и соглашения о выходных данных. Эти утилиты объединяют всё это в единый способ вызова.

Зачем использовать Ultralytics для экспорта моделей, не относящихся к YOLO?#

  • Единый API для 11 форматов: освой один способ вызова вместо целой дюжины.
  • Тот же процесс экспорта, что и для YOLO: во всех случаях экспорта Ultralytics YOLO используются одни и те же вспомогательные функции.
  • Квантование FP16 и INT8 с помощью одного аргумента quantize для форматов, которые его поддерживают.
  • Работает на CPU: для самого экспорта GPU не требуется, поэтому его можно выполнять локально на ноутбуке; экспорт CoreML не поддерживается в Windows, а для экспорта Core AI нужна macOS 26 или новее на Apple silicon либо Linux x86_64 с glibc 2.34 или новее и 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 silicon, 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"})), который встраивает пользовательские пары «ключ — значение» в экспортированный артефакт, если формат это поддерживает.

Пошаговые примеры#

Во всех примерах ниже используется одна и та же конфигурация: предварительно обученная модель ResNet-18 из timm в режиме оценки:

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 (или >=2025.2.0 на macOS 15.4 и новее) и 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 доступны wheel-пакеты для Python 3.10–3.13 на macOS и Linux. В более новых версиях Python не удаётся загрузить нативное расширение на C. Для экспорта в CoreML используй Python 3.10–3.13.

Экспорт в 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 silicon под macOS 26 или новее либо на Linux x86_64 с glibc 2.34 или новее и Python 3.11–3.14 (pip install coreai-torch), а quantize=16 записывает артефакт FP16, принимающий входные данные float16; артефакт работает на 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, чтобы добавить к ним .tflite в формате INT8.

Экспорт TensorFlow не выполняется на macOS с Python 3.13 или новее; используй Python 3.12 или старше на macOS либо 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
  • ai-edge-litert>=1.2.0,<1.4.0 на macOS (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 для твоей платформы:

  • paddlepaddle-gpu>=3.0.0,<3.3.0 на CUDA
  • paddlepaddle==3.0.0 на CPU ARM64
  • paddlepaddle>=3.0.0,<3.3.0 на других CPU

Не поддерживается на 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 перед выпуском. Быстрый дымовой тест с ONNXBackend из ultralytics.nn.backends сравнивает результаты и помогает на раннем этапе выявить ошибки трассировки или квантования:

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-5 for an FP32 ONNX export
Ожидаемое отклонение

Для ResNet-18 большинство экспортов FP32 отличаются от PyTorch примерно на 1e-5 или меньше, а TorchScript совпадает точно. При этом три среды выполнения могут вычислять экспорт FP32 с пониженной точностью и давать отклонение около 1e-2–1e-1: NCNN включает арифметику FP16 на CPU с ее поддержкой, MNNBackend загружает модели с precision="low", а плагин CPU OpenVINO автоматически работает в FP16 в режиме выполнения PERFORMANCE по умолчанию на некоторых устройствах, например на чипах Apple. Отклонение, значительно превышающее базовое для самого формата, указывает на неподдерживаемые операции, неверную форму входных данных или то, что модель не переведена в режим оценки. Для экспортов FP16 и INT8 допуски шире. Проверяй модель на реальных данных, а не на случайных тензорах.

В других средах выполнения имя входного тензора может отличаться. Например, OpenVINO использует имя аргумента прямого прохода модели (обычно x для моделей общего назначения), тогда как значение torch2onnx по умолчанию — "images".

Запуск экспортированной модели#

Экспортированные модели, отличные от YOLO, загружаются обратно через стандартный API YOLO(). Приведенные выше экспорты не содержат метаданных о задаче 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 вызовет ошибку несоответствия формы.
  • Autograd: оборачивай вызовы в torch.inference_mode(). TorchScriptBackend возвращает тензор, в котором сохраняется граф градиентов.
  • Постобработка: без метаданных бэкенд оставляет task в виде None, а names — пустым. LiteRTBackend всё равно денормализует любой трехмерный выход, используя размер изображения, поскольку предполагает, что в выходе содержатся боксы YOLO. Для модели не на основе YOLO с трехмерным выходом это неверно. Двумерные выходы, например логиты классификатора, не затрагиваются.

Известные ограничения#

  • Неодинаковая поддержка нескольких входов: torch2onnx, torch2openvino и torch2torchscript принимают кортеж тензоров-примеров для моделей с несколькими входами. torch2coreml, torch2coreai, torch2ncnn, torch2paddle и torch2executorch предполагают наличие одного входного тензора.
  • Форматы только для YOLO: экспорт Axelera и Sony IMX500 требует специфичных для YOLO атрибутов модели и недоступен для моделей общего назначения.
  • Форматы для отдельных платформ: для TensorRT требуется GPU NVIDIA. Для RKNN требуется SDK rknn-toolkit2 (только Linux). Для Edge TPU требуется бинарный файл edgetpu_compiler (только Linux x86_64).

Заключение#

Эти утилиты позволяют преобразовать любую модель PyTorch из обычного torch.nn.Module в готовый к развертыванию артефакт ONNX, OpenVINO, CoreML, TensorFlow или мобильной среды выполнения через единый API. Выбери формат для целевого оборудования, проверь численное соответствие исходной модели, а затем выполни инструкции соответствующего руководства по интеграции, чтобы развернуть модель в нужной среде выполнения.

Часто задаваемые вопросы#

  • Любую 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 — единственный формат, для которого нужен GPU NVIDIA.

  • Используй последнюю версию. Для аргумента quantize нужна Ultralytics >=8.4.81, а для экспорта Core AI требуется >=8.4.131 (>=8.4.163 на Linux или с coreai-torch>=0.4.3).

  • Да. Классификаторы, детекторы и модели сегментации torchvision экспортируются в .mlpackage с помощью torch2coreml. Для моделей классификации изображений передай список имен классов в classifier_names, чтобы встроить классификационную голову. Выполняй экспорт в macOS или Linux. CoreML не поддерживается в Windows. Подробности развертывания на iOS см. в руководстве по интеграции CoreML.

  • Да, для нескольких форматов. При экспорте в OpenVINO, CoreML или MNN укажи quantize=16 для FP16 или quantize=8 для INT8; для файла LiteRT с INT8 укажи quantize=8 в onnx2saved_model. NCNN и Core AI по умолчанию экспортируют FP32, для FP16 укажи quantize=16, а вариант INT8 не предусмотрен. Для INT8 в OpenVINO также требуется аргумент calibration_dataset для квантования после обучения. О компромиссах квантования см. на странице интеграции каждого формата.

  • Запусти исходную модель PyTorch и экспортированную модель на одинаковых входных данных, а затем сравни результаты. Загрузи экспортированный файл с подходящим бэкендом (например, ONNXBackend для ONNX) и проверь максимальную абсолютную разницу. Сравнивай отклонение с базовым для самого формата: NCNN, MNNBackend и OpenVINO на некоторых CPU могут выполнять экспорты FP32 с пониженной точностью, давая отклонение около 1e-2–1e-1, тогда как у большинства других форматов оно близко к 1e-5. Значительно большее отклонение указывает на неподдерживаемые операции, неверную форму входных данных или то, что модель не переведена в режим оценки. Исполняемый пример см. в разделе «Проверка экспортированной модели».

Комментарии