Что такое SMOTE?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
SMOTE (Synthetic Minority Over-sampling Technique)
SMOTE — это техника для обработки несбалансированных датасетов путём создания синтетических примеров из меньшинства класса. Вместо простого дублирования примеров, SMOTE интерполирует новые точки в пространстве признаков, что помогает модели лучше обобщать.
Проблема дисбаланса классов
В реальных задачах данные часто несбалансированы:
# Пример несбалансированного датасета
import numpy as np
from collections import Counter
y = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) # 90% класс 0, 10% класс 1
print(Counter(y)) # Counter({0: 9, 1: 1})
# Проблема: модель просто предсказывает 0 и получает 90% accuracy
Просто дублировать примеры (over-sampling) плохо, так как модель переобучается на одних и тех же примерах.
Как работает SMOTE
SMOTE создаёт синтетические примеры путём интерполяции между существующими примерами меньшинства:
Алгоритм:
-
Для каждого примера меньшинства класса:
- Находим K ближайших соседей (обычно K=5) среди примеров того же класса
- Случайно выбираем одного соседа
- Генерируем новый пример как линейную комбинацию текущего и выбранного соседа
-
Формула синтетического примера:
x_synthetic = x_current + random(0, 1) * (x_neighbor - x_current)
Математическое описание
import numpy as np
def smote_create_sample(x_current, x_neighbor):
"""
Создаёт один синтетический пример
x_current: текущий пример меньшинства (shape: n_features)
x_neighbor: случайно выбранный сосед (shape: n_features)
"""
# Генерируем случайный вес
alpha = np.random.uniform(0, 1)
# Интерполируем
x_synthetic = x_current + alpha * (x_neighbor - x_current)
return x_synthetic
# Пример
x1 = np.array([1.0, 2.0])
x2 = np.array([3.0, 4.0])
x_new = smote_create_sample(x1, x2)
print(x_new) # Точка на линии между x1 и x2
Практический пример с scikit-learn
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import SMOTE
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, roc_auc_score
# 1. Создаём несбалансированный датасет
X, y = make_classification(
n_samples=1000,
n_features=20,
n_informative=10,
n_redundant=5,
weights=[0.9, 0.1], # 90% класс 0, 10% класс 1
random_state=42
)
print(f"Исходный датасет: {np.bincount(y)}")
# Output: Исходный датасет: [900 100]
# 2. Разделяем на train и test
# ВАЖНО: SMOTE применяем только к train данным!
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
# 3. Применяем SMOTE к train данным
smote = SMOTE(random_state=42, k_neighbors=5)
X_train_smote, y_train_smote = smote.fit_resample(X_train, y_train)
print(f"После SMOTE: {np.bincount(y_train_smote)}")
# Output: После SMOTE: [630 630] (классы сбалансированы)
# 4. Обучаем модель
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train_smote, y_train_smote)
# 5. Оцениваем
y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)[:, 1]
print(classification_report(y_test, y_pred))
print(f"ROC-AUC: {roc_auc_score(y_test, y_pred_proba):.4f}")
Визуализация SMOTE
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from imblearn.over_sampling import SMOTE
# Создаём 2D датасет для визуализации
X, y = make_classification(
n_samples=200,
n_features=2,
n_informative=2,
n_redundant=0,
weights=[0.85, 0.15],
random_state=42
)
# Применяем SMOTE
smote = SMOTE(random_state=42)
X_smote, y_smote = smote.fit_resample(X, y)
# Визуализация
plt.figure(figsize=(12, 5))
# До SMOTE
plt.subplot(1, 2, 1)
plt.scatter(X[y==0, 0], X[y==0, 1], label='Класс 0', alpha=0.7)
plt.scatter(X[y==1, 0], X[y==1, 1], label='Класс 1 (меньшинство)', alpha=0.7, c='red')
plt.title('До SMOTE')
plt.legend()
# После SMOTE
plt.subplot(1, 2, 2)
plt.scatter(X_smote[y_smote==0, 0], X_smote[y_smote==0, 1], label='Класс 0', alpha=0.7)
plt.scatter(X_smote[y_smote==1, 0], X_smote[y_smote==1, 1], label='Класс 1 (синтетические)', alpha=0.7, c='red')
plt.title('После SMOTE')
plt.legend()
plt.tight_layout()
plt.show()
Варианты SMOTE
1. Стандартный SMOTE
Интерполирует между случайным соседом:
from imblearn.over_sampling import SMOTE
smote = SMOTE(k_neighbors=5, random_state=42)
X_resampled, y_resampled = smote.fit_resample(X, y)
2. Borderline-SMOTE
Сосредоточивается на примерах вблизи границы между классами:
from imblearn.over_sampling import BorderlineSMOTE
bl_smote = BorderlineSMOTE(kind=1, k_neighbors=5, random_state=42)
X_resampled, y_resampled = bl_smote.fit_resample(X, y)
3. SVM-SMOTE
Использует опорные вектора для выбора примеров для SMOTE:
from imblearn.over_sampling import SVMSMOTE
svm_smote = SVMSMOTE(k_neighbors=5, random_state=42)
X_resampled, y_resampled = svm_smote.fit_resample(X, y)
4. ADASYN (Adaptive Synthetic Sampling)
Генерирует больше синтетических примеров в сложных регионах:
from imblearn.over_sampling import ADASYN
adasyn = ADASYN(n_neighbors=5, random_state=42)
X_resampled, y_resampled = adasyn.fit_resample(X, y)
Комбинирование SMOTE с Under-sampling
Часто SMOTE комбинируют с под-выборкой (under-sampling) для оптимального баланса:
from imblearn.pipeline import Pipeline
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler
# Создаём pipeline: SMOTE + Under-sampling
sm = SMOTE(random_state=42)
rUS = RandomUnderSampler(random_state=42)
pipeline = Pipeline([
('over', sm),
('under', rUS)
])
X_resampled, y_resampled = pipeline.fit_resample(X_train, y_train)
print(f"После SMOTE + Under-sampling: {np.bincount(y_resampled)}")
Лучшие практики использования SMOTE
1. Применяй только к train данным
# ✅ Правильно
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
smote = SMOTE()
X_train_balanced, y_train_balanced = smote.fit_resample(X_train, y_train)
model.fit(X_train_balanced, y_train_balanced)
model.predict(X_test) # Тестируем на оригинальных данных
# ❌ Неправильно
X_balanced, y_balanced = smote.fit_resample(X, y)
X_train, X_test, y_train, y_test = train_test_split(X_balanced, y_balanced)
2. Выбор правильного соотношения
# SMOTE с параметром sampling_strategy
smote = SMOTE(
sampling_strategy=0.5, # Минимальный класс будет 50% от большинства
k_neighbors=5,
random_state=42
)
3. Используй кросс-валидацию с SMOTE
from imblearn.pipeline import Pipeline
from sklearn.model_selection import cross_validate
pipe = Pipeline([
('smote', SMOTE()),
('model', RandomForestClassifier())
])
scores = cross_validate(pipe, X_train, y_train, cv=5, scoring='roc_auc')
print(f"ROC-AUC: {scores['test_score'].mean():.4f}")
Когда использовать SMOTE
Используй SMOTE если:
- Дисбаланс классов существенный (1:10 или больше)
- Меньшинство класса хорошо представлено в признаках
- У тебя достаточно памяти для синтетических данных
- Переобучение менее критично чем недообучение
НЕ используй SMOTE если:
- Дисбаланс очень сильный (1:1000)
- Меньшинство класса очень мало примеров (< 50)
- Признаки высоко коррелированы или содержат шум
- Вычислительные ресурсы ограничены
Альтернативы SMOTE
- Class weights: Увеличивают штраф за ошибки на меньшинстве
- Threshold adjustment: Меняют порог классификации
- Cost-sensitive learning: Разные затраты на ошибки классов
- Ensemble методы: Balanced Random Forest, Easy Ensemble
Заключение
SMOTE — это мощный и широко используемый метод для обработки несбалансированных данных. Он генерирует качественные синтетические примеры, которые помогают моделям лучше обобщать. Ключ к успеху — правильное применение (только на train) и выбор правильного соотношения балансировки.