Ultralytics YOLO27: Trag dich auf die Warteliste ein.
Get Started

So exportierst du Nicht-YOLO-PyTorch-Modelle mit Ultralytics#

Ultralytics stellt eigenständige Exportprogramme unter ultralytics.utils.export bereit, die mehrere Backends hinter einer einheitlichen Schnittstelle bündeln. Du kannst beliebige torch.nn.Module exportieren, darunter Bildmodelle von timm, Klassifikatoren und Detektoren von torchvision sowie deine eigenen Architekturen, nach TorchScript, ONNX, OpenVINO, CoreML, Core AI, TensorFlow SavedModel, TensorFlow Frozen Graph, PaddlePaddle, MNN, NCNN und ExecuTorch, ohne jedes Backend einzeln erlernen zu müssen.

Die Bereitstellung von PyTorch-Modellen in einer Produktionsumgebung bedeutet in der Regel, für jedes Ziel einen anderen Exporter zu verwenden: torch.onnx.export für ONNX, coremltools für Apple-Geräte, onnx2tf für TensorFlow, pnnx für NCNN und so weiter. Jedes Tool hat eine eigene API, Eigenheiten bei Abhängigkeiten und eigene Konventionen für die Ausgabe. Diese Programme fassen all das in einem einheitlichen Aufrufmuster zusammen.

Warum Ultralytics für den Export von Nicht-YOLO-Modellen verwenden?#

  • Eine API für 11 Formate: Lerne eine einzige Aufrufkonvention statt eines Dutzends.
  • Derselbe Codepfad wie bei YOLO-Exporten: Dieselben Hilfsfunktionen unterstützen alle Ultralytics-YOLO-Exporte.
  • FP16- und INT8-Quantisierung über ein einziges Argument quantize für Formate, die dies unterstützen.
  • Funktioniert auf CPU: Für den Export selbst ist keine GPU erforderlich, du kannst ihn also lokal auf einem Laptop ausführen. CoreML-Export wird unter Windows nicht unterstützt. Für Core AI-Export sind macOS 26 oder neuer auf Apple-Chips oder x86_64 Linux mit glibc 2.34 oder neuer sowie Python 3.11 bis 3.14 erforderlich.

Schnellstart#

Am schnellsten geht es mit einem Export nach ONNX in zwei Zeilen – ohne YOLO-Code und ohne weitere Einrichtung außer 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")

Unterstützte Exportformate#

Die Funktionen torch2* benötigen ein standardmäßiges torch.nn.Module und einen Beispiel-Eingabetensor. Für MNN, TF SavedModel und TF Frozen Graph wird ein dazwischenliegendes ONNX- oder Keras-Artefakt verwendet. In beiden Fällen sind keine YOLO-spezifischen Attribute erforderlich.

FormatFunktionInstallationAusgabe
TorchScripttorch2torchscript()in PyTorch enthaltenDatei .torchscript
ONNXtorch2onnx()pip install onnxDatei .onnx
OpenVINOtorch2openvino()pip install openvinoVerzeichnis _openvino_model/
CoreMLtorch2coreml()pip install coremltools.mlpackage
Core AItorch2coreai()pip install coreai-torch (Apple-Chips, macOS 26 oder neuer; x86_64 Linux, glibc 2.34 oder neuer; Python 3.11–3.14)Verzeichnis .aimodel
TF SavedModelonnx2saved_model()siehe ausführliche Anforderungen weiter untenVerzeichnis _saved_model/
TF Frozen Graphkeras2pb()siehe ausführliche Anforderungen weiter untenDatei .pb
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddleVerzeichnis _paddle_model/
MNNonnx2mnn()pip install MNNDatei .mnn
NCNNtorch2ncnn()pip install ncnn pnnxVerzeichnis _ncnn_model/
ExecuTorchtorch2executorch()pip install executorchVerzeichnis _executorch_model/
Metadaten einbetten

Einige Exportfunktionen akzeptieren ein optionales metadata-Wörterbuch (z. B. torch2torchscript(..., metadata={"author": "me"})), das benutzerdefinierte Schlüssel-Wert-Paare in das exportierte Artefakt einbettet, sofern das Format dies unterstützt.

Schritt-für-Schritt-Beispiele#

In jedem der folgenden Beispiele wird dieselbe Konfiguration verwendet: ein vortrainiertes ResNet-18 von timm im Evaluierungsmodus:

import timm
import torch

model = timm.create_model("resnet18", pretrained=True).eval()
im = torch.randn(1, 3, 224, 224)
Rufe vor dem Export immer `model.eval()` auf

Dropout, Batch-Normalisierung und andere Layer, die nur beim Training verwendet werden, verhalten sich bei der Inferenz anders. Wenn du .eval() überspringst, liefern die Exporte falsche Ergebnisse.

Nach ONNX exportieren#

from ultralytics.utils.export import torch2onnx

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

Für eine dynamische Batchgröße übergib ein dynamic-Wörterbuch:

torch2onnx(model, im, output_file="resnet18_dyn.onnx", dynamic={"images": {0: "batch_size"}})

Der standardmäßige Opset ist 14, und der standardmäßige Eingabename lautet "images". Du kannst diese Werte mit den Argumenten opset, input_names oder output_names überschreiben.

Nach TorchScript exportieren#

Es sind keine zusätzlichen Abhängigkeiten erforderlich. Im Hintergrund wird torch.jit.trace verwendet.

from ultralytics.utils.export import torch2torchscript

torch2torchscript(model, im, output_file="resnet18.torchscript")

Nach OpenVINO exportieren#

from ultralytics.utils.export import torch2openvino

ov_model = torch2openvino(model, im, output_dir="resnet18_openvino_model")

Das Verzeichnis enthält ein Paar aus model.xml und model.bin mit festen Dateinamen:

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

Übergib dynamic=True für dynamische Eingabeformen, quantize=16 für FP16 oder quantize=8 für die INT8-Quantisierung. Für INT8 ist zusätzlich das Argument calibration_dataset erforderlich.

Erfordert openvino>=2024.0.0 (oder >=2025.2.0 unter macOS 15.4 oder neuer) und torch>=2.1.

Nach CoreML exportieren#

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")

Bei Klassifizierungsmodellen kannst du eine Liste mit Klassennamen an classifier_names übergeben, um dem CoreML-Modell einen Klassifizierungskopf hinzuzufügen.

Erfordert coremltools>=9.0, torch>=1.11 und numpy<=2.3.5. Unter Windows nicht unterstützt.

Fehler bei `BlobWriter not loaded`

coremltools>=9.0 stellt Wheels für Python 3.10–3.13 unter macOS und Linux bereit. Bei neueren Python-Versionen lässt sich die native C-Erweiterung nicht laden. Verwende Python 3.10–3.13 für den CoreML-Export.

Nach Core AI exportieren#

from ultralytics.utils.export import torch2coreai

torch2coreai(model, im, output_file="resnet18.aimodel")

Das Artefakt .aimodel ist ein Verzeichnis:

resnet18.aimodel/
├── main.mlirb
├── main.hash
└── metadata.json

Der Export läuft unter macOS 26 oder neuer auf Apple-Chips oder unter x86_64 Linux mit glibc 2.34 oder neuer und Python 3.11 bis 3.14 (pip install coreai-torch). quantize=16 schreibt ein FP16-Artefakt, das float16-Eingaben erwartet; das Artefakt läuft unter iOS 27 und macOS 27. Siehe die Core AI-Integration, einschließlich des Hinweises zu FP16-Artefakten, die beim Laden abbrechen.

Nach TensorFlow SavedModel exportieren#

Der TF-SavedModel-Export verwendet ONNX als Zwischenschritt:

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")

Die Funktion gibt ein Keras-Modell zurück und erstellt außerdem FP32- und FP16-LiteRT-Dateien (.tflite) im Ausgabeverzeichnis:

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

Übergib quantize=8, um zusätzlich ein INT8-.tflite zu erstellen.

Der TensorFlow-Export läuft unter macOS nicht mit Python 3.13 oder neuer. Verwende unter macOS Python 3.12 oder älter oder nutze Linux.

Anforderungen für Python 3.12 oder älter (ab Python 3.13 erfordert der Export stattdessen tensorflow>2.19.0, tf_keras>2.19.0, onnx2tf>=2.3.0,<2.3.16 und 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 unter macOS (ai-edge-litert>=1.2.0 auf anderen Plattformen)
  • onnxslim>=0.1.82
  • onnx>=1.12.0,<2.0.0
  • protobuf>=5

Nach TensorFlow Frozen Graph exportieren#

Fahre mit dem obigen SavedModel-Export fort und wandle das zurückgegebene keras_model in einen eingefrorenen .pb-Graphen um:

from pathlib import Path

from ultralytics.utils.export import keras2pb

keras2pb(keras_model, output_file=Path("resnet18_saved_model/resnet18.pb"))

Nach NCNN exportieren#

from ultralytics.utils.export import torch2ncnn

torch2ncnn(model, im, output_dir="resnet18_ncnn_model")

Das Verzeichnis enthält Parameter- und Binärdateien mit festen Dateinamen sowie einen Python-Wrapper:

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

Nach MNN exportieren#

Für den MNN-Export ist eine ONNX-Datei als Eingabe erforderlich. Exportiere zuerst nach ONNX und konvertiere die Datei anschließend:

from ultralytics.utils.export import onnx2mnn, torch2onnx

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

Unterstützt quantize=16 für FP16- und quantize=8 für INT8-Quantisierung. Erfordert MNN>=2.9.6 und torch>=1.10.

Nach PaddlePaddle exportieren#

from ultralytics.utils.export import torch2paddle

torch2paddle(model, im, output_dir="resnet18_paddle_model")

Das Verzeichnis enthält das PaddlePaddle-Modell und die Parameterdateien:

resnet18_paddle_model/
├── inference_model/
│   ├── model.json
│   └── model.pdiparams
├── model.pdparams
└── x2paddle_code.py

Erfordert x2paddle und die passende PaddlePaddle-Distribution für deine Plattform:

  • paddlepaddle-gpu>=3.0.0,<3.3.0 mit CUDA
  • paddlepaddle==3.0.0 auf einer ARM64-CPU
  • paddlepaddle>=3.0.0,<3.3.0 auf anderen CPUs

Wird auf NVIDIA Jetson nicht unterstützt.

Nach ExecuTorch exportieren#

from ultralytics.utils.export import torch2executorch

torch2executorch(model, im, output_dir="resnet18_executorch_model")

Die exportierte Datei .pte wird im Ausgabeverzeichnis gespeichert:

resnet18_executorch_model/
└── model.pte

Erfordert torch>=2.9.0 und eine passende ExecuTorch-Laufzeitumgebung (pip install executorch). Informationen zur Verwendung der Laufzeitumgebung findest du in der ExecuTorch-Integration.

Exportiertes Modell überprüfen#

Überprüfe nach dem Export die numerische Übereinstimmung mit dem ursprünglichen PyTorch-Modell, bevor du es bereitstellst. Ein kurzer Schnelltest mit ONNXBackend aus ultralytics.nn.backends vergleicht die Ausgaben und erkennt frühzeitig Fehler durch Tracing oder Quantisierung:

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
Erwartete Abweichung

Bei ResNet-18 liegen die meisten FP32-Exporte innerhalb von etwa 1e-5 von PyTorch, und TorchScript stimmt exakt überein. Drei Laufzeitumgebungen können einen FP32-Export dennoch mit verringerter Genauigkeit berechnen und nahe bei 1e-2 bis 1e-1 liegen: NCNN aktiviert FP16-Arithmetik auf CPUs, die diese unterstützen, MNNBackend lädt Modelle mit precision="low", und das OpenVINO-CPU-Plugin läuft auf bestimmter Hardware, etwa Apple-Silicon, im standardmäßigen Ausführungsmodus PERFORMANCE automatisch mit FP16. Eine Abweichung, die deutlich über dem formatspezifischen Ausgangswert liegt, weist auf nicht unterstützte Operationen, eine falsche Eingabeform oder darauf hin, dass das Modell nicht im Evaluierungsmodus ist. Für FP16- und INT8-Exporte gelten großzügigere Toleranzen. Überprüfe das Modell mit echten Daten statt mit Zufallstensoren.

Bei anderen Laufzeitumgebungen kann der Name des Eingabetensors abweichen. OpenVINO verwendet beispielsweise den Namen des Forward-Arguments des Modells (bei generischen Modellen meist x), während torch2onnx standardmäßig "images" verwendet.

Exportiertes Modell ausführen#

Exportierte Modelle, die nicht zu YOLO gehören, lassen sich über die normale YOLO()-API wieder laden. Die obigen Exporte enthalten keine Ultralytics-Metadaten zur Aufgabe oder Eingabegröße. Gib daher task explizit an und setze imgsz passend zum Beispiel-Tensor, den du für den Export verwendet hast:

from ultralytics import YOLO

results = YOLO("resnet18.onnx", task="classify")("path/to/image.jpg", imgsz=224)
print(results[0].probs.top1)

imgsz ist wichtig, wenn der Export eine feste Eingabeform hat: Die obigen ONNX- und TF-SavedModel-Exporte weisen den Standardwert 640 zurück. Die obigen TorchScript- und NCNN-Exporte akzeptieren zwar auch andere Größen, aber keiner der beiden Exporteure garantiert das: Beide erstellen einen Trace anhand des Beispiel-Tensors. Ein Modell, das in eine Linear-Schicht abgeflacht wird, behält daher eine feste Form bei. Überprüfe deinen eigenen Export.

Der Wert wird anschließend auf ein Vielfaches des Modell-Strides aufgerundet, der ohne Metadaten 32 beträgt. Ein Export mit fester Form von 200x200 erhält daher eine Eingabe mit 224x224 und wird zurückgewiesen, obwohl imgsz=200 dazu passt. Bei Eingabegrößen, die keine Vielfachen von 32 sind, ruf das Backend direkt auf.

Ein Backend direkt aufrufen#

Um Rohtensoren ohne Ultralytics-Vorverarbeitung und Nachverarbeitung zu verwenden, nutze die formatspezifischen Klassen in ultralytics.nn.backends, wie es auch das obige Beispiel zur Überprüfung tut. Jede Klasse verwendet das exportierte Artefakt und ein Gerät als Eingabe und ist aufrufbar:

FormatBackendEingabelayout
TorchScriptTorchScriptBackendBCHW
ONNXONNXBackendBCHW
OpenVINOOpenVINOBackendBCHW
CoreMLCoreMLBackendBHWC
Core AICoreAIBackendBCHW
TF SavedModel, eingefrorener GraphTensorFlowBackendBHWC
LiteRTLiteRTBackendBCHW
PaddlePaddlePaddleBackendBCHW
MNNMNNBackendBCHW
NCNNNCNNBackendBCHW
ExecuTorchExecuTorchBackendBCHW

TensorFlowBackend deckt zwei Formate ab und verwendet standardmäßig format="saved_model". Gib für einen eingefrorenen Graphen daher format="pb" an.

Drei Dinge, die der YOLO()-Weg für dich erledigt, ein direkter Aufruf jedoch nicht:

  • Eingabelayout: CoreMLBackend und TensorFlowBackend erwarten BHWC. Transponiere den Tensor zuerst mit im.permute(0, 2, 3, 1); ein BCHW-Tensor führt zu einer Abweichung der Formen.
  • Autograd: Umhülle Aufrufe mit torch.inference_mode(). TorchScriptBackend gibt einen Tensor zurück, der weiterhin einen Gradienten-Graphen enthält.
  • Nachverarbeitung: Ohne Metadaten gibt ein Backend task als None zurück und lässt names leer. LiteRTBackend denormalisiert weiterhin jede dreidimensionale Ausgabe anhand der Bildgröße, unter der Annahme, dass sie YOLO-Boxen enthält. Das ist bei einem Modell, das nicht zu YOLO gehört und eine dreidimensionale Ausgabe hat, falsch. Zweidimensionale Ausgaben wie Klassifikator-Logits sind davon nicht betroffen.

Bekannte Einschränkungen#

  • Die Unterstützung mehrerer Eingaben ist uneinheitlich: torch2onnx, torch2openvino und torch2torchscript akzeptieren bei Modellen mit mehreren Eingaben ein Tupel aus Beispiel-Tensoren. torch2coreml, torch2coreai, torch2ncnn, torch2paddle und torch2executorch gehen von einem einzelnen Eingabe-Tensor aus.
  • Formate nur für YOLO: Exporte für Axelera und Sony IMX500 erfordern modellspezifische YOLO-Attribute und sind für generische Modelle nicht verfügbar.
  • Plattformspezifische Formate: TensorRT erfordert eine NVIDIA-GPU. RKNN erfordert das SDK rknn-toolkit2 (nur Linux). Edge TPU erfordert die Binärdatei edgetpu_compiler (nur x86_64 Linux).

Fazit#

Mit diesen Dienstprogrammen kannst du jedes PyTorch-Modell – vom einfachen torch.nn.Module bis hin zu einem für die Bereitstellung geeigneten ONNX-, OpenVINO-, CoreML-, TensorFlow- oder Artefakt für mobile Laufzeitumgebungen – über eine einheitliche API konvertieren. Wähle das Format passend zur Zielhardware, überprüfe die numerische Übereinstimmung mit dem Originalmodell und befolge dann die passende Integrationsanleitung, um die formatspezifischen Bereitstellungsschritte auszuführen.

Häufig gestellte Fragen#

  • Jedes torch.nn.Module. Dazu gehören Modelle aus timm und torchvision sowie alle benutzerdefinierten PyTorch-Modelle. Das Modell muss vor dem Export im Evaluierungsmodus (model.eval()) sein. ONNX, OpenVINO und TorchScript akzeptieren bei Modellen mit mehreren Eingaben außerdem ein Tupel aus Beispiel-Tensoren.

  • Alle unterstützten Formate (TorchScript, ONNX, OpenVINO, CoreML, Core AI, TF SavedModel, TF Frozen Graph, PaddlePaddle, MNN, NCNN, ExecuTorch) lassen sich auf der CPU exportieren. Für den Export selbst ist keine GPU erforderlich. TensorRT ist das einzige Format, das eine NVIDIA-GPU voraussetzt.

  • Verwende die neueste Version. Für das Argument quantize ist Ultralytics >=8.4.81 erforderlich. Für den Core AI-Export wird >=8.4.131 benötigt (>=8.4.163 unter Linux oder mit coreai-torch>=0.4.3).

  • Ja. Klassifikatoren, Detektoren und Segmentierungsmodelle aus torchvision lassen sich über torch2coreml nach .mlpackage exportieren. Übergib bei Bildklassifizierungsmodellen eine Liste mit Klassennamen an classifier_names, um einen Klassifizierungskopf einzubinden. Führe den Export unter macOS oder Linux aus. CoreML wird unter Windows nicht unterstützt. Weitere Informationen zur Bereitstellung unter iOS findest du in der CoreML-Integration.

  • Ja, für mehrere Formate. Übergib beim Export nach OpenVINO, CoreML oder MNN quantize=16 für FP16 oder quantize=8 für INT8. Für eine INT8-LiteRT-Datei benötigt onnx2saved_model den Wert quantize=8. NCNN und Core AI exportieren standardmäßig mit FP32; übergib quantize=16 für FP16. Eine INT8-Option gibt es dort nicht. Für INT8 mit OpenVINO ist außerdem das Argument calibration_dataset für die Quantisierung nach dem Training erforderlich. Informationen zu den Vor- und Nachteilen der Quantisierung findest du auf der Integrationsseite des jeweiligen Formats.

  • Führe das ursprüngliche PyTorch-Modell und das exportierte Modell mit derselben Eingabe aus und vergleiche anschließend die Ausgaben. Lade die exportierte Datei mit dem passenden Backend (zum Beispiel ONNXBackend für ONNX) und überprüfe die maximale absolute Abweichung. Vergleiche die Abweichung mit dem formatspezifischen Ausgangswert: NCNN, MNNBackend und OpenVINO auf manchen CPUs können FP32-Exporte mit verringerter Genauigkeit ausführen und Werte nahe 1e-2 bis 1e-1 erreichen, während die meisten anderen Formate nahe bei 1e-5 liegen. Eine deutlich größere Abweichung weist auf nicht unterstützte Operationen, eine falsche Eingabeform oder darauf hin, dass das Modell nicht im Evaluierungsmodus ist. Ein ausführbares Beispiel findest du unter Exportiertes Modell überprüfen.

Kommentare