Ultralytics YOLO27: Trag dich in die Warteliste ein.
Get Started
Ultralytics YOLO in der Produktion·Skalieren und beobachten·Lektion 9/9
LektionFortgeschrittene

Kosten und Latenz optimieren

Erstelle ein Profil der gesamten Pipeline, betätige der Reihe nach die Stellschrauben, die Latenz und Kosten tatsächlich beeinflussen, und höre auf, sobald du dein Budget einhältst.

Teams verlieren Wochen damit, Millisekunden bei einem Modell einzusparen, während ein Datenbank-Schreibvorgang oder ein RTSP-Decoder der eigentliche Engpass ist. Es gibt nur wenige große Stellschrauben, deren Wirkung gut messbar ist: YOLO26s benötigt auf einer CPU 2.2-mal so lange wie YOLO26n und erzielt dabei 7.7 mAP-Punkte mehr; eine Erkennung nur in jedem zweiten Bild halbiert den Aufwand. In dieser Lektion lernst du, zuerst ein Profil zu erstellen, die wirksamste passende Stellschraube zu betätigen und aufzuhören, sobald du ein vorab festgelegtes Budget einhältst.

Lernergebnis

Erstelle ein Profil der gesamten Pipeline, wende eine Stellschraube an und vergleiche Latenz und mAP vorher und nachher. Begründe, warum du an dieser Stelle aufgehört hast.

Schnellkurs
Wenn du dich bereits auskennst, findest du hier die Kurzfassung.
  1. Zuerst ein Profil erstellen: Stufen von r.speed sowie deine eigenen Messwerte für Dekodierung und Senke.

  2. Stellschrauben der Reihe nach: kleineres Modell → kleinere imgsz → schnellere Laufzeitumgebung und FP16 → INT8 → vid_stride.

  3. Miss Latenz und mAP vor und nach jeder Änderung auf der Zielhardware und mit demselben Validierungsdatensatz.

  4. Lege zuerst das Budget fest (Tage, mAP-Verlust, angestrebte FPS); ist es ausgeschöpft, verbessere stattdessen die Daten.

Praxisnah

Zuerst die gesamte Pipeline messen#

Jedes Results-Objekt teilt seine eigene Laufzeit in Stufen auf:

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
totals = {"preprocess": 0.0, "inference": 0.0, "postprocess": 0.0}
for n, r in enumerate(model.predict(video, stream=True, verbose=False), 1):
    for stage in totals:
        totals[stage] += r.speed[stage]
print({stage: round(ms / n, 1) for stage, ms in totals.items()}, "ms per frame")
{'preprocess': 0.7, 'inference': 36.1, 'postprocess': 0.7} ms per frame

Hier entfallen 95 % der Modelllaufzeit auf die Inferenz, daher helfen Stellschrauben am Modell. Miss Dekodierung und deine eigenen Senken auf dieselbe Weise (jeweils time.perf_counter() darum herum) und wende dann die passende Regel an:

Deine MesswerteZuerst beheben
60 ms für RTSP-Dekodierung, 5 ms für InferenzHardwaredekodierung, niedrigere Streamauflösung oder mehr Decoder-Threads
30 ms für Inferenz, 100 ms für Python-NachverarbeitungMit NumPy vektorisieren oder in einen Worker-Prozess auslagern
5 ms für Inferenz, 200 ms für DatenbankschreibvorgängeSchreibvorgänge bündeln oder asynchron ausführen
Die Inferenz ist der EngpassDie Rangliste der Stellschrauben unten

Die Rangliste der Stellschrauben#

Probiere sie von oben nach unten aus und höre auf, sobald du dein Budget einhältst:

StellschraubeTypische BeschleunigungTypischer Genauigkeitsverlust
Kleineres Modell (m → s, s → n)1.5–2.5×4.5–7.7 mAP-Punkte auf COCO; bei eng umrissenen Aufgaben weniger
Kleinere imgsz (640 → 480)etwa 1.8×Zuerst kleine und weit entfernte Objekte betroffen
Schnellere Laufzeitumgebung, FP16etwa 1.5× auf der GPUNormalerweise keiner; Ergebnisgleichheit überprüfen
INT8 mit Kalibrierungzusätzlich 1.2× auf der GPU; bis zu 2.9× auf der CPU1 Punkt (YOLO26n) bis 4–6 Punkte (größere Modelle); QAT gleicht den größten Teil aus
Weniger Bilder (vid_stride=N)bis zu N×Schnelle Objekte und kurze Ereignisse können übersehen werden
Größere Batchgröße (Server)Höherer Durchsatz, aber keine geringere LatenzKeine

Die Zahlen stammen aus den Benchmarktabellen für YOLO26, TensorRT und OpenVINO sowie aus den folgenden Messungen; deine Ergebnisse werden abweichen, also miss selbst.

Kleineres Modell#

Die YOLO26-Benchmarktabelle auf COCO:

ModellmAP50-95CPU ONNX (ms)T4 TensorRT10 (ms)
YOLO26n40.938.91.7
YOLO26s48.687.22.5
YOLO26m53.1220.04.7

Auf der CPU ist jeder Schritt zur nächstkleineren Variante 2.2–2.5× schneller. Der Genauigkeitsabstand auf COCO stellt den ungünstigsten Fall dar: Ein Lagerdetektor mit 3 Klassen und großen Objekten verliert beim Wechsel von m zu n üblicherweise deutlich weniger als ein allgemeiner Detektor mit 80 Klassen. Daher solltest du das kleinere Modell immer mit deinem eigenen Validierungsdatensatz testen.

Kleinere Eingabegröße#

Der Rechenaufwand skaliert ungefähr mit der Pixelzahl, daher benötigt imgsz=480 etwa 56 % des Rechenaufwands von 640. Gemessen auf der CPU des Testlaptops mit ONNX Runtime bei Batchgröße 1 (Median aus 25 Durchläufen auf bus.jpg):

Modell640480320
YOLO26n21.3 ms12.1 ms7.1 ms
YOLO26s55.0 ms24.2 ms12.9 ms

Entscheidungsregel: Wenn dein kleinstes wichtiges Objekt bei der neuen Größe noch etwa 10 Pixel oder mehr umfasst, probiere es aus; wenn die Objekte klein oder weit entfernt sind (Drohnen, Regale weit von der Kamera entfernt), behalte die Auflösung bei und verkleinere stattdessen das Modell.

Weniger Bilder#

Wenn die Inferenz deine Bildrate begrenzt und sich die Szene langsam bewegt, führe die Erkennung mit vid_stride=N nur in jedem N-ten Bild aus und lass den Tracker die Lücken überbrücken:

import time

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
model.predict(video, vid_stride=30, verbose=False)  # warm up so the first timing is fair

for stride in (1, 2, 3):
    start, ids, frames = time.perf_counter(), set(), 0
    for r in model.track(video, stream=True, vid_stride=stride, tracker="bytetrack.yaml", classes=[0], verbose=False):
        frames += 1
        if r.boxes.is_track:
            ids.update(r.boxes.id.int().tolist())
    print(f"vid_stride={stride}: {frames} frames in {time.perf_counter() - start:.2f} s, {len(ids)} person IDs")
vid_stride=1: 62 frames in 2.66 s, 32 person IDs
vid_stride=2: 31 frames in 1.19 s, 30 person IDs
vid_stride=3: 20 frames in 0.95 s, 28 person IDs

Bei der Hälfte der Bilder halbiert sich ungefähr auch der Zeitaufwand, und der Tracker behielt 30 von 32 Identitäten bei. Bei vid_stride=3 gingen 4 verloren – ein Zeichen dafür, dass du bei dieser Szene mit Gehgeschwindigkeit aufhören solltest.

Cloud-Kosten senken#

Übersicht zur Bereitstellung auf der Ultralytics Platform: 59 HTTP-Anfragen in 24 Stunden, 0 % Fehlerquote, 226 ms P95-Latenz und ein Endpunkt mit 1 vCPU und 4 GiB in us-west2 für 0.17 $ pro Stunde

Die Kosten entsprechen den Instanzstunden geteilt durch die nützliche Arbeit. Eine Karte wie diese enthält die beiden benötigten Zahlen: die Kosten pro Stunde (hier 0.17 $ für 1 vCPU und 4 GiB) und die geleistete Arbeit (59 Anfragen in 24 Stunden bei 226 ms P95 – ein größtenteils ungenutzter Endpunkt). Dann:

  • Auslastung erhöhen. Eine GPU mit 30 % Auslastung bedeutet, dass 70 % ihrer Kosten verschwendet werden. Bündle Streams (siehe frühere Lektionen) oder stelle sie über den Triton Inference Server bereit.
  • Ressourcen passend dimensionieren. Führe Benchmarks auf einer kleineren GPU oder auf einer CPU mit OpenVINO aus, bevor du davon ausgehst, dass du eine große GPU benötigst.
  • Nutze Spot- oder unterbrechbare Kapazitäten für Batch-Aufträge. Bei der erneuten Verarbeitung archivierter Videos sind Unterbrechungen tolerierbar; bei Live-Streams nicht.

Stolperfallen#

Das Modell optimieren, obwohl es nicht der Engpass ist

Symptom: Zwei Wochen Abstimmung sparen 10 ms, aber das System ist weiterhin langsam. Lösung: Erstelle zuerst ein Profil der gesamten Pipeline und behebe die größte Verzögerung – unabhängig davon, wodurch sie verursacht wird.

Nach der Inferenz Frames überspringen

Symptom: if i % 2: continue innerhalb der Ergebnisschleife spart nichts. Lösung: Das Modell wurde bereits auf diesem Frame ausgeführt; überspringe Frames an der Quelle mit vid_stride.

Stapelverarbeitung zur Verringerung der Latenz

Symptom: Der Durchsatz ist gestiegen, aber jeder Frame kommt jetzt später an. Lösung: Größere Stapel steigern den Durchsatz pro Euro, nicht die Latenz pro Frame; verwende sie für viele Streams oder Offline-Aufträge, nicht für eine einzelne Echtzeitkamera.

Der Hebel Daten

Wenn die Möglichkeiten zur Modelloptimierung ausgeschöpft sind, sind bessere Daten der nächste Hebel. Mit einem Datensatz, der der Produktion entspricht, kann ein kleineres Modell die Genauigkeit erreichen, für die ein größeres Modell nötig war: geringere Kosten ohne Genauigkeitsverlust.

Ausprobieren

Lege ein Optimierungsbudget fest (Tage, maximaler mAP-Verlust, Ziel-FPS auf der Zielhardware). Profiliere deine Pipeline mit r.speed sowie deinen eigenen Messwerten für Dekodierung und Ausgabe, wähle den wirksamsten Hebel, der zum Engpass passt, setze ihn um und halte Latenz und mAP50-95 vorher und nachher auf derselben Hardware und demselben Validierungsdatensatz fest. Liefere das Budget, das Profil, die Vorher-nachher-Tabelle und den Punkt, an dem du aufgehört hast.

Commit
git add -A && git commit -m "perf(production): tuned model + runtime for target latency budget"
Abgeschlossen, wenn
Du hast die Lektion abgeschlossen, wenn alle folgenden Punkte zutreffen.
  • Du weißt anhand von Zahlen, welche Phase den größten Anteil an der durchgängigen Framezeit hat.

  • Du hast einen Hebel umgesetzt und Latenz sowie mAP vorher und nachher auf derselben Hardware und demselben Validierungsdatensatz festgehalten.

  • Du hast bei dem von dir festgelegten Budget aufgehört und kannst sagen, was du als Nächstes versuchen würdest.

Lösung anzeigen
from ultralytics import YOLO

if __name__ == "__main__":  # validation starts worker processes on macOS and Windows
    data, img = "coco8.yaml", "https://ultralytics.com/images/bus.jpg"  # use your val YAML and a production frame
    for weights, imgsz in (("yolo26s.pt", 640), ("yolo26n.pt", 640), ("yolo26n.pt", 480)):
        model = YOLO(weights)
        runs = [model(img, imgsz=imgsz, verbose=False)[0].speed["inference"] for _ in range(20)][5:]
        m = model.val(data=data, imgsz=imgsz, batch=1, plots=False, verbose=False).box.map
        print(f"{weights} imgsz={imgsz}: {sorted(runs)[len(runs) // 2]:.1f} ms, mAP50-95 {m:.3f}")

Jede Zeile lässt die ersten fünf Durchläufe zum Aufwärmen aus und gibt den Median der übrigen Durchläufe an. Wähle die erste Zeile, die das Latenzbudget einhält und deren mAP-Verlust innerhalb deines Budgets liegt. Ein Beispiel für ein festgelegtes Budget: „zwei Tage, höchstens 1 mAP-Punkt Verlust, 20 FPS auf dem Rechner im Geschäft“. Wenn yolo26s bei 640 die Grenze von 50 ms pro Frame verfehlt und yolo26n bei 640 sie auf deinem Validierungsdatensatz mit höchstens 1 Punkt Verlust einhält, hör dort auf; verwende den zweiten Tag nicht für 480.

Wie es weitergeht

Kurs abgeschlossen. Lege die Prüfung ab, um dein Zertifikat zu erhalten, und mach dann mit Entwickeln mit der Ultralytics Platform weiter, die diesen Arbeitsablauf als verwaltetes durchgängiges System ausführt.