Ultralytics YOLO27: Встать в лист ожидания.
Get Started
Ultralytics YOLO в продакшене·Масштаб и наблюдение·Урок 8/9
Уроксредний

Наблюдаемость и дрейф

Собирай метрики, выявляющие незаметное снижение точности, проверяй распределения уверенности на предмет дрейфа и создавай оповещения только по сигналам, требующим действий.

Сервис компьютерного зрения может работать, быть быстрым и при этом ошибаться. Камеру задели, освещение изменилось, появилась новая линейка товаров, а модель продолжает возвращать боксы, пока все панели мониторинга инфраструктуры показывают зелёный статус. В этом уроке затемнение ролика для имитации сумерек изменило среднюю уверенность всего на 0,01 — на графике это незаметно, — но двухвыборочный тест KS выявил изменение при p = 0,007. Ты создашь эту проверку и настроишь сбор данных, которые она использует.

Результат

Записывай метрики каждого кадра в JSON, проверяй дрейф с помощью теста KS относительно исходного уровня при запуске, валидируй отложенную выборку и настраивай оповещения, каждое из которых связано с конкретным действием.

Ускоренное прохождение
Если ты уже освоился, вот краткая версия.
  1. Для каждого кадра: камера, r.speed["inference"], число детекций, средняя уверенность, исходные значения уверенности.

  2. Сохрани значения уверенности при запуске в качестве исходного уровня; ежедневно сравнивай с scipy.stats.ks_2samp.

  3. Еженедельно проверяй размеченную отложенную выборку с помощью yolo val; настраивай оповещение при падении более чем на 1 пункт mAP.

  4. Настраивай оповещения только по сигналам с описанным последующим действием; всё остальное должно оставаться на панели мониторинга.

Практические занятия

Три уровня сигналов#

УровеньВопросСигналы
ИнфраструктураСервис работает и успевает обрабатывать данные?Задержка, пропускная способность, потерянные кадры, загрузка CPU и GPU
Поведение моделиМодель ведёт себя так же, как на прошлой неделе?Детекции на кадр, средняя уверенность, распределение классов, размеры боксов
Точность моделиМодель по-прежнему выдаёт правильные результаты?mAP на отложенной выборке (метрики производительности), выборочная проверка человеком, доля ложных срабатываний

Большинство команд покрывают первый уровень стандартными инструментами и на этом останавливаются. Именно на втором и третьем уровнях системы компьютерного зрения незаметно дают сбои; руководство по мониторингу модели описывает полный цикл обслуживания.

Страница развёртывания Ultralytics Platform на вкладке Logs: над строками запросов INFO с временными метками для мониторинга и тестовых вызовов расположены фильтры Errors и All

Если ты развёртываешь модель через Ultralytics Platform, на странице развёртывания доступны метрики уровня инфраструктуры (запросы, частота ошибок, задержка P95) и журналы запросов, в которых можно отфильтровать ошибки, как показано выше; они рассматриваются в уроке «Мониторинг работоспособности и точности». Уровни поведения и точности тебе предстоит реализовать самостоятельно — этим и займёмся в оставшейся части урока.

Записываем метрики для каждого кадра#

Каждый объект Results содержит данные о времени обработки и боксах. Записывай по одной строке JSON на каждый кадр и отправляй её в используемую тобой систему метрик:

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()],  # исходные значения для проверки дрейфа
        }
        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}

Для чего нужно каждое поле:

  • inference_ms: отправляй оповещение, если p95 превышает заданный бюджет.
  • detections: резкое падение почти до нуля обычно означает, что камера заблокирована или неисправна; всплеск указывает на смену сцены или ложные срабатывания.
  • mean_conf: удобная для быстрого просмотра линия тренда, но сама по себе она слишком грубая, чтобы выявлять дрейф.
  • confs: полное распределение, необходимое для теста на дрейф ниже.

Проверка дрейфа распределений confidence#

Дрейф данных проявляется в значениях confidence задолго до того, как кто-либо разметит новые данные. При запуске собери значения confidence для базового уровня, затем собери их за недавний период и сравни два распределения с помощью двухвыборочного критерия Колмогорова — Смирнова (pip install scipy). Этот скрипт имитирует сумерки, затемняя тот же видеоклип:

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

Разберём всё по пунктам:

  • Та же сцена: идентичные распределения дают статистику KS, равную 0, и p = 1 — это контрольный результат.
  • Сумерки: на 39 детекций меньше, а среднее значение всего на 0,011 ниже — этого никто не заметил бы на графике. Статистика KS (наибольший разрыв между двумя кумулятивными распределениями) равна 0,064, а p = 0,0068 < 0,01 говорит о том, что сдвиг реален.

Оповещение о дрейфе — повод просмотреть примеры кадров, а не доказательство сбоя. Если дрейф подтвердился, собери и разметь кадры с затронутых камер: в руководстве по сбору и аннотации данных описано, что собирать, а в руководстве по дообучению — как выполнять повторное обучение. Размеры боксов (ширина × высота) дают ещё один простой сигнал дрейфа: именно в них в первую очередь проявляются изменения крепления и оптики.

Еженедельная проверка на отложенной выборке#

Сигналы поведения показывают, что что-то изменилось; только разметка покажет, снизилась ли точность. Храни отложенную выборку из 200–500 размеченных производственных изображений и проверяй каждую версию модели, а также выполняй проверку каждую неделю:

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

Записывай mAP50-95 и AP по классам с течением времени и отправляй оповещение, если mAP падает более чем на 1 пункт за неделю. Самая частая причина — замена модели, которая должна была ничего не изменить; вторая по частоте — дрейф. Матрица ошибок на той же отложенной выборке наглядно покажет регрессии по классам.

Качество модели с течением времени: качество остается стабильным, пока поступают новые размеченные данные, а затем снижается после ожидаемого момента спада; вопрос в том, переобучать модель сейчас или подождать

Именно отложенная выборка помогает ответить на вопрос на этом графике. Переобучай модель, когда выборка показывает, что качество начинает падать, или когда у тебя накопится достаточно новых размеченных данных для условий, в которых возник дрейф, а не по фиксированному календарному графику.

Настраивай оповещения по действиям, а не по числам#

СигналУсловие срабатыванияДействие
p95 inference_msПревышение бюджета в течение 3 минут или дольшеУвеличь число экземпляров или переключись на оптимизированный экспорт
Детекции на кадрПадение на 50% или больше в течение 5 минутПроверь видеопоток с камеры, затем версию модели
Пропущенные кадрыБолее 5% в течение 10 минутДобавь вычислительные ресурсы или снизь FPS для каждого потока
Тест KS для confidencep < 0,01 на суточных интервалахПросмотри кадры, затем запусти проверку на отложенной выборке
mAP50-95 на отложенной выборкеПадение на 1 пункт или больше за неделюСобери данные, разметь их и выполни дообучение

Подводные камни#

Сравнение несопоставимых временных окон

Симптом: проверка KS срабатывает каждый вечер и каждый понедельник. Решение: суточные и недельные циклы действительно меняют распределение. Сравнивай сопоставимые данные (те же камеры, часы и дни недели) и используй окна в несколько тысяч детекций.

Устаревшая отложенная выборка

Симптом: mAP на отложенной выборке выглядит нормально, но пользователи сообщают о пропусках. Решение: отложенная выборка должна отражать текущую рабочую среду. Обновляй её каждый квартал новыми размеченными кадрами с важных камер.

Если никто не будет предпринимать никаких действий, не отправляй оповещение

Для каждого оповещения нужно письменно указать следующее действие. Сигнал, за которым ты лишь хочешь наблюдать, должен быть на панели мониторинга, а не в системе оповещений.

Попробовать

Добавь запись JSON в свой конвейер и запусти его на час (или на длинном видео). Сохрани первое окно как baseline.jsonl, затем проверь решение на KS-тесте, сравнив его со вторым окном: выбери другое время суток, другую камеру или затемнённую копию. Предоставь статистику KS и p-value, а также таблицу оповещений, где для каждой строки указано действие.

Критерии готовности
Урок считается завершённым, если выполнены все условия ниже.
  • Твой конвейер записывает одну строку JSON на каждый кадр: задержку, число детекций, среднее значение confidence и исходные значения confidence.

  • Ты провёл KS-тест между базовым и недавним окном и записал статистику и p-value.

  • Для каждого заданного тобой оповещения определены порог, длительность и письменное дальнейшее действие.

Показать решение

Сохрани первое здоровое окно вывода monitor.py как baseline.jsonl, затем сравнивай с ним каждое новое окно:

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

Это ожидаемый результат, если оба файла получены из одного и того же видео. Запускай проверку ежедневно по расписанию, отправляй статистику и p-value в ту же систему метрик, что и логи по кадрам, а review frames направляй человеку, а не в систему пейджинга.

Что дальше

В последнем уроке разберём, какой рычаг задействовать первым, когда панели мониторинга показывают, что задержка или стоимость превышает бюджет.

Войди, чтобы сохранять прогресс