Ultralytics YOLO27 : Rejoins la liste d’attente.
Get Started

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 quantize pour 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.

FormatFonctionInstallationSortie
TorchScripttorch2torchscript()inclus avec PyTorchfichier .torchscript
ONNXtorch2onnx()pip install onnxfichier .onnx
OpenVINOtorch2openvino()pip install openvinorépertoire _openvino_model/
CoreMLtorch2coreml()pip install coremltools.mlpackage
Core AItorch2coreai()pip install coreai-torch (Apple silicon macOS 26+, Linux x86_64 avec glibc 2.34+ ; Python 3.11-3.14)répertoire .aimodel
TF SavedModelonnx2saved_model()voir les exigences détaillées ci-dessousrépertoire _saved_model/
TF Frozen Graphkeras2pb()voir les exigences détaillées ci-dessousfichier .pb
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddlerépertoire _paddle_model/
MNNonnx2mnn()pip install MNNfichier .mnn
NCNNtorch2ncnn()pip install ncnn pnnxrépertoire _ncnn_model/
ExecuTorchtorch2executorch()pip install executorchrépertoire _executorch_model/
Intégration de métadonnées

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)
Appelle toujours `model.eval()` avant l’exportation

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

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

Erreur `BlobWriter not loaded`

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

L’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.tflite

Transmets 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.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 sous macOS (ai-edge-litert>=1.2.0 sur les autres plateformes)
  • onnxslim>=0.1.82
  • onnx>=1.12.0,<2.0.0
  • protobuf>=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.py

Exporter 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.py

Nécessite x2paddle et la distribution PaddlePaddle adaptée à ta plateforme :

  • paddlepaddle-gpu>=3.0.0,<3.3.0 sur CUDA
  • paddlepaddle==3.0.0 sur un CPU ARM64
  • paddlepaddle>=3.0.0,<3.3.0 sur 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.pte

Né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 export
Différence attendue

Avec 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 :

FormatBackendDisposition des entrées
TorchScriptTorchScriptBackendBCHW
ONNXONNXBackendBCHW
OpenVINOOpenVINOBackendBCHW
CoreMLCoreMLBackendBHWC
Core AICoreAIBackendBCHW
TF SavedModel, graphe figéTensorFlowBackendBHWC
LiteRTLiteRTBackendBCHW
PaddlePaddlePaddleBackendBCHW
MNNMNNBackendBCHW
NCNNNCNNBackendBCHW
ExecuTorchExecuTorchBackendBCHW

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 : CoreMLBackend et TensorFlowBackend attendent des entrées BHWC. Transpose d’abord le tenseur avec im.permute(0, 2, 3, 1) ; un tenseur BCHW provoque une incompatibilité de forme.
  • Autograd : place les appels dans torch.inference_mode(). TorchScriptBackend renvoie un tenseur qui conserve un graphe de gradient.
  • Post-traitement : sans métadonnées, un backend laisse task sous forme de None et names vide. LiteRTBackend dé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, torch2openvino et torch2torchscript acceptent un tuple de tenseurs d’exemple pour les modèles à entrées multiples. torch2coreml, torch2coreai, torch2ncnn, torch2paddle et torch2executorch supposent 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 binaire edgetpu_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 quantize nécessite Ultralytics >=8.4.81, et l’exportation Core AI nécessite >=8.4.131 (>=8.4.163 sous Linux ou avec coreai-torch>=0.4.3).

  • Oui. Les classificateurs, détecteurs et modèles de segmentation torchvision s’exportent vers .mlpackage via torch2coreml. Pour les modèles de classification d’images, passe une liste de noms de classes à classifier_names afin 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=16 pour FP16 ou quantize=8 pour INT8 lors de l’exportation vers OpenVINO, CoreML ou MNN ; onnx2saved_model accepte quantize=8 pour un fichier LiteRT INT8, tandis que NCNN et Core AI exportent en FP32 par défaut, acceptent quantize=16 pour FP16 et ne proposent pas de voie INT8. La quantification INT8 avec OpenVINO nécessite également un argument calibration_dataset pour 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, ONNXBackend pour ONNX) et vérifie l’écart absolu maximal. Évalue cet écart par rapport à la référence propre au format : NCNN, MNNBackend et OpenVINO sur certains CPU peuvent exécuter les exportations FP32 en précision réduite et présenter un écart proche de 1e-2 à 1e-1, tandis que la plupart des autres formats présentent un écart proche de 1e-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.

Commentaires