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

Beobachtbarkeit und Drift

Protokolliere Kennzahlen, die einen unbemerkten Genauigkeitsverlust aufdecken, prüfe Konfidenzverteilungen auf Drift und richte nur bei Signalen mit konkretem Handlungsbedarf Alarme ein.

Ein Bildverarbeitungsdienst kann verfügbar und schnell sein – und trotzdem falsche Ergebnisse liefern. Eine Kamera wird angestoßen, die Beleuchtung ändert sich oder eine neue Produktlinie kommt hinzu, und das Modell liefert weiterhin Boxen, während alle Infrastruktur-Dashboards grün bleiben. In dieser Lektion verschob eine Verdunkelung des Clips, die die Abenddämmerung simulierte, die mittlere Konfidenz nur um 0,01 – in einem Diagramm kaum sichtbar. Ein zweiseitiger KS-Test erkannte die Veränderung dennoch mit p = 0,007. Du erstellst diese Prüfung und die zugehörige Protokollierung.

Lernergebnis

Erfasse pro Frame JSON-Kennzahlen, führe einen KS-Drifttest anhand einer Referenz aus dem Startzeitpunkt durch, validiere einen zurückgehaltenen Datensatz und definiere Alarme, die jeweils eine konkrete Maßnahme auslösen.

Schnellkurs
Wenn du dich bereits auskennst, findest du hier die Kurzfassung.
  1. Pro Frame: Kamera, r.speed["inference"], Anzahl der Erkennungen, mittlere Konfidenz, rohe Konfidenzwerte.

  2. Speichere die Konfidenzwerte beim Start als Referenz und vergleiche sie täglich mit scipy.stats.ks_2samp.

  3. Validiere wöchentlich einen beschrifteten zurückgehaltenen Datensatz mit yolo val; richte einen Alarm ein, wenn mAP um mehr als einen Punkt sinkt.

  4. Richte nur für Signale mit einer schriftlich festgehaltenen nächsten Maßnahme Alarme ein; alles andere gehört in ein Dashboard.

Praxisnah

Drei Signalebenen#

EbeneFrageSignale
InfrastrukturIst der Dienst verfügbar und hält er Schritt?Latenz, Durchsatz, verworfene Frames, CPU- und GPU-Auslastung
ModellverhaltenVerhält sich das Modell wie letzte Woche?Erkennungen pro Frame, mittlere Konfidenz, Klassenverteilung, Boxgrößen
ModellgenauigkeitLiefert das Modell weiterhin richtige Ergebnisse?mAP auf einem zurückgehaltenen Datensatz (Leistungskennzahlen), stichprobenartige menschliche Prüfung, Fehlalarmrate

Die meisten Teams decken die erste Ebene mit Standardwerkzeugen ab und belassen es dabei. Auf der zweiten und dritten Ebene versagen Bildverarbeitungssysteme unbemerkt; der Leitfaden zur Modellüberwachung behandelt den gesamten Wartungsprozess.

Bereitstellungsseite der Ultralytics Platform im Tab „Logs“ mit den Filtern „Errors“ und „All“ über zeitgestempelten INFO-Anfragezeilen für Überwachung und Beispielaufrufe

Wenn du über die Ultralytics Platform bereitstellst, zeigt dir die Bereitstellungsseite die Infrastrukturebene (Anfragen, Fehlerrate, P95-Latenz) und Anfrageprotokolle, die du wie oben nach Fehlern filtern kannst; die zugehörige Lektion Gesundheit und Genauigkeit überwachen behandelt diese Aspekte. Die Verhaltens- und Genauigkeitsebenen musst du selbst aufbauen – genau darum geht es im Rest dieser Lektion.

Kennzahlen pro Frame erfassen#

Jedes Results-Objekt enthält Zeitmessungen und Boxen. Schreibe pro Frame eine JSON-Zeile und leite sie an das von dir verwendete Kennzahlensystem weiter:

import json
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"
with open("metrics.jsonl", "w") as log:
    for r in model.predict(video, stream=True, verbose=False):
        conf = r.boxes.conf
        record = {
            "ts": time.time(),
            "camera": "lobby-1",
            "inference_ms": round(r.speed["inference"], 1),
            "detections": len(r.boxes),
            "mean_conf": round(conf.mean().item(), 3) if len(conf) else None,
            "confs": [round(c, 3) for c in conf.tolist()],  # Rohwerte für den Drifttest
        }
        log.write(json.dumps(record) + "\n")
print({k: v for k, v in record.items() if k != "confs"})
{'ts': 1792003254.33, 'camera': 'lobby-1', 'inference_ms': 34.7, 'detections': 22, 'mean_conf': 0.543}

Wofür die einzelnen Felder stehen:

  • inference_ms: Alarm auslösen, wenn p95 dein Budget überschreitet.
  • detections: Ein plötzlicher Rückgang auf nahezu null deutet meist auf eine blockierte oder defekte Kamera hin; ein Anstieg auf einen Szenenwechsel oder Fehlalarme.
  • mean_conf: eine schnelle Trendlinie, aber allein zu grob, um Drift zu erkennen.
  • confs: die vollständige Verteilung, die der folgende Drift-Test benötigt.

Konfidenzverteilungen auf Drift testen#

Datenverschiebungen zeigen sich in den Konfidenzen, lange bevor jemand neue Daten labelt. Erfasse beim Start Konfidenzen als Basiswert, erfasse ein aktuelles Zeitfenster und vergleiche die beiden Verteilungen mit einem Kolmogorov-Smirnov-Test für zwei Stichproben (pip install scipy). Dieses Skript simuliert die Abenddämmerung, indem es denselben Clip abdunkelt:

import cv2
import numpy as np
from scipy.stats import ks_2samp

from ultralytics import YOLO
from ultralytics.utils.downloads import safe_download

model = YOLO("yolo26n.pt")
video = str(safe_download("https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"))

def confidences(darken=1.0):
    """Collect every detection confidence from the clip, optionally darkened to simulate dusk."""
    cap, out = cv2.VideoCapture(video), []
    while True:
        ok, frame = cap.read()
        if not ok:
            break
        out += model((frame * darken).astype(np.uint8), verbose=False)[0].boxes.conf.tolist()
    return np.array(out)

baseline = confidences()  # what "normal" looked like at launch
for name, recent in (("same scene", confidences()), ("dusk (35% brightness)", confidences(darken=0.35))):
    stat, p = ks_2samp(baseline, recent)
    print(f"{name:22s} n={len(recent):4d} mean_conf={recent.mean():.3f} KS={stat:.3f} p={p:.2g}")
same scene             n=1376 mean_conf=0.519 KS=0.000 p=1
dusk (35% brightness)  n=1337 mean_conf=0.508 KS=0.064 p=0.0068

Gehe die Ergebnisse Zeile für Zeile durch:

  • Gleiche Szene: Identische Verteilungen ergeben eine KS-Statistik von 0 und p = 1 – die Kontrollbedingung.
  • Abenddämmerung: 39 Erkennungen weniger und ein Mittelwert, der nur um 0.011 niedriger liegt – das würde in einem Diagramm niemand bemerken. Die KS-Statistik (der größte Abstand zwischen den beiden kumulativen Verteilungen) beträgt 0.064, und p = 0.0068 < 0.01 zeigt, dass die Verschiebung real ist.

Ein Driftalarm ist ein Anlass, Beispielbilder zu prüfen, kein Beweis für einen Fehler. Ist die Verschiebung real, erfasse und label die Bilder der betroffenen Kameras: Der Leitfaden zur Datenerfassung und Annotation beschreibt, was du erfassen solltest, und der Leitfaden zum Fine-Tuning erklärt das erneute Training. Die Boxgrößen (Breite × Höhe) liefern ein weiteres einfaches Drift-Signal: Neue Halterungen und Objektive machen sich dort zuerst bemerkbar.

Wöchentlich einen Holdout-Datensatz validieren#

Verhaltenssignale zeigen, dass sich etwas geändert hat; nur Labels zeigen, ob die Genauigkeit gesunken ist. Halte einen Holdout-Datensatz mit 200–500 gelabelten Produktionsbildern vor und validiere jede Modellversion sowie jede Woche:

yolo val model=yolo26n.pt data=coco8.yaml > weekly_metrics.txt  # use your production model and holdout YAML

Erfasse mAP50-95 und AP pro Klasse im zeitlichen Verlauf und löse einen Alarm aus, wenn mAP von einer Woche zur nächsten um mehr als 1 Punkt sinkt. Am häufigsten ist ein Modellwechsel die Ursache, der eigentlich nichts ändern sollte; an zweiter Stelle steht Drift. Eine Konfusionsmatrix desselben Holdout-Datensatzes macht Rückgänge bei einzelnen Klassen offensichtlich.

Modellqualität im zeitlichen Verlauf: Die Qualität bleibt stabil, solange neue gelabelte Daten hinzukommen, und sinkt dann nach dem erwarteten Zeitpunkt des Rückgangs. Die Frage ist, ob jetzt erneut trainiert oder noch gewartet werden soll

Der Holdout-Datensatz liefert die Antwort auf die Frage in diesem Diagramm. Trainiere erneut, wenn die Qualität laut Holdout zu sinken beginnt oder wenn genügend neue gelabelte Daten aus den von Drift betroffenen Bedingungen vorliegen – nicht nach einem festen Kalender.

Alarme bei Aktionen auslösen, nicht bei Zahlen#

SignalAlarm auslösen, wennMaßnahme
p95 inference_msMehr als 3 Minuten über dem BudgetAuf weitere Instanzen skalieren oder zum optimierten Export wechseln
Erkennungen pro Bild5 Minuten lang um mindestens 50 % gesunkenKamerabild prüfen, dann die Modellversion
Verworfene Bilder10 Minuten lang über 5 %Kapazität hinzufügen oder FPS pro Stream senken
KS-Test der Konfidenzenp < 0.01 in täglichen ZeitfensternBilder prüfen, dann den Holdout ausführen
mAP50-95 des Holdout-DatensatzesVon einer Woche zur nächsten um mindestens 1 Punkt gesunkenErfassen, labeln und Fine-Tuning durchführen

Stolperfallen#

Nicht vergleichbare Zeitfenster vergleichen

Symptom: Der KS-Test schlägt jeden Abend und jeden Montag an. Lösung: Tages- und Wochenzyklen sind tatsächliche Verteilungsänderungen. Vergleiche Gleiches mit Gleichem (dieselben Kameras, dieselben Uhrzeiten, derselbe Wochentag) und verwende Zeitfenster mit Tausenden von Detektionen.

Ein veralteter Holdout-Datensatz

Symptom: Die Holdout-mAP sieht gut aus, während Nutzer Fehlklassifikationen melden. Lösung: Der Holdout muss der aktuellen Produktion entsprechen. Aktualisiere ihn jedes Quartal mit neu beschrifteten Frames von den wichtigen Kameras.

Wenn niemand darauf reagieren würde, löse dafür keinen Pager-Alarm aus

Für jeden Alarm muss eine nächste Maßnahme schriftlich festgehalten sein. Ein Signal, das du lediglich beobachten möchtest, gehört auf ein Dashboard und nicht in den Pager.

Ausprobieren

Füge die JSON-Protokollierung deiner Pipeline hinzu und lasse sie eine Stunde lang (oder über ein langes Video hinweg) laufen. Behalte das erste Zeitfenster als baseline.jsonl bei und führe dann den KS-Test der Lösung mit einem zweiten Zeitfenster durch: einer anderen Tageszeit, einer anderen Kamera oder einer abgedunkelten Kopie. Gib die KS-Statistik und den p-Wert sowie deine Alarmtabelle mit einer Aktion für jede Zeile aus.

Abgeschlossen, wenn
Du hast die Lektion abgeschlossen, wenn alle folgenden Punkte zutreffen.
  • Deine Pipeline schreibt für jedes Bild eine JSON-Zeile mit Latenz, Anzahl der Erkennungen, mittlerer Konfidenz und rohen Konfidenzwerten.

  • Du hast einen KS-Test zwischen einem Basiswert und einem aktuellen Zeitfenster ausgeführt und die Statistik sowie den p-Wert erfasst.

  • Jeder von dir definierte Alarm hat einen Schwellenwert, eine Dauer und eine schriftlich festgehaltene nächste Aktion.

Lösung anzeigen

Behalte das erste fehlerfreie Zeitfenster der Ausgabe von monitor.py als baseline.jsonl bei und vergleiche jedes neue Zeitfenster damit:

import json

import numpy as np
from scipy.stats import ks_2samp

def load(path):
    """Flatten the per-frame 'confs' lists written by monitor.py."""
    with open(path) as f:
        return np.array([c for line in f for c in json.loads(line)["confs"]])

baseline, recent = load("baseline.jsonl"), load("metrics.jsonl")
stat, p = ks_2samp(baseline, recent)
print(f"KS={stat:.3f} p={p:.2g} -> {'review frames' if p < 0.01 else 'ok'}")
KS=0.000 p=1 -> ok

Das ist das erwartete Ergebnis, wenn beide Dateien aus demselben Filmmaterial stammen. Führe den Test täglich über einen Scheduler aus, sende die Statistik und den p-Wert an dasselbe Metriksystem wie die Protokolle pro Bild und leite review frames an eine Person weiter, nicht an einen Pager.

Wie es weitergeht

Letzte Lektion: Wenn die Dashboards zeigen, dass Latenz oder Kosten das Budget überschreiten, erfährst du, an welcher Stellschraube du zuerst drehen solltest.