MiddleДебаггингЧастоЕщё не отвечали
CV показывает 0.92, а прод 0.71 — какие каналы утечки объясняют этот разрыв?
Модель фрода даёт ROC-AUC 0.92 на кросс-валидации и 0.71 в проде.
Ограничения:
df— одна строка на транзакцию, упорядочены поtxn_at; у пользователя много транзакций.chargeback_atзаполняется только когда транзакцию позже оспорили.- Примерно 4% строк — точные повторы других строк.
df["days_to_chargeback"] = (df["chargeback_at"] - df["txn_at"]).dt.days
X = df.drop(columns=["is_fraud"])
y = df["is_fraud"]
scores = cross_val_score(model, X, y, cv=KFold(5, shuffle=True, random_state=0))
Назовите все присутствующие каналы утечки и способ устранения каждого.
Четыре канала: утечка цели из поля, заполняемого после исхода, загрязнение дублями строк в разных частях, групповая утечка при одном пользователе по обе стороны и временная утечка от перемешивания упорядоченных данных. Устранение канала роняет метрику.
- ✗Списывать на дрейф разрыв, который полностью объясняется признаком из будущего
- ✗Считать перемешанное деление безопасным на упорядоченных во времени данных
- ✗Доверять усреднённой метрике, пока дубли лежат в разных частях
- →Как вы упорядочите четыре канала по обычному размеру наносимого ущерба?
- →Какая проверка ловит признак из будущего ещё до обучения любой модели?
Разрыв объясняют четыре канала.
- Утечка цели.
days_to_chargebackсчитается изchargeback_at, которое заполняется только после спора. Для честных транзакций там пропуск, и модель читает метку напрямую. Признак нужно удалить или пересчитать на момент предсказания. - Загрязнение дублями. 4% повторов расходятся по разным частям, поэтому модель узнаёт уже виденную строку. Дедупликация до деления.
- Групповая утечка. У пользователя много транзакций, и
KFoldкладёт их по обе стороны. Нужны части по группеuser_id. - Временная утечка.
shuffle=Trueна упорядоченных данных даёт обучение на будущем. Нужно деление по времени.
df = df.drop(columns=["days_to_chargeback", "chargeback_at"]).drop_duplicates()
cv = GroupKFold(n_splits=5) # одна группа — одна часть
scores = cross_val_score(model, X, y, groups=df["user_id"], cv=cv)
# для честной оценки под прод — только вперёд по времени:
scores = cross_val_score(model, X, y, cv=TimeSeriesSplit(n_splits=5))
Проверка канала: устранить его в одиночку и убедиться, что метрика падает.