Comment exporter des modèles PyTorch autres que YOLO avec Ultralytics#
Ultralytics fournit des utilitaires d’exportation autonomes dans ultralytics.utils.export, qui réunissent plusieurs moteurs d’exécution derrière une interface cohérente. Tu peux exporter n’importe quel torch.nn.Module, y compris des modèles d’image timm, des classificateurs et détecteurs torchvision ou tes propres architectures personnalisées, vers TorchScript, ONNX, OpenVINO, CoreML, Core AI, TensorFlow SavedModel, TensorFlow Frozen Graph, PaddlePaddle, MNN, NCNN et ExecuTorch, sans avoir à apprendre à utiliser chaque moteur séparément.
Déployer des modèles PyTorch en production implique généralement de jongler avec un exportateur différent pour chaque cible : torch.onnx.export pour ONNX, coremltools pour les appareils Apple, onnx2tf pour TensorFlow, pnnx pour NCNN, et ainsi de suite. Chaque outil possède sa propre API, ses particularités en matière de dépendances et ses conventions de sortie. Ces utilitaires regroupent tout cela dans un modèle d’appel unique.
Pourquoi utiliser Ultralytics pour exporter des modèles autres que YOLO ?#
- Une seule API pour 11 formats : apprends une seule convention d’appel au lieu d’une douzaine.
- Même chemin d’exécution que pour les exportations YOLO : les mêmes fonctions auxiliaires gèrent toutes les exportations Ultralytics YOLO.
- Quantification FP16 et INT8 au moyen d’un seul argument
quantizepour les formats compatibles. - Fonctionne sur CPU : aucune GPU n’est nécessaire pour l’étape d’exportation elle-même, tu peux donc l’exécuter localement sur un ordinateur portable ; l’exportation CoreML n’est pas prise en charge sous Windows, et l’exportation Core AI nécessite macOS 26 ou une version ultérieure sur Apple silicon, ou Linux x86_64 avec glibc 2.34 ou une version ultérieure, ainsi que Python 3.11 à 3.14.
Démarrage rapide#
La méthode la plus rapide consiste à exporter vers ONNX en deux lignes, sans code YOLO ni configuration autre que 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")Formats d’exportation pris en charge#
Les fonctions torch2* prennent en entrée un objet standard torch.nn.Module et un tenseur d’entrée d’exemple. MNN, TF SavedModel et TF Frozen Graph passent par un artefact intermédiaire ONNX ou Keras. Aucun attribut spécifique à YOLO n’est requis dans les deux cas.
| Format | Fonction | Installation | Sortie |
|---|---|---|---|
| TorchScript | torch2torchscript() | inclus avec PyTorch | fichier .torchscript |
| ONNX | torch2onnx() | pip install onnx | fichier .onnx |
| OpenVINO | torch2openvino() | pip install openvino | répertoire _openvino_model/ |
| CoreML | torch2coreml() | pip install coremltools | .mlpackage |
| Core AI | torch2coreai() | pip install coreai-torch (Apple silicon macOS 26+, Linux x86_64 avec glibc 2.34+ ; Python 3.11-3.14) | répertoire .aimodel |
| TF SavedModel | onnx2saved_model() | voir les exigences détaillées ci-dessous | répertoire _saved_model/ |
| TF Frozen Graph | keras2pb() | voir les exigences détaillées ci-dessous | fichier .pb |
| PaddlePaddle | torch2paddle() | pip install paddlepaddle x2paddle | répertoire _paddle_model/ |
| MNN | onnx2mnn() | pip install MNN | fichier .mnn |
| NCNN | torch2ncnn() | pip install ncnn pnnx | répertoire _ncnn_model/ |
| ExecuTorch | torch2executorch() | pip install executorch | répertoire _executorch_model/ |
Plusieurs fonctions d’exportation acceptent un dictionnaire facultatif metadata (par exemple, torch2torchscript(..., metadata={"author": "me"})) qui intègre des paires clé-valeur personnalisées à l’artefact exporté lorsque le format le permet.
Exemples étape par étape#
Chaque exemple ci-dessous utilise la même configuration : un ResNet-18 préentraîné provenant de timm, en mode évaluation :
import timm
import torch
model = timm.create_model("resnet18", pretrained=True).eval()
im = torch.randn(1, 3, 224, 224)Dropout, normalisation par lots et les autres couches réservées à l’entraînement se comportent différemment lors de l’inférence. Si tu ignores .eval(), les exportations produiront des résultats incorrects.
Exporter vers ONNX#
from ultralytics.utils.export import torch2onnx
torch2onnx(model, im, output_file="resnet18.onnx")Pour utiliser une taille de lot dynamique, transmets un dictionnaire dynamic :
torch2onnx(model, im, output_file="resnet18_dyn.onnx", dynamic={"images": {0: "batch_size"}})L’opset par défaut est 14 et le nom d’entrée par défaut est "images". Remplace-les à l’aide des arguments opset, input_names ou output_names.
Exporter vers TorchScript#
Aucune dépendance supplémentaire n’est nécessaire. Utilise torch.jit.trace en arrière-plan.
from ultralytics.utils.export import torch2torchscript
torch2torchscript(model, im, output_file="resnet18.torchscript")Exporter vers OpenVINO#
from ultralytics.utils.export import torch2openvino
ov_model = torch2openvino(model, im, output_dir="resnet18_openvino_model")Le répertoire contient une paire de fichiers aux noms fixes, model.xml et model.bin :
resnet18_openvino_model/
├── model.xml
└── model.binTransmets dynamic=True pour des formes d’entrée dynamiques, quantize=16 pour FP16 ou quantize=8 pour la quantification INT8. La quantification INT8 nécessite également un argument calibration_dataset.
Nécessite openvino>=2024.0.0 (ou >=2025.2.0 sous macOS 15.4+) et torch>=2.1.
Exporter vers 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")Pour les modèles de classification, transmets une liste de noms de classes à classifier_names pour ajouter une tête de classification au modèle CoreML.
Nécessite coremltools>=9.0, torch>=1.11 et numpy<=2.3.5. N’est pas pris en charge sous Windows.
coremltools>=9.0 fournit des wheels pour Python 3.10–3.13 sous macOS et Linux. Avec les versions plus récentes de Python, l’extension C native ne parvient pas à se charger. Utilise Python 3.10–3.13 pour l’exportation CoreML.
Exporter vers Core AI#
from ultralytics.utils.export import torch2coreai
torch2coreai(model, im, output_file="resnet18.aimodel")L’artefact .aimodel est un répertoire :
resnet18.aimodel/
├── main.mlirb
├── main.hash
└── metadata.jsonL’exportation s’exécute sous macOS 26 ou une version ultérieure sur Apple silicon, ou sous Linux x86_64 avec glibc 2.34 ou une version ultérieure, avec Python 3.11 à 3.14 (pip install coreai-torch), et quantize=16 écrit un artefact FP16 qui accepte des entrées float16 ; l’artefact s’exécute sous iOS 27 et macOS 27. Consulte l’intégration Core AI, notamment la remarque sur les artefacts FP16 qui provoquent un arrêt au chargement.
Exporter vers TensorFlow SavedModel#
L’exportation TF SavedModel passe par ONNX comme étape intermédiaire :
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 fonction renvoie un modèle Keras et génère également des fichiers LiteRT FP32 et FP16 (.tflite) dans le répertoire de sortie :
resnet18_saved_model/
├── saved_model.pb
├── variables/
├── assets/
├── fingerprint.pb
├── resnet18_float32.tflite
└── resnet18_float16.tfliteTransmets quantize=8 pour ajouter un .tflite INT8 aux fichiers existants.
L’exportation TensorFlow ne fonctionne pas sous macOS avec Python 3.13 ou une version ultérieure ; utilise Python 3.12 ou une version antérieure sous macOS, ou Linux.
Configuration requise avec Python 3.12 ou une version antérieure (avec Python 3.13 ou une version ultérieure, l’exportation nécessite plutôt tensorflow>2.19.0, tf_keras>2.19.0, onnx2tf>=2.3.0,<2.3.16 et 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.0sous macOS (ai-edge-litert>=1.2.0sur les autres plateformes)onnxslim>=0.1.82onnx>=1.12.0,<2.0.0protobuf>=5
Exporter vers TensorFlow Frozen Graph#
À partir de l’exportation SavedModel ci-dessus, convertis le keras_model renvoyé en graphe .pb figé :
from pathlib import Path
from ultralytics.utils.export import keras2pb
keras2pb(keras_model, output_file=Path("resnet18_saved_model/resnet18.pb"))Exporter vers NCNN#
from ultralytics.utils.export import torch2ncnn
torch2ncnn(model, im, output_dir="resnet18_ncnn_model")Le répertoire contient des fichiers param et bin aux noms fixes, ainsi qu’un wrapper Python :
resnet18_ncnn_model/
├── model.ncnn.param
├── model.ncnn.bin
└── model_ncnn.pyExporter vers MNN#
L’exportation MNN nécessite un fichier ONNX en entrée. Commence par exporter vers ONNX, puis effectue la conversion :
from ultralytics.utils.export import onnx2mnn, torch2onnx
torch2onnx(model, im, output_file="resnet18.onnx")
onnx2mnn("resnet18.onnx", output_file="resnet18.mnn")Prend en charge quantize=16 pour la quantification FP16 et quantize=8 pour la quantification INT8. Nécessite MNN>=2.9.6 et torch>=1.10.
Exporter vers PaddlePaddle#
from ultralytics.utils.export import torch2paddle
torch2paddle(model, im, output_dir="resnet18_paddle_model")Le répertoire contient le modèle PaddlePaddle et les fichiers de paramètres :
resnet18_paddle_model/
├── inference_model/
│ ├── model.json
│ └── model.pdiparams
├── model.pdparams
└── x2paddle_code.pyNécessite x2paddle et la distribution PaddlePaddle adaptée à ta plateforme :
paddlepaddle-gpu>=3.0.0,<3.3.0sur CUDApaddlepaddle==3.0.0sur un CPU ARM64paddlepaddle>=3.0.0,<3.3.0sur d’autres CPU
Non pris en charge sur NVIDIA Jetson.
Exporter vers ExecuTorch#
from ultralytics.utils.export import torch2executorch
torch2executorch(model, im, output_dir="resnet18_executorch_model")Le fichier .pte exporté est enregistré dans le répertoire de sortie :
resnet18_executorch_model/
└── model.pteNécessite torch>=2.9.0 et un environnement d’exécution ExecuTorch compatible (pip install executorch). Pour utiliser l’environnement d’exécution, consulte l’intégration ExecuTorch.
Vérifier le modèle exporté#
Après l’exportation, vérifie la parité numérique avec le modèle PyTorch d’origine avant le déploiement. Un rapide test de fumée avec ONNXBackend depuis ultralytics.nn.backends compare les sorties et signale rapidement les erreurs de traçage ou de quantification :
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 exportAvec ResNet-18, la plupart des exportations FP32 présentent un écart d’environ 1e-5 par rapport à PyTorch, et TorchScript correspond exactement. Trois environnements d’exécution peuvent néanmoins calculer une exportation FP32 en précision réduite et présenter un écart proche de 1e-2 à 1e-1 : NCNN active le calcul FP16 sur les CPU qui le prennent en charge, MNNBackend charge les modèles avec precision="low", et le plug-in CPU OpenVINO s’exécute automatiquement en FP16 avec son mode d’exécution par défaut PERFORMANCE sur certains matériels, comme les puces Apple. Un écart bien supérieur à la référence propre au format indique des opérations non prises en charge, une forme d’entrée incorrecte ou un modèle qui n’est pas en mode évaluation. Les exportations FP16 et INT8 ont des tolérances plus larges. Valide le résultat sur des données réelles plutôt que sur des tenseurs aléatoires.
Pour les autres environnements d’exécution, le nom du tenseur d’entrée peut différer. OpenVINO, par exemple, utilise le nom de l’argument de la méthode forward du modèle (généralement x pour les modèles génériques), tandis que torch2onnx utilise "images" par défaut.
Exécuter le modèle exporté#
Les modèles non-YOLO exportés se rechargent via l’API standard YOLO(). Les exportations ci-dessus ne contiennent aucune métadonnée Ultralytics concernant la tâche ou la taille d’entrée : passe donc explicitement task et imgsz correspondant au tenseur d’exemple utilisé pour l’exportation :
from ultralytics import YOLO
results = YOLO("resnet18.onnx", task="classify")("path/to/image.jpg", imgsz=224)
print(results[0].probs.top1)imgsz est important lorsque l’exportation a une forme d’entrée fixe : les exportations ONNX et TF SavedModel ci-dessus rejettent la valeur par défaut 640. Les exportations TorchScript et NCNN ci-dessus acceptent d’autres tailles, mais aucun des deux exportateurs ne le garantit : tous deux tracent le modèle à partir du tenseur d’exemple, donc un modèle qui aplati les données dans une couche Linear conserve une taille fixe. Vérifie ta propre exportation.
La valeur est ensuite arrondie au multiple supérieur du pas du modèle, qui est de 32 en l’absence de métadonnées. Une exportation à forme fixe de 200x200 reçoit donc une entrée de 224x224 et est rejetée, même si imgsz=200 correspond. Pour les tailles d’entrée qui ne sont pas des multiples de 32, appelle directement le backend.
Appeler directement un backend#
Pour traiter des tenseurs bruts sans le prétraitement ni le post-traitement Ultralytics, utilise les classes propres à chaque format dans ultralytics.nn.backends, comme le fait l’exemple de vérification ci-dessus. Chaque classe prend l’artefact exporté et un périphérique en entrée, et peut être appelée :
| Format | Backend | Disposition des entrées |
|---|---|---|
| TorchScript | TorchScriptBackend | BCHW |
| ONNX | ONNXBackend | BCHW |
| OpenVINO | OpenVINOBackend | BCHW |
| CoreML | CoreMLBackend | BHWC |
| Core AI | CoreAIBackend | BCHW |
| TF SavedModel, graphe figé | TensorFlowBackend | BHWC |
| LiteRT | LiteRTBackend | BCHW |
| PaddlePaddle | PaddleBackend | BCHW |
| MNN | MNNBackend | BCHW |
| NCNN | NCNNBackend | BCHW |
| ExecuTorch | ExecuTorchBackend | BCHW |
TensorFlowBackend couvre deux formats et utilise format="saved_model" par défaut ; passe donc format="pb" pour un graphe figé.
Voici trois opérations prises en charge par le chemin YOLO(), mais pas par un appel direct :
- Disposition des entrées :
CoreMLBackendetTensorFlowBackendattendent des entrées BHWC. Transpose d’abord le tenseur avecim.permute(0, 2, 3, 1); un tenseur BCHW provoque une incompatibilité de forme. - Autograd : place les appels dans
torch.inference_mode().TorchScriptBackendrenvoie un tenseur qui conserve un graphe de gradient. - Post-traitement : sans métadonnées, un backend laisse
tasksous forme deNoneetnamesvide.LiteRTBackenddénormalise malgré tout toute sortie 3D en fonction de la taille de l’image, en supposant qu’elle contient des boîtes YOLO, ce qui est incorrect pour un modèle non-YOLO produisant une sortie 3D. Les sorties 2D, comme les logits de classification, ne sont pas affectées.
Limitations connues#
- Prise en charge variable des entrées multiples :
torch2onnx,torch2openvinoettorch2torchscriptacceptent un tuple de tenseurs d’exemple pour les modèles à entrées multiples.torch2coreml,torch2coreai,torch2ncnn,torch2paddleettorch2executorchsupposent un seul tenseur d’entrée. - Formats réservés à YOLO : les exportations Axelera et Sony IMX500 nécessitent des attributs de modèle propres à YOLO et ne sont pas disponibles pour les modèles génériques.
- Formats propres à une plateforme : TensorRT nécessite un GPU NVIDIA. RKNN nécessite le SDK
rknn-toolkit2(Linux uniquement). Edge TPU nécessite le binaireedgetpu_compiler(Linux x86_64 uniquement).
Conclusion#
Ces utilitaires permettent de convertir n’importe quel modèle PyTorch, même un simple torch.nn.Module, en artefact ONNX, OpenVINO, CoreML, TensorFlow ou pour environnement mobile prêt au déploiement, grâce à une API cohérente. Choisis le format adapté à ton matériel cible, vérifie la parité numérique avec le modèle d’origine, puis suis le guide d’intégration correspondant pour les étapes de déploiement propres à l’environnement d’exécution.
FAQ#
N’importe quel
torch.nn.Module. Cela inclut les modèles de timm, torchvision ou tout modèle PyTorch personnalisé. Le modèle doit être en mode évaluation (model.eval()) avant l’exportation. ONNX, OpenVINO et TorchScript acceptent également un tuple de tenseurs d’exemple pour les modèles à entrées multiples.Tous les formats pris en charge (TorchScript, ONNX, OpenVINO, CoreML, Core AI, TF SavedModel, TF Frozen Graph, PaddlePaddle, MNN, NCNN, ExecuTorch) peuvent être exportés sur CPU. Aucun GPU n’est nécessaire pour le processus d’exportation. TensorRT est le seul format qui nécessite un GPU NVIDIA.
Utilise la dernière version. L’argument
quantizenécessite Ultralytics>=8.4.81, et l’exportation Core AI nécessite>=8.4.131(>=8.4.163sous Linux ou aveccoreai-torch>=0.4.3).Oui. Les classificateurs, détecteurs et modèles de segmentation torchvision s’exportent vers
.mlpackageviatorch2coreml. Pour les modèles de classification d’images, passe une liste de noms de classes àclassifier_namesafin d’intégrer une tête de classification. Lance l’exportation sous macOS ou Linux. CoreML n’est pas pris en charge sous Windows. Consulte l’intégration CoreML pour les détails du déploiement sur iOS.Oui, pour plusieurs formats. Passe
quantize=16pour FP16 ouquantize=8pour INT8 lors de l’exportation vers OpenVINO, CoreML ou MNN ;onnx2saved_modelacceptequantize=8pour un fichier LiteRT INT8, tandis que NCNN et Core AI exportent en FP32 par défaut, acceptentquantize=16pour FP16 et ne proposent pas de voie INT8. La quantification INT8 avec OpenVINO nécessite également un argumentcalibration_datasetpour la quantification après entraînement. Consulte la page d’intégration de chaque format pour connaître les compromis liés à la quantification.Exécute le modèle PyTorch d’origine et le modèle exporté sur la même entrée, puis compare les sorties. Charge le fichier exporté avec le backend correspondant (par exemple,
ONNXBackendpour ONNX) et vérifie l’écart absolu maximal. Évalue cet écart par rapport à la référence propre au format : NCNN,MNNBackendet OpenVINO sur certains CPU peuvent exécuter les exportations FP32 en précision réduite et présenter un écart proche de1e-2à1e-1, tandis que la plupart des autres formats présentent un écart proche de1e-5. Un écart nettement supérieur indique des opérations non prises en charge, une forme d’entrée incorrecte ou un modèle qui n’est pas en mode évaluation. Consulte Vérifier le modèle exporté pour obtenir un exemple exécutable.