Ultralytics YOLO27:

Come esportare modelli PyTorch non YOLO con Ultralytics#

Ultralytics include utility di esportazione autonome in ultralytics.utils.export che integrano più backend in un'unica interfaccia coerente. Puoi esportare qualsiasi torch.nn.Module, inclusi i modelli di visione artificiale timm, i classificatori e rilevatori torchvision o le tue architetture personalizzate, nei formati TorchScript, ONNX, OpenVINO, CoreML, Core AI, TensorFlow SavedModel, TensorFlow Frozen Graph, PaddlePaddle, MNN, NCNN ed ExecuTorch, senza dover imparare a usare separatamente ogni backend.

Portare in produzione modelli PyTorch di solito significa destreggiarsi tra un esportatore diverso per ogni destinazione: torch.onnx.export per ONNX, coremltools per i dispositivi Apple, onnx2tf per TensorFlow, pnnx per NCNN e così via. Ogni strumento ha la propria API, dipendenze con peculiarità e convenzioni di output. Queste utility riuniscono tutto in un unico schema di chiamata.

Perché usare Ultralytics per esportare modelli non YOLO?#

  • Un'unica API per 11 formati: impara un solo schema di chiamata invece di una dozzina.
  • Stesso percorso del codice delle esportazioni YOLO: gli stessi helper gestiscono tutte le esportazioni YOLO di Ultralytics.
  • Quantizzazione FP16 e INT8 tramite un unico argomento quantize per i formati che la supportano.
  • Funziona su CPU: non serve una GPU per la fase di esportazione, quindi puoi eseguirla localmente su un laptop; l'esportazione CoreML non è supportata su Windows, mentre l'esportazione Core AI richiede macOS 26 o versioni successive su silicio Apple, oppure Linux x86_64 con glibc 2.34 o versioni successive e Python dalla 3.11 alla 3.14.

Avvio rapido#

Il modo più rapido è esportare in ONNX con due righe, senza codice YOLO e senza alcuna configurazione 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#

Le funzioni torch2* 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 servono attributi specifici di YOLO.

FormatoFunzioneInstallazioneOutput
TorchScripttorch2torchscript()incluso in PyTorchfile .torchscript
ONNXtorch2onnx()pip install onnxfile .onnx
OpenVINOtorch2openvino()pip install openvinodirectory _openvino_model/
CoreMLtorch2coreml()pip install coremltools.mlpackage
Core AItorch2coreai()pip install coreai-torch (silicio Apple con macOS 26 o versioni successive, oppure Linux x86_64 con glibc 2.34 o versioni successive; Python dalla 3.11 alla 3.14)directory .aimodel
TF SavedModelonnx2saved_model()consulta i requisiti dettagliati qui sottodirectory _saved_model/
TF Frozen Graphkeras2pb()consulta i requisiti dettagliati qui sottofile .pb
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddledirectory _paddle_model/
MNNonnx2mnn()pip install MNNfile .mnn
NCNNtorch2ncnn()pip install ncnn pnnxdirectory _ncnn_model/
ExecuTorchtorch2executorch()pip install executorchdirectory _executorch_model/
Incorporare metadati

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

Esempi passo passo#

Tutti gli esempi qui sotto usano la stessa configurazione: un ResNet-18 pretrained 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, normalizzazione batch e gli altri layer usati solo in addestramento si comportano diversamente durante l'inferenza. Se non esegui .eval(), le esportazioni produrranno output errati.

Esporta in ONNX#

from ultralytics.utils.export import torch2onnx

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

Per usare una dimensione del 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 di input predefinito è "images". Puoi cambiarli con gli argomenti opset, input_names o output_names.

Esportare in TorchScript#

Non servono dipendenze aggiuntive. Usa torch.jit.trace internamente.

from ultralytics.utils.export import torch2torchscript

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

Esportare 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 con nomi fissi:

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

Passa dynamic=True per le forme di input dinamiche, quantize=16 per FP16 oppure quantize=8 per la quantizzazione INT8. INT8 richiede anche l'argomento calibration_dataset.

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

Esportare 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, classifier_names=None, output_file="resnet18.mlpackage")

Per i modelli di classificazione, passa un elenco di nomi di classe a classifier_names per aggiungere una testa 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 include wheel per Python 3.10–3.13 su macOS e Linux. Nelle versioni più recenti di Python, l'estensione C nativa non riesce a caricarsi. Per esportare in CoreML, usa Python 3.10–3.13.

Esportare in Core AI#

from ultralytics.utils.export import torch2coreai

torch2coreai(model, im, output_file="resnet18.aimodel")

L'elemento .aimodel è una directory:

resnet18.aimodel/
├── main.mlirb
├── main.hash
└── metadata.json

L'esportazione viene eseguita su macOS 26 o versioni successive su silicio Apple, oppure su Linux x86_64 con glibc 2.34 o versioni successive e Python dalla 3.11 alla 3.14 (pip install coreai-torch), mentre quantize=16 scrive un asset FP16 che accetta input float16; l'asset viene eseguito su iOS 27 e macOS 27. Consulta l'integrazione Core AI, inclusa la nota sugli asset FP16 che causano un arresto anomalo durante il caricamento.

Esportare in TensorFlow SavedModel#

L'esportazione TF SavedModel passa da 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 [LiteRT](https://ultralytics-translation-0.invalid FP32 e FP16 (.tflite) nella directory di output:

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

Passa quantize=8 per aggiungere un elemento .tflite INT8.

L'esportazione TensorFlow non funziona su macOS con Python 3.13 o versioni successive; su macOS usa Python 3.12 o versioni precedenti, oppure usa Linux.

Requisiti con Python 3.12 o versioni precedenti (con Python 3.13 o versioni successive, l'esportazione richiede invece tensorflow>2.19.0, tf_keras>2.19.0, onnx2tf>=2.3.0,<2.3.16 e 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 su macOS (ai-edge-litert>=1.2.0 sulle altre piattaforme)
  • onnxslim>=0.1.82
  • onnx>=1.12.0,<2.0.0
  • protobuf>=5

Esportare in TensorFlow Frozen Graph#

Partendo dall'esportazione SavedModel qui sopra, converti il keras_model restituito in un grafico .pb congelato:

from pathlib import Path

from ultralytics.utils.export import keras2pb

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

Esportare in NCNN#

from ultralytics.utils.export import torch2ncnn

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

La directory contiene file param e bin con nomi fissi, oltre a un wrapper Python:

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

Esportare 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 quantize=16 per FP16 e quantize=8 per la quantizzazione INT8. Richiede MNN>=2.9.6 e torch>=1.10.

Esportare in PaddlePaddle#

from ultralytics.utils.export import torch2paddle

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

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

resnet18_paddle_model/
├── inference_model/
│   ├── model.json
│   └── model.pdiparams
├── model.pdparams
└── x2paddle_code.py

Richiede x2paddle e la distribuzione PaddlePaddle corretta 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.

Esportare in ExecuTorch#

from ultralytics.utils.export import torch2executorch

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

Il file .pte esportato viene salvato nella directory di output:

resnet18_executorch_model/
└── model.pte

Richiede torch>=2.9.0 e un runtime ExecuTorch compatibile (pip install executorch). Per usare il runtime, consulta l'integrazione ExecuTorch.

Verificare il modello esportato#

Dopo l'esportazione, verifica la parità numerica con il modello PyTorch originale prima di distribuirlo. Un rapido test di funzionamento con ONNXBackend da ultralytics.nn.backends confronta gli output e segnala tempestivamente gli errori di tracing o 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}")  # ~1e-6 for an FP32 ONNX export
Differenza prevista

Con ResNet-18, la maggior parte delle esportazioni FP32 si discosta da PyTorch di circa 1e-5 al massimo, mentre TorchScript corrisponde esattamente. Tre runtime possono comunque calcolare un'esportazione FP32 con precisione ridotta e discostarsi di circa 1e-2–1e-1: NCNN abilita l'aritmetica FP16 sulle CPU che la supportano, MNNBackend carica i modelli con precision="low" e il plugin CPU OpenVINO esegue automaticamente in FP16, con la modalità di esecuzione predefinita PERFORMANCE, su alcuni hardware, come il silicio Apple. Una differenza molto superiore alla baseline del formato stesso indica operazioni non supportate, una forma di input errata o un modello non in modalità eval. Le esportazioni FP16 e INT8 hanno tolleranze più ampie. Convalida usando dati reali anziché tensori casuali.

Per altri runtime, il nome del tensore di input può essere diverso. OpenVINO, ad esempio, usa il nome dell'argomento forward del modello (in genere x per i modelli generici), mentre torch2onnx usa "images" come valore predefinito.

Eseguire il modello esportato#

Puoi caricare i modelli non YOLO esportati tramite la normale API YOLO(). Le esportazioni qui sopra non includono metadati Ultralytics relativi all'attività o alle dimensioni di input: passa quindi task in modo esplicito e imposta imgsz in modo che corrisponda al tensore di esempio usato per l'esportazione:

from ultralytics import YOLO

results = YOLO("resnet18.onnx", task="classify")("path/to/image.jpg", imgsz=224)
print(results[0].probs.top1)

imgsz è importante quando l'esportazione ha una forma di input fissa: le esportazioni ONNX e TF SavedModel qui sopra rifiutano il valore predefinito 640. Le esportazioni TorchScript e NCNN qui sopra accettano anche altre dimensioni, ma nessuno dei due esportatori lo garantisce: entrambi eseguono il tracing a partire dal tensore di esempio, quindi un modello che appiattisce i dati in un layer Linear mantiene dimensioni fisse. Verifica la tua esportazione.

Il valore viene quindi arrotondato per eccesso al multiplo più vicino dello stride del modello, che senza metadati è 32. Un'esportazione con forma fissa di 200x200 riceve quindi un input di 224x224 e viene rifiutata, anche se imgsz=200 corrisponde. Per dimensioni di input che non sono multipli di 32, chiama direttamente il backend.

Chiamare direttamente un backend#

Per usare tensori grezzi senza preprocessing e post-processing di Ultralytics, usa le classi specifiche per formato in ultralytics.nn.backends, come nell'esempio di verifica qui sopra. Ogni classe accetta l'artefatto esportato e un dispositivo ed è richiamabile:

FormatoBackendLayout di input
TorchScriptTorchScriptBackendBCHW
ONNXONNXBackendBCHW
OpenVINOOpenVINOBackendBCHW
CoreMLCoreMLBackendBHWC
Core AICoreAIBackendBCHW
TF SavedModel, Frozen GraphTensorFlowBackendBHWC
LiteRTLiteRTBackendBCHW
PaddlePaddlePaddleBackendBCHW
MNNMNNBackendBCHW
NCNNNCNNBackendBCHW
ExecuTorchExecuTorchBackendBCHW

TensorFlowBackend supporta due formati e usa format="saved_model" come valore predefinito, quindi passa format="pb" per un grafico congelato.

Tre operazioni che il percorso YOLO() gestisce al posto tuo, ma non una chiamata diretta:

  • Layout di input: CoreMLBackend e TensorFlowBackend si aspettano BHWC. Trasponi prima con im.permute(0, 2, 3, 1); un tensore BCHW genera un errore di mancata corrispondenza della forma.
  • Autograd: racchiudi le chiamate in torch.inference_mode(). TorchScriptBackend restituisce un tensore che mantiene un grafo del gradiente.
  • Post-processing: senza metadati, un backend lascia task come None e names vuoti. LiteRTBackend continua a denormalizzare qualsiasi output 3D in base alle dimensioni dell'immagine, supponendo che contenga box YOLO: un comportamento errato per un modello non YOLO con output 3D. Gli output bidimensionali, come i logit di un classificatore, non ne risentono.

Limitazioni note#

  • Il supporto multi-input è disomogeneo: torch2onnx, torch2openvino e torch2torchscript accettano una tupla di tensori di esempio per i modelli con più input. torch2coreml, torch2coreai, torch2ncnn, torch2paddle e torch2executorch presuppongono un singolo tensore di input.
  • Formati riservati a YOLO: le esportazioni Axelera e Sony IMX500 richiedono attributi specifici dei modelli YOLO e non sono disponibili per i modelli generici.
  • Formati specifici per piattaforma: TensorRT richiede una GPU NVIDIA. RKNN richiede l'SDK rknn-toolkit2 (solo Linux). Edge TPU richiede il binario edgetpu_compiler (solo Linux).

Conclusione#

Queste utility convertono qualsiasi modello PyTorch, da un semplice torch.nn.Module a un artefatto ONNX, OpenVINO, CoreML, TensorFlow o per runtime mobile pronto per la distribuzione, tramite un'unica API coerente. Scegli il formato adatto all'hardware di destinazione, verifica la parità numerica rispetto al modello originale, poi segui la guida all'integrazione corrispondente per i passaggi di distribuzione specifici del runtime.

Domande frequenti#

  • Qualsiasi torch.nn.Module. Sono inclusi i modelli di timm, torchvision e qualsiasi modello PyTorch personalizzato. Prima dell'esportazione, il modello deve essere in modalità di valutazione (model.eval()). Anche ONNX, OpenVINO e TorchScript accettano una tupla di tensori di esempio per i modelli con più input.

  • Tutti i formati supportati (TorchScript, ONNX, OpenVINO, CoreML, Core AI, TF SavedModel, TF Frozen Graph, PaddlePaddle, MNN, NCNN ed ExecuTorch) possono essere esportati su CPU. Il processo di esportazione non richiede una GPU. TensorRT è l'unico formato che richiede una GPU NVIDIA.

  • Usa la versione più recente. L'argomento quantize richiede Ultralytics >=8.4.81, mentre l'esportazione Core AI richiede >=8.4.131 (>=8.4.163 su Linux o con coreai-torch>=0.4.3).

  • Sì. I classificatori, i rilevatori e i modelli di segmentazione torchvision si esportano in .mlpackage tramite torch2coreml. Per i modelli di classificazione delle immagini, passa un elenco di nomi delle classi a classifier_names per integrare una testa di classificazione. Esegui l'esportazione su macOS o Linux. CoreML non è supportato su Windows. Consulta l'integrazione CoreML per i dettagli sul deployment su iOS.

  • Sì, per diversi formati. Passa quantize=16 per FP16 o quantize=8 per INT8 quando esporti in OpenVINO, CoreML o MNN; onnx2saved_model accetta quantize=8 per un file LiteRT INT8, mentre NCNN e Core AI esportano in FP32 per impostazione predefinita, accettano quantize=16 per FP16 e non offrono un percorso INT8. INT8 in OpenVINO richiede inoltre un argomento calibration_dataset per la quantizzazione post-addestramento. Consulta la pagina di integrazione di ciascun formato per conoscere i compromessi della quantizzazione.

  • Esegui il modello PyTorch originale e il modello esportato sullo stesso input, quindi confronta gli output. Carica il file esportato con il backend corrispondente (ad esempio, ONNXBackend per ONNX) e controlla la differenza assoluta massima. Valuta lo scarto rispetto alla baseline specifica del formato: NCNN, MNNBackend e OpenVINO su alcune CPU possono eseguire le esportazioni FP32 con precisione ridotta, con scarti vicini a 1e-2–1e-1, mentre la maggior parte degli altri formati si attesta intorno a 1e-5. Uno scarto molto più ampio indica operazioni non supportate, una forma di input errata o un modello non in modalità eval. Consulta Verifica il modello esportato per un esempio eseguibile.

Commenti