Ultralytics YOLO27: Únete a la lista de espera.
Get Started
Ultralytics YOLO en producción·Escalar y observar·Lección 8/9
Lecciónintermedio

Observabilidad y deriva

Registra las métricas que detectan pérdidas silenciosas de precisión, comprueba si hay deriva en las distribuciones de confianza y genera alertas solo ante señales que requieran una acción.

Un servicio de visión puede estar activo, ser rápido y equivocarse. Una cámara se mueve, cambia la iluminación, llega una nueva línea de productos y el modelo sigue devolviendo cajas mientras todos los paneles de infraestructura permanecen en verde. En esta lección, al oscurecer un clip para simular el anochecer, la confianza media solo cambió 0,01 —algo imperceptible en un gráfico—, pero una prueba KS de dos muestras lo detectó con p = 0,007. Crearás esa comprobación y el registro que la alimenta.

Resultado

Emite métricas JSON por fotograma, ejecuta una prueba de deriva KS frente a una referencia inicial, valida un conjunto reservado y define alertas vinculadas cada una a una acción.

Vía rápida
Si ya tienes experiencia, aquí tienes un resumen.
  1. Por fotograma: cámara, r.speed["inference"], número de detecciones, confianza media y confianzas sin procesar.

  2. Guarda las confianzas al iniciar como referencia; compara cada día con scipy.stats.ks_2samp.

  3. Valida semanalmente un conjunto reservado y etiquetado con yolo val; genera una alerta si la mAP cae más de 1 punto.

  4. Genera alertas solo ante señales que tengan una acción siguiente documentada; todo lo demás va en un panel del cuadro de mando.

Práctico

Tres capas de señales#

CapaPreguntaSeñales
Infraestructura¿El servicio está activo y mantiene el ritmo?Latencia, rendimiento, fotogramas perdidos, uso de CPU y GPU
Comportamiento del modelo¿Se comporta el modelo como la semana pasada?Detecciones por fotograma, confianza media, distribución de clases y tamaños de las cajas
Precisión del modelo¿Sigue acertando el modelo?mAP del conjunto reservado (métricas de rendimiento), revisión humana de muestras y tasa de falsas alarmas

La mayoría de los equipos cubre la primera capa con herramientas estándar y se detiene ahí. Los fallos silenciosos de los sistemas de visión se producen en la segunda y la tercera; la guía de supervisión de modelos explica todo el ciclo de mantenimiento.

Página de implementación de Ultralytics Platform en la pestaña Logs, con los filtros Errors y All encima de líneas INFO de solicitudes con marca de tiempo para la supervisión y llamadas de muestra

Si implementas mediante Ultralytics Platform, la página de implementación te ofrece la capa de infraestructura (solicitudes, tasa de errores y latencia P95) y registros de solicitudes que puedes filtrar por errores, como se muestra arriba; la lección Supervisar el estado y la precisión los explica. Tú debes crear las capas de comportamiento y precisión; el resto de esta lección te enseña a hacerlo.

Emite métricas por fotograma#

Cada objeto Results incluye tiempos y cajas. Escribe una línea JSON por fotograma y envíala al sistema de métricas que utilices:

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()],  # valores sin procesar para la prueba de 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}

Para qué sirve cada campo:

  • inference_ms: alerta cuando p95 supere tu presupuesto.
  • detections: una caída repentina hasta casi cero suele indicar que una cámara está bloqueada o averiada; un aumento repentino indica un cambio de escena o falsos positivos.
  • mean_conf: una línea de tendencia rápida, pero demasiado general para detectar una deriva por sí sola.
  • confs: la distribución completa que necesita la prueba de deriva de abajo.

Probar si hay deriva en las distribuciones de confianza#

La deriva de datos se refleja en las confianzas mucho antes de que alguien etiquete datos nuevos. Recopila las confianzas al iniciar para establecer una referencia, recopila una ventana reciente y compara las dos distribuciones con una prueba de Kolmogórov-Smirnov para dos muestras (pip install scipy). Este script simula el anochecer oscureciendo el mismo vídeo:

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

Léelo línea por línea:

  • Misma escena: las distribuciones idénticas dan un estadístico KS de 0 y p = 1; este es el control.
  • Anochecer: 39 detecciones menos y una media solo 0,011 inferior, algo que nadie advertiría en un gráfico. El estadístico KS (la mayor diferencia entre las dos distribuciones acumuladas) es 0,064, y p = 0,0068 < 0,01 indica que el desplazamiento es real.

Una alerta de deriva es un motivo para revisar fotogramas de muestra, no una prueba de fallo. Si la deriva es real, recopila y etiqueta fotogramas de las cámaras afectadas: la guía de recopilación y anotación de datos explica qué recopilar y la guía de ajuste fino explica cómo volver a entrenar. El tamaño de las cajas (ancho × alto) ofrece una segunda señal de deriva sencilla: los nuevos soportes y las nuevas ópticas se reflejan primero ahí.

Valida una partición de prueba cada semana#

Las señales de comportamiento indican que algo ha cambiado; solo las etiquetas indican si la precisión ha bajado. Conserva una partición de prueba con entre 200 y 500 imágenes de producción etiquetadas y valida cada versión del modelo, además de hacerlo cada semana:

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

Registra mAP50-95 y AP por clase a lo largo del tiempo, y activa una alerta cuando mAP baje más de 1 punto de una semana a otra. La causa más habitual es un cambio de modelo que debía ser inocuo; la segunda es la deriva. Una matriz de confusión sobre la misma partición de prueba permite detectar fácilmente las regresiones por clase.

Calidad del modelo a lo largo del tiempo: la calidad se mantiene mientras llegan datos etiquetados nuevos y después decae tras el punto previsto de deterioro; ¿conviene volver a entrenar ahora o esperar?

La partición de prueba es lo que responde a la pregunta de este gráfico. Vuelve a entrenar cuando muestre que la calidad empieza a caer o cuando tengas suficientes datos etiquetados nuevos de las condiciones con deriva; no lo hagas siguiendo un calendario fijo.

Activa alertas ante acciones, no ante números#

SeñalActivar cuandoAcción
p95 inference_msSupere el presupuesto durante más de 3 minutosEscala horizontalmente o cambia a la exportación optimizada
Detecciones por fotogramaBajen un 50 % o más durante 5 minutosComprueba la señal de la cámara y, después, la versión del modelo
Fotogramas perdidosSuperen el 5 % durante 10 minutosAñade capacidad o reduce los FPS por flujo
Prueba KS de confianzap < 0,01 en ventanas diariasRevisa los fotogramas y, después, ejecuta la validación de la partición de prueba
mAP50-95 de la partición de pruebaBaje 1 punto o más de una semana a otraRecopila datos, etiquétalos y ajusta el modelo

Errores habituales#

Comparar ventanas distintas

Síntoma: la prueba KS se activa todas las tardes y todos los lunes. Solución: los ciclos diarios y semanales son cambios reales en la distribución. Compara situaciones equivalentes (las mismas cámaras, las mismas horas y el mismo día de la semana) y usa ventanas de miles de detecciones.

Un conjunto de prueba obsoleto

Síntoma: el mAP del conjunto de prueba parece correcto, pero los usuarios informan de fallos de detección. Solución: el conjunto de prueba debe parecerse a la producción actual. Actualízalo cada trimestre con fotogramas recién etiquetados de las cámaras relevantes.

Si nadie va a actuar en consecuencia, no envíes una alerta

Cada alerta necesita una siguiente acción por escrito. Si solo quieres observar una señal, debe aparecer en un panel, no activar una alerta.

Pruébalo

Añade el registro JSON a tu pipeline y ejecútalo durante una hora (o con un vídeo largo). Conserva la primera ventana como baseline.jsonl y, después, ejecuta la comprobación KS de la solución con una segunda ventana: de otra hora del día, de otra cámara o una copia oscurecida. Entrega el estadístico KS y el valor p, junto con la tabla de alertas y una acción para cada fila.

Cuándo se considera terminado
Has terminado la lección cuando se cumplen todas estas condiciones.
  • Tu pipeline escribe una línea JSON por fotograma con la latencia, el número de detecciones, la confianza media y las confianzas sin procesar.

  • Has ejecutado una prueba KS entre una ventana de referencia y una reciente, y has registrado el estadístico y el valor p.

  • Cada alerta que has definido tiene un umbral, una duración y una siguiente acción por escrito.

Mostrar solución

Conserva la primera ventana saludable de resultados de monitor.py como baseline.jsonl y, después, compara cada ventana nueva con ella:

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

Ese es el resultado esperado cuando ambos archivos proceden del mismo metraje. Ejecútalo cada día con un programador, envía el estadístico y el valor p al mismo sistema de métricas que los registros por fotograma y dirige review frames a una persona, no a un buscapersonas.

Qué viene después

Última lección: cuando los paneles indican que la latencia o el coste superan el presupuesto, qué palanca conviene accionar primero.