YOLO Vision 2026 :

Comment exporter des modèles PyTorch non-YOLO avec Ultralytics#

Ultralytics propose des utilitaires d'exportation autonomes sous ultralytics.utils.export qui encapsulent plusieurs backends derrière une interface cohérente. Tu peux exporter n'importe quel torch.nn.Module, y compris les modèles d'image timm, les classifieurs et détecteurs torchvision ou tes propres architectures personnalisées vers ONNX, TorchScript, OpenVINO, CoreML, NCNN, PaddlePaddle, MNN, ExecuTorch et TensorFlow SavedModel sans avoir à apprendre chaque backend séparément.

Déployer des modèles PyTorch en production signifie généralement 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 de dépendances et ses conventions de sortie. Ces utilitaires condensent tout cela en un seul modèle d'appel.

Pourquoi utiliser Ultralytics pour l'exportation non-YOLO ?#

  • Une seule API pour 10 formats : apprends une convention d'appel unique au lieu d'une douzaine.
  • Surface d'utilitaires partagée : les assistants d'exportation se trouvent sous ultralytics.utils.export. Une fois les paquets du backend installés, tu peux conserver le même modèle d'appel d'un format à l'autre.
  • Même chemin de code que les exportations YOLO : les mêmes assistants alimentent toutes les exportations YOLO d'Ultralytics.
  • Quantification FP16 et INT8 intégrée pour les formats qui la prennent en charge (OpenVINO, CoreML, MNN, NCNN).
  • Fonctionne sur CPU : aucun GPU requis pour l'étape d'exportation elle-même, tu peux donc l'exécuter localement sur n'importe quel ordinateur portable.

Démarrage rapide#

Le chemin le plus rapide est un export en deux lignes vers ONNX sans code YOLO et sans autre configuration 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 un torch.nn.Module standard 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.

FormatFonctionInstallerSortie
ONNXtorch2onnx()pip install onnxFichier .onnx
TorchScripttorch2torchscript()inclus avec PyTorchFichier .torchscript
OpenVINOtorch2openvino()pip install openvinoRépertoire _openvino_model/
CoreMLtorch2coreml()pip install coremltools.mlpackage
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
NCNNtorch2ncnn()pip install ncnn pnnxRépertoire _ncnn_model/
MNNonnx2mnn()pip install MNNFichier .mnn
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddleRépertoire _paddle_model/
ExecuTorchtorch2executorch()pip install executorchRépertoire _executorch_model/
ONNX comme format intermédiaire

Les exports MNN, TF SavedModel et TF Frozen Graph passent par ONNX comme étape intermédiaire. Exporte d'abord vers ONNX, puis convertis.

Métadonnées intégrées

Plusieurs fonctions d'exportation acceptent un dictionnaire optionnel metadata (par exemple, torch2torchscript(..., metadata={"author": "me"})) qui intègre des paires clé-valeur personnalisées dans l'artefact exporté lorsque le format le prend en charge.

Exemples étape par étape#

Chaque exemple ci-dessous utilise la même configuration, un ResNet-18 pré-entraîné 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 d'exporter

Le décrochage (dropout), la normalisation par lots et d'autres couches réservées à l'entraînement se comportent différemment pendant l'inférence. Omettre .eval() produit des exports avec des sorties incorrectes.

Exporter vers ONNX#

from ultralytics.utils.export import torch2onnx

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

Pour une taille de lot dynamique, passe 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 avec les arguments opset, input_names ou output_names.

Exporter vers TorchScript#

Aucune dépendance supplémentaire nécessaire. Utilise torch.jit.trace sous le capot.

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 model.xml et model.bin au nom fixe :

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

Passe dynamic=True pour des formes d'entrée dynamiques, quantize=16 pour le FP16, ou quantize=8 pour la quantification INT8. L'INT8 nécessite en outre un argument calibration_dataset.

Nécessite openvino>=2024.0.0 (ou >=2025.2.0 sur 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, passe 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. Non pris en charge sur Windows.

Erreur `BlobWriter not loaded`

coremltools>=9.0 fournit des wheels pour Python 3.10–3.13 sur macOS et Linux. Sur les versions plus récentes de Python, l'extension C native ne parvient pas à se charger. Utilise Python 3.10–3.13 pour l'export CoreML.

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 TFLite (.tflite) dans le répertoire de sortie :

resnet18_saved_model/
├── saved_model.pb
├── variables/
├── resnet18_float32.tflite
├── resnet18_float16.tflite
└── resnet18_int8.tflite

Exigences :

  • 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 sur 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#

Dans la continuité de l'export SavedModel ci-dessus, convertis le keras_model renvoyé en un graphe figé (.pb) :

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 au nom fixe ainsi qu'un wrapper Python :

resnet18_ncnn_model/
├── model.ncnn.param
├── model.ncnn.bin
└── model_ncnn.py

torch2ncnn() vérifie la présence de ncnn et pnnx lors de la première utilisation.

Exporter vers MNN#

L'exportation MNN nécessite un fichier ONNX en entrée. Exporte d'abord vers ONNX, puis convertis :

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 le 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/
├── model.pdmodel
└── model.pdiparams

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 processeur ARM64
  • paddlepaddle>=3.0.0,<3.3.0 sur les autres processeurs

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 runtime ExecuTorch correspondant (pip install executorch). Pour l'utilisation du runtime, consulte l'intégration ExecuTorch.

Vérifie ton modèle exporté#

Après l'exportation, vérifie la parité numérique avec le modèle PyTorch d'origine avant la livraison. Un test rapide avec ONNXBackend provenant de 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}")  # typically ~1e-5, well under 1e-4 for FP32
Différence attendue

Pour les exports FP32, la différence absolue maximale est généralement d'environ 1e-5 et doit rester bien inférieure à 1e-4. Des différences plus importantes indiquent 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 exports FP16 et INT8 ont des tolérances plus larges. Valide sur des données réelles plutôt que sur des tenseurs aléatoires.

Pour les autres runtimes, le nom du tenseur d'entrée peut différer. OpenVINO, par exemple, utilise le nom de l'argument forward du modèle (généralement x pour les modèles génériques), tandis que torch2onnx utilise par défaut "images".

Limitations connues#

  • La prise en charge des entrées multiples est inégale : torch2onnx et torch2openvino acceptent un tuple ou une liste de tenseurs d'exemple pour les modèles à entrées multiples. torch2torchscript, torch2coreml, torch2ncnn, torch2paddle et torch2executorch supposent un seul tenseur d'entrée.
  • ExecuTorch a besoin de flatc : Le runtime ExecuTorch nécessite le compilateur FlatBuffers. Installe-le avec brew install flatbuffers sur macOS ou apt install flatbuffers-compiler sur Ubuntu.
  • Pas d'inférence via Ultralytics : Les modèles non-YOLO exportés ne peuvent pas être rechargés via YOLO() pour l'inférence. Utilise le runtime natif pour chaque format (ONNX Runtime, OpenVINO Runtime, etc.).
  • Formats réservés à YOLO : Les exports Axelera et Sony IMX500 nécessitent des attributs de modèle spécifiques à YOLO et ne sont pas disponibles pour les modèles génériques.
  • Formats spécifiques à la 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 uniquement).

Conclusion#

Ces utilitaires permettent de transformer n'importe quel modèle PyTorch, d'un simple torch.nn.Module à un artefact ONNX, OpenVINO, CoreML, TensorFlow ou compatible mobile prêt pour le déploiement, via une API unique et cohérente. Choisis le format qui correspond à ton matériel cible, vérifie la parité numérique par rapport au modèle d'origine, puis suis le guide d'intégration correspondant pour les étapes de déploiement propres au runtime.

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 et OpenVINO acceptent en outre un tuple de tenseurs d'exemple pour les modèles à entrées multiples.

  • Tous les formats pris en charge (TorchScript, ONNX, OpenVINO, CoreML, TF SavedModel, TF Frozen Graph, NCNN, PaddlePaddle, MNN, ExecuTorch) peuvent être exportés sur CPU. Aucun GPU n'est requis pour le processus d'exportation lui-même. TensorRT est le seul format qui nécessite un GPU NVIDIA.

  • Utilise >=8.4.38 d'Ultralytics, qui inclut le module ultralytics.utils.export et les arguments standardisés output_file/output_dir.

  • Oui. Les classifieurs, 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 pour intégrer une tête de classification. Exécute l'exportation sur macOS ou Linux. CoreML n'est pas pris en charge sur Windows. Consulte l'intégration CoreML pour les détails du déploiement sur iOS.

  • Oui, pour plusieurs formats. Passe quantize=16 pour le FP16 ou quantize=8 pour l'INT8 lors de l'exportation vers OpenVINO, CoreML, MNN ou NCNN. L'INT8 dans OpenVINO nécessite également un argument calibration_dataset pour la quantification post-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 la différence absolue maximale. Pour les exports FP32, elle est généralement d'environ 1e-5 et doit rester bien inférieure à 1e-4 ; des écarts plus importants indiquent des opérations non prises en charge, une forme d'entrée erronée ou un modèle qui n'est pas en mode évaluation. Consulte Vérifier votre modèle exporté pour un exemple exécutable.

Commentaires