SMOTE: объяснение алгоритма c практическими примерами на Python
В задачах машинного обучения, особенно в классификации, часто возникает проблема несбалансированных классов. Когда один класс (обычно доминирующий) представлен существенно больше, чем другой (минорный класс), алгоритмы могут смещать внимание в сторону более частого класса.

Это приводит к завышенной общей точности, но низкой чувствительности к редким классам. SMOTE (Synthetic Minority Over-sampling Technique) — один из наиболее популярных методов преодоления этой проблемы за счёт создания синтетических образцов минорного класса, что улучшает обобщающие способности моделей.
2. Проблема дисбаланса классов
2.1 Влияние на модель
- Смещённая оценка качества: Модель может показывать высокую точность, если большая часть предсказаний приходится на доминирующий класс, игнорируя меньшинство.
- Плохая обобщаемость: Алгоритмы обучаются на недостаточном объёме данных для редкого класса, что ухудшает способность распознавания «редких» объектов.
- Примеры из практики: В задачах обнаружения мошенничества, диагностики заболеваний или анализа отказов оборудования объекты минорного класса критически важны.
2.2 Стандартные подходы к решению
- Undersampling: Удаление части объектов доминирующего класса. Недостаток – возможная потеря важной информации.
- Oversampling: Простое копирование объектов минорного класса. Недостаток – риск переобучения модели из-за избыточной корреляции между примерами.
3. Основы SMOTE
SMOTE решает задачу oversampling’а, не просто дублируя данные, а создавая синтетические примеры, генерируя их на основе линейной интерполяции между соседними точками в пространстве признаков.
3.1 Основная идея и алгоритм
- Выбор исходного объекта: Из выборки минорного класса случайным образом выбирается объект xix_ixi.
- Поиск соседей: Для объекта xix_ixi вычисляются kkk ближайших соседей (обычно k=5k = 5k=5) среди других объектов этого же класса с использованием метрики (например, Евклидовой).
- Повторение: Процесс повторяется до создания требуемого числа синтетических примеров, что позволяет сбалансировать выборку.
3.2 Математическая интерполяция
Интерполяция здесь означает, что новый пример – это линейная комбинация выбранного примера и его соседа. Такая генерация помогает «размыть» границы классов, создавая более гладкое распределение данных в пространстве признаков.
4. Основные параметры SMOTE и их влияние
4.1 k_neighbors
- Описание: Число ближайших соседей, используемых для генерации синтетических примеров.
- Влияние:
- Малое значение (kkk) может привести к недостаточному разнообразию синтетических данных.
- Слишком большое значение может включить нежелательные точки, особенно если данные имеют сложную структуру или присутствуют выбросы.
4.2 sampling_strategy
- Описание: Определяет, сколько синтетических примеров нужно создать для каждого класса. Может задаваться как отношение (например, 0.5 означает, что количество объектов минорного класса после применения должно составлять 50% от числа объектов мажоритарного класса) или как словарь, где явно указываются размеры классов.
- Влияние: Корректный выбор этого параметра позволяет гибко управлять степенью балансировки.
4.3 random_state
- Описание: Параметр для воспроизводимости результатов. При фиксированном значении генерация синтетических примеров будет детерминированной.
5. Модификации SMOTE
В реальных задачах иногда требуется более тонкая настройка генерации синтетических данных. Существуют модификации стандартного SMOTE:
5.1 Borderline-SMOTE
- Идея: Фокусируется на объектах, расположенных у границы между классами. Генерируемые примеры создаются для точек, которые имеют много соседей из другого класса, что помогает лучше разделять классы.
- Варианты: Обычно различают Borderline-SMOTE1 и Borderline-SMOTE2, отличающиеся методом выбора точек для генерации.
5.2 SVM-SMOTE
- Идея: Использует алгоритм SVM для определения границ между классами и создания синтетических примеров вдоль линии раздела.
- Преимущество: Может быть эффективен при наличии сложных границ между классами.
5.3 ADASYN (Adaptive Synthetic Sampling)
- Идея: Подход, похожий на SMOTE, но с адаптивной стратегией: чем труднее классифицировать объект, тем больше синтетических примеров генерируется в его окрестности.
- Преимущество: Помогает сосредоточиться на «трудных» для классификации участках данных.
6. Практические рекомендации по подготовке данных
6.1 Масштабирование и нормализация
Поскольку SMOTE использует расстояния для определения соседей, важно привести данные к единому масштабу (например, с помощью StandardScaler или MinMaxScaler). Без масштабирования разница в порядках величин признаков может исказить результаты поиска ближайших соседей.
6.2 Анализ выбросов
Перед применением SMOTE необходимо проанализировать данные на наличие выбросов. Выбросы могут привести к генерации нехарактерных синтетических примеров, ухудшающих качество модели.
6.3 Интеграция в пайплайн
Использование SMOTE в составе пайплайна (например, с помощью imblearn.pipeline.Pipeline
) позволяет обеспечить корректное применение метода только к обучающей выборке, предотвращая утечку информации при кросс-валидации.
7. Примеры кода на Python
Ниже представлены расширенные примеры кода, демонстрирующие базовое применение SMOTE, его интеграцию в пайплайн и использование модификаций.
7.1 Пример 1: Базовое применение SMOTE и визуализация
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from imblearn.over_sampling import SMOTE
# Создаем искусственный несбалансированный набор данных
X, y = make_classification(n_samples=200, n_features=2, n_redundant=0, n_informative=2,
n_clusters_per_class=1, weights=[0.1, 0.9], flip_y=0,
random_state=10)
print("Распределение классов до SMOTE:", np.bincount(y))
# Применяем SMOTE для создания синтетических примеров
sm = SMOTE(random_state=42, k_neighbors=5)
X_res, y_res = sm.fit_resample(X, y)
print("Распределение классов после SMOTE:", np.bincount(y_res))
# Визуализируем результаты до и после SMOTE
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.title("До SMOTE")
plt.scatter(X[:, 0], X[:, 1], c=y, cmap='viridis', edgecolor='k')
plt.xlabel("Признак 1")
plt.ylabel("Признак 2")
plt.subplot(1, 2, 2)
plt.title("После SMOTE")
plt.scatter(X_res[:, 0], X_res[:, 1], c=y_res, cmap='viridis', edgecolor='k')
plt.xlabel("Признак 1")
plt.ylabel("Признак 2")
plt.tight_layout()
plt.show()
Пояснения:
- Функция
make_classification
генерирует двумерный набор данных с явно выраженным дисбалансом (10% против 90%). - SMOTE создаёт новые объекты минорного класса с использованием 5 ближайших соседей.
- Графическая визуализация помогает увидеть, как синтетические точки распределены между исходными объектами.
7.2 Пример 2: Интеграция SMOTE в пайплайн с классификатором
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report
from imblearn.pipeline import Pipeline
from imblearn.over_sampling import SMOTE
from sklearn.datasets import make_classification
# Создадим искусственный набор данных с большим числом признаков
X, y = make_classification(n_samples=1000, n_features=20, n_redundant=2, n_informative=10,
n_clusters_per_class=2, weights=[0.2, 0.8], flip_y=0, random_state=42)
# Разделение данных на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# Создаем пайплайн: сначала SMOTE для балансировки, затем RandomForestClassifier
pipeline = Pipeline([
('smote', SMOTE(random_state=42, sampling_strategy=0.5)), # Меньшинственный класс составит 50% от мажоритарного
('classifier', RandomForestClassifier(random_state=42))
])
# Обучаем модель на сбалансированных данных
pipeline.fit(X_train, y_train)
# Предсказываем и оцениваем модель
y_pred = pipeline.predict(X_test)
print(classification_report(y_test, y_pred))
Пояснения:
- Пайплайн гарантирует, что SMOTE применяется только к обучающей выборке, что исключает утечку данных.
- Параметр
sampling_strategy=0.5
задаёт желаемое соотношение между классами. - Оценка модели производится по стандартным метрикам (precision, recall, f1-score, accuracy).
7.3 Пример 3: Использование Borderline-SMOTE
from imblearn.over_sampling import BorderlineSMOTE
# Применяем Borderline-SMOTE для генерации синтетических примеров
bsmote = BorderlineSMOTE(random_state=42, k_neighbors=5, kind='borderline-1')
X_res_b, y_res_b = bsmote.fit_resample(X, y)
print("Распределение классов после Borderline-SMOTE:", np.bincount(y_res_b))
Пояснения:
- Borderline-SMOTE уделяет внимание точкам, находящимся у границы между классами, что может повысить чувствительность модели к «сложным» примерам.
- Параметр
kind
позволяет выбрать между различными реализациями метода.
7.4 Пример 4: Комбинирование SMOTE с кросс-валидацией и GridSearchCV
При оптимизации гиперпараметров модели важно включать этап балансировки данных в кросс-валидацию. Пример ниже показывает, как это можно сделать:
from sklearn.model_selection import GridSearchCV, StratifiedKFold
from sklearn.svm import SVC
from imblearn.pipeline import Pipeline
# Пайплайн с SMOTE и SVM-классификатором
pipeline = Pipeline([
('smote', SMOTE(random_state=42)),
('svc', SVC())
])
# Определяем параметры для поиска
param_grid = {
'smote__k_neighbors': [3, 5, 7],
'svc__C': [0.1, 1, 10],
'svc__kernel': ['rbf', 'linear']
}
# Настраиваем кросс-валидацию (Stratified для сохранения пропорций классов)
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
grid_search = GridSearchCV(pipeline, param_grid, cv=cv, scoring='f1', n_jobs=-1)
grid_search.fit(X_train, y_train)
print("Лучшие параметры:", grid_search.best_params_)
print("Лучший f1-score:", grid_search.best_score_)
Пояснения:
- GridSearchCV в сочетании с пайплайном позволяет одновременно настраивать параметры SMOTE и классификатора.
- StratifiedKFold гарантирует, что в каждой подвыборке сохранены пропорции классов.
- Использование метрики F1-score особенно актуально для несбалансированных данных.
8. Риски, ограничения и рекомендации по использованию SMOTE
8.1 Риски и недостатки
- Генерация шумовых данных: Если в исходном наборе присутствуют выбросы или объекты с нечеткими границами, SMOTE может создать нехарактерные синтетические точки, что снизит качество модели.
- Повышенная вычислительная сложность: При больших объемах данных поиск ближайших соседей может оказаться ресурсоёмким.
- Неидеальное отражение сложных зависимостей: Линейная интерполяция может не захватывать нелинейные взаимосвязи между признаками, что особенно важно в сложных реальных задачах.
8.2 Рекомендации
- Предварительная обработка: Обязательно выполняйте масштабирование и анализ выбросов. Рассмотрите возможность удаления аномальных точек перед применением SMOTE.
- Интеграция в пайплайн: Используйте инструменты пайплайнов (например,
imblearn.pipeline.Pipeline
), чтобы избежать утечки данных и обеспечить корректную работу кросс-валидации. - Эксперименты с модификациями: В зависимости от задачи тестируйте не только стандартный SMOTE, но и его модификации (Borderline-SMOTE, SVM-SMOTE, ADASYN) для выбора оптимального метода.
- Оценка результатов: Обязательно анализируйте метрики не только по accuracy, но и по precision, recall и f1-score, чтобы понять влияние балансировки на каждую из классовых групп.
SMOTE — это мощный и гибкий инструмент для борьбы с проблемой дисбалансированных данных, позволяющий создавать синтетические образцы посредством линейной интерполяции между ближайшими соседями меньшинственного класса. Благодаря таким особенностям, как возможность адаптации (через параметры и модификации) и интеграция в стандартные пайплайны машинного обучения, SMOTE нашёл широкое применение в самых разных областях. Однако, как и любой метод, он требует тщательной настройки и предварительного анализа данных, чтобы избежать генерации шумовых примеров и ухудшения качества модели.
Дополнительные ресурсы
- Официальная документация imbalanced-learn
- Статьи и исследования по SMOTE и его модификациям (поиск по ключевым словам «SMOTE», «Borderline-SMOTE», «ADASYN»)
