Machine-Learning-Frameworks, Modell-Tools und Deployment-Strategien in der ML-Pipeline

Machine-Learning-Frameworks, spezialisierte Modell-Tools und Deployment-Lösungen übernehmen jeweils unterschiedliche Aufgaben innerhalb eines Machine-Learning-(ML)-Workflows. In jeder Phase – von der Modellerstellung über Training und Optimierung bis hin zu Rollout und Inferenz – zeigen sich spezifische Stärken und Schwächen dieser Technologien.

In diesem Leitfaden betrachten wir fünf zentrale Werkzeuge und verwandte Technologien: PyTorch, TensorFlow, LiteRT (früher TensorFlow Lite), TensorRT und ONNX. Wir beschreiben ihre Funktionen, Vorteile und Kompromisse und zeigen, wie sie sich in einen typischen Machine-Learning-Lebenszyklus einfügen. Außerdem gehen wir darauf ein, wie die Tools zusammenspielen können, stellen typische Deployment-Muster vor und nutzen Codebeispiele, um die Konzepte zu verdeutlichen.

Wichtige Erkenntnisse zu ML-Tools und Deployment-Workflows

  • Tools an die Pipeline-Phasen anpassen: Verwende PyTorch oder TensorFlow vor allem für das Training, ONNX zum Transfer von Modellen zwischen Ökosystemen, TensorRT zur Optimierung von Workloads auf NVIDIA-GPUs und LiteRT für den Einsatz auf mobilen oder Edge-Geräten.
  • Spezialisierte Optimierungen nutzen: TensorRT zielt darauf ab, Latenzen zu reduzieren und den Durchsatz auf GPU-Hardware zu erhöhen, während LiteRT darauf ausgelegt ist, Binärgröße und Speicherverbrauch auf ressourcenarmen Geräten zu minimieren.
  • Produktionsinfrastruktur vorbereiten: TensorFlow stellt integrierte Produktionspipelines bereit (z. B. TFX und TensorFlow Serving), während PyTorch-Setups üblicherweise TorchServe mit ONNX Runtime oder TensorRT kombinieren und zusätzlichen individuellen Integrationsaufwand erfordern.
  • Interoperabilität gezielt einsetzen: Ein gängiges Muster ist PyTorch → ONNX → TensorRT für GPU-basiertes Serving oder TensorFlow → LiteRT für On-Device- und Edge-Anwendungen.
  • Operationen mit einer Managed-Plattform vereinfachen: Eine verwaltete AI-Umgebung bei centron kann Training und Deployment für PyTorch und TensorFlow vereinheitlichen und ONNX, TensorRT sowie LiteRT in einen durchgängigen Workflow integrieren.

PyTorch: Flexibles Training und Forschungsorientiertes Design

PyTorch ist ein Open-Source-Deep-Learning-Framework, das auf einem dynamischen (define-by-run) Rechengraphen und einer Python-zentrierten Programmierweise basiert. Der Schwerpunkt liegt auf Flexibilität: Modelle werden als normale Python-Programme formuliert, was das Schreiben, Anpassen und Debuggen besonders intuitiv macht.

Auch wenn PyTorch dynamisches Verhalten in den Vordergrund stellt, sorgt der optimierte C++-Kern mit seinen Tensor-Bibliotheken (einschließlich GPU-beschleunigter Backends wie cuDNN) für eine Leistung, die mit statischen Graphenlösungen mithalten kann oder diese sogar übertrifft. Im Laufe der Zeit hat sich PyTorch von einem reinen Forschungswerkzeug zu einem Framework entwickelt, das auch für Entwicklung und Produktion geeignet ist – unterstützt durch Features wie TorchScript (zur Serialisierung von Modellen in optimierte Artefakte für C++ oder Mobile) und TorchServe für das Model Serving.

PyTorch: Stärken und Schwächen

Die folgenden Abschnitte zeigen, in welchen Bereichen PyTorch besonders glänzt und wo bestimmte Kompromisse in Kauf genommen werden müssen.

Stärken von PyTorch

  • Python-native API: Die Schnittstelle fühlt sich wie idiomatisches Python an und macht das Erstellen und Debuggen von Modellen übersichtlich.
  • Dynamischer Rechengraph: Der define-by-run-Ansatz unterstützt schnelles Experimentieren und eine natürlichere Fehlersuche.
  • Breites Ökosystem und aktive Community: Umfassende Unterstützung für Computer Vision, NLP, Tools und zahlreiche externe Bibliotheken.
  • Integration in die Python-Toolchain: Reibungsloses Zusammenspiel mit gängigen Python-Debuggern, Profilern und Entwicklungswerkzeugen.
  • Produktivitätsorientierte Verbesserungen: JIT-Compiler und Ahead-of-Time-(AOT)-Kompilierung in PyTorch 2.x beschleunigen die Inferenz und erleichtern das Deployment.
  • GPU-first-Design: Einfache CUDA-Integration sowie robuste Unterstützung für verteiltes und Multi-GPU-Training und -Serving.
  • Reifer werdende Deployment-Strategie: TorchScript, TorchServe und Torch-TensorRT verringern zunehmend die Lücke zwischen Forschungsumgebungen und gehärteten Produktionssystemen.

Schwächen von PyTorch

  • Nicht vollständig „out of the box“ produktionsreif im Vergleich zu TensorFlow: Ein typischer PyTorch-Produktionsstack kombiniert TorchServe mit ONNX Runtime oder TensorRT sowie eigener CI/CD- und Orchestrierungslogik, was Integrations- und Betriebsaufwand erhöht.
  • Zusätzliche Härtung für den Produktivbetrieb erforderlich: Da PyTorch auf einem dynamischen Graphen basiert, sind zusätzliche Schritte wie TorchScript-Konvertierung oder AOT-Kompilierung notwendig, um deterministische, reproduzierbare Artefakte zu erzeugen. Sorgfältige Parity-Tests sind entscheidend, um Abweichungen zwischen Entwicklungs- und Produktionsverhalten zu vermeiden.
  • Zerklüftete MLOps-Landschaft: PyTorch stützt sich meist auf eine heterogene Toolkette, was die Angriffsfläche für Versionsdrift, Upgrades und Sicherheits-Patches vergrößert.
  • Großer Runtime-Footprint: Die vollständige PyTorch-Runtime (inklusive CUDA und weiterer Beschleuniger-Abhängigkeiten) ist relativ schwergewichtig und daher weniger geeignet für stark eingeschränkte Mobile- oder Edge-Szenarien.

PyTorch in Aktion: Trainings- und Deployment-Workflow

PyTorch kommt am häufigsten in den Phasen der Modellkonzeption und des Trainings zum Einsatz. Für das Deployment kann PyTorch ebenfalls direkt für Inferenz genutzt werden – etwa beim Ausführen von Modellen auf einem Server, der über einen Webservice (z. B. mit Flask) oder über TorchServe angebunden wird. Alternativ lassen sich Modelle in leichtere Repräsentationen konvertieren, die für bestimmte Zielplattformen optimiert sind.

PyTorch-Beispiel: Trainingsworkflow für ein Modell

Im folgenden Beispiel wird ein einfaches, vollständig verbundenes neuronales Netz erstellt und mit den nn.Module-Abstraktionen und Optimierungs-APIs von PyTorch trainiert. Dank des dynamischen Rechengraphen wird die Trainingsschleife direkt in Python formuliert, wobei jede Iteration einfach den Forward-Pass des Modells aufruft. Nach dem Training werden die Modellparameter gespeichert. Später können die Gewichte entweder erneut in PyTorch geladen oder nach ONNX exportiert werden, um sie mit anderen Runtimes zu verwenden (wie in diesem Leitfaden später beschrieben).

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# Define a simple neural network
class SimpleNet(nn.Module):
    def __init__(self):
        super(SimpleNet, self).__init__()
        self.fc1 = nn.Linear(784, 128)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(128, 10)
    def forward(self, x):
        x = x.view(-1, 784)  # Flatten images
        x = self.relu(self.fc1(x))
        return self.fc2(x)

# Prepare the training dataset and DataLoader
transform = transforms.Compose([transforms.ToTensor()])
train_dataset = datasets.MNIST(root="./data", train=True, transform=transform, download=True)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)

# Model setup
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = SimpleNet().to(device)
optimizer = optim.Adam(model.parameters(), lr=1e-3)
criterion = nn.CrossEntropyLoss()

# Training loop
for epoch in range(5):
    for batch_x, batch_y in train_loader:
        batch_x, batch_y = batch_x.to(device), batch_y.to(device)
        optimizer.zero_grad()
        outputs = model(batch_x)
        loss = criterion(outputs, batch_y)
        loss.backward()
        optimizer.step()

print("Training completed.")
torch.save(model.state_dict(), "model_weights.pth")

TensorFlow: Produktionsreifes Framework mit umfassendem Ökosystem

TensorFlow ist ein weiteres bedeutendes Deep-Learning-Framework, das ursprünglich bei Google entwickelt wurde. In den frühen Versionen machte TensorFlow statische Berechnungsgraphen populär: Zuerst wird der Graph eines Modells definiert und anschließend ausgeführt. Dieser Ansatz erleichtert globale Optimierungen auf dem gesamten Graphen und eine effiziente Bereitstellung auf vielen Plattformen, geht jedoch zulasten von Benutzerfreundlichkeit und Flexibilität. Als Reaktion auf Feedback aus der Community und den Wettbewerb mit PyTorch führte TensorFlow 2.x standardmäßig Eager Execution ein – einen dynamischeren Ausführungsstil ähnlich zu PyTorch – bietet aber weiterhin die Möglichkeit, durch den tf.function-Decorator und den XLA-Compiler von den Vorteilen statischer Graphoptimierungen zu profitieren.

Zudem verwendet TensorFlow Keras als primäre High-Level-API zur Modellierung. Über das eigentliche Training hinaus bringt TensorFlow ein umfangreiches Produktions-Ökosystem mit: Modelle können über TensorFlow Serving auf klassischen Servern ausgeliefert, über TensorFlow Lite (heute LiteRT) auf mobilen oder eingebetteten Geräten betrieben, mit TensorFlow.js direkt im Browser ausgeführt oder auf spezialisierten Beschleunigern wie den TPUs von Google verwendet werden.

TensorFlow: Stärken und Schwächen

TensorFlow ist in der Industrie weit verbreitet, insbesondere für großskaliges Training und produktive ML-Workloads. Das Ökosystem bietet einen durchgängigen End-to-End-Ansatz, bringt jedoch auch einige Kompromisse für Entwicklerinnen und Entwickler mit sich.

Stärken von TensorFlow

  • Hohe Skalierbarkeit: Integrierte Unterstützung für verteiltes Training über mehrere GPUs und sogar über mehrere Maschinen hinweg.
  • End-to-End-Ökosystem mit Produktionsfokus: TensorFlow Extended (TFX) stellt Komponenten für Datenanbindung, Datenvalidierung und Serving bereit und unterstützt damit unternehmensweite ML-Pipelines.
  • Optimierter Rechenpfad: Wenn statische Graphausführung und XLA aktiviert sind, kann TensorFlow spürbare Performancegewinne erzielen.
  • Umfassende TPU-Integration: Native Unterstützung für Training und Inferenz auf Google TPUs für sehr große Workloads.
  • Reibungsloser Deployment-Flow: Das SavedModel-Format lässt sich direkt mit TensorFlow Serving nutzen und ermöglicht so unkomplizierte Setups für produktive Inferenz.
  • Bereit für Edge und Mobile: Die Umwandlung von TensorFlow-Modellen nach LiteRT ist ein vergleichsweise geradliniger Prozess für On-Device-Deployments.
  • Großer Fundus an Lernmaterial: Eine große Community, umfangreiche Dokumentation und viele vortrainierte Modelle erleichtern Transfer Learning und schnelle Experimente.

Schwächen von TensorFlow

  • Historisch steile Lernkurve: TensorFlow 1.x erforderte explizites Management von Graphen und Sessions, was für Einsteigerinnen und Einsteiger oft wenig intuitiv war.
  • Erschwerte Fehlersuche: In TensorFlow 2.x kann der Einsatz des @tf.function-Decorators und des Graphmodus die Zeilen-für-Zeilen-Ausführung einschränken und Debugging-Prozesse verkomplizieren.
  • Umfangreiche Laufzeitumgebung: Das vollständige TensorFlow-Paket ist groß und für den direkten Einsatz auf vielen Geräten ungeeignet, was den Bedarf nach LiteRT/TFLite unterstreicht.
  • Aufwendige Entwicklung benutzerdefinierter Operationen: Neue Kernel oder Ops zu implementieren erfordert in der Regel tiefe Kenntnisse über die TensorFlow-Interna.
  • Verzögerte Unterstützung neuester Forschung: Viele neue Layer und Techniken erscheinen zunächst in PyTorch und werden erst später nach TensorFlow portiert.
  • Weniger flexibel bei schnellem Prototyping: Das Verhalten statischer Graphen kann im Vergleich zum dynamischen Graph-Ansatz von PyTorch restriktiv wirken, wenn schnell iteriert werden soll.

TensorFlow End-to-End: Vom Modelldesign bis zum Deployment

Mit TensorFlow lässt sich ein Modell – typischerweise über die Keras-API – erstellen und trainieren, anschließend exportieren und schließlich in eine Zielumgebung für das Deployment überführen.

Das exportierte Modell kann auf einem Server für Inferenz eingesetzt werden, etwa über TensorFlow Serving oder die TensorFlow-C++-API. Für Edge-Szenarien wie Smartphones, IoT-Geräte oder andere Embedded-Hardware lässt sich dasselbe Modell in das LiteRT- (TFLite-)Format konvertieren und direkt auf dem Gerät ausführen. Darüber hinaus integriert TensorFlow TensorRT (TF-TRT), um GPU-Inferenz zu beschleunigen. In einem dokumentierten Beispiel erreichte ein ResNet-50-Modell auf einer NVIDIA-T4-GPU ungefähr das 2,4-fache an Inferenzdurchsatz im Vergleich zu nicht optimierter TensorFlow-GPU-Ausführung.

TensorFlow-Beispiel – Keras-Modelltraining

Im folgenden Beispiel wird ein einfaches Feedforward-Netzwerk für Bildklassifikation mit Keras definiert. Das Modell wird auf den vorbereiteten MNIST-Daten (x_train, y_train) kompiliert, trainiert und anschließend auf der Festplatte gespeichert. Die SavedModel-Datei (model_saved.keras) kann später für Inferenz geladen oder für verschiedene Deployment-Szenarien weiterverarbeitet werden. Die High-Level-API von TensorFlow nimmt Ihnen den expliziten Aufbau des Low-Level-Berechnungsgraphen ab, kann diesen intern im Produktionsbetrieb aber dennoch optimieren.

import tensorflow as tf

# Load MNIST dataset
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()

# Normalize the pixel values to [0, 1]
x_train = x_train / 255.0
x_test = x_test / 255.0

# Define a simple model using Keras (e.g., for MNIST classification)
model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=(28, 28)),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dense(10)
])

# Compile the model with optimizer, loss, and metrics
model.compile(
    optimizer='adam',
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=['accuracy']
)

# Train the model on training data
model.fit(x_train, y_train, epochs=5, batch_size=32)

# Evaluate on test data
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=2)
print(f"\nTest accuracy: {test_acc:.4f}")

# Save the trained model to disk (SavedModel format)
model.save("model_saved.keras")

LiteRT: Schlanke On-Device-Inferenz-Laufzeit

LiteRT (Lite Runtime) ist eine kompakte Inferenz-Engine, die aus TensorFlow Lite hervorgegangen ist. Sie ist darauf ausgelegt, vortrainierte Modelle auf Geräten mit begrenzten Ressourcen auszuführen, etwa auf Smartphones, Tablets, IoT-Hardware, Edge-Systemen oder sogar Mikrocontrollern.

Zu Beginn war LiteRT vor allem für Modelle gedacht, die direkt in TensorFlow entwickelt wurden. Inzwischen hat das AI-Edge-Team von Google den Funktionsumfang erweitert, sodass auch Modelle aus anderen Frameworks unterstützt werden. Entsprechende Konverter können Modelle aus PyTorch, JAX oder TensorFlow in das auf FlatBuffers basierende .tflite-Format überführen.

LiteRT: Stärken und Schwächen

LiteRT ist speziell auf On-Device-Inferenz in mobilen, eingebetteten und Edge-Machine-Learning-Szenarien ausgerichtet. Die folgenden Abschnitte fassen die wichtigsten Vorteile und die entsprechenden Trade-offs zusammen.

Stärken von LiteRT

  • Sehr kleine Laufzeitumgebung: Die Engine kann zu einem extrem kleinen Binary kompiliert werden; die schlanksten Builds liegen bei rund 300 KB – ein großer Vorteil für Mobile- und Embedded-Anwendungen, bei denen Speicherplatz knapp ist.
  • Unterstützung für Hardware-Beschleunigung: LiteRT bindet Android NNAPI für DSP/NPU-Offloading ein, unterstützt iOS Core ML und stellt einen GPU-Delegate für mobile Grafikprozessoren bereit.
  • Optimierungen auf Modellebene: Post-Training-Quantisierung (int8 oder float16), Pruning und Clustering können genutzt werden, um Modellgröße und Latenz zu reduzieren und gleichzeitig die Genauigkeit möglichst zu erhalten.
  • Gezielt für On-Device-Performance optimiert: Aggressive Optimierungen und Delegates sorgen für Echtzeit-Inferenz und geringeren Energieverbrauch auf unterstützten Geräten.
  • Plattformübergreifende Verfügbarkeit: LiteRT läuft unter Android, iOS, Linux (einschließlich Plattformen wie Raspberry Pi) sowie auf Mikrocontrollern (über LiteRT Micro).
  • Offline- und datenschutzfreundlicher Betrieb: Da die Inferenz lokal auf dem Gerät stattfindet, entfallen Netzwerklatenzen und sensible Nutzerdaten bleiben auf dem Endgerät.

Schwächen von LiteRT

  • Nur für Inferenz ausgelegt: Allgemeines On-Device-Training wird nicht unterstützt; lediglich eingeschränkte Transfer-Learning-Szenarien sind in Spezialfällen möglich.
  • Lücken in der Operator-Abdeckung: Nicht jede Operation aus TensorFlow oder PyTorch ist umgesetzt. In manchen Fällen müssen Modelle angepasst oder eigene Ops entwickelt werden.
  • Aufwändige Konvertierung: Um ein .tflite-Modell zu erhalten, kann es nötig sein, Graphen zu segmentieren oder Quantisierung und weitere Transformationen anzuwenden, was den Prozess verkompliziert.
  • Erschwerte Fehlersuche: Statische FlatBuffer-Modelle lassen sich im Vergleich zu Framework-nativen Repräsentationen schwerer inspizieren und debuggen.
  • Begrenzte Ressourcen: Sehr große Modelle – etwa viele Transformer-Varianten – können die realistischen Latenz- und Speicherbudgets der Zielgeräte überschreiten.
  • Möglicher Bedarf an Server-Offloading: Übersteigt ein Modell die Möglichkeiten des Endgeräts, sind hybride Architekturen mit aufgeteilten Berechnungen zwischen Client und Server erforderlich.

LiteRT in der Machine-Learning-Pipeline

Ein typischer LiteRT-Workflow sieht folgendermaßen aus: Zunächst wird ein Modell in TensorFlow oder PyTorch trainiert, anschließend mit dem passenden Konverter in das .tflite-Format überführt. Diese .tflite-Datei wird dann in eine mobile App eingebunden oder auf einer Embedded-Plattform ausgerollt, die das Modell mit der LiteRT-Runtime ausführt.

Während der Konvertierung können Optimierungen wie Quantisierung oder Pruning angewandt werden. Das resultierende Modell wird in der Anwendung über den LiteRT-Interpreter angesprochen, der aus verschiedenen Programmiersprachen genutzt werden kann – etwa Java oder Kotlin auf Android, Swift auf iOS, C++ für native Anwendungen oder Python für schnelle Prototypen.

Im Vergleich zum Einsatz einer vollständigen Framework-Runtime direkt auf dem Gerät liefert LiteRT in der Regel deutlich bessere Performance bei geringerem Ressourcenverbrauch. In einem Beispiel-Benchmark auf einem Samsung S21 lief ein Bildklassifikationsmodell mit TensorFlow Lite bei etwa 23 ms pro Inferenz und rund 89 MB Speicherbedarf. Zum Vergleich: Dasselbe Modell erreichte mit ONNX Runtime ungefähr 31 ms (112 MB) und mit PyTorch Mobile etwa 38 ms (126 MB). Diese Zahlen verdeutlichen den Fokus von LiteRT auf latenzarme, speichereffiziente Ausführung auf Mobilgeräten.

LiteRT-Beispiel – Konvertierung und Ausführung eines Modells

Im folgenden Beispiel wird ein in Python mit TensorFlow trainiertes Modell in das LiteRT-Format überführt und anschließend für Inferenz genutzt.

# ready MNIST → SavedModel → LiteRT(TFLite) → Inference pipeline
import tensorflow as tf
import numpy as np

print("TF version:", tf.__version__)

# 1) Load & prep MNIST
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
# Normalize to [0,1]
x_train = (x_train / 255.0).astype("float32")
x_test  = (x_test  / 255.0).astype("float32")

# 2) Define & train a simple Keras model
model = tf.keras.Sequential([
    tf.keras.layers.Input(shape=(28, 28)),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(128, activation="relu"),
    tf.keras.layers.Dense(10)  # logits
])

model.compile(optimizer="adam",
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=["accuracy"])

model.fit(x_train, y_train, epochs=2, batch_size=128, validation_split=0.1, verbose=1)

# Quick test set eval
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)
print(f"TF model test accuracy: {test_acc:.4f}")

# 3) Export a TensorFlow SavedModel (needed for TFLite conversion)
# In TF 2.15+, prefer model.export(). If not available, fallback to tf.saved_model.save
if hasattr(model, "export"):
    model.export("model_saved")            # TF ≥ 2.15
else:
    tf.saved_model.save(model, "model_saved")  # Older TF fallback

# 4) Convert to LiteRT/TFLite (FP32 with default optimizations)
converter = tf.lite.TFLiteConverter.from_saved_model("model_saved")
converter.optimizations = [tf.lite.Optimize.DEFAULT]   # dynamic range quantization if weights permit
tflite_model = converter.convert()

with open("model_fp32.tflite", "wb") as f:
    f.write(tflite_model)
print("Wrote model_fp32.tflite")

# --- OPTIONAL: Full INT8 quantization with representative dataset ---
do_full_int8 = True
if do_full_int8:
    def rep_data():
        # Yield a few hundred samples to calibrate ranges
        for i in range(500):
            # TFLite expects a batch dimension
            yield [np.expand_dims(x_train[i], 0)]
    converter_int8 = tf.lite.TFLiteConverter.from_saved_model("model_saved")
    converter_int8.optimizations = [tf.lite.Optimize.DEFAULT]
    converter_int8.representative_dataset = rep_data
    # Force int8 I/O where supported
    converter_int8.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
    converter_int8.inference_input_type = tf.int8
    converter_int8.inference_output_type = tf.int8
    try:
        tflite_int8 = converter_int8.convert()
        with open("model_int8.tflite", "wb") as f:
            f.write(tflite_int8)
        print("Wrote model_int8.tflite")
    except Exception as e:
        print("INT8 conversion fell back / failed:", e)
        tflite_int8 = None

# 5) Run inference with the TFLite Interpreter (FP32 model)
import tensorflow.lite as tflite

def tflite_predict(tflite_path, image_28x28):
    interpreter = tflite.Interpreter(model_path=tflite_path)
    interpreter.allocate_tensors()
    in_details = interpreter.get_input_details()
    out_details = interpreter.get_output_details()

    inp = image_28x28
    # Match dtype & shape expected by the model
    if in_details[0]["dtype"] == np.float32:
        inp = inp.astype(np.float32)
    elif in_details[0]["dtype"] == np.int8:
        # Quantized model expects int8; apply quantization params
        scale, zero_point = in_details[0]["quantization"]
        if scale == 0:
            # Safety: if no scale provided (rare), just cast
            inp = inp.astype(np.int8)
        else:
            inp = (inp / scale + zero_point).round().astype(np.int8)

    # Add batch dimension
    inp = np.expand_dims(inp, 0)

    interpreter.set_tensor(in_details[0]["index"], inp)
    interpreter.invoke()
    out = interpreter.get_tensor(out_details[0]["index"])

    # If output is int8, dequantize back to float for softmax/argmax
    if out_details[0]["dtype"] == np.int8:
        scale, zero_point = out_details[0]["quantization"]
        if scale != 0:
            out = (out.astype(np.float32) - zero_point) * scale

    # Convert logits to probabilities and pick class
    probs = tf.nn.softmax(out, axis=-1).numpy()[0]
    pred  = int(np.argmax(probs))
    conf  = float(probs[pred])
    return pred, conf

# Test on a few MNIST samples with FP32 model
for idx in [0, 1, 2]:
    pred, conf = tflite_predict("model_fp32.tflite", x_test[idx])
    print(f"[FP32] Sample {idx}: pred={pred}, conf={conf:.3f}, true={y_test[idx]}")

# If INT8 model exists, test it as well
if 'tflite_int8' in locals() and tflite_int8 is not None:
    for idx in [0, 1, 2]:
        pred, conf = tflite_predict("model_int8.tflite", x_test[idx])
        print(f"[INT8] Sample {idx}: pred={pred}, conf={conf:.3f}, true={y_test[idx]}")

Dieses Skript lädt und normalisiert zunächst den MNIST-Datensatz, definiert und trainiert dann ein kompaktes vollständig verbundenes Netzwerk und bewertet dessen Genauigkeit. Anschließend wird das trainierte Modell als SavedModel exportiert, das als Eingabe für den TFLiteConverter dient. Der Konverter erzeugt zwei Varianten: ein standardmäßiges FP32-LiteRT-Modell und optional ein vollständig quantisiertes INT8-Modell, das mithilfe eines repräsentativen Datensatzes zur Bereichskalibrierung erstellt wird. Abschließend wird eine Hilfsfunktion tflite_predict() implementiert, die eine .tflite-Datei lädt, die Eingabedaten bei Bedarf (de-)quantisiert, Inferenz ausführt und sowohl die vorhergesagte Ziffer als auch die zugehörige Konfidenz zurückgibt. Einige Beispiel-Eingaben werden durch die FP32- und INT8-Modelle geschickt, um das Deployment zu validieren und exemplarische Ausgaben zu demonstrieren.

TensorRT: Hochleistungs-Inferenz auf NVIDIA-GPUs

NVIDIA TensorRT ist ein SDK und eine Laufzeitumgebung, die speziell für latenzarme, hochperformante Bereitstellung neuronaler Netze auf NVIDIA-GPUs entwickelt wurde. TensorRT lässt sich wie ein Compiler für Deep-Learning-Modelle verstehen: Du übergibst ein trainiertes Modell (häufig im ONNX-Format oder in einer framework-spezifischen Repräsentation), und TensorRT führt eine Reihe von Optimierungsschritten aus, um daraus eine stark abgestimmte Inferenz-Engine für die GPU zu erzeugen.

Zu den wichtigsten Optimierungen gehören unter anderem:

  • Layer-Fusion: Zusammenfassen kompatibler Operationen, um Speicherzugriffe und Kernel-Starts zu reduzieren.
  • Automatisches Kernel-Tuning: Auswahl der schnellsten CUDA-Kernel in Abhängigkeit von Tensor-Formen und Zielhardware.
  • Speicherplanung: Optimierung von Lebensdauer und Nutzung von Tensoren sowie Workspace, um Kopien und Spitzenauslastung zu minimieren.
  • Berechnungen mit reduzierter Genauigkeit: Aktivierung von FP16- und INT8-Modi (unterstützt durch Kalibrierung oder quantisierungsbewusstes Training) für deutliche Geschwindigkeitsgewinne und geringeren Bandbreitenbedarf.
  • Umgang mit dynamischen Formen und Profil-Caching: Aufbau von Ausführungsprofilen für unterschiedliche Shape-Bereiche, um wiederholte Optimierungen zur Laufzeit zu vermeiden.

Das Ergebnis ist eine optimierte Binär-Engine, die den Forward-Pass des Modells deutlich schneller ausführen kann als Standard-Implementierungen der ursprünglichen Frameworks.

TensorRT: Stärken und Schwächen

Im Folgenden werden die wichtigsten Vorteile von TensorRT sowie die praktischen Trade-offs beleuchtet, die Teams im Einsatz berücksichtigen sollten.

Stärken von TensorRT

  • Sehr hohe GPU-Performance: Durch FP16-/INT8-Berechnungen und NVIDIA-architekturspezifische Optimierungen kann TensorRT die Inferenz drastisch beschleunigen – oft bis auf etwa das 40-Fache gegenüber einer reinen CPU-Ausführung.
  • Effiziente Auslastung der GPU: Im Vergleich zu nicht optimiertem TensorFlow oder PyTorch auf derselben GPU sind 2–5× niedrigere Latenzzeiten oder deutlich höherer Durchsatz möglich.
  • Unterstützung von Batching und Parallelität: Ideal für großskalige Inferenzdienste, die viele parallele Anfragen verarbeiten müssen und bei denen Durchsatz im Vordergrund steht.
  • Flexible Deployment-Optionen: TensorRT kann als eigenständige Komponente laufen, in den Triton Inference Server integriert, als Execution Provider mit ONNX Runtime genutzt oder in TensorFlow eingebunden werden.
  • Umfangreiche Ökosystem-Integrationen: Anbindung an NVIDIA-SDKs wie DeepStream (für Video-Analytics) oder Riva (für Sprach- und Konversations-KI), um vollständige End-to-End-Lösungen zu realisieren.
  • Unterstützung mehrerer Programmiersprachen: Python- und C++-APIs erlauben die Integration in individuelle Datenverarbeitungs- und Serving-Pipelines.

Schwächen von TensorRT

  • Abhängigkeit von NVIDIA-Hardware: TensorRT ist ausschließlich für NVIDIA-GPUs verfügbar; Inferenz-Setups, die darauf aufbauen, lassen sich nicht ohne Weiteres auf reine CPU-Umgebungen oder andere Beschleuniger übertragen.
  • Aufwendige Modellkonvertierung: Modelle müssen in das TensorRT-Engine-Format überführt werden, was bei nicht unterstützten Layern Anpassungen oder die Entwicklung eigener Plugins erfordern kann.
  • Manueller Tuning-Aufwand: Um Spitzenleistung zu erreichen, ist häufig Feinabstimmung von Parametern wie Workspace-Größe, Präzisionsmodus und Eingabe-Shape-Profilen nötig.
  • Hardware-spezifische Engines: TensorRT-Engines werden auf bestimmte GPU-Familien und -Architekturen optimiert. Ein Wechsel auf andere Hardware (etwa vom Datacenter-GPU-Server zu einem Jetson-Edge-Device) macht in der Regel einen Neuaufbau der Engine erforderlich.
  • Erneute Optimierung bei Modelländerungen: Jede Änderung am zugrunde liegenden Modell erfordert eine neue Konvertierungs- und Optimierungsrunde, um das Leistungsniveau zu halten.
  • Höhere Deployment-Komplexität: Im Vergleich zu integrierteren Lösungen wie reinem TensorFlow Serving oder ONNX Runtime ist die Einrichtung und Pflege von TensorRT-basierten Deployments meist mit mehr Engineering-Aufwand verbunden.

TensorRT in der Pipeline: Rolle als reine Inferenz-Engine

TensorRT konzentriert sich ausschließlich auf die Inferenzphase. Ein typischer Workflow besteht darin, ein Modell mit PyTorch oder TensorFlow auf GPUs zu trainieren, es anschließend nach ONNX zu exportieren (ein Format, das TensorRT nativ unterstützt) und dieses ONNX-Modell dann mit TensorRT-APIs oder -Tools zu deployen. In diesem Prozess wird eine Engine erstellt (gegebenenfalls inklusive INT8-Kalibrierung mit Beispieldaten), die später innerhalb der Anwendung ausgeführt wird.

Nachdem die Engine erzeugt wurde, kann sie entweder in einer C++-Serveranwendung oder über Python-Bindings geladen werden – abhängig von Skalierung und Architektur. In hochperformanten Produktionsumgebungen mit sehr vielen Anfragen ist es gängige Praxis, TensorRT-Engines im NVIDIA Triton Inference Server zu betreiben, der mehrere Modelle und gleichzeitige Requests verwaltet.

TensorRT-Beispiel – Konvertierung eines ONNX-Modells in eine TensorRT-Engine

Das folgende vereinfachte Pseudocode-Beispiel nutzt die Python-API von TensorRT, um aus einem ONNX-Modell eine Engine zu erzeugen und Inferenz durchzuführen. Es zeigt den grundlegenden Ablauf, ohne alle API-Details auszuschöpfen.

import tensorrt as trt

onnx_file = "model.onnx"
engine_file = "model.plan"  # TensorRT engine file

# Set up TensorRT logger and builder
logger = trt.Logger(trt.Logger.INFO)
builder = trt.Builder(logger)
network = builder.create_network(flags=1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
parser = trt.OnnxParser(network, logger)

# Parse the ONNX model to populate the TensorRT network
with open(onnx_file, "rb") as f:
    parser.parse(f.read())
# (In practice, check parser.error for unsupported ops here.)

# Configure builder
builder.max_batch_size = 1
config = builder.create_builder_config()
config.max_workspace_size = 1 << 30  # 1GB workspace for optimization
config.flags |= trt.BuilderFlag.FP16  # enable FP16 precision if supported

# Build the TensorRT engine
engine = builder.build_engine(network, config)
with open(engine_file, "wb") as f:
    f.write(engine.serialize())  # save the engine to file

# Use the engine for inference
runtime = trt.Runtime(logger)
with open(engine_file, "rb") as f:
    engine_bytes = f.read()
engine = runtime.deserialize_cuda_engine(engine_bytes)
context = engine.create_execution_context()

# Assuming a single input and single output for simplicity
input_shape = engine.get_binding_shape(0)
output_shape = engine.get_binding_shape(1)
# Allocate device memory for inputs and outputs (using PyCUDA or similar)
# ... (omitted for brevity)
# Execute inference
context.execute_v2(bindings=[d_input_ptr, d_output_ptr])
# Copy results from device memory to host and use the output

Dieses Beispiel zeigt die wichtigsten Schritte, um aus einem ONNX-Modell eine TensorRT-Engine zu erzeugen. Zunächst werden ein Builder und ein OnnxParser angelegt, um den Modellgraphen einzulesen. Danach folgt die Konfiguration des Builders (z. B. Workspace-Größe und Aktivierung von FP16). Der Aufruf von build_engine stößt die Optimierungen von TensorRT an und liefert eine Engine, die in model.plan serialisiert wird. Später kann diese Engine deserialisiert, ein Execution Context erzeugt und Inferenz ausgeführt werden.

Konzeptionell umfasst der Einsatz von TensorRT für Inferenz folgende Schritte:

  • Modell parsen: Laden und Interpretieren des Modells mit dem Parser von TensorRT oder verwandten Tools, um eine interne Netzwerkrepräsentation aufzubauen.
  • Optimierte Engine erzeugen: Erstellen einer hardware-spezifischen Engine aus dem geparsten Modell – typischerweise inklusive Optimierungen wie Layer-Fusion und Präzisionskalibrierung.
  • Inferenz ausführen: Aufruf der optimierten Engine, wofür das Management des GPU-Speichers für Ein- und Ausgaben sowie die Ausführung von execute_v2 im Execution Context erforderlich ist.

In realen Systemen kommen weitere Aspekte wie dynamische Tensorformen oder mehrere Ein- und Ausgabebindings hinzu, die detaillierteren TensorRT-Code erfordern. In der Praxis greifen viele Teams auf High-Level-Wrapper zurück oder nutzen ONNX Runtime mit TensorRT-Backend, um nicht selbst umfangreichen Low-Level-Integrationscode schreiben zu müssen.

ONNX: Modell-Interoperabilität und plattformübergreifendes Deployment

ONNX (Open Neural Network Exchange) ist kein Trainingsframework, sondern ein offenes Format zur Modellrepräsentation, zu dem es mit ONNX Runtime auch eine dedizierte Laufzeit zur Ausführung dieser Modelle gibt.

Mit ONNX kannst du ein Modell in einem Framework wie PyTorch oder TensorFlow trainieren, es anschließend in das ONNX-Format (einen aus standardisierten Operationen aufgebauten Berechnungsgraphen) exportieren und dann mit einem anderen Tool oder sogar auf einer anderen Hardwareplattform ausführen. Diese Trennung von Trainingsframework und Inferenz-Runtime ist in Produktionsumgebungen besonders wertvoll, da man ein Framework für Entwicklung und ein anderes Laufzeitsystem für Betrieb, Kostenoptimierung oder Performance wählen kann.

ONNX: Stärken und Schwächen

Die folgende Übersicht zeigt, in welchen Bereichen ONNX und ONNX Runtime überzeugen und welche Einschränkungen derzeit bestehen.

Stärken von ONNX / ONNX Runtime

  • Hohe Flexibilität und Interoperabilität: Training in einem Framework (z. B. PyTorch), Deployment in einem anderen (z. B. ONNX Runtime in C++).
  • Leichte, auf Inferenz fokussierte Runtime: Geringerer Installationsumfang als vollständige Frameworks, da kein Training unterstützt wird und der Fokus auf Inferenz liegt.
  • Plattformübergreifende Unterstützung: Läuft unter Windows, Linux, macOS und auf mobilen Geräten – etwa mit ONNX Runtime Mobile und reduzierter Operatormenge.
  • Hohe Performance: Häufig vergleichbar mit oder besser als native Framework-Inferenz, besonders auf CPUs; bei manchen Batch-1-GPU-Szenarien kann ONNX Runtime nach Graphoptimierungen sogar schneller sein (beispielsweise rund 24,2 ms statt etwa 30,4 ms auf ResNet-50).
  • Bewährt im großen Maßstab: In einigen Microsoft-Produktions-Workloads hat ONNX Runtime Berichten zufolge bessere Ergebnisse als TorchScript erzielt.
  • Gesundes Ökosystem: Umfangreiche Auswahl an Konvertern, öffentlichen Modell-Sammlungen und Tools wie Netron oder onnxoptimizer.

Schwächen von ONNX / ONNX Runtime

  • Komplexe Konvertierung: Der Export kann scheitern oder zu Leistungseinbußen führen, wenn ein Modell auf Operatoren setzt, die noch nicht vollständig standardisiert oder unterstützt sind.
  • Lücken bei benutzerdefinierten Operatoren: Modelle mit nicht standardisierten Layern benötigen unter Umständen Fallbacks oder eigene Plugins, die schnell zu schwer durchschaubaren „Black Boxes“ werden.
  • Erschwertes Debugging: Statische ONNX-Graphen sind vom ursprünglichen Framework, dessen Quellcode und Tooling entkoppelt, was die Fehlersuche anspruchsvoller macht.
  • Weniger „Batterien im Lieferumfang“: Schritte wie Pre- und Post-Processing sowie weiteres Pipeline-Glue befinden sich in der Regel außerhalb von ONNX Runtime.
  • Zusätzlicher Workflow-Aufwand: Durch ONNX kommt ein separater Export- und Validierungsschritt hinzu, der dauerhaft gepflegt werden muss.

ONNX als Übergabeschicht: Überall trainieren, überall deployen

ONNX befindet sich häufig in der Mitte der ML-Pipeline. Ein typisches Muster: Ein Modell wird in PyTorch trainiert, anschließend mit torch.onnx.export als model.onnx exportiert. Dieses ONNX-Modell kann dann in einem Produktionsdienst mit ONNX Runtime ausgeführt werden, der für Effizienz in C++ implementiert oder – je nach Bedarf – auch in Python realisiert sein kann.

Ein weiteres Beispiel: Du arbeitest mit einem TensorFlow-Modell, möchtest aber TensorRT direkt nutzen, ohne auf die integrierte TensorFlow-Anbindung zurückzugreifen. In diesem Fall konvertierst du das TensorFlow-Modell nach ONNX und übergibst es an TensorRT, da TensorRT ONNX nativ versteht.

ONNX spielt auch in Workflows zur Modellkompression und -quantisierung eine wichtige Rolle. So kannst du ein trainiertes Modell nach ONNX exportieren und dann mit den Tools von ONNX Runtime Post-Training-Quantisierung darauf anwenden.

Interoperabilität und typische ML-Tooling-Workflows

Für erfahrene ML-Teams ist durchgängige Interoperabilität häufig ein Schlüsselfaktor. In produktiven End-to-End-Systemen wird keines der hier beschriebenen Werkzeuge isoliert eingesetzt. Stattdessen kombiniert man meist zwei oder drei davon, um Training, Optimierung und Deployment abzudecken. Die folgende Tabelle zeigt typische Pipelines und wie die Bausteine ineinandergreifen.

Pipeline Ziel / Umgebung Zentrale Schritte
PyTorch → ONNX → TensorRT (GPU-Deployment) NVIDIA-GPU-Server oder Edge-Systeme mit CUDA Training in PyTorch. Export nach ONNX (opset wählen, Graph vereinfachen). Aufbau einer TensorRT-Engine (FP16/INT8, Profile definieren). Engine deployen und Anfragen bedienen.
TensorFlow → LiteRT (Mobiles Deployment) Android und iOS (On-Device-Ausführung) Training in TensorFlow/Keras. Konvertierung mit dem LiteRT- (TFLite-)Konverter zu einer .tflite-Datei. Einbettung des Modells in die App und Aktivierung von Hardware-Delegates. Optional Quantization-Aware-Training mithilfe der TensorFlow-Model-Optimization-Tools.
PyTorch → LiteRT (direkt oder über ONNX) Android und iOS (On-Device-Ausführung) Training in PyTorch. Direkte Konvertierung nach .tflite oder Umweg über ONNX und den TensorFlow-Konverter. Integration des resultierenden Modells in mobile Anwendungen.
PyTorch → ONNX → ONNX Runtime (CPU/GPU) Windows, Linux, macOS und mobile Plattformen Training im bevorzugten Framework. Export des Modells nach ONNX. Ausführung mit ONNX Runtime, wobei pro Plattform der passende Execution Provider gewählt wird.
TensorFlow → TensorRT (TF-TRT oder ONNX) NVIDIA-GPU-Server Option A: Einsatz von TF-TRT, bei dem Teile des TensorFlow-Graphs durch TensorRT-Engines ersetzt werden (kompatibel mit TensorFlow Serving). Option B: Export nach ONNX und direkter Aufbau einer TensorRT-Engine aus der ONNX-Datei.

In diesem Ökosystem fungieren PyTorch und TensorFlow als „Frontends“ zur Modellerstellung und -schulung. ONNX übernimmt die Rolle eines gemeinsamen „Übergabeformats“, das Modelle zwischen Frameworks und Runtimes transportiert. TensorRT und LiteRT stellen spezialisierte „Endpunkte“ dar, die jeweils auf bestimmte Hardware optimiert sind – GPUs im Fall von TensorRT und Edge- oder Mobilgeräte bei LiteRT.

FAQ

PyTorch vs. TensorFlow – wann ist welches Werkzeug die bessere Wahl?

PyTorch eignet sich besonders gut für schnelle Forschungszyklen und Python-freundliches Debugging, während TensorFlow häufig bevorzugt wird, wenn es um durchgängige, produktionsreife ML-Pipelines (z. B. TFX, TensorFlow Serving und TPU-Integration) und reibungslose Abläufe im Enterprise-Umfeld geht.

Was ist LiteRT (früher TFLite) und wann setze ich es ein?

LiteRT ist eine schlanke Laufzeitumgebung für On-Device-Inferenz, die speziell für Mobile- und Edge-Szenarien entwickelt wurde. Du trainierst dein Modell in TensorFlow oder PyTorch, wandelst es in eine .tflite-Datei um und führst es dann mit Hardware-Delegates wie NNAPI, Core ML oder GPU-Delegates für latenzarme, energieeffiziente Inferenz aus.

Wie spielen ONNX und TensorRT zusammen?

Du exportierst dein trainiertes Modell zunächst in das ONNX-Format und lässt TensorRT anschließend diesen ONNX-Graphen in eine stark optimierte Engine für NVIDIA-GPUs übersetzen (inklusive FP16-/INT8-Unterstützung und Kernel-Fusion). In diesem Zusammenspiel dient ONNX als Interoperabilitätsbrücke, während TensorRT als leistungsstarker GPU-Beschleuniger fungiert.

Fazit

Die Wahl der passenden Werkzeuge sollte sich daran orientieren, wo deine Modelle ausgeführt werden und wie sie skalieren sollen: Nutze PyTorch oder TensorFlow für Modellentwicklung und Training, ONNX zur Entkopplung von Training und Inferenz, TensorRT für maximale Performance auf NVIDIA-GPUs und LiteRT für kompakte, latenzarme On-Device-Inferenz. Erfolgreiche Produktions-Stacks basieren in der Regel auf mehreren Frameworks – etwa PyTorch → ONNX → TensorRT für GPU-Serving oder TensorFlow → LiteRT für mobile Szenarien – und führen zu einem einheitlichen exportierten Artefakt, das sich benchmarken, validieren und ausrollen lässt.

Ein praxisnaher Weg, dies bei centron umzusetzen, ist die Nutzung der gemanagten AI- und GPU-Infrastruktur von centron. So kannst du verwaltete GPU-Notebooks starten, Modelle trainieren und beschleunigte Endpunkte bereitstellen, ohne dich um die zugrunde liegende Infrastruktur kümmern zu müssen. Auf diese Weise lässt sich mit PyTorch oder TensorFlow sowie ONNX, TensorRT und LiteRT in einem einzigen, durchgängigen Workflow arbeiten.

Quelle: digitalocean.com

Jetzt 200€ Guthaben sichern

Registrieren Sie sich jetzt in unserer ccloud³ und erhalten Sie 200€ Startguthaben für Ihr Projekt.

Das könnte Sie auch interessieren: