Мы привыкли: упали метрики — смотрим PSI или KS — видим дрифт — идем переобучать модель. Но сам дрифт не говорит, почему модель реагирует. В production-рекомендательных системах с high-cardinality признаками статистические тесты часто дают ложные срабатывания или пропускают реальные сдвиги, скрытые за мультимодальностью. Типичная ошибка: считать дрифт по одному признаку без учета влияния на выход модели.
Контрфактуальные сэмплы как детектор влияния
Идея: вместо сравнения распределений синтезировать контрфактуальные сэмплы для каждого признака. Вы обучаете генеративную модель (VAE или диффузионную) на референсном distribution A и генерируете сэмплы, которые были бы реальны без дрифта. Затем сравниваете предсказания на реальных данных и на синтетических контрфактуалах. Если разница значимая — дрифт подтвержден, и вы фиксируете его влияние на выход модели, а не просто сдвиг распределения.
Пример на production-рекомендательной системе: модель обучена на distribution A, на новых данных (distribution B) AUC падает на 5%. Для признака "время сессии" вы генерируете VAE на distribution A, получаете контрфактуальные сэмплы и считаете drift_score как |pred_real — pred_cf|. При score = 0.7 вы знаете, что в пиковые часы рекомендации стали нерелевантны. Решение: не переобучать всю модель, а изменить препроцессинг (например, бинаризовать временной интервал) или обновить политику отбора кандидатов.
generator = VAE(input_dim=1, latent_dim=8)
generator.fit(X_train['age'])
cf_age = generator.sample(n=1000)
pred_real = model.predict(X_test['age'])
pred_cf = model.predict(cf_age)
drift_score = np.abs(pred_real - pred_cf).mean()
if drift_score > epsilon: print("дрифт по возрасту")
Инженерные trade-offs и типичные ошибки
Плюсы подхода: не привязан к типу модели (GBDT, нейросети, правила — все равно), работает с мультимодальными распределениями, дает actionable insights — "дрифт по признаку X из-за смещения на Y% влево". Но есть и ограничения: генеративная модель требует референсных данных без дрифта и может сама дрифтовать (проблема сдвига генерации). Типичная ошибка: использовать простые VAE для признаков с резкими выбросами — синтезированные сэмплы будут сглаживать хвосты. Практический совет: для high-cardinality признаков (user_id, item_id) используйте conditional VAE с embedding, чтобы сохранить индивидуальность категорий.
Где это особенно полезно: сценарии с частым ретренингом (daily/hourly), где нужен не просто детектор, а первопричина для бизнес-объяснения. Например, в рекомендательной системе с дневным обновлением модель может падать 3 дня подряд — контрфактуальные сэмплы покажут, какой признак (категория контента или время) отвечает за деградацию, а не заставляют искать "модель устарела".
Вывод: Контрфактуальные сэмплы превращают дрифт из статистической метрики в интерпретируемый сигнал с actionable инсайтами, критично для инженеров, которые хотят не тушить пожары, а понимать, что менять в пайплайне.