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.
| Formato | Funzione | Installa | Output |
|---|---|---|---|
| ONNX | torch2onnx() | pip install onnx | .onnx file |
| TorchScript | torch2torchscript() | inclusa con PyTorch | .torchscript file |
| OpenVINO | torch2openvino() | pip install openvino | _openvino_model/ directory |
| CoreML | torch2coreml() | pip install coremltools | .mlpackage |
| TF SavedModel | onnx2saved_model() | vedi i requisiti dettagliati qui sotto | _saved_model/ directory |
| TF Frozen Graph | keras2pb() | vedi i requisiti dettagliati qui sotto | .pb file |
| NCNN | torch2ncnn() | pip install ncnn pnnx | _ncnn_model/ directory |
| MNN | onnx2mnn() | pip install MNN | .mnn file |
| PaddlePaddle | torch2paddle() | pip install paddlepaddle x2paddle | _paddle_model/ directory |
| ExecuTorch | torch2executorch() | pip install executorch | _executorch_model/ directory |
MNN, TF SavedModel, e le esportazioni TF Frozen Graph passano attraverso ONNX come passaggio intermedio. Esporta prima in ONNX, poi converti.
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)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.binPassa 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.
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.tfliteRequisiti:
tensorflow>=2.0.0,<=2.19.0onnx2tf>=1.26.3,<1.29.0tf_keras<=2.19.0sng4onnx>=1.0.1onnx_graphsurgeon>=0.3.26(installa con--extra-index-url https://pypi.ngc.nvidia.com)ai-edge-litert>=1.2.0,<1.4.0su macOS (ai-edge-litert>=1.2.0su altre piattaforme)onnxslim>=0.1.71onnx>=1.12.0,<2.0.0protobuf>=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.pytorch2ncnn() 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.pdiparamsRichiede x2paddle e la corretta distribuzione PaddlePaddle 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 all'interno della directory di output:
resnet18_executorch_model/
└── model.pteRichiede 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-5Per 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:
torch2onnxetorch2openvinoaccetta una tupla o una lista di tensori di esempio per modelli con input multipli.torch2torchscript,torch2coreml,torch2ncnn,torch2paddle, etorch2executorchpresuppone un singolo tensore di input. - ExecuTorch necessita di
flatc: Il runtime ExecuTorch richiede il compilatore FlatBuffers. Installalo conbrew install flatbufferssu macOS oapt install flatbuffers-compilersu 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-toolkit2SDK (solo Linux). Edge TPU richiede l'edgetpu_compilerbinario (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