← Назад к вопросам
Что делал при сильной дисбалансе классов?
2.3 Middle🔥 171 комментариев
#Машинное обучение
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Проблема дисбаланса
Дисбаланс классов — когда количество примеров одного класса намного больше другого. Модель предсказывает всегда класс большинства и получает высокую accuracy, но не работает на меньшинстве.
Решения (от простых к сложным)
1. Выбор правильной метрики (Самое важное!)
from sklearn.metrics import precision, recall, f1_score, roc_auc_score
# НЕПРАВИЛЬНО для дисбаланса
accuracy = (TP + TN) / (TP + TN + FP + FN) # 99% даже если не работает!
# ПРАВИЛЬНО для дисбаланса
precision = TP / (TP + FP)
recall = TP / (TP + FN)
f1 = 2 * (precision * recall) / (precision + recall)
roc_auc = roc_auc_score(y_true, y_pred_proba)
scores = cross_validate(
model, X, y,
scoring=['precision', 'recall', 'f1', 'roc_auc'],
cv=5
)
2. Переуменьшение/передискретизация (SMOTE)
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline
# SMOTE: синтетическое увеличение меньшинства
smote = SMOTE(sampling_strategy=0.5)
X_smote, y_smote = smote.fit_resample(X_train, y_train)
# ВАЖНО: Применяй только на train, не на test!
pipeline = Pipeline([
('smote', SMOTE(sampling_strategy=0.5)),
('classifier', LogisticRegression())
])
pipeline.fit(X_train, y_train) # SMOTE на train
y_pred = pipeline.predict(X_test) # На test не применяется
3. Регулировка весов классов
from sklearn.linear_model import LogisticRegression
# Способ 1: Вручную
weights = {0: 1, 1: 10} # Класс 1 в 10 раз важнее
model = LogisticRegression(class_weight=weights)
# Способ 2: Автоматически (balanced)
model = LogisticRegression(class_weight='balanced')
# Способ 3: XGBoost с scale_pos_weight
ratio = (y == 0).sum() / (y == 1).sum()
model = xgb.XGBClassifier(scale_pos_weight=ratio)
4. Пороговая оптимизация
from sklearn.metrics import precision_recall_curve
y_pred_proba = model.predict_proba(X_test)[:, 1]
precision, recall, thresholds = precision_recall_curve(y_test, y_pred_proba)
f1_scores = 2 * (precision * recall) / (precision + recall + 1e-10)
optimal_idx = np.argmax(f1_scores)
optimal_threshold = thresholds[optimal_idx]
y_pred_optimized = (y_pred_proba >= optimal_threshold).astype(int)
5. Ensemble методы
from sklearn.ensemble import BaggingClassifier
import xgboost as xgb
# Bagging
bagging = BaggingClassifier(n_estimators=100)
# XGBoost (хорошо с дисбалансом)
xgb_model = xgb.XGBClassifier(
scale_pos_weight=10,
eval_metric='logloss'
)
6. Для Deep Learning
import tensorflow as tf
from sklearn.utils.class_weight import compute_class_weight
class_weights = compute_class_weight(
'balanced',
classes=np.unique(y_train),
y=y_train
)
class_weight_dict = {0: class_weights[0], 1: class_weights[1]}
model.fit(
X_train, y_train,
class_weight=class_weight_dict,
epochs=50
)
Моя стратегия на практике
- Выбираю правильные метрики первым делом — F1, ROC-AUC, PR-AUC
- Пробую SMOTE + класс-веса — обычно хорошо работает
- Если не помогает → пробую ансамбли (XGBoost, Random Forest)
- Проверяю recall vs precision — выбираю threshold по business требованиям
- Высокий recall: найти максимум positives (fraud, болезни)
- Высокий precision: быть уверенным в positives (рекомендации)
from imblearn.pipeline import Pipeline
from imblearn.over_sampling import SMOTE
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
pipeline = Pipeline([
('scaler', StandardScaler()),
('smote', SMOTE(sampling_strategy=0.5)),
('classifier', LogisticRegression(class_weight='balanced'))
])
scores = cross_validate(
pipeline, X, y,
scoring=['f1', 'roc_auc', 'precision', 'recall'],
cv=5
)
print(f"F1: {scores['test_f1'].mean():.3f}")
print(f"ROC-AUC: {scores['test_roc_auc'].mean():.3f}")