Главная
#Математика и ML #Classification #Data Science

Классовый дисбаланс в Data Science в 2026 году: почему accuracy может обманывать

Классовый дисбаланс в Data Science появляется тогда, когда один класс в данных встречается заметно чаще другого. На словах это звучит как техническая деталь, но в реальной задаче именно здесь часто начинается самая дорогая ошибка. Модель может выглядеть аккуратной, давать высокую accuracy, хорошо вести себя на общей статистике и при этом систематически пропускать именно те объекты, ради которых задача вообще была поставлена.

Содержание
  1. Почему классовый дисбаланс опасен
  2. Как дисбаланс влияет на функцию потерь
  3. Почему accuracy недостаточно
  4. Как исправляют дисбаланс классов
  5. Пример на Python
  6. Что важно запомнить
  7. Kaggle notebook по теме:

Почему классовый дисбаланс опасен

Это часто происходит в задачах антифрода, медицинской диагностики, обнаружения дефектов, предсказания оттока, модерации контента. Во всех этих случаях нормальных объектов много, а действительно важные события редки. И здесь модель быстро усваивает опасную логику: если почти все наблюдения относятся к одному классу, выгоднее научиться угадывать именно его.

Эту тему полезно сразу связывать не с общей «подготовкой данных», а с тем, как модель реагирует на редкий класс в реальной оптимизации. Поэтому рядом особенно полезно держать материал про class weights и rebalancing: он хорошо показывает, как меняется само обучение, когда ошибка на меньшинстве становится для алгоритма заметнее.

Представим выборку из 10 000 клиентов, где только 100 действительно уйдут из сервиса. Если модель всем скажет «не уйдет», то она ошибется только на этих ста клиентах и получит accuracy (\frac{9900}{10000} = 0.99). На уровне общей доли правильных ответов все выглядит прекрасно. Но по смыслу такая модель бесполезна: она не нашла ни одного клиента, которого нужно было удерживать.

Поэтому классовый дисбаланс важно понимать не как «классов не поровну», а как ситуацию, где стандартная цель обучения начинает работать не в интересах задачи. Алгоритм минимизирует общую ошибку. Если объектов одного класса слишком много, именно они начинают задавать направление оптимизации. Редкий класс становится статистически малозаметным, хотя прикладная цена ошибки на нем может быть самой высокой.

Как дисбаланс влияет на функцию потерь

Раздел математики:теория вероятностей, математическая статистика и оптимизация.

(L = -\frac{1}{N}\sum_{i=1}^{N}\left[y_i\log(\hat{p}_i) + (1-y_i)\log(1-\hat{p}_i)\right])

Что означает каждый символ:

(L) — средняя функция потерь, которую минимизирует модель.

(N) — число объектов в обучающей выборке.

(i) — индекс объекта.

(y_i) — истинная метка класса для объекта (i), обычно (0) или (1).

(\hat{p}_i) — предсказанная моделью вероятность положительного класса.

(\log) — логарифм, который усиливает штраф за уверенные неправильные ответы.

Эта формула используется в логистической регрессии и во многих задачах бинарной классификации. Ее роль в алгоритме простая: она задает цену ошибки. Если положительный класс редкий, то в сумме большинство слагаемых относится к отрицательному классу. Значит, оптимизатор получает сигнал прежде всего от большинства и начинает улучшать качество именно на нем.

Геометрически это можно представить так: в пространстве признаков массивная часть точек принадлежит одному классу, а другой класс выглядит как небольшие островки. Если просто минимизировать среднюю ошибку, граница решения естественным образом смещается в пользу плотной области. Модель начинает смотреть на пространство глазами большинства.

Численный пример для этой идеи: пусть в выборке 100 объектов, из них 95 относятся к классу (0) и только 5 — к классу (1). Если модель всегда отвечает (0), то accuracy равна (\frac{95}{100} = 0.95). Но recall для положительного класса равен (\frac{TP}{TP + FN} = \frac{0}{0 + 5} = 0). То есть модель не нашла ни одного нужного объекта.

Почему accuracy недостаточно

Именно поэтому accuracy при дисбалансе классов может обманывать. Она полезна, когда классы примерно сопоставимы и стоимость ошибок близка. Но если редкий класс несет основной бизнес-смысл, приходится смотреть на другие метрики: precision, recall, F1, ROC-AUC, PR-AUC и confusion matrix. Каждая из них отвечает на свой вопрос, и выбор метрики здесь уже связан не только с математикой, но и с прикладной ценой ошибки.

Recall особенно важен там, где пропуск дорог. Если модель пропускает мошенническую операцию или опасное медицинское состояние, цена такой ошибки высока. Precision важен там, где ложная тревога создает большой операционный шум. F1 нужен там, где хочется держать баланс между этими двумя сторонами. Поэтому классовый дисбаланс нельзя лечить вслепую. Сначала нужно понять, какой тип ошибки для задачи действительно критичен.

Именно в таких задачах особенно полезно отдельно смотреть на precision-recall curve: она показывает поведение модели там, где положительный класс редок и общая accuracy почти ничего не объясняет.

Как исправляют дисбаланс классов

На уровне машинного обучения у нас есть несколько способов влиять на ситуацию. Можно изменить метрику оценки. Можно изменить саму функцию потерь через веса классов. Можно изменить состав обучающей выборки через oversampling или undersampling. Можно изменить порог, после которого вероятность считается положительным классом. Все эти методы по-разному вмешиваются в обучение, и у каждого есть своя цена.

Один из самых практичных подходов — дать редкому классу больший вес в обучении.

(w_c = \frac{N}{K\,n_c})

Что означает каждый символ:

(w_c) — вес класса (c) в функции потерь.

(N) — общее число объектов.

(K) — число классов.

(n_c) — число объектов класса (c).

Такая логика используется, например, в параметре (\texttt{class_weight = balanced}) в scikit-learn. Ее смысл не в том, чтобы механически выровнять данные, а в том, чтобы изменить вклад классов в оптимизацию. Если редкого класса мало, ошибка на нем должна быть для алгоритма заметнее.

Численный пример: пусть (N = 1000), классов два, объектов класса (0)(950), объектов класса (1)(50). Тогда вес первого класса равен (w_0 = \frac{1000}{2\cdot 950} \approx 0.53), а вес второго — (w_1 = \frac{1000}{2\cdot 50} = 10). Это означает, что ошибка на редком классе станет для оптимизатора значительно важнее.

Связь с оптимизацией здесь прямая. Градиентный спуск обновляет параметры так, чтобы уменьшать функцию потерь. Если ошибка на редком классе получила больший вес, то и соответствующий вклад в градиент становится сильнее. Иными словами, модель начинает чаще «слышать» редкий класс во время обучения. Это не косметическая настройка, а изменение самого направления, в котором движется алгоритм.

Второй путь — работа с данными. Oversampling увеличивает число объектов редкого класса. Самый простой вариант — дублировать примеры. Более продвинутый — SMOTE, где новые объекты генерируются между соседними точками меньшинства. Undersampling делает обратное: уменьшает число объектов массового класса. Такой подход полезен, когда данных очень много и избыток большинства просто подавляет сигнал от меньшинства.

Но здесь важно понимать ограничения. Oversampling может привести к переобучению, потому что модель начинает видеть почти одни и те же редкие примеры снова и снова. Undersampling может выбросить часть полезной структуры большинства. SMOTE хорошо работает не везде: если граница между классами сложная, синтетические точки могут оказаться слишком искусственными. Поэтому такие методы нужно проверять на кросс-валидации, а не принимать как универсальное лекарство.

Еще один важный слой — порог классификации. Часто модель выдает вероятность, а метка класса назначается при пороге (0.5). Но при дисбалансе этот порог редко оптимален. Если задача требует поймать максимум редких объектов, порог можно понизить. Это увеличит recall, но, вероятно, снизит precision. Таким образом, мы не меняем модель, а меняем правило принятия решения поверх нее.

На практике это уже прямой переход к задаче выбора рабочего порога: одна и та же модель может вести себя совсем по-разному в зависимости от того, где именно вы проводите границу между «редким событием» и «обычным случаем».

Именно здесь становится видна связь между математикой, алгоритмом и Python-реализацией. Математика описывает функцию потерь и метрики. Алгоритм машинного обучения минимизирует выбранную цель. Python позволяет явно задать веса классов, ресемплинг и порог. Если хотя бы один из этих уровней выбран без учета прикладного смысла, результат на отчете и результат в реальной задаче начнут расходиться.

Хорошая практическая последовательность обычно выглядит так. Сначала строят baseline без сложных вмешательств и смотрят confusion matrix. Затем считают precision, recall, F1 и PR-AUC. После этого пробуют class weights. Если этого мало, тестируют oversampling или SMOTE. И уже потом подбирают порог, исходя из реальной стоимости ложноположительных и ложноотрицательных ошибок. Такой подход дисциплинирует и не дает начать с технических трюков до понимания самой задачи.

Именно поэтому в паре с этой статьей полезно держать и разбор того, когда смотреть на ROC-AUC, а когда на PR-AUC: при сильном дисбалансе выбор самой диагностической кривой уже меняет то, как вы интерпретируете качество модели.

Пример на Python

Ниже — простой пример на Python, где мы создаем несбалансированную выборку и обучаем логистическую регрессию с учетом дисбаланса.

from sklearn.datasets import make_classification
# Импортируем генератор искусственных данных для задачи классификации.

from sklearn.model_selection import train_test_split
# Импортируем функцию разбиения выборки на train и test.

from sklearn.linear_model import LogisticRegression
# Импортируем логистическую регрессию.

from sklearn.metrics import confusion_matrix, classification_report
# Импортируем матрицу ошибок и отчет по метрикам.

X, y = make_classification(
    n_samples=5000,
    n_features=12,
    n_informative=5,
    n_redundant=0,
    weights=[0.95, 0.05],
    random_state=42
)
# Создаем данные, где 95% объектов относятся к одному классу и 5% к другому.

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, stratify=y, random_state=42
)
# Делим данные на обучающую и тестовую части, сохраняя исходную долю классов.

model = LogisticRegression(class_weight='balanced', max_iter=1000)
# Создаем модель и указываем ей учитывать дисбаланс классов через веса.

model.fit(X_train, y_train)
# Обучаем модель на тренировочной выборке.

y_pred = model.predict(X_test)
# Получаем предсказанные классы на тестовой выборке.

print(confusion_matrix(y_test, y_pred))
# Выводим матрицу ошибок, чтобы увидеть структуру промахов модели.

print(classification_report(y_test, y_pred, digits=3))
# Выводим precision, recall и F1 для каждого класса.

Что важно в этом коде. Параметр (\texttt{stratify = y}) нужен, чтобы train и test сохранили ту же пропорцию классов, что и вся выборка. Без этого можно случайно исказить оценку. Параметр (\texttt{class_weight = balanced}) меняет важность ошибок разных классов в обучении. А (\texttt{classification_report}) сразу показывает, как модель ведет себя на каждом классе по отдельности.

Но даже после такого шага не стоит автоматически верить самим вероятностям модели: если они плохо откалиброваны, то и выбор порога, и интерпретация риска будут искажены. Поэтому для зрелой настройки рядом полезно смотреть и на калибровку вероятностей.

Что важно запомнить

Если подвести итог с учебной точки зрения, то классовый дисбаланс — это не просто статистическая неровность в таблице. Это ситуация, где наивная цель обучения начинает расходиться с реальной целью задачи. Именно поэтому начинающему Data Scientist важно не запоминать один «правильный» прием, а понимать механизм. Почему высокая accuracy может быть ложным сигналом. Почему редкий класс теряется в функции потерь. Почему метрики нужно выбирать под стоимость ошибки. И почему решение почти всегда лежит на стыке данных, оптимизации и прикладного смысла.

Когда это понимание появляется, сама тема становится значительно проще. Дисбаланс классов — не исключение из правил машинного обучения, а хороший пример того, как математика, алгоритмы и практика должны быть согласованы между собой. Если они согласованы, модель начинает решать реальную задачу. Если нет, она просто учится быть удобной для отчета.

Kaggle notebook по теме:

https://www.kaggle.com/code/rafjaa/resampling-strategies-for-imbalanced-datasets

Что читать дальше

Связанные статьи по этой теме

Инструменты Python в 2026 году: современный стек для профессиональной разработки Что должен знать Junior Data Scientist в 2026 году? Как собрать GitHub-портфолио для Data Science
Вернуться в блог