Модель CatBoost даёт 0.94 ROC-AUC на кросс-валидации и 0.78 в проде — найдите причину.
Модель фрода даёт 0.94 ROC-AUC на кросс-валидации и 0.78 на первой неделе в проде при том же списке признаков.
Ограничения:
orders.csv— лог событий за 18 месяцев со столбцомevent_time.- У
merchant_idоколо 40 000 различных значений. - В проде каждый заказ скорится один раз, в момент поступления.
import pandas as pd
from catboost import CatBoostClassifier
from sklearn.model_selection import cross_val_score
df = pd.read_csv("orders.csv")
y = df.pop("is_fraud")
df["merchant_te"] = y.groupby(df["merchant_id"]).transform("mean")
df = df.drop(columns=["merchant_id", "event_time"])
model = CatBoostClassifier(iterations=2000, verbose=0)
print(cross_val_score(model, df, y, cv=5, scoring="roc_auc").mean()) # 0.94
Найдите дефект и почините оценку качества.
Среднее по мерчанту считается по всем меткам до разбиения, поэтому кодировка каждого фолда содержит его собственные таргеты — это утечка, а не переобучение на CV. Передайте merchant_id в cat_features, чтобы его кодировали ordered statistics, и валидируйте по времени.
- ✗Списывают всё на переобучение CV, хотя кодировщик видел метки всех строк
- ✗Считают, что ordered statistics CatBoost защищают самодельные числовые кодировки
- ✗Валидируют упорядоченные по времени события случайным K-fold
- →Как посчитать target encoding без утечки вручную?
- →Какая схема валидации подходит для лога событий за 18 месяцев?
merchant_te считается по всему столбцу y до всякого разбиения, поэтому метка каждой строки уже сидит в её собственном признаке. Любой фолд кросс-валидации оценивается на данных, чьи таргеты в него протекли, — отсюда 0.94. В проде такого признака построить нельзя: метка ещё неизвестна. Плюс случайный cv=5 перемешивает 18 месяцев событий и позволяет учиться на будущем.
Лечение — отдать категорию самому CatBoost (его ordered target statistics кодируют строку только по предшествующим ей строкам перестановки) и валидировать разбиением по времени.
import pandas as pd
from catboost import CatBoostClassifier
from sklearn.metrics import roc_auc_score
df = pd.read_csv("orders.csv").sort_values("event_time")
y = df.pop("is_fraud")
cut = int(len(df) * 0.8)
X = df.drop(columns=["event_time"])
X_tr, X_va = X.iloc[:cut], X.iloc[cut:]
y_tr, y_va = y.iloc[:cut], y.iloc[cut:]
model = CatBoostClassifier(iterations=2000, early_stopping_rounds=100, verbose=0)
model.fit(X_tr, y_tr, cat_features=["merchant_id"], eval_set=(X_va, y_va))
print(roc_auc_score(y_va, model.predict_proba(X_va)[:, 1]))
Полученный AUC близок к продовому, потому что схема оценки наконец повторяет продовые условия.