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
quantizeper 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.
| Formato | Funzione | Installazione | Output |
|---|---|---|---|
| TorchScript | torch2torchscript() | incluso con PyTorch | file .torchscript |
| ONNX | torch2onnx() | pip install onnx | file .onnx |
| OpenVINO | torch2openvino() | pip install openvino | directory _openvino_model/ |
| CoreML | torch2coreml() | pip install coremltools | .mlpackage |
| Core AI | torch2coreai() | 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 SavedModel | onnx2saved_model() | consulta i requisiti dettagliati qui sotto | directory _saved_model/ |
| TF Frozen Graph | keras2pb() | consulta i requisiti dettagliati qui sotto | file .pb |
| PaddlePaddle | torch2paddle() | pip install paddlepaddle x2paddle | directory _paddle_model/ |
| MNN | onnx2mnn() | pip install MNN | file .mnn |
| NCNN | torch2ncnn() | pip install ncnn pnnx | directory _ncnn_model/ |
| ExecuTorch | torch2executorch() | pip install executorch | directory _executorch_model/ |
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)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.binPassa 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.
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.jsonL'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.tflitePassa 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.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.0su macOS (ai-edge-litert>=1.2.0sulle altre piattaforme)onnxslim>=0.1.82onnx>=1.12.0,<2.0.0protobuf>=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.pyEsportazione 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.pyRichiede x2paddle e la distribuzione PaddlePaddle corretta per la tua piattaforma:
paddlepaddle-gpu>=3.0.0,<3.3.0su CUDApaddlepaddle==3.0.0su CPU ARM64paddlepaddle>=3.0.0,<3.3.0su 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.pteRichiede 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 exportCon 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:
| Formato | Backend | Layout di input |
|---|---|---|
| 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 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:
CoreMLBackendeTensorFlowBackendsi aspettano BHWC. Esegui prima la trasposizione conim.permute(0, 2, 3, 1); un tensore BCHW genera un errore di incompatibilità delle forme. - Autograd: racchiudi le chiamate in
torch.inference_mode().TorchScriptBackendrestituisce un tensore che mantiene un grafo del gradiente. - Post-processing: senza metadati, un backend lascia
taskcomeNoneenamesvuoto.LiteRTBackendcontinua 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,torch2openvinoetorch2torchscriptaccettano una tupla di tensori di esempio per i modelli con più input.torch2coreml,torch2coreai,torch2ncnn,torch2paddleetorch2executorchpresuppongono 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 binarioedgetpu_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
quantizerichiede Ultralytics>=8.4.81; per esportare in Core AI serve>=8.4.131(>=8.4.163su Linux o concoreai-torch>=0.4.3).Sì. I classificatori, i rilevatori e i modelli di segmentazione torchvision si esportano in
.mlpackagetramitetorch2coreml. Per i modelli di classificazione delle immagini, passa un elenco di nomi delle classi aclassifier_namesper 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=16per FP16 oquantize=8per INT8 quando esporti in OpenVINO, CoreML o MNN;onnx2saved_modelaccettaquantize=8per un file LiteRT INT8, mentre NCNN e Core AI esportano in FP32 per impostazione predefinita, accettanoquantize=16per FP16 e non supportano INT8. Per usare INT8 in OpenVINO serve anche l'argomentocalibration_datasetper 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,
ONNXBackendper ONNX) e verifica la differenza assoluta massima. Valuta lo scarto rispetto al valore di riferimento specifico del formato: NCNN,MNNBackende OpenVINO su alcune CPU possono eseguire esportazioni FP32 a precisione ridotta, con differenze vicine a1e-2–1e-1, mentre la maggior parte degli altri formati si attesta intorno a1e-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.