Введение

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

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

В этом исследовании классификация будет использоваться для присвоения классов и меток набору данных. В прогнозном моделировании классификация включает прогнозирование меток классов для конкретных выборок входных данных. Это конкретное исследование сосредоточено на бинарной классификации, при этом целевой меткой является ранее упомянутый «Статус кредита». Для анализа рассматриваются различные классификаторы, такие как случайный лес, дерево решений, логистическая регрессия, XGBoost и дополнительный классификатор дерева. Используя данные о прошлых кредитах и ​​информацию о дефолтах заемщика, эти модели могут оценивать уровень риска, связанного с кредитом, давая дискретные результаты, такие как списание. Ожидается, что результаты эксперимента продемонстрируют оптимальную производительность для нескольких классификаторов. Для оценки производительности модели используются пять показателей: отзыв, точность, точность, оценка F1 и AUC. Выявление ключевых переменных, которые четко указывают на дефолт, было бы выгодно для кредитных учреждений. Вооружившись этими знаниями, учреждения могут оценить риск в рамках своего портфеля и оценить возможность полного погашения кредита новыми потенциальными клиентами.

Набор данных можно найти по следующей ссылке: https://www.kaggle.com/datasets/karan842/lending-data/download?datasetVersionNumber=1

Сводка данных

Lending Club, сеть P2P-кредитования, предлагает кредиты нуждающимся лицам, определяя процентную ставку на основе их кредитной истории и других соответствующих переменных. Набор данных, используемый в этом анализе, состоит из 396 029 строк и 27 столбцов, включая такую ​​информацию, как сумма кредита, срок кредита, процентная ставка, годовой доход и статус кредита, среди прочего. Однако набор данных демонстрирует дисбаланс: класс меньшинства представляет только около 20% данных. Для решения этой проблемы будут использоваться различные методы машинного обучения и методы повторной выборки.

Набор данных включает 12 числовых переменных, 8 категориальных переменных, 2 порядковые переменные и 6 избыточных переменных, которые не вносят существенного вклада в анализ. В качестве первого шага из набора данных были удалены избыточные переменные, такие как sub_grade, emp_title, issue_d, Early_cr_line, address и title. Такие переменные, как issue_d и Early_cr_line, представляют собой переменные даты, которые не имеют отношения к анализу, в то время как остальные переменные имеют множество категорий, которые не имеют прогностического значения.

Описание данных

  1. credit_amnt: сумма кредита, на которую подал заявку заемщик, которая может быть скорректирована кредитным отделом.
  2. срок: продолжительность кредита в месяцах, 36 или 60.
  3. int_rate: процентная ставка по кредиту.
  4. рассрочка: ежемесячный платеж, который заемщик должен внести, если кредит будет одобрен.
  5. Оценка: оценка кредита, присвоенная кредитной платформой.
  6. sub_grade: субкласс кредита, назначенный кредитной платформой.
  7. emp_title: Должность, указанная заемщиком при подаче заявки на кредит.
  8. emp_length: стаж работы в годах в диапазоне от 0 (менее одного года) до 10 (десять и более лет).
  9. home_ownership: статус собственности на жилье, указанный заемщиком, включая такие категории, как АРЕНДА, СОБСТВЕННОСТЬ, ИПОТЕКА и ДРУГОЕ.
  10. годовой_inc: Годовой доход заемщика, о котором сообщают сами.
  11. Verification_status: указывает, был ли доход заемщика подтвержден кредитной платформой.
  12. issue_d: месяц, в котором был профинансирован кредит.
  13. кредит_статус: Текущий статус кредита.
  14. цель: категория, предоставленная заемщиком, указывающая цель запроса кредита.
  15. title: Название кредита, предоставленное заемщиком.
  16. dti: отношение долга к доходу заемщика, рассчитанное путем деления общей суммы ежемесячных платежей по долгу (за исключением ипотечного кредита и запрошенного кредита) на ежемесячный доход, о котором сообщают сами заемщики.
  17. Early_cr_line: месяц, когда была открыта самая ранняя из заявленных кредитных линий заемщика.
  18. open_acc: количество открытых кредитных линий в кредитном файле заемщика.
  19. pub_rec: количество оскорбительных публичных записей.
  20. revol_bal: общий возобновляемый баланс кредита.
  21. revol_util: коэффициент использования возобновляемой линии, показывающий использование кредита заемщиком по отношению к доступному возобновляемому кредиту.
  22. total_acc: общее количество кредитных линий в кредитном файле заемщика.
  23. initial_list_status: начальный листинговый статус ссуды, обозначаемый буквами «W» или «F».
  24. application_type: указывает, является ли заявка на получение кредита индивидуальной или совместной заявкой.
  25. mort_acc: количество ипотечных счетов.
  26. pub_rec_bankruptcies: количество публичных банкротств.
  27. адрес: адрес заемщика, указанный в кредитной заявке.

Импорт пакетов и набора данных

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline  

loan_df = pd.read_csv('/kaggle/input/lending-data/lending_club_loan_two.csv')
loan_df

Давайте проведем описательный анализ набора данных.

loan_df.info()

loan_df.isnull().sum()

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

Вменение нулевых значений

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

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

Разделение переменных на числовые и категориальные переменные

data_types = loan_df.dtypes
numerical_variables = data_types[data_types != 'object'].index.tolist()
categorical_variables = data_types[data_types == 'object'].index.tolist()

Вменение значений

for numbers in numerical_variables:
    loan_df[numbers] = loan_df[numbers].fillna(loan_df[numbers].median())

for variable in categorical_variables:
    mode_value = loan_df[variable].mode().values[0]
    loan_df[variable].fillna(mode_value, inplace=True)

Это будет вменять значения нулевым значениям

Визуализация данных

  1. Однофакторный анализ
sns.set_palette('Set2')
sns.countplot(x = loan_df['loan_status'])
plt.grid()

На приведенной выше гистограмме представлена ​​переменная результата «Статус кредита». Переменная результата классифицируется как полностью выплаченные и списанные кредиты. Можно заметить, что доля полностью выплаченных кредитов намного выше, чем списанных кредитов. Набор данных включает 80,39% полностью выплаченных кредитов и 19,61% списанных кредитов. Модель, построенная с таким дисбалансом классов, будет благоприятствовать результатам класса большинства, поэтому перед разработкой модели используется подходящий метод повторной выборки для избыточной выборки данных. Это подробно обсуждается в последующих главах настоящего исследования.

sns.histplot(loan_df['loan_amnt'], bins=10)
plt.xlabel('Loan Amount')
plt.ylabel('Number of occurrence')
plt.grid()

sns.histplot(loan_df['int_rate'], bins=10)
plt.xlabel('Interest Rate')
plt.ylabel('Number of occurrence')
plt.grid()

sns.histplot(loan_df['installment'], bins=10)
plt.xlabel('Installment Amount')
plt.ylabel('Number of occurrence')
plt.grid()

Графики гистограмм, представленные выше, демонстрируют взаимосвязь между различными атрибутами и вероятностью одобрения кредита. На основании графиков видно, что наибольшая частота одобренных кредитов приходится на сумму от 5000 до 15000 долларов США. Распределение суммы кредита, процентной ставки и суммы взноса демонстрирует перекос вправо. Примечательно, что значительное количество кредитов утверждается, когда процентная ставка колеблется от 10% до 15%. Кроме того, значительная часть выдаваемых кредитов соответствует суммам рассрочки от 200 до 400. Важно отметить, что с увеличением суммы рассрочки также увеличивается риск невозврата кредита для заемщиков, что приводит к снижению количества одобренных кредитов.

fig = plt.figure(figsize=(15,15))
grid = fig.add_gridspec(2,2)

ax0 = fig.add_subplot(grid[0,0])
sns.boxplot(loan_df['pub_rec'], orient='h', ax=ax0)
plt.grid()
ax0.set_xlabel('\nPublic Records')

ax1 = fig.add_subplot(grid[0,1])
sns.boxplot(loan_df['pub_rec_bankruptcies'], orient='h', ax=ax1)
plt.grid()
ax1.set_xlabel('\nPublic Record Bankruptcies')

ax2 = fig.add_subplot(grid[1,0])
sns.boxplot(loan_df['mort_acc'], orient='h', ax=ax2)
plt.grid()
ax2.set_xlabel('\nNumber of Mortgage Accounts')

ax3 = fig.add_subplot(grid[1,1])
sns.boxplot(loan_df['total_acc'], orient='h', ax=ax3)
plt.grid()
ax3.set_xlabel('\nTotal Accounts')

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

В случае общедоступных записей диапазон простирается от 0 до более чем 90, при этом у большинства клиентов нет общедоступных записей. Что касается общедоступных записей о банкротстве, то большинство клиентов не имеют записей о банкротстве, хотя у некоторых исключений есть до 8 записей.

Для количества ипотечных счетов значения в основном находятся в диапазоне от 0 до 6 с небольшими отклонениями. Общее количество счетов, которыми владеют клиенты, сильно варьируется: максимум 140 счетов, принадлежащих одному клиенту. Однако большинство клиентов имеют от 1 до 55 учетных записей. Медиана (50-й процентиль) для ипотечных счетов составляет примерно 2, в то время как медиана для всех счетов составляет около 25.

plt.figure(figsize=(20, 5))
sns.histplot(loan_df['annual_inc'], color= 'red')
plt.xlabel('Annual Income')
plt.ylabel('Count')
plt.title('Distribution of Annual Income')

plt.show()

Гистограмма годового дохода выше показывает годовой доход клиента.

2. Двумерный анализ

plt.figure(figsize=(15, 5))
sns.countplot(x = loan_df.purpose, hue=loan_df.loan_status)
plt.xlabel('Purpose')
plt.ylabel('count')
plt.xticks(rotation=90)
plt.title('Purpose of Loan')
plt.grid()

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

plt.figure(figsize=(15, 5))
sns.countplot(x = loan_df.home_ownership, hue=loan_df.loan_status)
plt.xlabel('Home Ownership')
plt.ylabel('count')
plt.title('Home Ownership Vs Loan Status')
plt.grid()

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

plt.figure(figsize=(15, 5))
sns.countplot(x = loan_df.term, hue=loan_df.loan_status)
plt.xlabel('term')
plt.ylabel('count')
plt.title('Term Vs Loan Status')
plt.grid()

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

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

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

plt.figure(figsize=(15, 5))
sns.countplot(x = loan_df.grade, hue=loan_df.loan_status)
plt.xlabel('Grade')
plt.ylabel('count')
plt.title('Grade Vs Loan Status')
plt.grid()

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

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

График особенно интересен, так как он показывает, что уровни B и C встречаются чаще, чем уровень A. Это говорит о том, что людям с уровнем A, возможно, не нужно брать деньги взаймы так часто, что приводит к снижению спроса на кредиты среди этой группы.

plt.figure(figsize=(15, 5))
sns.countplot(x = loan_df.emp_length, hue=loan_df.loan_status)
plt.xlabel('Employment Length')
plt.ylabel('count')
plt.title('Employment Length Vs Loan Status')
plt.grid()

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

plt.figure(figsize=(15, 5))
sns.boxplot(x = loan_df.term, y = loan_df.loan_amnt, hue=loan_df.loan_status)
plt.xlabel('Term')
plt.ylabel('Loan Amount')
plt.title('Term Vs Loan Amount Vs Loan Status')
plt.grid()

Блочные диаграммы дают представление о распределении сумм кредита по разным срокам кредита. В случае 36-месячных кредитов медиана (50-й процентиль) как для полностью выплаченных, так и для списанных кредитов находится в диапазоне от 6000 до 16000 долларов. Для 60-месячных кредитов средняя сумма кредита составляет от 15 000 до 25 000 долларов США.

Примечательно, что при обоих сроках кредита медианные суммы кредита как для полностью выплаченных, так и для списанных кредитов выше в случае 60-месячных кредитов по сравнению с 36-месячными кредитами. Это говорит о том, что заемщики, выбирающие более долгосрочные кредиты, как правило, запрашивают более высокие суммы кредита.

Кроме того, наблюдается, что количество как полностью выплаченных, так и списанных кредитов выше для 60-месячных кредитов по сравнению с 36-месячными кредитами. Это указывает на то, что больший объем кредитов, как успешных, так и невозвратных, связан с более длительным сроком кредита.

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

plt.figure(figsize=(15, 5))
sns.boxplot(x = loan_df.term, y = loan_df.int_rate, hue=loan_df.loan_status)
plt.xlabel('Term')
plt.ylabel('Mortgage')
plt.title('Term Vs Number of Interest Rate Vs Loan Status')
plt.grid()

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

plt.figure(figsize=(15, 5))
sns.boxplot(x = loan_df.term, y = loan_df.mort_acc, hue=loan_df.loan_status)
plt.xlabel('Term')
plt.ylabel('Mortgage')
plt.title('Term Vs Number of Mortgage Accounts Vs Loan Status')
plt.grid()

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

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

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

Новая переменная под названием «avail_bal» была создана путем расчета разницы между переменными «revol_bal» (остаток возобновляемого кредита) и «revol_util» (коэффициент использования возобновляемой линии). Эта новая переменная представляет доступный баланс для каждого клиента.

Вычитая сумму использованного возобновляемого кредита из общего остатка возобновляемого кредита, можно получить доступный остаток. Эта переменная дает представление о сумме кредита, которая остается неиспользованной или доступной для каждого клиента.

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

loan_df['available_balance'] = loan_df['revol_bal'] - loan_df['revol_util']

Обработка выбросов

Давайте проверим перекос набора данных

loan_df.skew()

Для устранения выбросов в наборе данных был реализован подход к обработке. Первоначально была оценена асимметрия каждой числовой переменной, и переменные с асимметрией более 2 были идентифицированы как кандидаты на обработку выбросов. Было обнаружено, что из 13 исследованных числовых переменных 8 имеют асимметрию либо влево, либо вправо.

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

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

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

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

skewed_variables = ['annual_inc', 'dti', 'open_acc','pub_rec', 'revol_bal','mort_acc', 'pub_rec_bankruptcies','available_balance'] 

for variable in skewed_variables:
    lower_limit = np.percentile(loan_df[variable], 10)
    upper_limit = np.percentile(loan_df[variable], 90)
    loan_df[variable] = np.where(loan_df[variable] < lower_limit, lower_limit, loan_df[variable])
    loan_df[variable] = np.where(loan_df[variable] > upper_limit, upper_limit, loan_df[variable])

Давайте проверим асимметрию после обработки

Кодировка этикетки

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

Для переменной результата «Статус кредита», которая имеет два класса, представленных строками «Полностью оплачен» и «Списан», применяется двоичное кодирование. «Полностью оплачено» присваивается значение 1, а «Списано» — значение 0.

Точно так же для переменной «срок», где есть две категории, 36 месяцев и 60 месяцев, используется двоичное кодирование. Срочные ссуды на 36 месяцев преобразуются в значение 1, а срочные ссуды на 60 месяцев присваиваются значения 0.

Переменная состояния исходного списка, представленная значениями «f» и «w», также кодируется с использованием двоичного кодирования. «f» присваивается значение 1, а «w» присваивается значение 0.

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

Наконец, переменная «Оценка», которая находится в диапазоне от A до G, преобразуется в числовые значения от 1 до 7 соответственно. Это решение принимается на основе корреляции, наблюдаемой между оценкой и процентной ставкой, где процентная ставка увеличивается по мере того, как оценка перемещается от A к G.

encodable_variables = ['loan_status', 'initial_list_status', 'term', 'home_ownership', 'verification_status', 'purpose', 'application_type']
loan_df = pd.get_dummies(loan_df, columns=encodable_variables, drop_first=True)

grade_mapping = {'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'F': 6, 'G': 7}
loan_df['grade'] = loan_df['grade'].map(grade_mapping)

Переменная «стаж работы», первоначально представленная буквенно-цифровыми символами, была преобразована в числовой формат. Преобразование включает в себя сопоставление количества лет опыта с числовым диапазоном от 1 до 10.

Особые случаи обрабатываются следующим образом:

  • «‹1 год» объединяется и представляется как 1, что означает менее одного года опыта.
  • «10+ лет» преобразуется в 10, что соответствует максимальному стажу.

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

emp_length_mapping = {'< 1 year': 1, '1 year': 1, '2 years': 2, '3 years': 3, '4 years': 4,
                        '5 years': 5, '6 years': 6, '7 years': 7, '8 years': 8, '9 years': 9,
                        '10+ years': 10}

loan_df['emp_length'] = loan_df['emp_length'].replace(emp_length_mapping)

Важность функции

Классификатор XGB можно использовать для определения существенных особенностей прогнозирования переменной результата. Набор данных, состоящий из 34 переменных, был разделен на X (независимые переменные) и y (зависимая переменная) для ввода в модель для анализа важности признаков.

Результаты показали, что переменные «int_rate» и «term» одинаково важны для прогнозирования статуса кредита, каждая из которых составляет 15% от общей важности функции. Эти две переменные были определены как наиболее влиятельные факторы в процессе прогнозирования.

Учитывая значимость различных признаков, для дальнейшего анализа были выбраны 10 основных переменных. Стоит отметить, что эксперименты проводились с использованием топ-5 и топ-15 переменных, но они не дали плодотворных результатов. На приведенном ниже графике наглядно представлены 10 основных переменных и их значение для прогнозирования статуса кредита.

import xgboost as xgb

drop_var = ['loan_status_Fully Paid', 'sub_grade', 'emp_title', 'issue_d', 'title', 'earliest_cr_line', 'address']

X = loan_df.drop(drop_var, axis=1)
y = loan_df['loan_status_Fully Paid']

xgb_classifier = xgb.XGBClassifier(n_estimators=100, random_state=42)
xgb_classifier.fit(X, y)

importance_scores = xgb_classifier.feature_importances_

feature_importance_df = pd.DataFrame({'Feature': X.columns, 'Importance': importance_scores})
feature_importance_df = feature_importance_df.sort_values(by='Importance', ascending=False)

top_10_features = feature_importance_df.head(10)


plt.figure(figsize=(10, 6))
plt.bar(top_10_features['Feature'], top_10_features['Importance'])
plt.xticks(rotation=45)
plt.title('Feature Importances obtained from coefficients ')
plt.show()

Проверка мультиколлинеарности

Чтобы решить проблему мультиколлинеарности, для оценки корреляции между переменными использовался коэффициент инфляции дисперсии (VIF). Перед проверкой VIF переменные, которые не соответствовали распределению 0–1, были нормализованы для достижения единообразия. Для значений VIF было установлено пороговое значение 5. Переменные с VIF выше 5 систематически удалялись, начиная с переменной, имеющей самый высокий VIF, для наблюдения за влиянием на оставшиеся переменные.

После удаления переменной «int_rate» с высоким VIF значения VIF были переоценены. Тем не менее, «annual_inc» по-прежнему демонстрировал VIF выше 6. Следовательно, «annual_inc» также был удален, и процесс повторялся до тех пор, пока все оставшиеся переменные не имели значения VIF ниже 5.

В результате были удалены только «int_rate» и «annual_inc», что привело к сокращению количества переменных до окончательного набора из 8. Эти выбранные переменные были сочтены подходящими для разработки модели. На приведенном ниже графике показаны переменные, которые в конечном итоге были выбраны для модели.

from statsmodels.stats.outliers_influence import variance_inflation_factor
from sklearn.preprocessing import MinMaxScaler


numerical_variables = loan_df.select_dtypes(include=['float64', 'uint8'])
scaler = MinMaxScaler()
numerical_variables = pd.DataFrame(scaler.fit_transform(numerical_variables), columns=numerical_variables.columns)

vif = pd.DataFrame()
vif["Variable"] = numerical_variables.columns
vif["VIF"] = [variance_inflation_factor(numerical_variables.values, i) for i in range(numerical_variables.shape[1])]
print(vif.round(2))

vif = vif.drop(vif[vif['VIF'] > 4.6].index)

select_var = vif['Variable'].tolist()
itr2 = loan_df[select_var]
vif = pd.DataFrame()
vif["Variable"] = itr2.columns
vif["VIF"] = [variance_inflation_factor(itr2.values, i) for i in range(itr2.shape[1])]
print(vif.round(2))

После выбора переменных с наивысшими оценками vif мы получаем -dti, mort_acc, срок_ 60 месяцев, home_ownership_RENT, цель_свадьба, статус_проверки_Источник подтвержден, цель_малый_бизнес, тип_заявки_СОЕДИНЕНИЕ

Разделение переменных на цель и переменные. Также разбивая их на поезд и набор тестов

X = loan_df[['dti','mort_acc', 'term_ 60 months', 'home_ownership_RENT', 'purpose_wedding', 'verification_status_Source Verified', 'purpose_small_business', 'application_type_JOINT']]
Y = loan_df['loan_status_Fully Paid']


from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.3)

Моделирование

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

1. Логистическая регрессия

2.Дерево решений

3. Случайный лес

4.XGBoost

5. Дополнительный классификатор дерева

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

Учитывая несбалансированный характер набора данных, который является общей характеристикой наборов данных в режиме реального времени, класс интереса в этом анализе представляет собой списанные кредиты. Отзыв считается наиболее подходящей мерой для оценки эффективности списанных кредитов (класс 0), поскольку он рассчитывает процент ложных срабатываний для всех возможных экземпляров класса 0. Кроме того, в качестве меры используется AUC (площадь под кривой). поскольку он сочетает в себе как отзыв, так и специфичность, и на его кривую не влияют различия в пропорциях классов. Более высокая AUC указывает на лучшую производительность модели при различении положительных и отрицательных классов. Другие показатели, такие как F1-score и Precision, также используются для оценки производительности модели.

Логистическая регрессия

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

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

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

from sklearn.linear_model import LogisticRegression

log_reg = LogisticRegression()
log_reg.fit(X_train, y_train)
from sklearn.metrics import precision_score, recall_score, accuracy_score, f1_score

lr_accuracy = (accuracy_score(y_test, Y_pred_log)*100).round(2)
lr_precision = (precision_score(y_test, Y_pred_log)*100).round(2)
lr_recall = (recall_score(y_test, Y_pred_log)*100).round(2)
lr_f1 = (f1_score(y_test, Y_pred_log)*100).round(2)

Также создание функции для построения матрицы путаницы

from sklearn.metrics import confusion_matrix

def plot_confusion_matrix(y,y_predict):
    cm = confusion_matrix(y, y_predict)
    ax= plt.subplot()
    sns.heatmap(cm, cmap='coolwarm',annot=True,fmt = " ", ax = ax);
    ax.set_xlabel('Predicted labels')
    ax.set_ylabel('True labels')
    ax.set_title('Confusion Matrix');
    ax.xaxis.set_ticklabels(['Fully Paid','Charged Off']);
plot_confusion_matrix(y_test, Y_pred_log)

Гиперпараметрическая настройка модели логистической регрессии

from sklearn.model_selection import GridSearchCV

param_grid = {
    'penalty': ['l1', 'l2'],
    'C': [0.1, 1, 10]
}

log_reg_cv = GridSearchCV(log_reg, param_grid, cv = 5)
log_reg_cv.fit(X_train, y_train)
best_model = log_reg_cv.best_estimator_
Y_pred_logcv = best_model.predict(X_test)
print("Best Hyperparameters: ", log_reg_cv.best_params_)
lrcv_accuracy = (accuracy_score(y_test, Y_pred_logcv)*100).round(2)
lrcv_precision = (precision_score(y_test, Y_pred_logcv)*100).round(2)
lrcv_recall = (recall_score(y_test, Y_pred_logcv)*100).round(2)
lrcv_f1 = (f1_score(y_test, Y_pred_logcv)*100).round(2)
plot_confusion_matrix(y_test, Y_pred_logcv)

Древо решений

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

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

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

from sklearn.tree import DecisionTreeClassifier

dtc = DecisionTreeClassifier()
dtc.fit(X_train, y_train)
dtc_accuracy = (accuracy_score(y_test, Y_pred_dtc)*100).round(2)
dtc_precision = (precision_score(y_test, Y_pred_dtc)*100).round(2)
dtc_recall = (recall_score(y_test, Y_pred_dtc)*100).round(2)
dtc_f1 = (f1_score(y_test, Y_pred_dtc)*100).round(2)
plot_confusion_matrix(y_test, Y_pred_dtc)

Гиперпараметрическая настройка модели дерева решений

param_grid = {
    'criterion': ['gini', 'entropy'],
    'max_depth': [3, 5, 7],
    'min_samples_split': [2, 5, 10],
    'min_samples_leaf': [1, 2, 4]
}

dtc_cv = GridSearchCV(dtc, param_grid, cv = 5)
dtc_cv.fit(X_train, y_train)
best_model = dtc_cv.best_estimator_
Y_pred_dtccv = best_model.predict(X_test)
dtc_cv_accuracy = (accuracy_score(y_test, Y_pred_dtccv)*100).round(2)
dtc_cv_precision = (precision_score(y_test, Y_pred_dtccv)*100).round(2)
dtc_cv_recall = (recall_score(y_test, Y_pred_dtccv)*100).round(2)
dtc_cv_f1 = (f1_score(y_test, Y_pred_dtccv)*100).round(2)
plot_confusion_matrix(y_test, Y_pred_dtccv)

Случайный лес

Random Forest — очень мощный и широко используемый алгоритм в машинном обучении. Он относится к категории ансамблевых моделей, то есть объединяет несколько моделей для прогнозирования. В частности, Random Forest использует в своем процессе несколько моделей деревьев решений, а окончательный прогноз определяется путем усреднения независимых прогнозов каждого дерева.

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

Используя различные подмножества данных и атрибутов для создания некоррелированных моделей, Random Forest эффективно снижает риск переобучения. Это означает, что модель хорошо обобщается на новые, невидимые данные и не слишком адаптирована к обучающим данным.

from sklearn.ensemble import RandomForestClassifier

rfc = RandomForestClassifier()
rfc.fit(X_train, y_train)
rfc_accuracy = (accuracy_score(y_test, Y_pred_rfc)*100).round(2)
rfc_precision = (precision_score(y_test, Y_pred_rfc)*100).round(2)
rfc_recall = (recall_score(y_test, Y_pred_rfc)*100).round(2)
rfc_f1 = (f1_score(y_test, Y_pred_rfc)*100).round(2)
Y_pred_rfc = rfc.predict(X_test)
plot_confusion_matrix(y_test, Y_pred_rfc)

XGBoost

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

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

xgb_classifier.fit(X_train, y_train)
xgb_accuracy = (accuracy_score(y_test, Y_pred_xgb)*100).round(2)
xgb_precision = (precision_score(y_test, Y_pred_xgb)*100).round(2)
xgb_recall = (recall_score(y_test, Y_pred_xgb)*100).round(2)
xgb_f1 = (f1_score(y_test, Y_pred_xgb)*100).round(2)
plot_confusion_matrix(y_test, Y_pred_xgb)

Дополнительный классификатор дерева

Классификатор дополнительных деревьев — это метод ансамблевого обучения, который объединяет выходные данные нескольких деревьев решений, образуя «лес», для получения результатов классификации. Он имеет сходство с классификатором случайного леса, но отличается построением деревьев решений внутри леса.

В классификаторе дополнительных деревьев каждое дерево решений строится с использованием исходной обучающей выборки. Однако в процессе построения каждое дерево случайным образом выбирает подмножество признаков (k признаков) из всего набора признаков в каждом тестовом узле. Затем дерево определяет наилучшую функцию для разделения данных на основе определенных математических критериев, часто с использованием индекса Джини. Эта случайная выборка функций приводит к созданию нескольких деревьев решений, которые не коррелируют друг с другом.

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

from sklearn.ensemble import ExtraTreesClassifier
etc = ExtraTreesClassifier()
etc.fit(X_train, y_train)
etc_accuracy = (accuracy_score(y_test, Y_pred_etc)*100).round(2)
etc_precision = (precision_score(y_test, Y_pred_etc)*100).round(2)
etc_recall = (recall_score(y_test, Y_pred_etc)*100).round(2)
etc_f1 = (f1_score(y_test, Y_pred_etc)*100).round(2)
plot_confusion_matrix(y_test, Y_pred_etc)

Гиперпараметрическая настройка модели Extra Tree Classifier

param_grid = {
    'n_estimators': [100, 200, 300],
    'max_depth': [None, 5, 10],
    'min_samples_split': [2, 5, 10]
}

etc_cv = GridSearchCV(etc, param_grid, cv = 5)
etc_cv.fit(X_train, y_train)
etc_cv_accuracy = (accuracy_score(y_test, Y_pred_etc_cv)*100).round(2)
etc_cv_precision = (precision_score(y_test, Y_pred_etc_cv)*100).round(2)
etc_cv_recall = (recall_score(y_test, Y_pred_etc_cv)*100).round(2)
etc_cv_f1 = (f1_score(y_test, Y_pred_etc_cv)*100).round(2)

Давайте создадим фрейм данных с метриками

data = {
    'Metric': ['Accuracy', 'Precision', 'Recall', 'F1'],
    'LR': [lr_accuracy, lr_precision, lr_recall, lr_f1],
    'LRCV': [lrcv_accuracy, lrcv_precision, lrcv_recall, lrcv_f1],
    'DTC': [dtc_accuracy, dtc_precision, dtc_recall, dtc_f1],
    'DTC_CV': [dtc_cv_accuracy, dtc_cv_precision, dtc_cv_recall, dtc_cv_f1],
    'RFC': [rfc_accuracy, rfc_precision, rfc_recall, rfc_f1],
    'XGB': [xgb_accuracy, xgb_precision, xgb_recall, xgb_f1],
    'ETC': [etc_accuracy, etc_precision, etc_recall, etc_f1],
    'ETC_CV': [etc_cv_accuracy, etc_cv_precision, etc_cv_recall, etc_cv_f1]
}

metrics_df = pd.DataFrame(data)
metrics_df.set_index('Metric', inplace=True)

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

metrics_df.plot.bar(rot=0, figsize=(10, 6))
plt.xlabel('Metric')
plt.ylabel('Score')
plt.title('Performance Metrics')
plt.legend(loc='lower right')
plt.show()

Заключение

Extra Tree Classifier оказался лучшей моделью с высочайшей производительностью.