Von 110 ms auf 28 ms: PyTorch-Inferenz optimieren
Erfahren Sie, wie Sie PyTorch-Latenz für Echtzeit senken: Quantisierung, ONNX Runtime und dynamisches Batching für ein schnelles Python-Backend.
Auf dieser Seite
Die digitale Fitnessbranche boomt, und KI-gestützte Apps bieten Echtzeit-Feedback zur Übungsausführung. Stellen Sie sich vor, eine Nutzerin oder ein Nutzer macht eine Kniebeuge; ein KI-Coach könnte sofort sagen: „Senken Sie Ihre Hüften noch etwas weiter ab.“ Diese Magie erfordert ein Computer-Vision-Modell, das den Videostream verarbeitet, die Pose analysiert und Feedback gibt – alles innerhalb von Millisekunden. Wenn das Feedback verzögert eintrifft, hat die Person die Bewegung bereits beendet, und der Rat ist nutzlos.
Für Machine-Learning-Engineers stellt sich damit eine entscheidende Herausforderung: Wie stellen wir ein komplexes PyTorch-Computer-Vision-Modell mit der extrem niedrigen Latenz bereit, die für Echtzeitinteraktion erforderlich ist? Ein standardmäßiges PyTorch-Modell in einem Python-Backend ist oft zu langsam.
In diesem Deep Dive gehen wir das Problem direkt an. Wir nehmen ein vortrainiertes PyTorch-Modell zur Pose-Schätzung und optimieren es systematisch für ein Backend zur Echtzeit-Fitnessanalyse. Wir betrachten drei leistungsstarke Techniken: Modellquantisierung, die Konvertierung in die ONNX Runtime und effizientes Batching.
Voraussetzungen:
- Vertrautheit mit Python und PyTorch.
- Verständnis von REST-APIs (wir verwenden FastAPI).
- Docker für eine einfache Umgebungseinrichtung installiert.
- Grundkenntnisse von Computer-Vision-Konzepten.
Das Problem verstehen
Deep-Learning-Modelle in der Produktion bereitzustellen bedeutet mehr als nur model(input) aufzurufen. Bei einer Fitness-App in Echtzeit empfängt unser Python-Backend einen ständigen Strom von Videobildern von mehreren Nutzerinnen und Nutzern. Das Ziel ist, jedes Bild so schnell zu verarbeiten und Ergebnisse der Pose-Analyse zurückzugeben, dass sich die Interaktion unmittelbar anfühlt.
Die zentralen Herausforderungen:
- Hohe Latenz: Ein standardmäßiges PyTorch-Modell im Eager Mode verursacht erheblichen Overhead. Bei einem Computer-Vision-Modell kann die Inferenz auf einem einzelnen CPU-Kern Hunderte von Millisekunden dauern – viel zu lang für Echtzeit-Feedback.
- Große Modellgröße: Modelle wie ResNet oder eigene CNNs zur Pose-Schätzung können Hunderte von Megabyte groß sein. Das erhöht den Speicherverbrauch und kann die Ladezeiten verlängern.
- Ineffiziente CPU-Auslastung: Anfragen einzeln zu verarbeiten nutzt die parallele Rechenleistung moderner CPUs nicht voll aus, besonders bei gleichzeitigen Nutzerinnen und Nutzern.
Unser Vorgehen wird jeden dieser Engpässe systematisch angehen, um einen leistungsstarken Inferenzdienst aufzubauen.
Voraussetzungen
Bevor wir beginnen, richten wir unsere Umgebung ein. Wir verwenden ein vortrainiertes ResNet-Modell aus torchvision, um unser Pose-Schätzungsmodell zu simulieren, da die Optimierungsprinzipien dieselben sind.
Erforderliche Bibliotheken:
torch&torchvisiononnx&onnxruntimefastapi&uvicornnumpypsutil(zur Leistungsüberwachung)
Sie können sie mit pip installieren:
pip install torch torchvision onnx onnxruntime fastapi "uvicorn[standard]" numpy psutil
Beginnen wir mit unserer Baseline-Anwendung. Das ist ein einfacher FastAPI-Server, der die Inferenz mit einem standardmäßigen, nicht optimierten PyTorch-Modell durchführt.
Schritt 1: Eine Baseline festlegen
Zuerst müssen wir unseren Ausgangspunkt messen. Wie langsam ist ein standardmäßiges PyTorch-Modell in einem einfachen Webserver?
Was wir tun
Wir erstellen einen FastAPI-Endpunkt, der einen Dummy-Eingabetensor (als Simulation eines Bildframes) entgegennimmt, die Inferenz mit einem vortrainierten ResNet50-Modell ausführt und das Ergebnis zurückgibt.
Umsetzung
Speichern Sie den folgenden Code als baseline_server.py:```python
baseline_server.py
from fastapi import FastAPI import torch import torchvision.models as models import time import numpy as np
app = FastAPI()
1. Vortrainiertes FP32-Modell laden
model = models.resnet50(weights=models.ResNet50_Weights.DEFAULT) model.eval() # Auf Evaluationsmodus setzen
Context Manager verwenden, um Gradientenberechnungen für die Inferenz zu deaktivieren
@torch.inference_mode() @app.post("/predict") async def predict(): # 2. Einen Dummy-Eingabetensor erstellen (Batchgröße 1) # Dies simuliert ein einzelnes Videobild einer Nutzerin oder eines Nutzers dummy_input = torch.randn(1, 3, 224, 224)
# 3. Die Inferenzzeit messen
start_time = time.perf_counter()
output = model(dummy_input)
end_time = time.perf_counter()
latency_ms = (end_time - start_time) * 1000
# 4. Ergebnisse zurückgeben
# In einer echten App wären dies Pose-Daten (z. B. Gelenkkoordinaten)
probabilities = torch.nn.functional.softmax(output, dim=0)
top5_prob, top5_catid = torch.topk(probabilities, 5)
return {
"latency_ms": f"{latency_ms:.2f}",
"top_category": int(top5_catid),
"top_probability": float(top5_prob)
}
if name == "main": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)
### So funktioniert es
1. Wir laden ein standardmäßiges 32-Bit-Gleitkomma-Modell (FP32) ResNet50 aus `torchvision`.
2. Der Endpunkt `/predict` simuliert den Empfang einer Anfrage. Wir verwenden `@torch.inference_mode()`, eine bewährte Praxis für die Leistung, die Gradientenberechnungen deaktiviert.
3. Wir messen die Zeit für den Forward-Pass von `model()`.
4. Wir geben die Latenz und eine Beispielvorhersage zurück.
Starten Sie den Server: `uvicorn baseline_server:app --reload`
Testen Sie ihn nun von einem anderen Terminal aus mit `curl`:
```bash
curl -X POST http://127.0.0.1:8000/predict
Erwartete Ausgabe (variiert je nach Ihrer CPU):
{"latency_ms":"110.54","top_category":714,"top_probability":0.0069...}
Auf einer modernen Laptop-CPU liegt die Latenz über 100 ms. Das ist unsere Baseline. Bei einem Videostream mit 30 FPS haben wir nur etwa 33 ms pro Bild. Wir sind viel zu langsam.
Schritt 2: Optimieren mit Modellquantisierung
Unsere erste Optimierung ist die Quantisierung. Wir wandeln die Gewichte und Aktivierungen des Modells vom hochpräzisen FP32-Format in das niederpräzisere 8-Bit-Integer-Format (INT8) um. Das beschleunigt die Berechnung auf modernen CPUs erheblich und verringert die Modellgröße.
Es gibt zwei Hauptarten der Quantisierung:
- Post-Training Quantization (PTQ): Eine einfache Methode, die nach dem Training des Modells angewendet wird. Sie lässt sich schnell umsetzen, kann aber manchmal die Genauigkeit verringern.
- Quantization-Aware Training (QAT): Simuliert die Quantisierung während des Trainings. Es erfordert ein erneutes Training, führt aber in der Regel zu einer besseren Genauigkeit des quantisierten Modells.
Für die Backend-Bereitstellung, bei der wir oft ein vortrainiertes Modell erhalten, ist PTQ ein leistungsstarker und gängiger erster Schritt. Wir verwenden Post-Training Static Quantization, bei der das Modell anhand einer kleinen Stichprobe repräsentativer Daten kalibriert wird.
Was wir tun
Wir schreiben ein Skript, um unser FP32-ResNet50-Modell mit den integrierten Quantisierungswerkzeugen von PyTorch in ein INT8-Modell umzuwandeln.
Umsetzung
Erstellen Sie ein neues Skript quantize_model.py:
# quantize_model.py
import torch
import torchvision.models as models
from torch.quantization import quantize_dynamic, get_default_qconfig, prepare, convert
def calibrate_model(model, data_loader, num_batches=10):
model.eval()
with torch.no_grad():
for i, (images, _) in enumerate(data_loader):
if i >= num_batches:
break
model(images)
def main():
# 1. Das ursprüngliche FP32-Modell laden
float_model = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)
float_model.eval()
# 2. Quantisierungskonfiguration festlegen
# 'fbgemm' ist das empfohlene Backend für x86-CPUs
qconfig = get_default_qconfig('fbgemm')
float_model.qconfig = qconfig
# 3. Das Modell für die statische Quantisierung vorbereiten
# Dies fügt 'Observer'-Module ein, die Statistiken erfassen
prepared_model = prepare(float_model, inplace=False)
# 4. Mit Beispieldaten kalibrieren
# Verwenden Sie in einem realen Szenario einen repräsentativen Validierungsdatensatz
# Hier verwenden wir zur Demonstration nur Zufallsdaten
print("Calibrating the model...")
dummy_dataset = [(torch.randn(3, 224, 224), 0) for _ in range(100)]
dummy_loader = torch.utils.data.DataLoader(dummy_dataset, batch_size=10)
calibrate_model(prepared_model, dummy_loader)
print("Calibration complete.")
# 5. Das kalibrierte Modell in INT8 umwandeln
quantized_model = convert(prepared_model, inplace=False)
# 6. Das quantisierte Modell speichern
torch.jit.save(torch.jit.script(quantized_model), "quantized_resnet50.pt")
print("Quantized model saved as quantized_resnet50.pt")
if __name__ == '__main__':
main()
Führen Sie dieses Skript aus: python quantize_model.py
So funktioniert es
- Wir legen eine Quantisierungskonfiguration (
qconfig) für das Backendfbgemmfest, das für Server optimiert ist. torch.quantization.preparefügt Observer in das Modell ein, die den Wertebereich der Aktivierungen beobachten.- Anschließend „kalibrieren“ wir das Modell, indem wir einige Daten-Batches hindurchlaufen lassen. Die Observer zeichnen die Werteverteilungen auf.
- Schließlich verwendet
torch.quantization.convertdiese Statistiken, um die Gewichte und Aktivierungsfunktionen des Modells in INT8 umzuwandeln und die passenden Skalierungsfaktoren zu berechnen. - Wir speichern das resultierende Modell mit
torch.jit.savezum einfachen Laden.
Das ursprüngliche ResNet50-Modell ist etwa 98 MB groß. Das quantisierte Modell quantized_resnet50.pt ist nur etwa 25 MB – eine Reduktion um den Faktor 4!
Aktualisieren wir nun unseren Server, damit er dieses neue Modell verwendet. Erstellen Sie quantized_server.py:```python
quantized_server.py
from fastapi import FastAPI import torch import time
app = FastAPI()
1. Das mit JIT geskriptete quantisierte Modell laden
model = torch.jit.load("quantized_resnet50.pt")
Bei einem JIT-Modell sind model.eval() oder torch.inference_mode() nicht erforderlich
@app.post("/predict") async def predict(): # Der Eingabetensor muss für ein CPU-quantisiertes Modell auf der CPU erstellt werden dummy_input = torch.randn(1, 3, 224, 224)
start_time = time.perf_counter()
output = model(dummy_input)
end_time = time.perf_counter()
latency_ms = (end_time - start_time) * 1000
probabilities = torch.nn.functional.softmax(output, dim=0)
top5_prob, top5_catid = torch.topk(probabilities, 5)
return {
"latency_ms": f"{latency_ms:.2f}",
"top_category": int(top5_catid),
"top_probability": float(top5_prob)
}
Starten Sie ihn (`uvicorn quantized_server:app`) und testen Sie mit `curl`.
**Erwartete Ausgabe:**```json
{"latency_ms":"45.12","top_category":714,"top_probability":0.0071...}
Unsere Latenz sank von etwa 110 ms auf etwa 45 ms. Allein durch Quantisierung haben wir unsere Geschwindigkeit mehr als verdoppelt! ✨
Schritt 3: Weitere Beschleunigung mit ONNX Runtime
Die Quantisierung hilft PyTorch, schneller zu laufen, doch PyTorch selbst ist ein Framework für Training und Inferenz und bringt etwas Overhead mit. ONNX (Open Neural Network Exchange) ist ein offenes Format für ML-Modelle, und ONNX Runtime (ORT) ist eine hochleistungsfähige Inferenz-Engine, die speziell dafür entwickelt wurde, diese Modelle mit Höchstgeschwindigkeit auszuführen.
Die Konvertierung eines Modells nach ONNX ermöglicht der Runtime leistungsstarke Graph-Optimierungen und die Nutzung hardwarespezifischer Beschleunigungsbibliotheken, die die native Ausführung im Framework oft übertreffen.
Was wir tun
Wir konvertieren unser quantisiertes PyTorch-Modell in das ONNX-Format und aktualisieren unseren Server, damit er onnxruntime für die Inferenz verwendet.
Umsetzung
Erstellen Sie zuerst ein Exportskript export_to_onnx.py:
# export_to_onnx.py
import torch
import onnxruntime
# Das zuvor gespeicherte quantisierte Modell laden
quantized_model = torch.jit.load("quantized_resnet50.pt")
quantized_model.eval()
# Eine Dummy-Eingabe zum Nachverfolgen des Modellgraphen bereitstellen
dummy_input = torch.randn(1, 3, 224, 224)
onnx_path = "quantized_resnet50.onnx"
# Das Modell exportieren
print("Exporting model to ONNX format...")
torch.onnx.export(
quantized_model,
dummy_input,
onnx_path,
export_params=True,
opset_version=13, # Eine gängige, stabile Version
do_constant_folding=True,
input_names=['input'],
output_names=['output'],
dynamic_axes={'input': {0: 'batch_size'}, 'output': {0: 'batch_size'}}
)
print(f"Model saved to {onnx_path}")
# Optional: Das ONNX-Modell prüfen
print("Verifying the ONNX model...")
ort_session = onnxruntime.InferenceSession(onnx_path)
ort_inputs = {ort_session.get_inputs().name: dummy_input.numpy()}
ort_outs = ort_session.run(None, ort_inputs)
print("ONNX model verified successfully.")
Führen Sie das Skript aus: python export_to_onnx.py. Dadurch wird quantized_resnet50.onnx erstellt.
Bauen wir nun unseren endgültigen, optimierten Server onnx_server.py:
# onnx_server.py
from fastapi import FastAPI
import onnxruntime as ort
import numpy as np
import time
app = FastAPI()
# 1. Eine ONNX-Runtime-Inferenzsitzung erstellen
ort_session = ort.InferenceSession("quantized_resnet50.onnx")
input_name = ort_session.get_inputs().name
@app.post("/predict")
async def predict():
# 2. Eine Dummy-Eingabe erstellen, nun als NumPy-Array
dummy_input = np.random.randn(1, 3, 224, 224).astype(np.float32)
# 3. Inferenz mit ONNX Runtime ausführen
start_time = time.perf_counter()
ort_inputs = {input_name: dummy_input}
ort_outs = ort_session.run(None, ort_inputs)
end_time = time.perf_counter()
latency_ms = (end_time - start_time) * 1000
# 4. Ausgabe verarbeiten (ONNX gibt NumPy-Arrays zurück)
output = ort_outs
# Softmax- und Top-k-Logik ist dieselbe, nur mit numpy
exp_output = np.exp(output)
probabilities = exp_output / np.sum(exp_output)
top5_catid = np.argsort(probabilities)[-5:][::-1]
return {
"latency_ms": f"{latency_ms:.2f}",
"top_category": int(top5_catid),
}
Starten Sie ihn (uvicorn onnx_server:app) und testen Sie mit curl.
Erwartete Ausgabe:
{"latency_ms":"28.31","top_category":714}
Beeindruckend! Unsere Latenz ist von etwa 45 ms (quantisiertes PyTorch) auf unter 30 ms gesunken. Wir nähern uns der Geschwindigkeit, die für Echtzeitanwendungen erforderlich ist.
Schritt 4: Durchsatz mit effizientem Batching maximieren
Unser Server ist bei einer einzelnen Anfrage schnell, aber was geschieht, wenn 10 Personen gleichzeitig trainieren? Die sequenzielle Verarbeitung von Anfragen erzeugt einen Engpass. Die Lösung ist Batching: mehrere Anfragen zusammenfassen und sie dem Modell in einem einzigen Durchlauf übergeben. Das nutzt die parallele Berechnung auf CPU/GPU besser aus.
Static Batching ist einfach, aber ineffizient; es wartet auf eine feste Anzahl von Anfragen, was Verzögerung hinzufügen kann. Dynamic Batching ist intelligenter: Es wartet eine sehr kurze Zeit (z. B. 5–10 ms), um eingehende Anfragen zu sammeln, und verarbeitet, was vorhanden ist. Das gleicht Latenz und Durchsatz ideal aus.
Ein robustes System für dynamisches Batching von Grund auf zu implementieren ist komplex. Produktionssysteme wie der NVIDIA Triton Inference Server übernehmen dies automatisch. Wir können die Kernlogik jedoch in unserem Python-Backend simulieren, um das Konzept zu demonstrieren.
Was wir tun
Wir überarbeiten unseren ONNX-Server so, dass er eine Warteschlange und einen Hintergrund-Worker-Thread verwendet. Der API-Endpunkt fügt Anfragen schnell der Warteschlange hinzu, und der Worker sammelt sie einige Millisekunden lang, bevor er eine gebündelte Inferenz ausführt.
Umsetzung
Das ist unser bisher fortgeschrittenster Server, batched_onnx_server.py:
# batched_onnx_server.py
import asyncio
import time
from concurrent.futures import ThreadPoolExecutor
from queue import Queue, Empty
import numpy as np
import onnxruntime as ort
from fastapi import FastAPI, HTTPException
# --- Konfiguration ---
MAX_BATCH_SIZE = 16
BATCH_TIMEOUT_MS = 5 # Zeitfenster von 5 ms zum Sammeln von Anfragen
# --- Globale Ressourcen ---
app = FastAPI()
request_queue = Queue()
# Ein Dictionary, um Ergebnisse der richtigen Anfrage zuzuordnen
results_dict = {}
# --- Laden des ONNX-Modells ---
ort_session = ort.InferenceSession("quantized_resnet50.onnx")
input_name = ort_session.get_inputs().name
# --- Hintergrund-Worker für Batching ---
def inference_worker():
while True:
batch_items = []
# Auf die erste Anfrage warten
request_id, input_data = request_queue.get()
batch_items.append((request_id, input_data))
# Weitere Anfragen innerhalb des Zeitfensters sammeln
start_time = time.perf_counter()
while (time.perf_counter() - start_time) * 1000 < BATCH_TIMEOUT_MS and \
len(batch_items) < MAX_BATCH_SIZE:
try:
# Nicht blockierendes Abrufen
next_id, next_data = request_queue.get_nowait()
batch_items.append((next_id, next_data))
except Empty:
break # Keine weiteren Elemente in der Warteschlange
# Batch für die Inferenz vorbereiten
batch_size = len(batch_items)
if batch_size > 0:
batched_input = np.concatenate([item for item in batch_items], axis=0)
# Gebündelte Inferenz ausführen
ort_inputs = {input_name: batched_input}
ort_outs = ort_session.run(None, ort_inputs)
# Ergebnisse an die einzelnen Anfragen zurückverteilen
for i in range(batch_size):
request_id = batch_items[i]
result = ort_outs[i]
results_dict[request_id] = result
# --- FastAPI-Endpunkt ---
@app.on_event("startup")
async def startup_event():
# Den Hintergrund-Worker in einem separaten Thread starten
executor = ThreadPoolExecutor(max_workers=1)
executor.submit(inference_worker)
@app.post("/predict")
async def predict():
request_id = time.time() # Eindeutige ID für diese Anfrage
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)
# Die Anfrage in die Warteschlange legen
request_queue.put((request_id, input_data))
# Auf das Ergebnis des Inferenz-Workers warten
while request_id not in results_dict:
await asyncio.sleep(0.001) # Auf das Ergebnis pollen
# Ergebnis abrufen und entfernen
result = results_dict.pop(request_id)
# Ergebnis wie zuvor verarbeiten und zurückgeben
exp_output = np.exp(result)
probabilities = exp_output / np.sum(exp_output)
top_category = int(np.argmax(probabilities))
return {"top_category": top_category}
So funktioniert es
- Der Endpunkt
/predictist nun sehr leichtgewichtig. Er erzeugt eine eindeutige ID, erstellt die Eingabedaten und legt das Tupel(id, data)in eineQueue. Dann wartet er, bis das Ergebnis inresults_dicterscheint. - Ein einzelner
inference_workerläuft im Hintergrund. Er entnimmt ein Element aus der Warteschlange und versucht dann 5 Millisekunden lang, weitere Elemente zu entnehmen. - Er stapelt alle gesammelten Eingaben mit
np.concatenatezu einem einzigen großen Batch. - Er führt einen Inferenzaufruf für den gesamten Batch aus.
- Anschließend durchläuft er den Ausgabe-Batch und legt jedes einzelne Ergebnis mit der zugehörigen Anfrage-ID in
results_dictab.
Diese Architektur erhöht den Durchsatz erheblich. Die Latenz für eine einzelne Person kann durch die Wartezeit von 5 ms etwas höher sein, doch der Server kann nun viele gleichzeitige Nutzerinnen und Nutzer weit effizienter bedienen als der Ansatz Anfrage für Anfrage.
Alles zusammenfügen
Betrachten wir unseren Weg und die Leistungsgewinne in jeder Phase.
| Phase | Modellformat | Inferenz-Engine | Latenz (einzelne Anfrage) | Durchsatz (gleichzeitige Nutzer) | Modellgröße |
|---|---|---|---|---|---|
| 1. Baseline | PyTorch FP32 | PyTorch | ~110 ms | Niedrig | ~98 MB |
| 2. Quantisiert | PyTorch INT8 | PyTorch | ~45 ms | Niedrig | ~25 MB |
| 3. ONNX Runtime | ONNX INT8 | ONNX Runtime | ~28 ms | Niedrig | ~25 MB |
| 4. Gebündeltes ONNX | ONNX INT8 | ONNX Runtime | ~33 ms (5 ms Wartezeit) | Hoch | ~25 MB |
Wir haben die Latenz einer einzelnen Anfrage erfolgreich von 110 ms auf unter 30 ms gesenkt und ein System aufgebaut, das Szenarien mit hohem Durchsatz bewältigen kann. Wir liegen nun gut im Budget für eine Fitnessanalyse in Echtzeit!
Best Practices für die Sicherheit
- Eingabevalidierung: Validieren Sie in einer echten Anwendung stets die Eingabebilddaten. Stellen Sie sicher, dass sie die richtigen Abmessungen, den richtigen Typ und die richtige Normalisierung haben, bevor Sie sie dem Modell übergeben, um Fehler und mögliche Exploits zu verhindern.
- Ressourcenbegrenzung: Implementieren Sie Rate Limiting an Ihrem API-Endpunkt, um Denial-of-Service-(DoS-)Angriffe zu verhindern, die Ihren Inferenzserver überlasten könnten.
- Modellintegrität: Sichern Sie Ihre Modelldateien. Stellen Sie sicher, dass nur autorisierte Dienste auf die Dateien
.ptoder.onnxzugreifen oder sie verändern können.
Tipps für die Produktionsbereitstellung
- Einen Produktionsserver verwenden: Betreiben Sie FastAPI mit einem produktionsreifen ASGI-Server wie Uvicorn, verwaltet von Gunicorn, für Robustheit und Skalierbarkeit (
gunicorn -w 4 -k uvicorn.workers.UvicornWorker). - Triton Inference Server in Betracht ziehen: Verwenden Sie in komplexen Produktionsumgebungen mit hoher Nachfrage einen dedizierten Inferenzserver wie NVIDIA Triton. Er bietet dynamisches Batching, Multi-GPU-Unterstützung und Modellversionierung von Haus aus.
- CPU vs. GPU: Wir haben uns auf die CPU-Optimierung konzentriert, doch diese Techniken sind für GPUs ebenso leistungsstark. ONNX Runtime verfügt über Execution Provider für NVIDIA-GPUs (CUDA, TensorRT), die noch größere Leistungsgewinne ermöglichen können.
Fazit
Ein PyTorch-Modell für Echtzeit-Inferenz zu optimieren ist ein mehrstufiger Prozess, der sich enorm auszahlt. Ausgehend von einer Baseline haben wir methodisch Quantisierung angewendet, um unser Modell zu verkleinern und die Berechnung zu beschleunigen, und sind dann zur hochoptimierten ONNX Runtime gewechselt, um entscheidende Millisekunden einzusparen. Abschließend haben wir eine Strategie für dynamisches Batching umgesetzt, damit unser Server die Anforderungen einer realen Anwendung mit vielen gleichzeitigen Nutzerinnen und Nutzern bewältigen kann.
Dieser Weg von einem 110-ms-Koloss zu einer Inferenz-Engine mit unter 30 ms und hohem Durchsatz ist eine entscheidende Fähigkeit für jede ML-Engineerin und jeden ML-Engineer, die Modelle in die Produktion bringen. Nun verfügen Sie über die Werkzeuge und Techniken, damit auch Ihre eigenen Modelle fliegen.
Ressourcen
- Offizielle PyTorch-Dokumentation zur Quantisierung: https://pytorch.org/docs/stable/quantization.html
- Offizielle Website von ONNX Runtime: https://onnxruntime.ai/
- Tutorial zum Export von PyTorch nach ONNX: https://pytorch.org/tutorials/advanced/super_resolution_with_onnxruntime.html
Häufig gestellte Fragen
F: Wie weiß ich, ob das auf mich zutrifft? A: Besprechen Sie Ihre persönliche und familiäre Krankengeschichte mit Ihrer Ärztin oder Ihrem Arzt, um Ihre individuellen Risikofaktoren einzuschätzen und die geeigneten nächsten Schritte festzulegen.
F: Was sollte ich meine Ärztin oder meinen Arzt fragen? A: Gute Fragen sind: „Was bedeuten meine Ergebnisse?“, „Welche Möglichkeiten habe ich?“, „Welche Änderungen des Lebensstils würden helfen?“ und „Wann sollte ich zur Nachkontrolle kommen?“
F: Gibt es Änderungen des Lebensstils, die helfen können? A: Die meisten Gesundheitszustände profitieren von grundlegenden Maßnahmen: ausgewogene Ernährung, regelmäßige körperliche Aktivität (150+ Minuten/Woche), guter Schlaf (7–9 Stunden), Stressbewältigung und der Verzicht auf Tabak.
Medizinischer Haftungsausschluss
Dieser Artikel dient ausschließlich Bildungszwecken und stellt keine medizinische Beratung dar. Konsultieren Sie stets eine qualifizierte medizinische Fachperson, bevor Sie Entscheidungen über Ihre Gesundheit, Behandlung oder Versorgung treffen. Individuelle Umstände unterscheiden sich, und was für eine Person angemessen ist, muss für eine andere nicht angemessen sein.
Wenn Sie einen medizinischen Notfall erleben, rufen Sie sofort 911 (oder Ihre örtliche Notrufnummer) an.