Как экспортировать модели 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, не требуются.
| Формат | Функция | Установка | Результат |
|---|---|---|---|
| TorchScript | torch2torchscript() | входит в состав PyTorch | файл .torchscript |
| ONNX | torch2onnx() | pip install onnx | файл .onnx |
| OpenVINO | torch2openvino() | pip install openvino | каталог _openvino_model/ |
| CoreML | torch2coreml() | pip install coremltools | .mlpackage |
| Core AI | torch2coreai() | pip install coreai-torch (Apple silicon, macOS 26+; x86_64 Linux, glibc 2.34+; Python 3.11–3.14) | каталог .aimodel |
| TF SavedModel | onnx2saved_model() | см. подробные требования ниже | каталог _saved_model/ |
| TF Frozen Graph | keras2pb() | см. подробные требования ниже | файл .pb |
| PaddlePaddle | torch2paddle() | pip install paddlepaddle x2paddle | каталог _paddle_model/ |
| MNN | onnx2mnn() | pip install MNN | файл .mnn |
| NCNN | torch2ncnn() | pip install ncnn pnnx | каталог _ncnn_model/ |
| ExecuTorch | torch2executorch() | 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)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.
Для 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.0onnx2tf>=1.26.3,<1.29.0tf_keras<=2.19.0sng4onnx>=1.0.1onnx_graphsurgeon>=0.3.26ai-edge-litert>=1.2.0,<1.4.0на macOS (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.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на CUDApaddlepaddle==3.0.0на CPU ARM64paddlepaddle>=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, как в приведенном выше примере проверки. Каждый класс принимает экспортированный артефакт и устройство, после чего его можно вызвать:
| Формат | Бэкенд | Формат входных данных |
|---|---|---|
| TorchScript | TorchScriptBackend | BCHW |
| ONNX | ONNXBackend | BCHW |
| OpenVINO | OpenVINOBackend | BCHW |
| CoreML | CoreMLBackend | BHWC |
| Core AI | CoreAIBackend | BCHW |
| TF SavedModel, Frozen Graph | TensorFlowBackend | BHWC |
| LiteRT | LiteRTBackend | BCHW |
| PaddlePaddle | PaddleBackend | BCHW |
| MNN | MNNBackend | BCHW |
| NCNN | NCNNBackend | BCHW |
| ExecuTorch | ExecuTorchBackend | 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всё равно денормализует любой трехмерный выход, используя размер изображения, поскольку предполагает, что в выходе содержатся боксы 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. Значительно большее отклонение указывает на неподдерживаемые операции, неверную форму входных данных или то, что модель не переведена в режим оценки. Исполняемый пример см. в разделе «Проверка экспортированной модели».