YOLO Vision 2026:

Come esportare modelli PyTorch non-YOLO con Ultralytics#

Ultralytics fornisce utility di esportazione autonome sotto ultralytics.utils.export che incapsulano più backend dietro un'unica interfaccia coerente. Puoi esportare qualsiasi torch.nn.Module, inclusi i modelli di immagine timm, i classificatori e i 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.

Distribuire i modelli PyTorch in produzione solitamente 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 sua API, particolarità di dipendenza e convenzioni di output. Queste utility riducono tutto ciò a un unico pattern di chiamata.

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

  • Un'unica API su 10 formati: impara una sola convenzione di chiamata invece di una dozzina.
  • Superficie di utility condivisa: gli helper di esportazione si trovano sotto ultralytics.utils.export, quindi una volta installati i pacchetti del backend puoi mantenere lo stesso pattern di chiamata tra diversi formati.
  • Stesso percorso del 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: nessuna GPU richiesta per la fase di esportazione stessa, quindi puoi eseguirla localmente su qualsiasi portatile.

Avvio rapido#

Il percorso più rapido è un'esportazione in due righe verso ONNX 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 nessun dei due casi sono richiesti attributi specifici per YOLO.

FormatoFunzioneInstallaOutput
ONNXtorch2onnx()pip install onnxfile .onnx
TorchScripttorch2torchscript()incluso con PyTorchfile .torchscript
OpenVINOtorch2openvino()pip install openvinodirectory _openvino_model/
CoreMLtorch2coreml()pip install coremltools.mlpackage
TF SavedModelonnx2saved_model()vedi i requisiti dettagliati di seguitodirectory _saved_model/
TF Frozen Graphkeras2pb()vedi i requisiti dettagliati di seguitofile .pb
NCNNtorch2ncnn()pip install ncnn pnnxdirectory _ncnn_model/
MNNonnx2mnn()pip install MNNfile .mnn
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddledirectory _paddle_model/
ExecuTorchtorch2executorch()pip install executorchdirectory _executorch_model/
ONNX come formato intermedio

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

Incorporamento di metadati

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

Esempi passo dopo passo#

Ogni esempio di seguito utilizza la stessa configurazione, una ResNet-18 preaddestrata da timm in modalità 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

Il dropout, la normalizzazione batch e altri strati esclusivi dell'addestramento si comportano in modo diverso durante l'inferenza. Saltare .eval() produce esportazioni con output errati.

Esportazione in ONNX#

from ultralytics.utils.export import torch2onnx

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

Per 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 dell'input predefinito è "images". Esegui l'override con gli argomenti opset, input_names o output_names.

Esportazione in TorchScript#

Nessuna dipendenza extra necessaria. Utilizza torch.jit.trace sotto il cofano.

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 di model.xml e model.bin a nome fisso:

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

Passa dynamic=True per forme di input dinamiche, quantize=16 per FP16 o quantize=8 per la quantizzazione INT8. La quantizzazione INT8 richiede inoltre un argomento 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, 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 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 fornisce wheel per Python 3.10–3.13 su macOS e Linux. Sulle versioni più recenti di Python l'estensione C nativa non riesce a caricarsi. 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
  • ai-edge-litert>=1.2.0,<1.4.0 su macOS (ai-edge-litert>=1.2.0 su altre piattaforme)
  • onnxslim>=0.1.82
  • onnx>=1.12.0,<2.0.0
  • protobuf>=5

Esportazione in TensorFlow Frozen Graph#

Continuando dall'esportazione SavedModel precedente, converti il keras_model restituito in un grafo .pb bloccato:

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

torch2ncnn() verifica la presenza di ncnn e pnnx al primo utilizzo.

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 FP16 e quantize=8 per la 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 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.

Esporta 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, consulta l'integrazione ExecuTorch.

Verifica il tuo modello esportato#

Dopo l'esportazione, verifica la parità numerica con il modello PyTorch originale prima della distribuzione. Un rapido smoke test con ONNXBackend da ultralytics.nn.backends confronta gli output e segnala tempestivamente errori di tracciamento 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}")  # typically ~1e-5, well under 1e-4 for FP32
Differenza prevista

Per le esportazioni FP32, la differenza assoluta massima è tipicamente intorno a 1e-5 e dovrebbe rimanere ben al di sotto di 1e-4. Differenze maggiori indicano operazioni non supportate, una forma di input errata o un modello non in modalità di valutazione. Le esportazioni FP16 e INT8 hanno tolleranze più ampie. Effettua la convalida su dati reali anziché su tensori casuali.

Per altri runtime, il nome del tensore di input potrebbe differire. OpenVINO, ad esempio, utilizza il nome dell'argomento di forward del modello (tipicamente x per i modelli generici), mentre torch2onnx predefinisce "images".

Limitazioni note#

  • Il supporto per input multipli non è uniforme: torch2onnx e torch2openvino accettano una tupla o un elenco di tensori di esempio per i modelli con input multipli. torch2torchscript, torch2coreml, torch2ncnn, torch2paddle e torch2executorch assumono un singolo tensore di input.
  • ExecuTorch richiede flatc: Il runtime ExecuTorch richiede il compilatore FlatBuffers. Installa 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 ciascun formato (ONNX Runtime, OpenVINO Runtime, ecc.).
  • Formati esclusivi per YOLO: le esportazioni Axelera e Sony IMX500 richiedono attributi del modello specifici per YOLO e non sono disponibili per i modelli generici.
  • Formati specifici per la 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 portano 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 che corrisponde all'hardware di destinazione, verifica la parità numerica rispetto al modello originale, quindi segui la guida all'integrazione corrispondente per i passaggi di distribuzione specifici del runtime.

FAQ#

  • Qualsiasi torch.nn.Module. Ciò 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 i modelli con input multipli.

  • 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 in sé. TensorRT è l'unico formato che richiede una GPU NVIDIA.

  • Usa >=8.4.38 di Ultralytics, che include il modulo ultralytics.utils.export e gli argomenti standardizzati output_file/output_dir.

  • Sì. I classificatori, i rilevatori e i modelli di segmentazione torchvision vengono esportati in .mlpackage tramite torch2coreml. Per i modelli di classificazione delle immagini, passa un elenco di nomi di classe a classifier_names per incorporare una testina 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 durante l'esportazione in OpenVINO, CoreML, MNN o NCNN. INT8 in OpenVINO richiede inoltre un argomento calibration_dataset per la quantizzazione post-addestramento. Consulta la pagina di integrazione di ciascun formato per i compromessi legati alla 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. Per le esportazioni FP32 è tipicamente intorno a 1e-5 e dovrebbe rimanere ben al di sotto di 1e-4; scostamenti maggiori indicano operazioni non supportate, una forma di input errata o un modello non in modalità di valutazione. Vedi Verifica il tuo modello esportato per un esempio eseguibile.

Commenti