Ultralytics YOLO27 : Inscris-toi sur la liste d’attente.
Get Started
Ultralytics YOLO en production·Échelle et observation·Leçon 8/9
Leçonintermédiaire

Observabilité et dérive

Consigne les métriques qui détectent les pertes de précision silencieuses, teste les distributions de confiance pour repérer les dérives et ne déclenche des alertes que sur des signaux exploitables.

Un service de vision peut être opérationnel, rapide et pourtant se tromper. Une caméra est déplacée, l’éclairage change, une nouvelle gamme de produits arrive, et le modèle continue de renvoyer des boîtes alors que tous les tableaux de bord de l’infrastructure restent au vert. Dans cette leçon, l’assombrissement d’un clip pour simuler le crépuscule n’a modifié la confiance moyenne que de 0,01, variation invisible sur un graphique, mais un test KS à deux échantillons l’a détectée avec p = 0,007. Tu vas créer ce contrôle ainsi que la journalisation qui l’alimente.

Résultat

Émets des métriques JSON par image, exécute un test de dérive KS par rapport à une référence établie au lancement, valide un jeu de données de réserve et définis des alertes associées chacune à une action.

Parcours accéléré
Si tu connais déjà les bases, voici la version courte.
  1. Pour chaque image : caméra, r.speed["inference"], nombre de détections, confiance moyenne, valeurs de confiance brutes.

  2. Enregistre les valeurs de confiance au lancement comme référence ; compare chaque jour avec scipy.stats.ks_2samp.

  3. Valide chaque semaine un jeu de données de réserve annoté avec yolo val ; déclenche une alerte en cas de baisse supérieure à 1 point de mAP.

  4. Ne déclenche des alertes que sur des signaux associés à une prochaine action documentée ; tout le reste reste dans un panneau du tableau de bord.

Pratique

Trois niveaux de signaux#

NiveauQuestionSignaux
InfrastructureLe service est-il opérationnel et suit-il le rythme ?Latence, débit, images perdues, utilisation du CPU et du GPU
Comportement du modèleLe modèle se comporte-t-il comme la semaine dernière ?Détections par image, confiance moyenne, répartition des classes, tailles des boîtes
Précision du modèleLe modèle donne-t-il toujours des résultats corrects ?mAP sur le jeu de réserve (métriques de performance), échantillonnage pour une vérification humaine, taux de fausses alertes

La plupart des équipes couvrent le premier niveau avec des outils standards et s’arrêtent là. C’est aux deuxième et troisième niveaux que les systèmes de vision échouent sans bruit ; le guide de surveillance des modèles couvre l’ensemble du cycle de maintenance.

Page de déploiement Ultralytics Platform dans l’onglet Logs, avec les filtres Errors et All au-dessus de lignes INFO horodatées pour les requêtes de surveillance et les appels d’exemple

Si tu déploies avec Ultralytics Platform, la page de déploiement fournit les données de l’infrastructure (requêtes, taux d’erreur, latence P95) et les journaux des requêtes, que tu peux filtrer pour afficher les erreurs, comme ci-dessus ; la leçon Surveiller l’état et la précision les présente. À toi de construire les niveaux du comportement et de la précision ; le reste de cette leçon s’y consacre.

Émettre des métriques pour chaque image#

Chaque objet Results contient des temps d’exécution et des boîtes. Écris une ligne JSON par image et envoie-la au système de métriques que tu utilises :

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()],  # valeurs brutes pour le test de dérive
        }
        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}

À quoi sert chaque champ :

  • inference_ms : déclenche une alerte lorsque le p95 dépasse ton budget.
  • detections : une chute soudaine à près de zéro indique généralement qu’une caméra est bloquée ou en panne ; une hausse signale un changement de scène ou des faux positifs.
  • mean_conf : une courbe de tendance rapide, mais trop grossière pour détecter une dérive à elle seule.
  • confs : la distribution complète, nécessaire au test de dérive ci-dessous.

Tester la dérive des distributions de confiance#

La dérive des données se manifeste dans les confiances bien avant que quiconque n’annote de nouvelles données. Recueille les confiances au lancement pour établir une référence, collecte une fenêtre récente, puis compare les deux distributions à l’aide d’un test de Kolmogorov-Smirnov à deux échantillons (pip install scipy). Ce script simule le crépuscule en assombrissant le même extrait vidéo :

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

Examinons chaque ligne :

  • Même scène : des distributions identiques donnent une statistique KS de 0 et p = 1 ; c’est le contrôle.
  • Crépuscule : 39 détections en moins et une moyenne inférieure de seulement 0,011, ce que personne ne remarquerait sur un graphique. La statistique KS (le plus grand écart entre les deux distributions cumulées) est de 0,064, et p = 0,0068 < 0,01 indique que le décalage est réel.

Une alerte de dérive est une raison d’examiner des images échantillons, pas une preuve de défaillance. Si la dérive est réelle, collecte et annote des images des caméras concernées : le guide de collecte et d’annotation des données explique quoi collecter, et le guide de réglage fin explique comment réentraîner le modèle. La taille des boîtes (largeur × hauteur) constitue un deuxième indicateur de dérive facile à obtenir : les nouveaux supports et les nouvelles optiques s’y manifestent en premier.

Valider un jeu de données de contrôle chaque semaine#

Les signaux comportementaux indiquent qu’un changement a eu lieu ; seules les annotations permettent de savoir si la précision a baissé. Conserve un jeu de contrôle de 200 à 500 images de production annotées et valide chaque version du modèle, ainsi qu’une fois par semaine :

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

Enregistre mAP50-95 et l’AP par classe au fil du temps, et déclenche une alerte lorsque mAP baisse de plus d’un point d’une semaine à l’autre. La cause la plus fréquente est un changement de modèle qui était censé ne rien modifier ; la deuxième est la dérive. Une matrice de confusion calculée sur le même jeu de contrôle met clairement en évidence les régressions par classe.

Qualité du modèle au fil du temps : la qualité se maintient pendant l’arrivée de nouvelles données annotées, puis diminue après le point de déclin attendu ; faut-il réentraîner maintenant ou attendre ?

C’est le jeu de contrôle qui permet de répondre à la question posée par ce graphique. Réentraîne le modèle lorsque la qualité commence à baisser, ou lorsque tu disposes de suffisamment de nouvelles données annotées provenant des conditions ayant entraîné la dérive, et non selon un calendrier fixe.

Déclencher des alertes sur les actions, pas sur les chiffres#

SignalDéclenchementAction
p95 inference_msDépassement du budget pendant plus de 3 minutesAugmenter la capacité ou passer à l’export optimisé
Détections par imageBaisse d’au moins 50 % pendant 5 minutesVérifier le flux de la caméra, puis la version du modèle
Images perduesPlus de 5 % pendant 10 minutesAjouter de la capacité ou réduire le FPS par flux
Test KS des confiancesp < 0,01 sur des fenêtres quotidiennesExaminer les images, puis lancer la validation sur le jeu de contrôle
mAP50-95 sur le jeu de contrôleBaisse d’au moins 1 point d’une semaine à l’autreCollecter, annoter et régler finement le modèle

Pièges#

Comparer des fenêtres différentes

Symptôme : le test KS se déclenche tous les soirs et tous les lundis. Solution : les cycles quotidiens et hebdomadaires sont de véritables changements de distribution. Compare des situations similaires (mêmes caméras, mêmes heures, mêmes jours de la semaine) et utilise des fenêtres de plusieurs milliers de détections.

Un jeu de test obsolète

Symptôme : la mAP du jeu de test semble bonne, alors que les utilisateurs signalent des erreurs de détection. Solution : le jeu de test doit ressembler à la production actuelle. Mets-le à jour chaque trimestre avec de nouvelles images annotées provenant des caméras importantes.

Si personne n'interviendrait, ne déclenche pas d'alerte

Chaque alerte doit être accompagnée d'une prochaine action écrite. Un signal que tu veux seulement surveiller doit apparaître sur un tableau de bord, pas déclencher une alerte.

Essayer

Ajoute la journalisation JSON à ton pipeline et exécute-le pendant une heure (ou sur une longue vidéo). Garde la première fenêtre comme baseline.jsonl, puis exécute le test KS de la solution sur une deuxième fenêtre : à une autre heure de la journée, avec une autre caméra ou avec une copie assombrie. Fournis la statistique KS et la valeur p, ainsi que ton tableau d’alertes avec une action pour chaque ligne.

Terminé lorsque
Tu as terminé la leçon lorsque tous les éléments suivants sont réunis.
  • Ton pipeline écrit une ligne JSON par image, avec la latence, le nombre de détections, la confiance moyenne et les confiances brutes.

  • Tu as effectué un test KS entre une référence et une fenêtre récente, puis consigné la statistique et la valeur p.

  • Chaque alerte que tu as définie comporte un seuil, une durée et une prochaine action consignée par écrit.

Afficher la solution

Garde la première fenêtre saine de sortie monitor.py comme baseline.jsonl, puis compare chaque nouvelle fenêtre à celle-ci :

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

C’est le résultat attendu lorsque les deux fichiers proviennent des mêmes séquences vidéo. Exécute le test chaque jour à l’aide d’un planificateur, envoie la statistique et la valeur p au même système de métriques que les journaux par image, et dirige review frames vers une personne, pas vers un système d’alerte.

Et ensuite ?

Dernière leçon : lorsque les tableaux de bord indiquent que la latence ou le coût dépasse le budget, quel levier actionner en premier.