Условие
Антифрод: 50 млн транзакций в обучении, 0.3% — мошенничество. Какие подходы есть к работе с дисбалансом? Что не сработает / даст leakage? Дайте план эксперимента, который реально применим в проде.
Решение
Подходы
1. Class weights / sample weights
from sklearn.linear_model import LogisticRegression
LogisticRegression(class_weight='balanced') # 1/(n_classes * np.bincount(y))В LightGBM/XGBoost: scale_pos_weight = neg / pos. CatBoost: class_weights=[1, neg/pos].
Плюсы: ничего не делает с данными. Минусы: мало эффективно при extreme imbalance (1:1000+).
2. Undersampling большинства
Случайно выкинуть 95% «нормальных» транзакций.
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(sampling_strategy=0.1, random_state=0)
X_res, y_res = rus.fit_resample(X, y)Плюсы: быстрее обучение. Минусы: теряем сигнал из большинства; ROC-AUC может немного просесть.
3. Oversampling меньшинства (SMOTE)
Синтезирует новые «положительные» точки по интерполяции.
from imblearn.over_sampling import SMOTE
sm = SMOTE(sampling_strategy=0.1, k_neighbors=5, random_state=0)
X_res, y_res = sm.fit_resample(X, y)Плюсы: больше «положительных». Минусы: на табличных табличных данных с категориальными фичами SMOTE портит распределение (категории интерполировать нельзя).
4. SMOTENC / SMOTEENN / ADASYN — варианты для категориальных и с очисткой.
5. Threshold tuning
Оставить дисбаланс как есть, обучить модель, выбрать threshold под бизнес-цель:
# fix Precision >= 0.9
p, r, thr = precision_recall_curve(y_true, y_scores)
best = (p >= 0.9) & (r > 0)
thr_best = thr[best][np.argmax(r[best])]6. Cost-sensitive learning
Заменить cross-entropy на focal loss или асимметричную:
L = α (1 - p)^γ · log(p)
В XGBoost — кастомная loss. В Catboost — нет встроенной, можно через class_weights и auto_class_weights.
7. Anomaly detection (Isolation Forest, autoencoder) — если положительных вообще единицы.
Чего избегать
- SMOTE до сплита train/val — leakage: синтетические точки попадут и в val.
- Балансировка test — никогда. Метрики становятся бессмысленными.
- Accuracy — бесполезна (предсказание «всё нормально» = 99.7%).
- ROC-AUC как единственная метрика — может быть высоким при бесполезной модели на дисбалансе. PR-AUC честнее.
План эксперимента
- Baseline без балансировки: LightGBM + ROC-AUC + PR-AUC на чистом сплите (time-based).
- Class weights (
scale_pos_weight = n_neg / n_pos) — почти бесплатное улучшение. - Undersampling 1:10 — быстрее обучение, проверить, не упало ли PR-AUC.
- Threshold tuning под бизнес-цель (например, Precision ≥ 0.8 при максимуме Recall).
- Out-of-Time validation на последних 30 днях — обязательно для antifraud (дрейф паттернов).
- A/B на проде: новая модель vs текущая, замерять % одобрений и FN-стоимость.
Метрики для отчёта
from sklearn.metrics import (roc_auc_score, average_precision_score,
precision_recall_curve, fbeta_score)
print("ROC-AUC :", roc_auc_score(y_te, p))
print("PR-AUC :", average_precision_score(y_te, p))
print("F2 :", fbeta_score(y_te, (p > 0.5).astype(int), beta=2)) # больше веса recallПодводные камни
- SMOTE до сплита — главная ошибка. Делайте
fit_resampleтолько на train. scale_pos_weightискажает калибровку: предсказанные вероятности перестают быть «реальными». Если калибровка нужна —CalibratedClassifierCVпосле.- Undersampling теряет сигнал: в большинстве могут быть редкие, но информативные паттерны.
- SMOTE для категориальных не работает — нужен SMOTENC.
- OOT валидация для antifraud обязательна — паттерны меняются за дни.
- Метрика обучения ≠ бизнес-метрика: AUC может расти, а потери — тоже (если FN дорогой).
- Class imbalance ≠ всегда проблема: если модель и так выделяет минорный класс — не лезьте.
Эталонный ответ
При imbalance 1:300 порядок действий:
- Не балансировать сразу — попробовать class weights в бустинге.
- Использовать PR-AUC и Precision@K, не accuracy / ROC-AUC.
- Out-of-Time валидация (дрейф паттернов).
- Threshold tuning под бизнес-цель.
- Балансировка train (undersampling 1:10 или SMOTENC) — только если шаги 1-4 не помогли.
- Никогда не балансировать test / val.
- Считать деньги, а не только AUC — построить profit curve.