Class imbalance на собеседовании Data Scientist
Карьерник — Duolingo для аналитиков: 10 минут в день тренируй SQL, Python, A/B, статистику, метрики и ещё 3 темы собеса. 1500+ вопросов в Telegram-боте. Бесплатно.
Содержание:
Почему imbalance это проблема
Fraud detection: 99.9% транзакций — нормальные, 0.1% — мошеннические. Модель «всё нормально» имеет accuracy 99.9% — и нулевую бизнес-ценность. На собесе классический вопрос: «Дано 99% класса 0 и 1% класса 1. Модель показывает accuracy 99% — это хорошо или плохо?» Правильный ответ: «Бесполезно, потому что тривиальная модель predict(0) даст ту же метрику. Нужны precision/recall на меньшем классе».
Главная боль без понимания imbalance — DS приносит на ревью модель fraud-детектора с accuracy 99.5%. Через неделю тимлид смотрит — recall на fraud классе 5%, продакт спрашивает «почему мы пропускаем мошенничество». Кандидат, который не различает accuracy и recall, на этом и теряет оффер.
Imbalance бывает при fraud, churn, redkih medical conditions, отказах оборудования, cold start recsys. Основной приём — нет «универсальной серебряной пули», есть набор инструментов.
Метрики при дисбалансе
Accuracy не подходит. Модель predict(majority_class) даёт высокую accuracy, нулевую полезность.
Что использовать:
- Precision — из тех, кого назвали fraud, сколько реально fraud
- Recall — из всех реальных fraud, сколько поймали
- F1 — гармоническое среднее P и R; равноправие precision и recall
- F-beta (
F2,F0.5) — если recall важнее precision (F2) или наоборот - PR-AUC — площадь под precision-recall кривой; лучше показывает качество на дисбалансе, чем ROC-AUC
- ROC-AUC — устойчиво к дисбалансу, но на сильном дисбалансе может быть оптимистичным (большая часть FP не сказывается)
- Cohen's kappa — accuracy с поправкой на случайное совпадение
Что использовать в зависимости от стоимости ошибки:
- Цена FP высока (ложно списать деньги клиенту) → следить за precision
- Цена FN высока (пропустить мошенничество) → следить за recall
- Симметрично или неизвестно → F1
Confusion matrix на тестовой выборке — обязательно смотреть. Не просто число, а распределение.
Resampling: oversample vs undersample
Oversample (дублирование minority) — повторять примеры minority класса, пока классы не сравняются.
from sklearn.utils import resample
df_minority_upsampled = resample(df_minority, replace=True, n_samples=len(df_majority))
df_balanced = pd.concat([df_majority, df_minority_upsampled])Минусы: модель видит одни и те же примеры много раз → переобучение на их особенностях.
Undersample (удаление majority) — выкинуть часть majority, пока классы не сравняются.
df_majority_downsampled = resample(df_majority, replace=False, n_samples=len(df_minority))
df_balanced = pd.concat([df_majority_downsampled, df_minority])Минусы: теряем данные. На больших датасетах это ОК, на малых — критично.
Когда применять: на тренировочной выборке, после split. Никогда — на тесте/валидации, иначе метрики не отражают реальное распределение.
# правильный порядок
X_train, X_test, y_train, y_test = train_test_split(X, y)
X_train_res, y_train_res = oversample(X_train, y_train) # только train
model.fit(X_train_res, y_train_res)
metrics = evaluate(model, X_test, y_test) # тест в исходных пропорцияхSMOTE и его варианты
SMOTE (Synthetic Minority Oversampling Technique) — генерация синтетических примеров minority класса. Для каждого minority-примера ищет k ближайших соседей того же класса и делает интерполяцию между ними.
from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state=42)
X_res, y_res = smote.fit_resample(X_train, y_train)Плюсы: не дублирует, добавляет «новые» точки в окрестности.
Минусы:
- Линейная интерполяция в feature-space — для категориальных признаков ломается
- На noisy minority-точках усиливает шум
Варианты:
- Borderline-SMOTE — синтетика только для minority-точек у границы классов
- SMOTE-NC — для смешанных категориальных и числовых признаков
- ADASYN — адаптивная плотность, больше синтетики там, где minority «трудный»
Tomek links / Edited Nearest Neighbors — undersampling с удалением «спорных» majority точек около границы.
SMOTE + Tomek — комбинация: oversample minority + чистка majority около границы. Часто даёт лучший результат, чем чистый SMOTE.
Class weights и cost-sensitive learning
Вместо изменения данных — изменить функцию потерь. Штраф за ошибку на minority-классе выше.
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(class_weight='balanced')
# 'balanced' = w_c = n_samples / (n_classes * count(c))balanced — автоматически: класс с 1% весит в 99 раз больше класса с 99%.
Кастомные веса: если стоимость FN в 10 раз выше FP — задать class_weight={0: 1, 1: 10}.
В XGBoost / LightGBM:
model = XGBClassifier(scale_pos_weight=ratio_neg_to_pos)В нейросетях:
loss = nn.CrossEntropyLoss(weight=torch.tensor([1.0, 10.0]))Преимущество: не меняем выборку, не теряем данные, не дублируем. Часто эффективнее SMOTE на табличных данных.
Focal loss и threshold tuning
Focal loss (Lin et al., RetinaNet) — модификация cross-entropy:
FL = -α · (1 - p_t)^γ · log(p_t)(1 - p_t)^γ — фокусирующий множитель: уменьшает loss для уверенно правильных предсказаний, усиливает для трудных. Используется в object detection и сильном дисбалансе.
γ=2 — типичное значение. γ=0 — обычный cross-entropy.
Threshold tuning. Бинарный классификатор обычно использует порог 0.5 для отнесения к классу 1. На дисбалансе оптимальный порог редко 0.5.
from sklearn.metrics import precision_recall_curve, f1_score
proba = model.predict_proba(X_val)[:, 1]
prec, rec, thr = precision_recall_curve(y_val, proba)
f1 = 2 * prec * rec / (prec + rec + 1e-9)
best_thr = thr[np.argmax(f1)]Тюнить порог отдельно от тренировки модели — почти всегда дешёвый и эффективный приём. Особенно если бизнес-метрика — не accuracy, а tradeoff precision/recall с конкретными весами.
Частые ошибки
Resampling до train/test split. Информация из теста утечёт в трейн через интерполяцию SMOTE. Resampling — только на train.
Только accuracy. На дисбалансе бесполезна. Минимум — precision, recall, confusion matrix, PR-AUC.
SMOTE на категориальных признаках без SMOTE-NC. Линейная интерполяция категорий даёт нонсенс.
Балансировать к 1:1, когда не нужно. Если в проде дисбаланс 99:1, а на трейне 50:50 — модель будет выдавать неправильные вероятности. Калибровать через CalibratedClassifierCV или предпочесть class weights.
Не смотреть на калибровку. Модель может разделять классы (хороший AUC), но вероятности «всё или ничего». Для бизнес-применения часто нужны калиброванные вероятности.
Не учитывать стоимость ошибки. Метрика без бизнес-веса — оптимизация впустую. Спросить продакта: «сколько стоит FP? сколько FN?»
Думать, что SMOTE «всегда лучше». На современных бустингах с scale_pos_weight или class_weight часто результат не хуже, без раздувания выборки.
Связанные темы
- Что такое precision и recall
- Что такое AUC-ROC
- Регуляризация L1 и L2 на собесе DS
- Подготовка к собесу Data Scientist
- Cross-validation простыми словами
FAQ
Когда нужен resampling, а когда class weights?
Class weights — первая попытка. Дешёво, не меняет данные. Если результат недостаточен — пробовать SMOTE/undersample. На больших датасетах undersample эффективен (модель не видит дубли). На малых — SMOTE.
Какой дисбаланс считается серьёзным?
Условно: 1:10 — лёгкий, 1:100 — серьёзный, 1:1000+ — экстремальный. Точная граница зависит от размера выборки и сложности задачи.
SMOTE для нейросети работает?
Хуже, чем для линейных моделей и деревьев. Нейросеть и сама может научиться представлению, но при сильном дисбалансе синтетика часто помогает или используют focal loss / class weights в loss.
Как балансировать на multi-class?
class_weight='balanced' или явные веса, SMOTE с sampling_strategy='auto'. Метрики — macro-F1 (среднее F1 по классам, не взвешенное на размер класса) или weighted-F1.
Threshold вместо resampling — вариант?
Да, часто достаточный. Тренируем модель как есть, потом на val подбираем порог под бизнес-метрику (max F1, или max recall при precision > X). Не меняем выборку — вероятности откалиброваны.
Это официальная информация?
Нет. Статья основана на работах по imbalanced learning (Chawla et al., 2002 для SMOTE, Lin et al. 2017 для focal loss) и документации imbalanced-learn / sklearn.
Тренируйте Data Science — откройте тренажёр с 1500+ вопросами для собесов.