Come esportare modelli PyTorch non-YOLO con Ultralytics

Distribuire modelli PyTorch in produzione solitamente significa gestire un exporter diverso per ogni destinazione: torch.onnx.export per ONNX, coremltools per dispositivi Apple, onnx2tf per TensorFlow, pnnx per NCNN, e così via. Ogni strumento ha la sua API, stranezze nelle dipendenze e convenzioni di output.

Ultralytics fornisce utility di esportazione standalone che racchiudono molteplici backend sotto un'unica interfaccia coerente. Puoi esportare qualsiasi modello di visione torch.nn.Module, incluso timm, classificatori e rilevatori torchvision, o le tue architetture personalizzate, in ONNX, TorchScript, OpenVINO, CoreML, NCNN, PaddlePaddle, MNN, ExecuTorch, e TensorFlow SavedModel senza dover imparare ogni backend separatamente.

Perché usare Ultralytics per l'esportazione non-YOLO?

  • Un'unica API per 10 formati: impara una sola convenzione di chiamata invece di una dozzina.
  • Superficie di utilità condivisa: gli helper di esportazione risiedono in ultralytics.utils.export, quindi una volta installati i pacchetti backend puoi mantenere lo stesso schema di chiamata tra i vari formati.
  • Stesso percorso di codice delle esportazioni YOLO: gli stessi helper alimentano ogni esportazione YOLO di Ultralytics.
  • Quantizzazione FP16 e INT8 integrata per i formati che la supportano (OpenVINO, CoreML, MNN, NCNN).
  • Funziona su CPU: non è richiesta alcuna GPU per il passaggio di esportazione, quindi puoi eseguirlo localmente su qualsiasi laptop.

Avvio Rapido

Il percorso più veloce è un'esportazione in due righe verso ONNX senza codice YOLO e senza configurazioni oltre a 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")

Formati di esportazione supportati

La classe torch2* le funzioni accettano un torch.nn.Module standard e un tensore di input di esempio. MNN, TF SavedModel e TF Frozen Graph passano attraverso un artefatto intermedio ONNX o Keras. In entrambi i casi non sono richiesti attributi specifici per YOLO.

FormatoFunzioneInstallaOutput
ONNXtorch2onnx()pip install onnx.onnx file
TorchScripttorch2torchscript()inclusa con PyTorch.torchscript file
OpenVINOtorch2openvino()pip install openvino_openvino_model/ directory
CoreMLtorch2coreml()pip install coremltools.mlpackage
TF SavedModelonnx2saved_model()vedi i requisiti dettagliati qui sotto_saved_model/ directory
TF Frozen Graphkeras2pb()vedi i requisiti dettagliati qui sotto.pb file
NCNNtorch2ncnn()pip install ncnn pnnx_ncnn_model/ directory
MNNonnx2mnn()pip install MNN.mnn file
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddle_paddle_model/ directory
ExecuTorchtorch2executorch()pip install executorch_executorch_model/ directory
ONNX come formato intermedio

MNN, TF SavedModel, e le esportazioni TF Frozen Graph passano attraverso ONNX come passaggio intermedio. Esporta prima in ONNX, poi converti.

Metadati integrati

Diverse funzioni di esportazione accettano un dizionario metadata facoltativo (es., torch2torchscript(..., metadata={"author": "me"})) che integra coppie chiave-valore personalizzate nell'artefatto esportato dove il formato lo supporta.

Esempi passo dopo passo

Ogni esempio qui sotto utilizza la stessa configurazione, una ResNet-18 pre-addestrata da timm in modalità di valutazione:

import timm
import torch

model = timm.create_model("resnet18", pretrained=True).eval()
im = torch.randn(1, 3, 224, 224)
Chiama sempre `model.eval()` prima di esportare

Dropout, batch normalization, e altri livelli validi solo per l'addestramento si comportano in modo diverso durante l'inferenza. Saltare .eval() produce esportazioni con output errati.

Esporta in ONNX

from ultralytics.utils.export import torch2onnx

torch2onnx(model, im, output_file="resnet18.onnx")

Per una dimensione batch dinamica, passa un dizionario dynamic:

torch2onnx(model, im, output_file="resnet18_dyn.onnx", dynamic={"images": {0: "batch_size"}})

L'opset predefinito è 14 e il nome dell'input predefinito è "images". Sovrascrivi con gli argomenti opset, input_namesTraineroutput_names.

Esportazione in TorchScript

Nessuna dipendenza extra necessaria. Usa torch.jit.trace internamente.

from ultralytics.utils.export import torch2torchscript

torch2torchscript(model, im, output_file="resnet18.torchscript")

Esportazione in OpenVINO

from ultralytics.utils.export import torch2openvino

ov_model = torch2openvino(model, im, output_dir="resnet18_openvino_model")

La directory contiene una coppia model.xml e model.bin a nome fisso:

resnet18_openvino_model/
├── model.xml
└── model.bin

Passa dynamic=True per forme di input dinamiche, half=True per FP16, o int8=True per quantizzazione INT8. INT8 richiede inoltre un calibration_dataset.

Richiede openvino>=2024.0.0 (o >=2025.2.0 su macOS 15.4+) e torch>=2.1.

Esportazione in 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, output_file="resnet18.mlpackage")

Per classificazione modelli, passa una lista di nomi di classe a classifier_names per aggiungere una testina di classificazione al modello CoreML.

Richiede coremltools>=9.0, torch>=1.11, e numpy<=2.3.5. Non supportato su Windows.

Errore `BlobWriter not loaded`

coremltools>=9.0 distribuisce wheel per Python 3.10–3.13 su macOS e Linux. Sulle versioni di Python più recenti l'estensione C nativa non viene caricata. Usa Python 3.10–3.13 per l'esportazione CoreML.

Esportazione in TensorFlow SavedModel

L'esportazione TF SavedModel passa attraverso ONNX come passaggio intermedio:

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")

La funzione restituisce un modello Keras e genera anche file TFLite (.tflite) all'interno della directory di output:

resnet18_saved_model/
├── saved_model.pb
├── variables/
├── resnet18_float32.tflite
├── resnet18_float16.tflite
└── resnet18_int8.tflite

Requisiti:

  • 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 (installa con --extra-index-url https://pypi.ngc.nvidia.com)
  • ai-edge-litert>=1.2.0,<1.4.0 su macOS (ai-edge-litert>=1.2.0 su altre piattaforme)
  • onnxslim>=0.1.71
  • onnx>=1.12.0,<2.0.0
  • protobuf>=5

Esportazione in TensorFlow Frozen Graph

Continuando dall'esportazione SavedModel sopra, converti il modello Keras restituito in un grafo .pb frozen:

from pathlib import Path

from ultralytics.utils.export import keras2pb

keras2pb(keras_model, output_file=Path("resnet18_saved_model/resnet18.pb"))

Esportazione in NCNN

from ultralytics.utils.export import torch2ncnn

torch2ncnn(model, im, output_dir="resnet18_ncnn_model")

La directory contiene file param e bin a nome fisso insieme a un wrapper Python:

resnet18_ncnn_model/
├── model.ncnn.param
├── model.ncnn.bin
└── model_ncnn.py

torch2ncnn() controlla per ncnn e pnnx di Ultralytics al primo utilizzo.

Esportazione in MNN

L'esportazione MNN richiede un file ONNX come input. Esporta prima in ONNX, poi converti:

from ultralytics.utils.export import onnx2mnn, torch2onnx

torch2onnx(model, im, output_file="resnet18.onnx")
onnx2mnn("resnet18.onnx", output_file="resnet18.mnn")

Supporta half=True per FP16 e int8=True per quantizzazione INT8. Richiede MNN>=2.9.6 e torch>=1.10.

Esportazione in PaddlePaddle

from ultralytics.utils.export import torch2paddle

torch2paddle(model, im, output_dir="resnet18_paddle_model")

La directory contiene il modello PaddlePaddle e i file dei parametri:

resnet18_paddle_model/
├── model.pdmodel
└── model.pdiparams

Richiede x2paddle e la corretta distribuzione PaddlePaddle per la tua piattaforma:

  • paddlepaddle-gpu>=3.0.0,<3.3.0 su CUDA
  • paddlepaddle==3.0.0 su CPU ARM64
  • paddlepaddle>=3.0.0,<3.3.0 su altre CPU

Non supportato su NVIDIA Jetson.

Esportazione in ExecuTorch

from ultralytics.utils.export import torch2executorch

torch2executorch(model, im, output_dir="resnet18_executorch_model")

Il file .pte esportato viene salvato all'interno della directory di output:

resnet18_executorch_model/
└── model.pte

Richiede torch>=2.9.0 e un runtime ExecuTorch corrispondente (pip install executorch). Per l'utilizzo del runtime, vedi l'integrazione ExecuTorch.

Verifica il tuo modello esportato

Dopo l'esportazione, verifica la parità numerica con il modello PyTorch originale prima del rilascio. Un rapido smoke test con ONNXBackend da ultralytics.nn.backends confronta gli output e segnala tempestivamente errori di tracciamento o di quantizzazione:

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}")  # should be < 1e-5
Differenza prevista

Per le esportazioni FP32, la differenza assoluta massima dovrebbe essere inferiore a 1e-5. Differenze maggiori indicano operazioni non supportate, forma dell'input errata o un modello non in modalità eval. Le esportazioni FP16 e INT8 hanno tolleranze più ampie. Convalida su dati reali invece che su tensori casuali.

Per altri runtime, il nome del tensore di input potrebbe variare. OpenVINO, ad esempio, utilizza il nome dell'argomento forward del modello (solitamente x per modelli generici), mentre torch2onnx imposta come predefinito "images".

Limitazioni note

  • Il supporto multi-input è disomogeneo: torch2onnx e torch2openvino accetta una tupla o una lista di tensori di esempio per modelli con input multipli. torch2torchscript, torch2coreml, torch2ncnn, torch2paddle, e torch2executorch presuppone un singolo tensore di input.
  • ExecuTorch necessita di flatc: Il runtime ExecuTorch richiede il compilatore FlatBuffers. Installalo con brew install flatbuffers su macOS o apt install flatbuffers-compiler su Ubuntu.
  • Nessuna inferenza tramite Ultralytics: I modelli non-YOLO esportati non possono essere ricaricati tramite YOLO() per l'inferenza. Usa il runtime nativo per ogni formato (ONNX Runtime, OpenVINO Runtime, ecc.).
  • Formati solo YOLO: Axelera e Sony IMX500 le esportazioni richiedono attributi del modello specifici di YOLO e non sono disponibili per modelli generici.
  • Formati specifici per piattaforma: TensorRT richiede una GPU NVIDIA. RKNN richiede l' rknn-toolkit2 SDK (solo Linux). Edge TPU richiede l' edgetpu_compiler binario (solo Linux).

FAQ

Quali modelli posso esportare con Ultralytics?

Qualsiasi torch.nn.Module. Questo include modelli da timm, torchvision o qualsiasi modello PyTorch personalizzato. Il modello deve essere in modalità di valutazione (model.eval()) prima dell'esportazione. ONNX e OpenVINO accettano inoltre una tupla di tensori di esempio per modelli multi-input.

Quali formati di esportazione funzionano senza GPU?

Tutti i formati supportati (TorchScript, ONNX, OpenVINO, CoreML, TF SavedModel, TF Frozen Graph, NCNN, PaddlePaddle, MNN, ExecuTorch) possono essere esportati su CPU. Non è richiesta alcuna GPU per il processo di esportazione stesso. TensorRT è l'unico formato che richiede una GPU NVIDIA.

Quale versione di Ultralytics mi serve?

Usa Ultralytics >=8.4.38, che include il modulo ultralytics.utils.export e il output_file/output_dir.

standardizzato

Posso esportare un modello torchvision in CoreML per la distribuzione su iOS?.mlpackage tramite torch2coremlSì. I classificatori, i rilevatori e i modelli di segmentazione di torchvision si esportano in classifier_names. Per i modelli di classificazione delle immagini, passa una lista di nomi di classe a per integrare una testa di classificazione. Esegui l'esportazione su macOS o Linux. CoreML non è supportato su Windows. Vedi l'integrazione CoreML

per i dettagli sulla distribuzione iOS.

Posso quantizzare il mio modello esportato in INT8 o FP16?half=TrueSì, per diversi formati. Passa int8=True per FP16 o calibration_dataset per INT8 durante l'esportazione in OpenVINO, CoreML, MNN o NCNN. INT8 in OpenVINO richiede inoltre un argomento per la quantizzazione post-addestramento

Commenti