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

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

Различные алгоритмы также больше подходят для проблем с дисбалансом классов, в том числе основанных на повышении, таких как Adaptive Boosting (AdaBoost). AdaBoost работает, чтобы улучшить производительность так называемых слабых учеников (плохие модели прогнозирования, но лучше, чем случайное предположение). AdaBoost итеративно создает ансамбль слабых учеников, корректируя веса неверно классифицированных данных на каждой итерации. Для обучения первого слабого ученика AdaBoost присваивает одинаковый вес каждой выборке обучающего набора. Для каждого последующего слабого ученика веса пересчитываются таким образом, что больший вес присваивается выборкам, которые текущий слабый ученик неправильно классифицировал. Этот вес определяет вероятность того, что образец появится в обучении следующего слабого ученика. По этой причине алгоритмы повышения, такие как AdaBoost, особенно полезны для проблем с дисбалансом классов, потому что более высокий вес придается классу меньшинства на каждой последующей итерации, поскольку данные из этого класса часто неправильно классифицируются.

Методы выборки данных в сочетании с усилением могут быть эффективным способом решения проблем дисбаланса классов. Чтобы продемонстрировать это, давайте воспользуемся данными о ссуде Lending Club (доступны здесь), чтобы попытаться предсказать, будет ли кто-то неплатежеспособен по кредиту, используя два интересных подхода к выборке, которые были предложены и объединены с AdaBoost: SMOTEBoost и RUSBoost.

SMOTEBoost - это метод передискретизации, основанный на алгоритме SMOTE (техника передискретизации синтетических меньшинств). SMOTE использует k-ближайших соседей для создания синтетических примеров класса меньшинства. Затем SMOTEBoost вводит метод SMOTE на каждой итерации повышения. Преимущество этого подхода заключается в том, что в то время как стандартное повышение дает равные веса всем неверно классифицированным данным, SMOTE дает больше примеров класса меньшинства на каждом этапе повышения. Точно так же RUSBoost достигает той же цели, выполняя случайную недостаточную выборку (RUS) на каждой итерации повышения вместо SMOTE.

Пробуем: подготовка данных

Сначала я импортирую данные и удаляю все записи, не соответствующие loan_data = “Fully Paid” или “Charged Off”. Я установил цель “Fully Paid” на 0 и “Charged Off” на 1.

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import AdaBoostClassifier
from sklearn.metrics import classification_report
from sklearn.metrics import average_precision_score
from sklearn.metrics import precision_recall_curve
from sklearn.utils import resample
from imblearn.over_sampling import SMOTE
import smote
import rus
# read in data and set target variable
df = pd.read_csv("lending-club-loan-data/loan.csv", 
                 low_memory=False)
df['target'] = ''
df.loc[df.loan_status == 'Fully Paid', 'target'] = 0
df.loc[df.loan_status == 'Charged Off', 'target'] = 1
df = df[df.target != '']

Для построения бинарного классификатора я использую следующие возможности ссуды:

  • delinq_2yrs: количество просроченных платежей на 30+ дней в кредитном файле заемщика за последние 2 года
  • dti: отношение, рассчитанное на основе общих ежемесячных выплат по долгу заемщика к общим долговым обязательствам, исключая ипотеку и запрашиваемую ссуду в аккредитиве, деленное на ежемесячный доход заемщика, сообщаемый самим собой.
  • home_ownership: статус собственности на жилище, предоставленный заемщиком при регистрации. Значения АРЕНДА, СОБСТВЕННАЯ, ИПОТЕКА, ДРУГИЕ
  • grade: кредитный рейтинг присвоенный аккредитиву
  • int_rate: процентная ставка по кредиту
  • purpose: категория, указанная заемщиком для заявки на ссуду
  • revol_util: коэффициент использования возобновляемой линии или сумма кредита, используемого заемщиком, по отношению ко всем доступным возобновляемым кредитам
  • total_rec_late_fee: штрафы за просрочку платежа, полученные на сегодняшний день
features = ['grade','home_ownership','dti','purpose','int_rate',
            'delinq_2yrs','revol_util','total_rec_late_fee',
            'target']
df_model = df[features]

После удаления строк с отсутствующими данными ссуды по умолчанию составляют около 18% от выбранного набора данных.

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

X = pd.get_dummies(df_model.drop('target', axis=1))
y = df_model['target'].tolist() 
X_train, X_test, y_train, y_test = train_test_split(X, y, 
                                                    test_size=0.3, 
                                                    random_state=42)

Первоначальные результаты

Сначала я тренирую несколько моделей с помощью:

  1. просто AdaBoost,
  2. SMOTE sampling, а затем AdaBoost,
  3. и выборка RUS, а затем AdaBoost
def adaboost(X_train, X_test, y_train):
    model = AdaBoostClassifier(n_estimators=100, random_state=42)
    model.fit(X_train,y_train)
    y_pred = model.predict(X_test) 
    return y_pred
# AdaBoost
y_baseline = adaboost(X_train, X_test, y_train)
# SMOTE
sm = SMOTE(random_state=42)
X_train_sm, y_train_sm = sm.fit_sample(X_train, y_train)
y_smote = adaboost(X_train_sm, X_test, y_train_sm)
# RUS
X_full = X_train.copy()
X_full['target'] = y_train
X_maj = X_full[X_full.target==0]
X_min = X_full[X_full.target==1]
X_maj_rus = resample(X_maj,replace=False,n_samples=len(X_min),random_state=44)
X_rus = pd.concat([X_maj_rus, X_min])
X_train_rus = X_rus.drop(['target'], axis=1)
y_train_rus = X_rus.target
y_rus = adaboost(X_train_rus, X_test, y_train_rus)

Результаты этих внедрений дают представление о любых улучшениях, полученных от использования SMOTEBoost и RUSBoost.

Выборка значительно улучшила отзыв о классе меньшинства с пометкой «По умолчанию», причем наибольшее улучшение наблюдается при использовании RUS. Обратите внимание, что количество сэмплов, сгенерированных или удаленных в этой реализации, таково, что миноритарный и мажоритарный классы имеют равное количество сэмплов перед запуском AdaBoost.

Настройка SMOTEBoost и RUSBoost

Для реализации SMOTEBoost я использую реализацию AdaBoost из scikit-learn с измененной функцией fit, которую можно найти здесь. Перед каждым шагом повышения, повторная выборка SMOTE вычисляет новые синтетические примеры для класса меньшинства. Я использую значение по умолчанию k=5 в качестве числа ближайших соседей для вычисления новых данных и устанавливаю n_samples=300, которое представляет собой количество синтетически сгенерированных примеров, которые будут использоваться на каждом этапе повышения. Следует проявлять осторожность при установке параметра n_samples таким образом, чтобы количество образцов класса меньшинства не становилось больше, чем количество образцов класса большинства, вызывая противоположный дисбаланс класса. Веса, присвоенные новым данным, нормализуются на основе обучающего набора текущего шага повышения.

Точно так же реализация RUSBoost представляет собой модифицированную функцию подгонки (находится здесь), где перед каждым шагом повышения класс большинства подвергается недостаточной выборке. Я установил n_samples=300 как количество выборок, которые нужно удалить на каждом шаге повышения.

target_names = ['Not Default', 'Default']
for algorithm in [smote.SMOTEBoost(n_estimators=100, n_samples=300),
                  rus.RUSBoost(n_estimators=100, n_samples=300)]:
    algorithm.fit(X_train, y_train)
    y_pred = algorithm.predict(X_test)
    print()
    print(str(algorithm))
    print()
    print(classification_report(y_test, y_pred, 
                                target_names=target_names))

Полную реализацию этого примера невозврата кредита можно найти здесь.

Оценка результатов

Для проблем с дисбалансом классов такие показатели, как точность, отзыв и оценка f1, дают хорошее представление о том, как классификатор работает по отношению к классу меньшинства. В зависимости от проблемы цель состоит в том, чтобы оптимизировать точность и / или вспомнить классификатор. В этом случае мне нужна модель, которая улавливает наибольшее количество экземпляров класса меньшинства, даже если она увеличивает количество ложных срабатываний. Классификатор с высоким показателем отзыва даст наибольшее количество потенциальных дефолтов по ссуде или, по крайней мере, поднимет флажок для наиболее уязвимых случаев. В этом примере RUSBoost немного превосходит SMOTEBoost, если посмотреть на отзыв класса меньшинства для данной настройки.

SMOTEBoost:

RUSBoost:

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

Лучшим методом выборки для этой схемы моделирования является RUS. Это, безусловно, дает самый высокий отзыв для класса меньшинства.

Другими полезными методами оценки классификатора с дисбалансом классов являются кривые ROC и прецизионный отзыв (PR). Поскольку меня не так интересует, как модель работает с отрицательным классом (класс «Не по умолчанию»), я строю только кривые точности-отзыва, которые не учитывают истинные отрицания. Эти кривые PR можно использовать для сравнения результатов RUS с результатами RUSBoost.

Заключение

Хотя SMOTEBoost обещает улучшить AdaBoost для несбалансированных данных за счет расширения диапазона класса меньшинства, в этом случае он оказался более слабым из всех подходов к выборке для данной настройки модели. Точно так же RUSBoost не работал так же хорошо, как просто выборка RUS с последующим AdaBoost.

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

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