Как бороться с проблемой дисбаланса классов в текстовых данных?

Проблема дисбаланса классов возникает, когда количество примеров одного класса в датасете значительно превышает количество примеров других классов. Это типичная ситуация при решении задач классификации текстов (например, определение спама, выявление токсичности, выявление редких событий и др.). Дисбаланс приводит к тому, что модель обучается преимущественно на «большом» классе и игнорирует «маленькие», что ухудшает метрики на минорных классах (особенно recall и F1-score).

1. Анализ и выявление дисбаланса

Перед применением методов нужно:

  • Вывести распределение классов (np.bincount(labels), pd.Series(labels).value_counts()).

  • Посчитать метрики отдельно по каждому классу.

  • Использовать confusion matrix для визуального анализа ошибок.

2. Методы борьбы с дисбалансом

А. Балансировка данных (на уровне входного датасета)

Oversampling (повторение примеров минорного класса)
  • Повторяют примеры из меньшинства, чтобы приблизить распределение к равному.

  • Подходит для небольших датасетов.

  • Минус: переобучение на минорном классе.

Пример:

from imblearn.over_sampling import RandomOverSampler
X_resampled, y_resampled = RandomOverSampler().fit_resample(X, y)
Undersampling (удаление примеров мажорного класса)
  • Сокращает количество примеров большинства.

  • Минус: потеря информации.

  • Подходит для очень больших датасетов, где минорный класс всё ещё имеет достаточное представление.

SMOTE / ADASYN (синтетическое расширение минорного класса)
  • Генерация новых синтетических примеров путём интерполяции признаков.

  • В NLP используется осторожно, так как интерполяция между текстами требует дополнительных шагов — применяется на эмбеддингах (например, TF-IDF, BERT).

B. Изменение подхода к обучению модели

Взвешивание классов (class weights)
  • В модели увеличивается штраф за ошибку на минорном классе.

  • Можно передать вручную или автоматически с class_weight='balanced'.

Пример для логистической регрессии:

from sklearn.linear_model import LogisticRegression
clf = LogisticRegression(class_weight='balanced')

Пример для PyTorch:

loss = nn.CrossEntropyLoss(weight=torch.tensor(\[w0, w1, ...\]))
Custom Loss Functions
  • В задачах с сильным перекосом можно использовать focal loss, которая увеличивает фокус на ошибках на минорных примерах.

  • Применимо при работе с нейросетями (особенно в multi-class и multi-label задачах).

C. Аугментация текстовых данных

Особенно актуально для NLP, когда min-класс содержит мало текстов.

Техники:
  • **EDA (Easy Data Augmentation):
    **

    • Синонимизация

    • Удаление/перестановка слов

    • Вставка случайных слов

  • Back-translation: перевод текста на другой язык и обратно.

  • Paraphrasing: генерация переформулированных вариантов с помощью моделей (T5, Pegasus).

# пример paraphrase генерации через HuggingFace pipeline

from transformers import pipeline
paraphraser = pipeline("text2text-generation", model="Vamsi/T5_Paraphrase_Paws")
paraphraser("paraphrase: this is a test sentence", max_length=64)

D. Использование подходящих метрик

Accuracy — неинформативна при дисбалансе. Используются:

  • Precision, Recall и F1-score по каждому классу

  • macro-F1, weighted-F1

  • ROC AUC, PR AUC

  • Confusion matrix

  • Matthews Correlation Coefficient (MCC)

from sklearn.metrics import classification_report
print(classification_report(y_true, y_pred))

E. Переформулирование задачи

Если дисбаланс особенно критичен (например, выявление редких заболеваний, токсичности, мошенничества), можно:

  • Перевести задачу в бинарную классификацию: редкое событие vs все остальные.

  • Использовать One-Class Classification или Anomaly Detection подходы.

  • Использовать ranking models вместо прямой классификации.

F. Тестирование и валидация

При дисбалансе особенно важно:

  • Использовать stratified split (стратифицированная выборка) для train/test/val разбиения.

  • Оценивать модель не только на общей метрике, но и по классам.

  • Проводить cross-validation с сохранением распределения классов.

from sklearn.model_selection import StratifiedKFold
skf = StratifiedKFold(n_splits=5)

G. Применение продвинутых моделей

Современные языковые модели, такие как BERT, RoBERTa, XLM-R, имеют хорошую способность к обобщению и лучше справляются с дисбалансом за счёт контекстных эмбеддингов. Однако они тоже могут страдать от смещения в сторону мажорного класса при сильном перекосе.

Решения:

  • Fine-tune с использованием class weights.

  • Переподбор learning rate, scheduler, batch size.

  • Использование label smoothing.

Пример пайплайна с учетом дисбаланса (NLP):

  1. Очистка и токенизация текста.

  2. Векторизация текста (TF-IDF, BERT embeddings).

  3. Oversampling с использованием RandomOverSampler.

  4. Обучение классификатора (например, LogisticRegression или XGBoost) с class_weight.

  5. Оценка по macro-F1 и confusion matrix.

  6. Анализ ошибок минорного класса.

  7. Повторный fine-tuning или генерация новых примеров через paraphrasing/back-translation.

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