Концепт-дрейф
Иногда меняются не данные, а сам смысл задачи — и это куда коварнее.
Концепт-дрейф (concept drift) — изменение зависимости между признаками и целевой переменной: то же входное значение теперь соответствует другому ответу.
Дрейф данных против концепт-дрейфа
Это разные явления, и путать их опасно. Дрейф данных — меняется распределение входов P(X), но связь «вход → ответ» прежняя. Концепт-дрейф — меняется сама связь P(Y|X): входы могут выглядеть как раньше, но правильный ответ для них стал другим.
| Что меняется | Пример | |
| Дрейф данных | P(X) — распределение входов | приходят клиенты другого возраста |
| Концепт-дрейф | P(Y|X) — связь вход→ответ | в кризис «надёжный» профиль стал рисковым |
Классический пример концепт-дрейфа: спам-фильтр. Спамеры меняют тактику — те же слова, что раньше были безобидны, теперь признак спама. Сами письма по форме прежние (P(X) не изменилось), но метка для них перевернулась.
Виды по скорости
- Внезапный (sudden). Резкая смена — например, новый закон мгновенно меняет поведение.
- Постепенный (gradual). Старая и новая закономерности сосуществуют, новая вытесняет старую.
- Инкрементальный. Плавный непрерывный сдвиг.
- Сезонный/рекуррентный. Закономерность возвращается циклически (праздники, сезоны).
Почему концепт-дрейф коварнее
Дрейф данных видно без меток — по входам. Концепт-дрейф по входам не виден: P(X) может быть стабильным. Обнаружить его можно только по падению качества, а значит — нужны метки, которые запаздывают. Поэтому концепт-дрейф часто замечают позже, когда ущерб уже нанесён.
Демонстрация: смена правила
Смоделируем концепт-дрейф: фиксированная модель применяет старое правило, а истинная связь поменялась. Входы те же — качество падает.
# Модель выучила старое правило: класс = 1, если score > 50
def model_predict(score):
return 1 if score > 50 else 0
# Истинная связь СМЕНИЛАСЬ: теперь порог реальности = 70
def true_label(score):
return 1 if score > 70 else 0
scores = [40, 55, 60, 65, 75, 80, 45, 68]
correct = sum(1 for s in scores if model_predict(s) == true_label(s))
acc = correct / len(scores)
print("score | модель | истина | совпало")
for s in scores:
m, t = model_predict(s), true_label(s)
print(f" {s:3d} | {m} | {t} | {'да' if m==t else 'НЕТ'}")
print(f"Точность после концепт-дрейфа: {acc:.2f}")
Вывод:
score | модель | истина | совпало 40 | 0 | 0 | да 55 | 1 | 0 | НЕТ 60 | 1 | 0 | НЕТ 65 | 1 | 0 | НЕТ 75 | 1 | 1 | да 80 | 1 | 1 | да 45 | 0 | 0 | да 68 | 1 | 0 | НЕТ Точность после концепт-дрейфа: 0.50
Входные score те же, что модель видела при обучении, но истинная граница уехала с 50 на 70 — и точность рухнула до 0.50. Никакой дрейф входов этого бы не показал; виден только провал качества.
Что делать
- Переобучить на свежих размеченных данных, отражающих новую связь.
- Скользящее окно: учить на недавних данных, чтобы модель быстрее адаптировалась.
- Детекторы дрейфа по ошибкам (DDM, ADWIN): следят за ростом частоты ошибок и сигналят о смене концепта.
Как работает под капотом
Детекторы концепт-дрейфа работают по потоку ошибок модели: пока модель верна, частота ошибок низкая и стабильная; когда связь меняется, ошибки учащаются — детектор фиксирует статистически значимый рост и поднимает флаг. Это требует меток, поэтому концепт-дрейф ловят либо по запаздывающей обратной связи, либо по прокси-сигналам. Главное решение — переобучение на данных новой эпохи.
Частые ошибки
- Путать с дрейфом данных. Лечение разное: концепт-дрейф требует свежих меток и переобучения, а не просто наблюдения за входами.
- Полагаться только на мониторинг входов. Он не увидит концепт-дрейф при стабильном P(X).
- Игнорировать сезонность. Рекуррентный дрейф можно предвидеть и готовить модель заранее.
Итог
- Концепт-дрейф — смена связи P(Y|X): тот же вход теперь даёт другой правильный ответ.
- В отличие от дрейфа данных, он не виден по входам — только по падению качества, которое требует меток.
- Лечение — переобучение на данных новой эпохи; детекторы по ошибкам (DDM, ADWIN) помогают поймать смену концепта.