Ultralytics YOLO27: Iscriviti alla lista d'attesa.
Get Started

Come esportare modelli PyTorch non YOLO con Ultralytics#

Ultralytics include utilità di esportazione autonome in ultralytics.utils.export, che offrono un'interfaccia coerente per diversi backend. Puoi esportare qualsiasi torch.nn.Module, inclusi i modelli di immagini timm, i classificatori e i rilevatori torchvision o le tue architetture personalizzate, in TorchScript, ONNX, OpenVINO, CoreML, Core AI, TensorFlow SavedModel, TensorFlow Frozen Graph, PaddlePaddle, MNN, NCNN ed ExecuTorch, senza dover imparare a usare ciascun backend separatamente.

Distribuire modelli PyTorch in produzione significa solitamente dover gestire 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, le proprie particolarità relative alle dipendenze e le proprie convenzioni per gli output. Queste utilità riuniscono tutto in un'unica modalità di chiamata.

Perché usare Ultralytics per esportare modelli non YOLO?#

  • Un'unica API per 11 formati: impara un'unica convenzione di chiamata invece di una dozzina.
  • Lo stesso percorso del codice usato per le 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 è necessaria una GPU per la fase di esportazione, quindi puoi eseguirla localmente su un laptop; l'esportazione CoreML non è supportata su Windows e l'esportazione Core AI richiede macOS 26 o versioni successive su Apple silicon, oppure Linux x86_64 con glibc 2.34 o versioni successive e Python da 3.11 a 3.14.

Guida rapida#

Il modo più rapido consiste nell'esportare in ONNX con due righe, senza codice YOLO né configurazioni aggiuntive 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 da un artefatto ONNX o Keras intermedio. In entrambi i casi non sono necessari attributi specifici di YOLO.

FormatoFunzioneInstallazioneOutput
TorchScripttorch2torchscript()incluso con PyTorchfile .torchscript
ONNXtorch2onnx()pip install onnxfile .onnx
OpenVINOtorch2openvino()pip install openvinodirectory _openvino_model/
CoreMLtorch2coreml()pip install coremltools.mlpackage
Core AItorch2coreai()pip install coreai-torch (Apple silicon, macOS 26 o versioni successive; Linux x86_64, glibc 2.34 o versioni successive; Python da 3.11 a 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/
Incorporamento dei metadati

Diverse funzioni di esportazione accettano un dizionario metadata opzionale (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 preaddestrato di 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 altri layer usati solo durante l'addestramento si comportano diversamente durante l'inferenza. Se salti .eval(), le esportazioni produrranno output non corretti.

Esportazione in ONNX#

from ultralytics.utils.export import torch2onnx

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

Per usare dimensioni dinamiche del batch, 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 sostituirli con gli argomenti opset, input_names o output_names.

Esportazione in TorchScript#

Non sono necessarie dipendenze aggiuntive. Internamente usa torch.jit.trace.

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 con nomi fissi: model.xml e model.bin:

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

Passa dynamic=True per usare forme di input dinamiche, quantize=16 per FP16 o quantize=8 per la quantizzazione INT8. INT8 richiede inoltre l'argomento calibration_dataset.

Richiede openvino>=2024.0.0 (oppure >=2025.2.0 su macOS 15.4 o versioni successive) 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, classifier_names=None, output_file="resnet18.mlpackage")

Per i modelli di classificazione, passa un elenco di nomi di classi 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 pacchetti wheel per Python da 3.10 a 3.13 su macOS e Linux. Nelle versioni più recenti di Python l'estensione C nativa non riesce a caricarsi. Usa Python da 3.10 a 3.13 per l'esportazione CoreML.

Esportazione in Core AI#

from ultralytics.utils.export import torch2coreai

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

L'asset .aimodel è una directory:

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

L'esportazione viene eseguita su macOS 26 o versioni successive con Apple silicon, oppure su Linux x86_64 con glibc 2.34 o versioni successive e Python da 3.11 a 3.14 (pip install coreai-torch); quantize=16 scrive un asset FP16 che accetta input float16. L'asset funziona su iOS 27 e macOS 27. Consulta l'integrazione Core AI, inclusa la nota sugli asset FP16 che causano un'interruzione al caricamento.

Esportazione in TensorFlow SavedModel#

L'esportazione in TF SavedModel passa da ONNX come fase intermedia:

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 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 .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 per 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

Esportazione in TensorFlow Frozen Graph#

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

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 con nomi fissi, insieme a un wrapper Python:

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

Esportazione in MNN#

L'esportazione MNN richiede un file ONNX come input. Esporta prima in ONNX, quindi 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 la quantizzazione FP16 e quantize=8 per quella 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/
├── 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.

Esportazione 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 informazioni sull'uso del runtime, consulta l'integrazione di ExecuTorch.

Verifica il modello esportato#

Dopo l'esportazione, verifica la parità numerica con il modello PyTorch originale prima della distribuzione. Un rapido test di controllo con ONNXBackend da ultralytics.nn.backends confronta gli output e rileva tempestivamente 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-5 for an FP32 ONNX export
Differenza prevista

Con ResNet-18, la maggior parte delle esportazioni FP32 si discosta da PyTorch di circa 1e-5 o meno, mentre TorchScript coincide esattamente. Tre runtime possono comunque calcolare un'esportazione FP32 a precisione ridotta, con una differenza vicina a 1e-2–1e-1: NCNN abilita l'aritmetica FP16 sulle CPU che la supportano, MNNBackend carica i modelli con precision="low" e, su alcuni hardware come i chip Apple, il plug-in CPU di OpenVINO esegue automaticamente in FP16 nella modalità di esecuzione predefinita PERFORMANCE. Una differenza molto superiore al valore di riferimento del formato stesso può indicare operatori non supportati, una forma di input errata o un modello non in modalità di valutazione. Le esportazioni FP16 e INT8 hanno tolleranze più ampie. Convalida con dati reali, non con 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" per impostazione predefinita.

Esegui il modello esportato#

I modelli non YOLO esportati si caricano di nuovo tramite la normale API YOLO(). Le esportazioni precedenti non includono metadati Ultralytics relativi all'attività o alle dimensioni di input, quindi specifica esplicitamente task 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 precedenti rifiutano il valore predefinito 640. Le esportazioni TorchScript e NCNN precedenti 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 livello Linear mantiene una forma fissa. Verifica la tua esportazione.

Il valore viene quindi arrotondato per eccesso al multiplo più vicino dello stride del modello, che senza metadati è 32. Di conseguenza, a un'esportazione con forma fissa di 200x200 viene fornito un input di 224x224, che viene rifiutato 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 il preprocessing e il post-processing di Ultralytics, usa le classi specifiche per formato in ultralytics.nn.backends, come nell'esempio di verifica precedente. Ogni classe accetta l'artefatto esportato e un dispositivo, ed è invocabile:

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" per impostazione predefinita, quindi specifica format="pb" per un grafo congelato.

Tre aspetti gestiti da YOLO() ma non da una chiamata diretta:

  • Layout di input: CoreMLBackend e TensorFlowBackend si aspettano BHWC. Esegui prima la trasposizione con im.permute(0, 2, 3, 1); un tensore BCHW genera un errore di incompatibilità delle forme.
  • 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 vuoto. LiteRTBackend continua a denormalizzare ogni output 3D in base alle dimensioni dell'immagine, presumendo che contenga riquadri YOLO: un comportamento errato per un modello non YOLO con output 3D. Gli output bidimensionali, come i logit dei classificatori, non ne risentono.

Limitazioni note#

  • Il supporto per input multipli è 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 solo 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 x86_64).

Conclusione#

Queste utilità consentono di trasformare qualsiasi modello PyTorch, a partire da un semplice torch.nn.Module, in 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 e 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 e torchvision e qualsiasi modello PyTorch personalizzato. Prima dell'esportazione, il modello deve essere in modalità di valutazione (model.eval()). ONNX, OpenVINO e TorchScript accettano anche una tupla di tensori di esempio per i modelli con input multipli.

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

  • Usa l'ultima versione. L'argomento quantize richiede Ultralytics >=8.4.81; per esportare in Core AI serve >=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 sulla distribuzione 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 supportano INT8. Per usare INT8 in OpenVINO serve anche l'argomento calibration_dataset per la quantizzazione post-training. Consulta la pagina d'integrazione di ciascun formato per conoscere i compromessi della quantizzazione.

  • Esegui il modello PyTorch originale e quello esportato sullo stesso input, quindi confronta gli output. Carica il file esportato con il backend corrispondente (ad esempio, ONNXBackend per ONNX) e verifica la differenza assoluta massima. Valuta lo scarto rispetto al valore di riferimento specifico del formato: NCNN, MNNBackend e OpenVINO su alcune CPU possono eseguire esportazioni FP32 a precisione ridotta, con differenze vicine a 1e-2–1e-1, mentre la maggior parte degli altri formati si attesta intorno a 1e-5. Uno scarto molto più ampio può indicare operatori non supportati, una forma di input errata o un modello non in modalità di valutazione. Consulta Verifica il modello esportato per un esempio eseguibile.

Commenti