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

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 spécificités de dépendances et ses conventions de sortie.

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

Pourquoi utiliser Ultralytics pour l'exportation non-YOLO ?

  • Une seule API pour 10 formats : apprends une seule convention d'appel au lieu d'une douzaine.
  • Surface utilitaire partagée : les assistants d'exportation résident sous ultralytics.utils.export, donc une fois les paquets backend installés, tu peux conserver le même modèle d'appel à travers les formats.
  • Le même chemin de code que les exportations YOLO : les mêmes assistants alimentent chaque exportation Ultralytics YOLO.
  • Quantification FP16 et INT8 intégrée pour les formats qui la prennent en charge (OpenVINO, CoreML, MNN, NCNN).
  • Fonctionne sur CPU : aucune GPU requise 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 une exportation en deux lignes vers ONNX sans code YOLO et sans configuration au-delà de 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

La classe de modèle torch2* Les fonctions prennent un torch.nn.Module standard et un tenseur d'entrée exemple. MNN, TF SavedModel et TF Frozen Graph passent par un artefact ONNX ou Keras intermédiaire. Aucun attribut spécifique à YOLO n'est requis dans les deux cas.

FormatFonctionInstallerSortie
ONNXtorch2onnx()pip install onnx.onnx fichier
TorchScripttorch2torchscript()inclus avec PyTorch.torchscript fichier
OpenVINOtorch2openvino()pip install openvino_openvino_model/ répertoire
CoreMLtorch2coreml()pip install coremltools.mlpackage
TF SavedModelonnx2saved_model()vois les exigences détaillées ci-dessous_saved_model/ répertoire
TF Frozen Graphkeras2pb()vois les exigences détaillées ci-dessous.pb fichier
NCNNtorch2ncnn()pip install ncnn pnnx_ncnn_model/ répertoire
MNNonnx2mnn()pip install MNN.mnn fichier
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddle_paddle_model/ répertoire
ExecuTorchtorch2executorch()pip install executorch_executorch_model/ répertoire
ONNX comme format intermédiaire

MNN, TF SavedModel, et les exportations 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 metadata optionnel (ex: torch2torchscript(..., metadata={"author": "me"})) qui intègre des paires clé-valeur personnalisées dans l'artefact exporté là où 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

Dropout, batch normalization, et d'autres couches réservées à l'entraînement se comportent différemment pendant l'inférence. Oublier .eval() produit des exportations 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 avec les arguments opset, input_names, ou output_names.

Exporter vers TorchScript

Aucune dépendance supplémentaire requise. 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 model.xml et en model.bin au nom fixe :

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

Passe dynamic=True pour des formes d'entrée dynamiques, half=True pour FP16, ou int8=True pour la quantification INT8. INT8 nécessite en plus un calibration_dataset.

Requiert 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, output_file="resnet18.mlpackage")

Pour les classification modèles, passe une liste de noms de classes à classifier_names pour ajouter une tête de classification au modèle CoreML.

Requiert coremltools>=9.0, torch>=1.11, et la numpy<=2.3.5. Non supporté 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 échoue à se charger. Utilise Python 3.10–3.13 pour l'exportation 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 retourne un modèle Keras et génère également des fichiers TFLite (.tflite) à l'intérieur du 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 (installe avec --extra-index-url https://pypi.ngc.nvidia.com)
  • ai-edge-litert>=1.2.0,<1.4.0 sur macOS (ai-edge-litert>=1.2.0 sur d'autres plateformes)
  • onnxslim>=0.1.71
  • onnx>=1.12.0,<2.0.0
  • protobuf>=5

Exporter vers TensorFlow Frozen Graph

En continuant depuis l'exportation SavedModel ci-dessus, convertis le modèle Keras retourné en un 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 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 en 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 half=True pour FP16 et int8=True pour la quantification INT8. Requiert MNN>=2.9.6 et en 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

Requiert x2paddle et la distribution PaddlePaddle correcte pour ta plateforme :

  • paddlepaddle-gpu>=3.0.0,<3.3.0 sur CUDA
  • paddlepaddle==3.0.0 sur ARM64 CPU
  • paddlepaddle>=3.0.0,<3.3.0 sur d'autres CPUs

Non supporté 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é à l'intérieur du répertoire de sortie :

resnet18_executorch_model/
└── model.pte

Requiert torch>=2.9.0 et un runtime ExecuTorch correspondant (pip install executorch). Pour l'utilisation du runtime, consulte la documentation d'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 toute mise en production. Un test rapide avec ONNXBackend de ultralytics.nn.backends compare les sorties et signale rapidement les erreurs de tracing 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}")  # should be < 1e-5
Différence attendue

Pour les exportations FP32, la différence absolue maximale doit être inférieure à 1e-5. 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 exportations FP16 et INT8 ont des tolérances plus larges. Valide sur des données réelles au lieu de tenseurs aléatoires.

Pour d'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 en torch2openvino accepte un tuple ou une liste de tenseurs exemples pour les modèles avec plusieurs entrées. torch2torchscript, torch2coreml, torch2ncnn, torch2paddle, et la torch2executorch suppose un tenseur d'entrée unique.
  • ExecuTorch nécessite 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: Axelera et en Les exportations 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 à une plateforme: TensorRT nécessite un GPU NVIDIA. RKNN nécessite le SDK rknn-toolkit2 (Linux uniquement). Edge TPU nécessite le SDK edgetpu_compiler binaire (Linux uniquement).

FAQ

Quels modèles puis-je exporter avec Ultralytics ?

N'importe quel torch.nn.Module. Cela inclut les modèles provenant 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 exemples pour les modèles à entrées multiples.

Quels formats d'exportation fonctionnent sans GPU ?

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.

Quelle version d'Ultralytics dois-je utiliser ?

Utilise Ultralytics >=8.4.38, qui inclut le module ultralytics.utils.export et le standardisé output_file/output_dir.

Puis-je exporter un modèle torchvision vers CoreML pour un déploiement sur iOS ?

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 pour intégrer une tête de classification. Effectue l'exportation sur macOS ou Linux. CoreML n'est pas pris en charge sous Windows. Consulte l'intégration CoreML pour plus de détails sur le déploiement iOS.

Puis-je quantifier mon modèle exporté en INT8 ou FP16 ?

Oui, pour plusieurs formats. Utilise half=True pour FP16 ou int8=True pour INT8 lors de l'exportation vers OpenVINO, CoreML, MNN ou NCNN. L'INT8 dans OpenVINO nécessite en plus 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.

Commentaires