So exportierst du nicht auf YOLO basierende PyTorch-Modelle mit Ultralytics#
Ultralytics liefert eigenständige Export-Dienstprogramme unter ultralytics.utils.export, die mehrere Backends hinter einer einheitlichen Schnittstelle bündeln. Du kannst jedes torch.nn.Module – einschließlich timm-Bildmodellen, torchvision-Klassifikatoren und -Detektoren oder eigener benutzerdefinierter Architekturen – nach ONNX, TorchScript, OpenVINO, CoreML, NCNN, PaddlePaddle, MNN, ExecuTorch, Core AI, TensorFlow SavedModel und TensorFlow Frozen Graph exportieren, ohne jedes Backend einzeln erlernen zu müssen.
PyTorch-Modelle für den Produktionseinsatz bereitzustellen bedeutet normalerweise, 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 Werkzeug hat seine eigene API, eigene Eigenheiten bei Abhängigkeiten und eigene Konventionen für Ausgaben. Diese Hilfsprogramme führen all das auf ein einziges Aufrufmuster zurück.
Warum Ultralytics für den Export nicht auf YOLO basierender Modelle verwenden?#
- Eine API für 11 Formate: Lerne eine einzige Aufrufkonvention anstelle von einem Dutzend.
- Gemeinsame Hilfsfunktionen: Die Exporthelfer liegen unter
ultralytics.utils.export. Sobald die Backend-Pakete installiert sind, kannst du daher formatübergreifend dasselbe Aufrufmuster verwenden. - Derselbe Codepfad wie bei YOLO-Exporten: Dieselben Hilfsfunktionen werden für jeden Ultralytics-YOLO-Export verwendet.
- FP16- und INT8-Quantisierung sind für Formate integriert, die dies unterstützen (OpenVINO, CoreML und MNN; FP16 nur für NCNN und Core AI).
- Läuft auf der CPU: Für den Exportvorgang selbst ist keine GPU erforderlich, sodass du ihn lokal auf einem Laptop ausführen kannst. Der CoreML-Export wird unter Windows nicht unterstützt, und der Core AI-Export erfordert macOS 26 oder neuer auf Apple Silicon.
Schnellstart#
Der schnellste Weg ist ein zweizeiliger Export nach ONNX, ganz 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 unter torch2* übernehmen ein standardmäßiges torch.nn.Module und einen Beispiel-Eingabetensor. MNN, TF SavedModel und TF Frozen Graph verwenden ein zwischengeschaltetes ONNX- oder Keras-Artefakt. In beiden Fällen sind keine YOLO-spezifischen Attribute erforderlich.
| Format | Funktion | Installation | Ausgabe |
|---|---|---|---|
| ONNX | torch2onnx() | pip install onnx | .onnx-Datei |
| TorchScript | torch2torchscript() | in PyTorch enthalten | .torchscript-Datei |
| OpenVINO | torch2openvino() | pip install openvino | _openvino_model/-Verzeichnis |
| CoreML | torch2coreml() | pip install coremltools | .mlpackage |
| TF SavedModel | onnx2saved_model() | siehe unten die ausführlichen Anforderungen | _saved_model/-Verzeichnis |
| TF Frozen Graph | keras2pb() | siehe unten die ausführlichen Anforderungen | .pb-Datei |
| NCNN | torch2ncnn() | pip install ncnn pnnx | _ncnn_model/-Verzeichnis |
| MNN | onnx2mnn() | pip install MNN | .mnn-Datei |
| PaddlePaddle | torch2paddle() | pip install paddlepaddle x2paddle | _paddle_model/-Verzeichnis |
| ExecuTorch | torch2executorch() | pip install executorch | _executorch_model/-Verzeichnis |
| Core AI | torch2coreai() | pip install coreai-torch (macOS 26+ auf Apple Silicon) | .aimodel-Verzeichnis |
Exporte nach MNN, TF SavedModel und TF Frozen Graph verwenden ONNX als Zwischenschritt. Exportiere zuerst nach ONNX und konvertiere anschließend.
Mehrere 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#
Jedes Beispiel unten verwendet dieselbe Einrichtung: ein vortrainiertes ResNet-18 aus timm im Evaluierungsmodus:
import timm
import torch
model = timm.create_model("resnet18", pretrained=True).eval()
im = torch.randn(1, 3, 224, 224)Dropout, Batch-Normalisierung und andere Schichten, die nur während des Trainings verwendet werden, verhalten sich bei der Inferenz anders. Wenn du .eval() überspringst, entstehen Exporte mit falschen Ausgaben.
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"}})Das Standard-Operatorenset ist 14, und der standardmäßige Eingabename lautet "images". Überschreibe diese Werte mit den Argumenten opset, input_names oder output_names.
Nach TorchScript exportieren#
Es sind keine zusätzlichen Abhängigkeiten erforderlich. Intern 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 INT8-Quantisierung. Für INT8 ist zusätzlich ein calibration_dataset-Argument erforderlich.
Erfordert openvino>=2024.0.0 (oder unter macOS 15.4+ >=2025.2.0) 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 Klassifikationsmodellen übergib eine Liste von Klassennamen an classifier_names, um dem CoreML-Modell einen Klassifikationskopf hinzuzufügen.
Erfordert coremltools>=9.0, torch>=1.11 und numpy<=2.3.5. Unter Windows nicht unterstützt.
coremltools>=9.0 stellt Wheels für Python 3.10–3.13 unter macOS und Linux bereit. Bei neueren Python-Versionen kann die native C-Erweiterung nicht geladen werden. Verwende für den CoreML-Export Python 3.10–3.13.
Nach TensorFlow SavedModel exportieren#
Der Export nach TF SavedModel 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 erzeugt 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 daneben ein INT8-.tflite hinzuzufügen.
Anforderungen:
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.0unter macOS (ai-edge-litert>=1.2.0auf anderen Plattformen)onnxslim>=0.1.82onnx>=1.12.0,<2.0.0protobuf>=5
Nach TensorFlow Frozen Graph exportieren#
Ausgehend vom obigen SavedModel-Export konvertierst du das zurückgegebene keras_model in einen eingefrorenen .pb-Graphen:
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.pytorch2ncnn() prüft bei der ersten Verwendung auf ncnn und pnnx.
Nach MNN exportieren#
Für den MNN-Export wird eine ONNX-Datei als Eingabe benötigt. Exportiere zuerst nach ONNX und konvertiere 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/
├── model.pdmodel
└── model.pdiparamsErfordert x2paddle und die passende PaddlePaddle-Distribution für deine Plattform:
paddlepaddle-gpu>=3.0.0,<3.3.0unter CUDApaddlepaddle==3.0.0auf einer ARM64-CPUpaddlepaddle>=3.0.0,<3.3.0auf anderen CPUs
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 .pte-Datei wird im Ausgabeverzeichnis gespeichert:
resnet18_executorch_model/
└── model.pteErfordert torch>=2.9.0 und eine passende ExecuTorch-Laufzeitumgebung (pip install executorch). Informationen zur Verwendung zur Laufzeit findest du in der ExecuTorch-Integration.
Nach Core AI exportieren#
from ultralytics.utils.export import torch2coreai
torch2coreai(model, im, output_file="resnet18.aimodel")Das .aimodel-Asset ist ein Verzeichnis:
resnet18.aimodel/
├── main.mlirb
├── main.hash
└── metadata.jsonDer Export wird auf macOS 26 oder neuer auf Apple Silicon (pip install coreai-torch) ausgeführt, und quantize=16 schreibt ein FP16-Asset, das Float16-Eingaben verarbeitet; das Asset läuft auf iOS 27 und macOS 27. Siehe die Core AI-Integration, einschließlich des Hinweises zu FP16-Assets, die beim Laden abgebrochen werden.
Exportiertes Modell überprüfen#
Überprüfe nach dem Export die numerische Übereinstimmung mit dem ursprünglichen PyTorch-Modell, bevor du es bereitstellst. Ein schneller Smoke-Test mit ONNXBackend aus ultralytics.nn.backends vergleicht die Ausgaben und erkennt frühzeitig Fehler beim Tracing oder bei der 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-6 for an FP32 ONNX exportDie Toleranz gilt pro Format und nicht global. Bei einem ResNet-18 liegen die FP32-Exporte für ONNX, TF SavedModel und LiteRT bei ungefähr 1e-6 und für TorchScript genau bei 0. NCNN ist mit etwa 1e-2 ein Ausreißer: Seine CPU-Laufzeit aktiviert standardmäßig FP16-Packing und -Arithmetik, sodass selbst ein FP32-Export mit halber Genauigkeit ausgeführt wird. Eine deutlich über dem formatspezifischen Basiswert liegende Abweichung deutet auf nicht unterstützte Operatoren, eine falsche Eingabeform oder ein Modell hin, das sich nicht im Evaluierungsmodus befindet. Für FP16- und INT8-Exporte gelten größere Toleranzen. Validiere mit echten Daten statt mit zufälligen Tensoren.
Bei anderen Laufzeitumgebungen kann der Name des Eingabetensors abweichen. OpenVINO verwendet beispielsweise den Namen des Forward-Arguments des Modells (bei generischen Modellen typischerweise x), während torch2onnx standardmäßig auf "images" gesetzt ist.
Exportiertes Modell ausführen#
Exportierte Modelle, die nicht auf YOLO basieren, werden über die normale YOLO()-API geladen. Die oben genannten Exporte enthalten keine Ultralytics-Metadaten zu Aufgabe oder Eingabegröße. Übergib daher task explizit sowie imgsz, das dem Beispieltensor entspricht, mit dem du exportiert 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 oben genannten ONNX- und TF-SavedModel-Exporte lehnen den Standardwert 640 ab. Die oben genannten TorchScript- und NCNN-Exporte akzeptieren zwar auch andere Größen, aber keiner der beiden Exporter garantiert dies: Beide zeichnen den Beispieltensor nach, sodass ein Modell, das in eine Linear-Schicht abflacht, eine feste Form beibehält. Ü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 und 200x200 wird daher mit 224x224 gespeist und abgelehnt, obwohl imgsz=200 dazu passt. Für Eingabegrößen, die keine Vielfachen von 32 sind, rufe das Backend direkt auf.
Backend direkt aufrufen#
Für rohe Tensoren ohne Ultralytics-Vorverarbeitung und -Nachverarbeitung verwendest du die formatspezifischen Klassen in ultralytics.nn.backends, wie es das obige Überprüfungsbeispiel tut. Jede Klasse übernimmt das exportierte Artefakt und ein Gerät und ist aufrufbar:
| Format | Backend | Eingabelayout |
|---|---|---|
| ONNX | ONNXBackend | BCHW |
| TorchScript | TorchScriptBackend | BCHW |
| OpenVINO | OpenVINOBackend | BCHW |
| CoreML | CoreMLBackend | BHWC |
| TF SavedModel, Frozen Graph | TensorFlowBackend | BHWC |
| LiteRT | LiteRTBackend | BCHW |
| NCNN | NCNNBackend | BCHW |
| PaddlePaddle | PaddleBackend | BCHW |
| MNN | MNNBackend | BCHW |
| ExecuTorch | ExecuTorchBackend | BCHW |
| Core AI | CoreAIBackend | BCHW |
TensorFlowBackend deckt zwei Formate ab und verwendet standardmäßig format="saved_model". Übergebe daher format="pb" für einen Frozen Graph.
Drei Dinge, die der Pfad über YOLO() für dich übernimmt, ein direkter Aufruf jedoch nicht:
- Eingabelayout:
CoreMLBackendundTensorFlowBackenderwarten BHWC. Transponiere zuerst mitim.permute(0, 2, 3, 1); ein BCHW-Tensor führt zu einer nicht übereinstimmenden Form. - Autograd: Umschließe Aufrufe mit
torch.inference_mode().TorchScriptBackendgibt einen Tensor zurück, der weiterhin einen Gradienten-Graphen enthält. - Nachverarbeitung: Ohne Metadaten belässt ein Backend
taskalsNoneundnamesleer.LiteRTBackenddenormalisiert weiterhin jede 3D-Ausgabe anhand der Bildgröße unter der Annahme, dass sie YOLO-Boxen enthält. Das ist bei einem nicht auf YOLO basierenden Modell mit 3D-Ausgabe falsch. Zweidimensionale Ausgaben wie Klassifikationslogits sind davon nicht betroffen.
Bekannte Einschränkungen#
- Die Unterstützung für mehrere Eingaben ist ungleichmäßig:
torch2onnxundtorch2openvinoakzeptieren ein Tupel oder eine Liste von Beispieltensoren für Modelle mit mehreren Eingaben.torch2torchscript,torch2coreml,torch2ncnn,torch2paddle,torch2executorchundtorch2coreaigehen von einem einzelnen Eingangstensor aus. - ExecuTorch benötigt
flatc: Die ExecuTorch-Laufzeitumgebung erfordert den FlatBuffers-Compiler. Installiere ihn unter macOS mitbrew install flatbuffersoder unter Ubuntu mitapt install flatbuffers-compiler. - Keine eingebetteten Metadaten: Die oben genannten Exporte enthalten keine Ultralytics-Metadaten zu Aufgabe oder Eingabegröße. Daher kann
YOLO()beides nicht ableiten und benötigt beide Werte explizit. Siehe Exportiertes Modell ausführen. - Nur für YOLO geeignete Formate: Exporte für Axelera und Sony IMX500 erfordern YOLO-spezifische Modellattribute und sind für generische Modelle nicht verfügbar.
- Plattformspezifische Formate: TensorRT erfordert eine NVIDIA-GPU. RKNN erfordert das
rknn-toolkit2-SDK (nur Linux). Edge TPU erfordert dieedgetpu_compiler-Binärdatei (nur Linux).
Fazit#
Diese Hilfsprogramme übernehmen jedes PyTorch-Modell – von einem einfachen torch.nn.Module bis zu einem für die Bereitstellung geeigneten ONNX-, OpenVINO-, CoreML-, TensorFlow- oder Artefakt für mobile Laufzeitumgebungen – über eine einheitliche API. Wähle das Format, das zu deiner Zielhardware passt, überprüfe die numerische Übereinstimmung mit dem ursprünglichen Modell und folge anschließend dem passenden Integrationsleitfaden für die laufzeitspezifischen Schritte zur Bereitstellung.
FAQ#
Jedes
torch.nn.Module. Dazu gehören Modelle aus timm, torchvision und beliebige benutzerdefinierte PyTorch-Modelle. Das Modell muss sich vor dem Export im Evaluierungsmodus (model.eval()) befinden. ONNX und OpenVINO akzeptieren außerdem für Modelle mit mehreren Eingaben ein Tupel von Beispieltensoren.Alle unterstützten Formate (TorchScript, ONNX, OpenVINO, CoreML, TF SavedModel, TF Frozen Graph, NCNN, PaddlePaddle, MNN, ExecuTorch, Core AI) können auf der CPU exportiert werden. Für den Exportprozess selbst ist keine GPU erforderlich. TensorRT ist das einzige Format, das eine NVIDIA-GPU erfordert.
Verwende Ultralytics
>=8.4.38, das das Modulultralytics.utils.exportsowie die standardisierten Argumenteoutput_file/output_direnthält.Ja. Klassifikations-, Erkennungs- und Segmentierungsmodelle von torchvision werden über
torch2coremlnach.mlpackageexportiert. Übergib bei Bildklassifikationsmodellen eine Liste von Klassennamen anclassifier_names, um einen Klassifikationskopf fest zu integrieren. Führe den Export unter macOS oder Linux aus. CoreML wird unter Windows nicht unterstützt. Einzelheiten zur Bereitstellung auf iOS findest du in der CoreML-Integration.Ja, für mehrere Formate. Übergebe
quantize=16für FP16 oderquantize=8für INT8 beim Export nach OpenVINO, CoreML oder MNN. NCNN und Core AI exportieren standardmäßig FP32, akzeptierenquantize=16für FP16 und haben keinen INT8-Pfad. INT8 erfordert in OpenVINO zusätzlich eincalibration_dataset-Argument für die nach dem Training stattfindende Quantisierung. Siehe die Integrationsseite jedes Formats bezüglich der Abwägungen bei der Quantisierung.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
ONNXBackendfür ONNX) und überprüfe die maximale absolute Abweichung. Bewerte die Differenz anhand der eigenen Referenz des Formats. Im obigen ResNet-18-Beispiel liegen FP32 ONNX, TF SavedModel und LiteRT nahe bei1e-6, TorchScript bei0und NCNN nahe bei1e-2, da die CPU-Laufzeitumgebung standardmäßig FP16 verwendet. Eine deutlich größere Abweichung deutet auf nicht unterstützte Operatoren, eine falsche Eingabeform oder ein Modell hin, das sich nicht im Evaluierungsmodus befindet. Ein ausführbares Beispiel findest du unter Exportiertes Modell überprüfen.