Как будешь решать проблему неравномерного распределения данных?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение проблемы неравномерного распределения данных (Imbalanced Data)
Неравномерное распределение (дисбаланс классов) одна из самых распространенных проблем в machine learning. Это когда один класс существенно преобладает над другими, что приводит к смещению модели в сторону мажоритарного класса.
Диагностика проблемы
Сначала нужно оценить степень дисбаланса:
import pandas as pd
from sklearn.utils import class_weight
# Проверяем распределение классов
print(y_train.value_counts())
print(y_train.value_counts(normalize=True))
# Пример: 95% класс 0, 5% класс 1 (серьёзный дисбаланс)
# 0 9500
# 1 500
Стратегии решения
1. Взвешивание классов (Class Weights)
Это самый простой и часто эффективный способ. Модель штрафуется больше за ошибки на меньшинстве:
from sklearn.utils.class_weight import compute_class_weight
from sklearn.ensemble import RandomForestClassifier
# Автоматический расчёт весов
class_weights = compute_class_weight(
class_weight=balanced,
classes=np.unique(y_train),
y=y_train
)
# Использование с Random Forest
model = RandomForestClassifier(
class_weight=balanced, # или dict с явными весами
random_state=42
)
model.fit(X_train, y_train)
# С логистической регрессией
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(class_weight=balanced)
model.fit(X_train, y_train)
# С нейросетью
import tensorflow as tf
class_weights = {0: 1.0, 1: 19.0} # Класс 1 в 19 раз менее частый
model.fit(X_train, y_train,
class_weight=class_weights,
epochs=10)
2. Передискретизация (Resampling)
Модифицируем обучающий набор данных. Есть два подхода:
Oversampling (увеличение меньшинства):
from imblearn.over_sampling import RandomOverSampler, SMOTE
# Простой случайный oversampling
over_sampler = RandomOverSampler(random_state=42)
X_resampled, y_resampled = over_sampler.fit_resample(X_train, y_train)
# SMOTE (Synthetic Minority Over-sampling Technique) - создаёт синтетические примеры
smote = SMOTE(random_state=42, k_neighbors=5)
X_resampled, y_resampled = smote.fit_resample(X_train, y_train)
model = RandomForestClassifier()
model.fit(X_resampled, y_resampled)
Undersampling (уменьшение большинства):
from imblearn.under_sampling import RandomUnderSampler
under_sampler = RandomUnderSampler(random_state=42)
X_resampled, y_resampled = under_sampler.fit_resample(X_train, y_train)
model = RandomForestClassifier()
model.fit(X_resampled, y_resampled)
Комбинированный подход (SMOTE + Tomek Links):
from imblearn.pipeline import Pipeline
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import TomekLinks
pipeline = Pipeline([
(smote, SMOTE(random_state=42)),
(tomek, TomekLinks()),
(clf, RandomForestClassifier())
])
pipeline.fit(X_train, y_train)
3. Выбор правильных метрик оценки
Для дисбалансированных данных accuracy НЕПРАВИЛЬНАЯ метрика. Используй:
from sklearn.metrics import (
precision_recall_curve,
f1_score,
roc_auc_score,
confusion_matrix,
balanced_accuracy_score
)
# Плохо: accuracy не отражает качество
accuracy = (TP + TN) / (TP + TN + FP + FN)
# На дисбалансе может быть 95%, если просто предсказывать мажоритарный класс
# Хорошо: F1-score сбалансирован
f1 = model.score(X_test, y_test, scoring=f1)
# Хорошо: ROC-AUC не зависит от баланса
roc_auc = roc_auc_score(y_test, y_pred_proba)
# Хорошо: Precision и Recall
from sklearn.metrics import precision_score, recall_score
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
# Хорошо: Balanced Accuracy
balanced_acc = balanced_accuracy_score(y_test, y_pred)
4. Пороги классификации (Threshold Tuning)
По умолчанию порог = 0.5. Для дисбалансированных данных его можно изменить:
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)
# Пороги, балансирующие precision и recall
f1_scores = 2 * (precision * recall) / (precision + recall + 1e-10)
best_threshold = thresholds[np.argmax(f1_scores[:-1])]
# Используем новый порог
y_pred = (y_pred_proba > best_threshold).astype(int)
5. Использование ensemble методов
Некоторые методы более устойчивы к дисбалансу:
from imblearn.ensemble import (
BalancedRandomForestClassifier,
EasyEnsembleClassifier
)
# Специальный Random Forest для дисбалансированных данных
model = BalancedRandomForestClassifier(
n_estimators=100,
random_state=42
)
model.fit(X_train, y_train)
# EasyEnsemble комбинирует undersampling с ensemble
model = EasyEnsembleClassifier(
n_estimators=10,
random_state=42
)
model.fit(X_train, y_train)
Сравнительная таблица методов
| Метод | Скорость | Эффективность | Когда использовать |
|---|---|---|---|
| Class weights | Быстро | Хорошая | Первый выбор, простые модели |
| SMOTE | Средне | Отличная | Умеренный дисбаланс (1:10) |
| Undersampling | Быстро | Хорошая | Много данных, сильный дисбаланс |
| Threshold tuning | Быстро | Хорошая | Уже есть обученная модель |
| Balanced ensemble | Средне | Отличная | Экстремальный дисбаланс (1:1000) |
Рекомендуемый pipeline
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from imblearn.pipeline import Pipeline as ImbPipeline
from imblearn.over_sampling import SMOTE
# Правильный pipeline для дисбалансированных данных
pipeline = ImbPipeline([
(scaler, StandardScaler()),
(smote, SMOTE(random_state=42)),
(model, RandomForestClassifier(class_weight=balanced))
])
pipeline.fit(X_train, y_train)
# Оценка на валидационном наборе
from sklearn.model_selection import cross_val_score
scores = cross_val_score(
pipeline, X_train, y_train,
scoring=roc_auc, # Используем ROC-AUC вместо accuracy
cv=5
)
В 90% случаев комбинация class weights + SMOTE + правильные метрики решает проблему дисбаланса.