МАШИННОЕ ОБУЧЕНИЕ

Прогнозирование дефолта по кредиту — практические советы для успешного исполнения

Улучшите существующие базовые показатели прогнозирования с помощью расширенного машинного обучения.

Согласно данным по кредитным картам и персональным кредитам ТрансЮнион, ожидается, что просроченные платежи вырастут до уровня, невиданного с 2010 года в Соединенных Штатах.

TransUnion прогнозирует, что серьезные просроченные платежи по кредитным картам вырастут до 2,6% в конце 2023 года с 2,1% на конец 2022 года. Уровень просроченных платежей по необеспеченным личным кредитам вырастет до 4,3% с 4,1% за тот же период времени.

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

Фон

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

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

Описание Проекта

Подход включает анализ набора данных, содержащего транзакции по кредитным картам и записи о платежах для группы клиентов за 12-месячный период с 2021 по 2022 год.

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

Описание набора данных

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

  • D_* = переменные просрочки
  • S_* = переменные расходов
  • P_* = Платежные переменные
  • B_* = переменные баланса
  • R_* = переменные риска

Всего существует 100целочисленных функций и 100функций с плавающей запятой, представляющих статус клиента за последние 12 месяцев. Набор данных включает информацию о выписках клиентов, которые могут варьироваться от 1 до 13. Между каждой выпиской по кредитной карте для клиента может быть разрыв от 30 до 180 дней (т. е. выписки по кредитной карте для клиента могут отсутствовать). Каждый клиент представлен идентификатором клиента. Пример данных для первых 5 выписок клиента с customer_ID=0 показан ниже:

Из 7 миллионов идентификаторов customer_ID 98% имеют метку 0 (хороший клиент, нет по умолчанию), а 2% имеют метку 1 (плохой клиент, по умолчанию).

Типы подхода

Данные включают ежемесячные выписки по кредитным картам каждого клиента, и, следовательно, можно использовать множество подходов, которые используют свойство временных рядов или используют

  1. Модели дерева решений/нейронные сети, использующие агрегированные характеристики данных клиента.
  2. Рекуррентные нейронные сети/преобразователи с необработанными данными каждого клиента.

Разработка функций

Мы начинаем с создания агрегированных характеристик для всех клиентов:
поскольку данные содержат категориальные числовые характеристики. Мы обрабатываем их отдельно.

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

  1. Средние значения для последних 13 операторов
  2. Последние значения функций, т. е. последний статус клиента
  3. Минимальное значение функции
  4. Максимальное значение функции
  5. Для категориальных переменных мы можем подсчитать количество уникальных значений

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

Num_feats  = df.groupby("customer_ID")[numerical_features].agg(['mean', 'std', 'min', 'max', 'median', 'last'])
Num_feats.columns = ['_'.join(x) for x in Num_feats.columns]

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

Cat_feats  = df.groupby("customer_ID")[categorical_features].agg(['count','last','nunique'])
Cat_feats.columns = ['_'.join(x) for x in Cat_feats.columns]

Набор данных включает нулевые значения; мы заполняем их определенным значением (-128 в данном случае) или оставляем их как nan, поскольку модели на основе бустинг-деревьев способны учитывать значения nan.

Образец данных после разработки признаков выглядит так

Метрика оценки

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

  1. AUC-ROC: площадь под кривой рабочих характеристик приемника, которая измеряет способность модели различать положительные и отрицательные классы.
  2. Отзыв: доля истинно положительных результатов среди всех фактически положительных случаев.
  3. Нормализованный коэффициент Джини. Нормализованный коэффициент Джини равен 2*AUC-1 и находится в диапазоне от -1 до 1.
  4. Коэффициент регистрации по умолчанию при пороговом значении p%: доля истинно положительных результатов (отзыв) для порога, установленного на уровне p% от общего (взвешенного) количества выборок.

На приведенном выше рисунке красная область представляет показатель AUC-ROC, а пересечение зеленой линии с кривой представляет уровень по умолчанию, полученный при p% (чем выше пересечение, тем лучше).

Мы оцениваем наш подход, используя среднее значение нормализованного коэффициента Джини и коэффициент дефолта, зафиксированный на уровне 4%.

def evaluation_metric(y_true, y_pred):
    labels     = np.transpose(np.array([y_true, y_pred]))
    labels     = labels[labels[:, 1].argsort()[::-1]]
    weights    = np.where(labels[:,0]==0, 20, 1) # 0 labels are weighted by 20 due to upsampling of customers with label 0
    cut_vals   = labels[np.cumsum(weights) <= int(0.04 * np.sum(weights))]
    top_four   = np.sum(cut_vals[:,0]) / np.sum(labels[:,0]) #calculates default rate at 4%

    #calculation of gini=2*AUC-1
    gini = [0,0]
    for i in [1,0]:
        labels         = np.transpose(np.array([y_true, y_pred]))
        labels         = labels [labels[:, i].argsort()[::-1]]
        weight         = np.where(labels[:,0]==0, 20, 1)  #weighing labels
        weight_random  = np.cumsum(weight / np.sum(weight))
        total_pos      = np.sum(labels[:, 0] *  weight) 
        cum_pos_found  = np.cumsum(labels[:, 0] * weight)
        Lorentz        = cum_pos_found / total_pos  
        gini[i]        = np.sum((Lorentz - weight_random) * weight)

    return 0.5 * (gini[1]/gini[0] + top_four)

Стратегия оценки модели

Поскольку данные имеют несбалансированное распределение классов, использование стратифицированных K-кратностей в качестве стратегии перекрестной проверки является хорошим выбором при работе с несбалансированными наборами данных. Убедившись, что каждая складка имеет ту же долю положительного класса, что и общий набор данных, вы можете лучше оценить производительность своей модели в классе меньшинства. Графическое представление стратифицированных K-складок показано ниже.

Базовая модель

LightGBM (LGBM) — хороший выбор для задач классификации, особенно при работе с большими наборами данных, поскольку было обнаружено, что он дает самые современные результаты при работе с табличными наборами данных.

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

features = [f for f in train.columns if f != 'customer_ID' and f != 'target']

#for storing validation results.
score_list = []
y_pred_list = []

kf = StratifiedKFold(n_splits=5) # generating 5 stratified folds
target=train["target"]
for fold, (trn_ind, test_ind) in enumerate(kf.split(train, target)):
    start_time = datetime.datetime.now()
    
    #creating data for training
    X_train = train.iloc[trn_ind][features]
    y_train = target[trn_ind]
  
    #creating data fro validation
    X_valid = train.iloc[test_ind][features]
    y_valid = target[test_ind]
    
    #define lgbm classifier with parameters
    model = LGBMClassifier(n_estimators=1000,
                          learning_rate=0.03, reg_lambda=50,
                          min_child_samples=2400,
                          num_leaves=95,
                          colsample_bytree=0.19,
                          max_bins=511, random_state=101 )

  #to remove the warning by LGBM classifier
    with warnings.catch_warnings():
        warnings.filterwarnings('ignore', category=UserWarning)
    
    model.fit(X_train, y_train,
                  eval_set = [(X_valid, y_valid)], 
                  eval_metric=[evaluation_metric],
                  callbacks=[log_evaluation(100)])

    y_va_pred = model.predict_proba(X_va, raw_score=True)
    score = evaluation_metric(y_va, y_va_pred)

    #save model with best evaluation scores.
    n_trees = model.best_iteration_
    if n_trees is None: 
        n_trees = model.n_estimators
    
    print(f"Fold {fold} | {str(datetime.datetime.now() - start_time)[-12:-7]} |"
          f" {n_trees:5} trees |"f"                Score = {score:.5f}{Style.RESET_ALL}")
    
    score_list.append(score)
    
    if ONLY_FIRST_FOLD: break # for the training of first fold only
    
    #delete the training and validation data to save memory
    del X_train, y_train,X_valid, y_valid

print(f"OOF Score:    {np.mean(score_list):.5f}{Style.RESET_ALL}")
469 features
[100]	valid_0's binary_logloss: 0.247296	valid_0's : 0.764518
[200]	valid_0's binary_logloss: 0.22843	valid_0's : 0.779298
[300]	valid_0's binary_logloss: 0.223237	valid_0's : 0.786733
[400]	valid_0's binary_logloss: 0.220893	valid_0's : 0.790104
[500]	valid_0's binary_logloss: 0.219559	valid_0's : 0.791775
[600]	valid_0's binary_logloss: 0.218766	valid_0's : 0.792098
[700]	valid_0's binary_logloss: 0.218199	valid_0's : 0.793434
[800]	valid_0's binary_logloss: 0.217769	valid_0's : 0.79412
[900]	valid_0's binary_logloss: 0.21744	valid_0's : 0.794347
[1000]	valid_0's binary_logloss: 0.217244	valid_0's : 0.794655
Fold 0 | 04:34 |  1000 trees |                Score = 0.794655

По сравнению с максимальной оценкой 1 мы получаем оценку 0,794655 по нашей оценочной метрике, которая представляет собой среднее значение AUC и коэффициента по умолчанию, измеренного на уровне 4% для кратности 1 (обучение нашей модели на 80% данных и ее проверка на 20%). ).

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

import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
import plotly.express as px
def plotImp(model, X , num = 20, fig_size = (40, 20)):
    feature_imp = pd.DataFrame({'Value':model.feature_importances_,'Feature':X.columns})
    fig = px.bar(feature_imp.sort_values(by="Value",ascending=False)[0:num], x="Value", y="Feature", orientation='h',color='Feature')
    fig.show()
plotImp(model,train[features],15)

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

LGBMClassifier в пакете Pythonlightgbm предоставляет метод plot_tree() для построения одного дерева решений из изученного ансамбля деревьев. Это может быть полезно для визуализации внутренней работы модели и понимания того, как она делает прогнозы.

Теперь у нас есть готовая базовая модель. В нашем следующем блоге мы подробно обсудим другие подходы, такие как:

  1. Представляем больше методов проектирования функций.
  2. Использование характеристик временных рядов данных и их использование для обучения преобразователей и рекуррентных нейронных сетей (таких как GRU).
  3. Использование комбинации моделей на основе дерева вместе с нейронными сетями.

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