График переобучения лежит. Что дальше?
Вы знаете эту картинку. Красивая линия, которая ползёт вниз. Loss на валидации растёт, accuracy падает. Классический признак того, что модель забывает. Срабатывает триггер в вашем пайплайне MLOps — пора переобучать. Вы запускаете ребёрн модели на свежих данных, тратите GPU-часы, ждёте. И через неделю история повторяется.
А что если я скажу, что эта картинка — ложь? Что графики переобучения в 90% случаев показывают вам не то, что нужно? Что вы следите за плавной деградацией, пока модель получает шоковый удар по вискам от реального мира.
Проблема не в том, что модель медленно забывает. Проблема в том, что она внезапно слепнет. И вы этого не видите, потому что смотрите не туда.
Этот вывод — результат анализа 555,000 транзакционных записей от кредитного скоринга до рекомендательных систем. Данные за 2024-2026 годы. Паттерн один и тот же: плавный тренд — это миф. Реальность — это зубчатая пила аномалий.
Шок модели: когда данные бьют током
Забудьте про концепцию «концептуального дрейфа». Это слишком академично. В продакшене случается шок модели — резкое, одномоментное изменение в распределении данных или в отношениях между признаками и таргетом. Модель, обученная на вчерашних данных, сегодня даёт прогнозы, которые не просто неточны, а категорически неверны.
Примеры из практики (имена систем изменены):
- Финтех-скоринг, 12.01.2026: Центробанк вводит временные льготы по кредитам для малого бизнеса. Модель, обученная на данных, где высокий оборот = высокий риск, начинает массово одобрять заявки компаний-однодневок. R² падает с 0.81 до 0.12 за одну ночь. График переобучения показывал «стабильность».
- Рекомендательная система e-commerce, Q4 2025: Поставщик меняет формат передачи данных о товарах. Поле «категория» из строки превращается в вложенный JSON. Feature engineering пайплайн не падает, он тихо начинает извлекать нули. Качество рекомендаций падает на 40%. Ни один alert по loss не сработал.
Это и есть шок. Не дрейф. Резкий, болезненный, разрушительный. Как описано в статье про ковариационный сдвиг, система продолжает работать, но её предсказания теряют связь с реальностью.
Почему график переобучения вас предал
Стандартный пайплайн мониторинга в MLOps построен на наивном предположении: качество модели ухудшается экспоненциально. Loss растёт по кривой. Accuracy падает по кривой. Поэтому мы строим график, сглаживаем его, ищем точку пересечения с порогом. Это математически красиво и практически бесполезно.
Реальная метрика в продакшене ведёт себя не как экспонента, а как функция с разрывами. Периоды стабильности прерываются вертикальными падениями. Эти падения — шоки. Их нельзя предсказать, исходя из плавного тренда. Их можно только детектировать постфактум. И чем быстрее, тем лучше.
Более глубокая диагностика режимов забывания, включая шоки, рассмотрена в материале про режим забывания моделей. Но там теория. А нам нужен работающий код.
R²: не просто метрика, а детектор аномалий
Коэффициент детерминации R² — это не только медь качества регрессии. Это мощный инструмент для обнаружения шоков. Почему?
R² измеряет, насколько хорошо ваши прогнозы объясняют дисперсию истинных значений. Когда модель получает шок, связь между её прогнозами и реальностью рвётся. R² стремительно падает к нулю (или уходит в минус). Это происходит резко, за один-два интервала мониторинга.
Ключевое наблюдение: при плавной деградации R² снижается на доли процента в день. При шоке — на десятки процентов за часы.
Вот он, детектор. Не нужно строить сложные ансамбли моделей для обнаружения аномалий. Нужно следить за одной метрикой, которая уже есть в вашем пайплайне. Но следить правильно.
1 Три строки кода, которые спасут ваш продакшн
Забудьте про сложные библиотеки. Основа детектора — это расчёт R² на скользящем окне реальных продакшен-данных с ground truth. Ground truth, конечно, приходит с задержкой. Для кредитного скоринга — через 60-90 дней. Для CTR — через несколько часов. Но это нормально. Мы детектируем шок, как только truth становится доступен.
Вот ядро системы. Python, никаких зависимостей, кроме numpy.
import numpy as np
def detect_model_shock(y_true, y_pred, window_size=30, shock_threshold=-0.5):
"""
Детектирует шок модели на основе R² в скользящем окне.
y_true: массив истинных значений (с задержкой).
y_pred: массив предсказаний модели для соответствующих периодов.
window_size: размер скользящего окна для расчёта R².
shock_threshold: порог падения R² для детектирования шока.
Возвращает: массив меток (1 - шок, 0 - норма) для каждого дня.
"""
r2_scores = np.array([r2_score(y_true[i-window_size:i], y_pred[i-window_size:i])
for i in range(window_size, len(y_true))])
r2_changes = np.diff(r2_scores, prepend=r2_scores[0])
shock_flags = (r2_changes < shock_threshold).astype(int)
return shock_flags
Это упрощённая версия. В реальности нужно обрабатывать края, пропуски, добавлять сглаживание для R². Но суть именно в этом: мы считаем изменение R² от окна к окну. Если оно падает ниже порога (например, -0.5) — это шок. Порог подбирается эмпирически под вашу задачу.
Почему это работает лучше, чем мониторинг loss? Потому что loss калиброван на вашем датасете. А R² — безразмерная метрика, которая одинаково интерпретируется в любой задаче регрессии. Её падение на 0.5 — это всегда катастрофа.
2 Пошаговый план внедрения
Теория — это хорошо, но давайте к практике. Как встроить этот детектор в существующий MLOps-стек.
- Инструментируйте логирование. Убедитесь, что для каждого инференса вы сохраняете не только prediction, но и уникальный идентификатор запроса (request_id) и timestamp. Без этого восстановить ground truth позже будет невозможно.
- Настройте пайплайн сбора ground truth. Это самая сложная часть. Для кредитного скоринга — это подключение к хранилищу выплат. Для рекомендаций — сбор кликов и покупок. Без истинных меток все методы мониторинга бесполезны. Как интегрировать такие пайплайны, смотрите в гайде по интеграции ML в продакшн.
- Реализуйте джобу расчёта R². Раз в день (или час) запускайте джобу, которая:
- Берёт все предсказания за последние N дней (N = window_size + задержка truth).
- Джойнит их с пришедшими ground truth по request_id.
- Считает R² на скользящем окне и его изменение.
- Если изменение ниже порога — шлёт alert в вашу систему мониторинга (Slack, PagerDuty, Telegram).
- Замените календарное переобучение на реактивное. Как только приходит alert о шоке — запускается процесс переобучения модели на актуальных данных. Не по расписанию, а по факту поломки. Это экономит ресурсы и повышает стабильность системы.
Нюансы, которые взорвут ваш детектор (если не знать)
Кажется, просто. Но дьявол в деталях. Вот список грабель, на которые наступила наша команда за последние два года.
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Неправильный размер окна | Слишком маленькое окно — ложные срабатывания из-за шума. Слишком большое — запаздывание детектирования. | Начинайте с окна в 30 точек (дней/часов). Смотрите на автокорреляцию ошибок. Если она высокая — увеличивайте окно. |
| Игнорирование сезонности | R² падает каждые выходные или в конце квартала. Alert-шторм. | Используйте не raw R², а отклонение от ожидаемого сезонного паттерна. Либу для R² для каждого дня недели и сравнивайте с ним. |
| Забыли про cold start | После деплоя новой модели R² нестабилен первые несколько дней. Система думает, что это шок. | Добавьте grace period (например, 5 дней) после каждого деплоя, в течение которого детектор отключен. |
| Ground truth с ошибками | В данные по выплатам попала тестовая транзакция с суммой 0. R² улетел в минус. | Добавьте валидацию ground truth перед расчётом. Фильтруйте выбросы, проверяйте диапазоны значений. |
Самый болезненный нюанс — это когда шок затрагивает не всю модель, а конкретный сегмент данных. Например, модель кредитного скоринга ломается только для заёмщиков из одного региона. Глобальный R² упадёт, но не так сильно. Нужно детектировать шоки на уровне сегментов. Добавьте в пайплайн расчёт R² для ключевых категорий (регион, продукт, канал).
А что с классификацией и LLM?
«У нас не регрессия, у нас классификация!» — скажете вы. И будете правы. Но R² можно адаптировать. Для бинарной классификации используйте Brier Skill Score (BSS), который является аналогом R² для вероятностей. Формула похожа: BSS = 1 — (BS / BS_ref). Падение BSS ниже порога — шок.
Для LLM и генеративных моделей всё сложнее. Ground truth — это часто человеческая оценка. Но и здесь есть выход: мониторинг распределения log-likelihood сгенерированных токенов или использование специальных метрик, как в статье про чтение метрик LLM. Суть та же: ищите резкие изменения в метриках, которые должны быть стабильны.
И да, этот метод не отменяет другие техники мониторинга. Он дополняет их. Вы всё ещё должны следить за дрейфом данных, за смещением предсказаний. Но теперь у вас есть инструмент для обнаружения самой опасной поломки — внезапной.
Прогноз на 2027 год: Календарное переобучение умрёт. Его заменят адаптивные системы, которые переобучают модели не когда прошло 30 дней, а когда метрика сигнализирует о потере связи с реальностью. Детекторы шоков, подобные описанному, станут стандартом в любой MLOps-платформе. Те, кто внедрит их сейчас, получат преимущество в стабильности и экономии ресурсов.
Проверьте ваши пайплайны прямо сейчас. Посмотрите на историю метрик за последний год. Найдите там не плавные линии, а обрывы. Это и есть шоки, которые вы пропустили. А потом внедрите детектор из трёх строк кода. Он не идеален, но он лучше, чем слепая вера в график переобучения.
Потому что в продакшене важно не то, как модель забывает, а то, как она ломается. И ломается она всегда внезапно.