Osservabilità e deriva
Registra le metriche che rivelano il calo silenzioso della precisione, verifica la deriva con le distribuzioni di confidenza e invia avvisi solo per segnali che richiedono un'azione.
Un servizio di visione può essere attivo, veloce e sbagliato. Una telecamera viene spostata, l'illuminazione cambia, arriva una nuova linea di prodotti e il modello continua a restituire riquadri mentre tutte le dashboard dell'infrastruttura restano verdi. In questa lezione, scurire una clip per simulare il crepuscolo ha spostato la confidenza media di appena 0.01, una variazione invisibile su un grafico, eppure un test KS a due campioni l'ha rilevata con p = 0.007. Realizzerai questo controllo e la registrazione dei dati che lo alimenta.
Pratico
Tre livelli di segnali#
| Livello | Domanda | Segnali |
|---|---|---|
| Infrastruttura | Il servizio è attivo e riesce a tenere il passo? | Latenza, throughput, frame persi, utilizzo di CPU e GPU |
| Comportamento del modello | Il modello si comporta come la settimana scorsa? | Rilevamenti per frame, confidenza media, distribuzione delle classi, dimensioni dei riquadri |
| Precisione del modello | Il modello continua a fornire risultati corretti? | mAP sul set di holdout (metriche di prestazione), revisione umana a campione, tasso di falsi allarmi |
La maggior parte dei team copre il primo livello con strumenti standard e si ferma lì. È nel secondo e nel terzo che i sistemi di visione falliscono senza farsi notare; la guida al monitoraggio dei modelli descrive l'intero ciclo di manutenzione.

Se distribuisci tramite Ultralytics Platform, la pagina della distribuzione ti fornisce i dati dell'infrastruttura (richieste, tasso di errori, latenza P95) e i log delle richieste, filtrabili per errori come nell'esempio qui sopra; la lezione Monitorare lo stato e la precisione tratta questi aspetti. I livelli relativi al comportamento e alla precisione sono a tuo carico: il resto di questa lezione ti mostra come realizzarli.
Emetti metriche per ogni frame#
Ogni oggetto Results contiene tempi e riquadri. Scrivi una riga JSON per frame e inviala al sistema di metriche che usi:
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()], # valori grezzi per il test di deriva
}
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}A cosa serve ciascun campo:
- inference_ms: genera un avviso quando p95 supera il tuo budget.
- detections: un calo improvviso quasi a zero di solito indica una telecamera bloccata o guasta; un aumento indica un cambio di scena o falsi positivi.
- mean_conf: una rapida linea di tendenza, ma troppo approssimativa per rilevare da sola la deriva.
- confs: la distribuzione completa, necessaria per il test di deriva qui sotto.
Verificare la deriva nelle distribuzioni di confidenza#
La deriva dei dati si manifesta nei valori di confidenza molto prima che qualcuno etichetti nuovi dati. Raccogli i valori di confidenza al lancio come riferimento, raccogli una finestra recente e confronta le due distribuzioni con un test di Kolmogorov-Smirnov a due campioni (pip install scipy). Questo script simula il crepuscolo scurendo lo stesso video:
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.0068Esaminiamole una per una:
- Stessa scena: distribuzioni identiche producono una statistica KS pari a 0 e p = 1: il controllo.
- Crepuscolo: 39 rilevamenti in meno e una media inferiore di appena 0.011, cosa che nessuno noterebbe su un grafico. La statistica KS (la distanza massima tra le due distribuzioni cumulative) è 0.064 e p = 0.0068 < 0.01 indica che lo spostamento è reale.
Un avviso di deriva è un motivo per esaminare alcuni fotogrammi campione, non la prova di un malfunzionamento. Quando la deriva è reale, raccogli ed etichetta i fotogrammi delle telecamere interessate: la guida alla raccolta e all'annotazione dei dati spiega cosa raccogliere, mentre la guida al fine-tuning spiega come eseguire il nuovo addestramento. Anche le dimensioni dei riquadri (larghezza × altezza) forniscono un secondo indicatore semplice di deriva: i nuovi supporti e le nuove ottiche si notano prima lì.
Convalida settimanalmente un set di holdout#
I segnali comportamentali indicano che qualcosa è cambiato; solo le etichette dicono se la precisione è diminuita. Conserva un set di holdout di 200–500 immagini di produzione etichettate e convalida ogni versione del modello, oltre a farlo ogni settimana:
yolo val model=yolo26n.pt data=coco8.yaml > weekly_metrics.txt # use your production model and holdout YAMLRegistra mAP50-95 e AP per classe nel tempo e genera un avviso quando mAP cala di più di 1 punto da una settimana all'altra. La causa più comune è la sostituzione del modello che avrebbe dovuto essere ininfluente; la seconda è la deriva. Una matrice di confusione sullo stesso set di holdout rende evidenti le regressioni per classe.

Il set di holdout permette di rispondere alla domanda del grafico. Esegui il nuovo addestramento quando mostra che la qualità inizia a calare o quando hai raccolto abbastanza nuovi dati etichettati nelle condizioni soggette a deriva, non seguendo un calendario fisso.
Genera avvisi sulle azioni, non sui numeri#
| Segnale | Quando scatta | Azione |
|---|---|---|
p95 inference_ms | Sopra il budget per più di 3 minuti | Aumenta la capacità o passa all'esportazione ottimizzata |
| Rilevamenti per fotogramma | In calo di almeno il 50% per 5 minuti | Controlla il flusso della telecamera, poi la versione del modello |
| Fotogrammi persi | Oltre il 5% per 10 minuti | Aggiungi capacità o riduci gli FPS per flusso |
| Test KS dei valori di confidenza | p < 0.01 nelle finestre giornaliere | Esamina i fotogrammi, poi esegui la convalida del set di holdout |
| mAP50-95 del set di holdout | In calo di almeno 1 punto da una settimana all'altra | Raccogli, etichetta ed esegui il fine-tuning |
Insidie#
Sintomo: il test KS si attiva ogni sera e ogni lunedì. Soluzione: i cicli giornalieri e settimanali sono cambiamenti reali nella distribuzione. Confronta dati omogenei (stesse telecamere, stesse ore, stesso giorno della settimana) e usa finestre di migliaia di rilevamenti.
Sintomo: la mAP sul set di test sembra buona mentre gli utenti segnalano mancate rilevazioni. Soluzione: il set di test deve rispecchiare la produzione attuale. Aggiornalo ogni trimestre con fotogrammi appena etichettati, provenienti dalle telecamere più importanti.
Ogni avviso deve indicare per iscritto la prossima azione da intraprendere. Un segnale che vuoi solo monitorare va inserito in una dashboard, non inviato al sistema di paging.
Mostra la soluzione
Conserva la prima finestra sana di output di monitor.py come baseline.jsonl, poi confronta ogni nuova finestra con essa:
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È il risultato previsto quando entrambi i file provengono dallo stesso filmato. Esegui il test ogni giorno tramite uno scheduler, invia la statistica e il p-value allo stesso sistema di metriche dei log per fotogramma e inoltra review frames a una persona, non a un sistema di pager.