Создавайте прогностические модели для автоматизации процесса поиска подходящих кандидатов.

Вступление

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

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



Зарегистрируйтесь в учетной записи IBM Cloud, чтобы попробовать это руководство -



Оглавление

  1. Подготовка системы и загрузка данных
  2. Понимание данных
  3. Исследовательский анализ данных (EDA)
    i. Одномерный анализ
    ii. Двумерный анализ
  4. Отсутствие значения и обработка выбросов
  5. Метрики оценки для задач классификации
  6. Построение модели: Часть 1
  7. Логистическая регрессия с использованием стратифицированной перекрестной проверки k-кратных значений
  8. Функциональная инженерия
  9. Построение модели: Часть 2
    i. Логистическая регрессия
    ii. Дерево решений
    iii. Случайный лес
    iv. XGBoost

Подготовка системы и загрузка данных

В этом курсе мы будем использовать Python вместе с перечисленными ниже библиотеками.

Технические характеристики

  • Python
  • панды
  • морской
  • Sklearn
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
import warnings
warnings.filterwarnings(“ignore”)

Данные

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

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

Чтение данных

train = pd.read_csv(‘Dataset/train.csv’)
train.head()

test = pd.read_csv(‘Dataset/test.csv’)
test.head()

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

train_original=train.copy()
test_original=test.copy()

Понимание данных

train.columns
Index(['Loan_ID', 'Gender', 'Married', 'Dependents', 'Education',
       'Self_Employed', 'ApplicantIncome', 'CoapplicantIncome', 'LoanAmount',
       'Loan_Amount_Term', 'Credit_History', 'Property_Area', 'Loan_Status'],
      dtype='object')

У нас есть 12 независимых переменных и 1 целевая переменная, то есть Loan_Status в наборе обучающих данных.

test.columns
Index(['Loan_ID', 'Gender', 'Married', 'Dependents', 'Education',
       'Self_Employed', 'ApplicantIncome', 'CoapplicantIncome', 'LoanAmount',
       'Loan_Amount_Term', 'Credit_History', 'Property_Area'],
      dtype='object')

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

train.dtypes
Loan_ID               object
Gender                object
Married               object
Dependents            object
Education             object
Self_Employed         object
ApplicantIncome        int64
CoapplicantIncome    float64
LoanAmount           float64
Loan_Amount_Term     float64
Credit_History       float64
Property_Area         object
Loan_Status           object
dtype: object

Мы видим, что существует три формата типов данных:

  • объект: Формат объекта означает, что переменные категоричны. Категориальные переменные в нашем наборе данных: Loan_ID, Gender, замужем, иждивенцы, Education, Self_Employed, Property_Area, Loan_Status.
  • int64: представляет целочисленные переменные. Заявленный доход имеет этот формат.
  • float64: представляет переменную, в которой задействованы некоторые десятичные значения. Они также числовые
train.shape
(614, 13)

У нас есть 614 строк и 13 столбцов в наборе данных поезда.

test.shape
(367, 12)

У нас 367 строк и 12 столбцов в тестовом наборе данных.

train[‘Loan_Status’].value_counts()
Y    422
N    192
Name: Loan_Status, dtype: int64

Для параметра "Нормализация" можно установить значение "Истина", чтобы печатать пропорции вместо числа.

train[‘Loan_Status’].value_counts(normalize=True) 
Y    0.687296
N    0.312704
Name: Loan_Status, dtype: float64
train[‘Loan_Status’].value_counts().plot.bar()

Кредит одобрен 422 (около 69%) человек из 614.

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

  • Категориальные функции. У этих функций есть категории (Пол, Женат, Самостоятельная работа, Кредитная история, Статус ссуды).
  • Порядковые характеристики: переменные в категориальных характеристиках, имеющие определенный порядок (иждивенцы, образование, Property_Area).
  • Числовые характеристики. Эти характеристики имеют числовые значения (доход заявителя, доход второго заявителя, сумма займа, срок_средства_суды).

Независимая переменная (категориальная)

train[‘Gender’].value_counts(normalize=True).plot.bar(figsize=(20,10), title=’Gender’)
plt.show()
train[‘Married’].value_counts(normalize=True).plot.bar(title=’Married’)
plt.show()
train[‘Self_Employed’].value_counts(normalize=True).plot.bar(title=’Self_Employed’)
plt.show()
train[‘Credit_History’].value_counts(normalize=True).plot.bar(title=’Credit_History’)
plt.show()

Из приведенных выше гистограмм можно сделать вывод, что:

  • 80% кандидатов в наборе данных - мужчины.
  • Около 65% заявителей в наборе данных состоят в браке.
  • Около 15% кандидатов в наборе данных работают не по найму.
  • Около 85% претендентов оправдали свои сомнения.

Независимая переменная (порядковый номер)

train[‘Dependents’].value_counts(normalize=True).plot.bar(figsize=(24,6), title=’Dependents’)
plt.show()
train[‘Education’].value_counts(normalize=True).plot.bar(title=’Education’)
plt.show()
train[‘Property_Area’].value_counts(normalize=True).plot.bar(title=’Property_Area’)
plt.show()

Следующие выводы можно сделать из приведенных выше гистограмм:

  • У большинства соискателей нет иждивенцев.
  • Около 80% поступающих - выпускники.
  • Большинство соискателей из пригорода.

Независимая переменная (числовая)

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

sns.distplot(train[‘ApplicantIncome’])
plt.show()
train[‘ApplicantIncome’].plot.box(figsize=(16,5))
plt.show()

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

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

train.boxplot(column=’ApplicantIncome’, by = ‘Education’) 
plt.suptitle(“”)

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

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

sns.distplot(train[‘CoapplicantIncome’])
plt.show()
train[‘CoapplicantIncome’].plot.box(figsize=(16,5))
plt.show()

Мы видим такое же распределение, что и доход заявителя. Доход большинства соискателей колеблется от 0 до 5000. Мы также видим много выбросов в доходе заявителя, который обычно не распределяется.

train.notna()
sns.distplot(train[‘LoanAmount’])
plt.show()
train[‘LoanAmount’].plot.box(figsize=(16,5))
plt.show()

Мы видим много выбросов в этой переменной, и распределение довольно нормальное. Мы рассмотрим выбросы в следующих разделах.

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

Напомним некоторые из выдвинутых нами ранее гипотез:

  • Кандидаты с высокими доходами должны иметь больше шансов на одобрение кредита.
  • Кандидаты, которые выплатили свои предыдущие долги, должны иметь более высокие шансы на одобрение кредита.
  • Утверждение ссуды также должно зависеть от суммы ссуды. Если сумма ссуды меньше, шансы на одобрение ссуды должны быть высокими.
  • Чем меньше сумма, которая будет выплачиваться ежемесячно для погашения ссуды, тем выше шансы на одобрение ссуды.

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

Рассмотрев каждую переменную индивидуально в одномерном анализе, мы теперь снова исследуем их в отношении целевой переменной.

Категориальная независимая переменная vs целевая переменная

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

Gender=pd.crosstab(train[‘Gender’],train[‘Loan_Status’])
Gender.div(Gender.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True,figsize=(4,4))
plt.show()

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

Теперь давайте визуализируем оставшиеся категориальные переменные по сравнению с целевой переменной.

Married=pd.crosstab(train[‘Married’],train[‘Loan_Status’])
Dependents=pd.crosstab(train[‘Dependents’],train[‘Loan_Status’])
Education=pd.crosstab(train[‘Education’],train[‘Loan_Status’])
Self_Employed=pd.crosstab(train[‘Self_Employed’],train[‘Loan_Status’])
Married.div(Married.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True,figsize=(4,4))
plt.show()
Dependents.div(Dependents.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True,figsize=(4,4))
plt.show()
Education.div(Education.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True,figsize=(4,4))
plt.show()
Self_Employed.div(Self_Employed.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True,figsize=(4,4))
plt.show()

  • Доля заявителей, состоящих в браке, выше для утвержденных ссуд.
  • Распределение заявителей с 1 или 3+ иждивенцами одинаково для обеих категорий Loan_Status.
  • Из графика Self_Employed vs Loan_Status мы не можем сделать ничего существенного.

Теперь посмотрим на взаимосвязь между оставшимися категориальными независимыми переменными и Loan_Status.

Credit_History=pd.crosstab(train[‘Credit_History’],train[‘Loan_Status’])
Property_Area=pd.crosstab(train[‘Property_Area’],train[‘Loan_Status’])
Credit_History.div(Credit_History.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True,figsize=(4,4))
plt.show()
Property_Area.div(Property_Area.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True)
plt.show()

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

Теперь давайте визуализируем числовые независимые переменные относительно целевой переменной.

Числовая независимая переменная против целевой переменной

Мы попытаемся найти средний доход людей, для которых ссуда была одобрена, по сравнению со средним доходом людей, для которых ссуда не была одобрена.

train.groupby(‘Loan_Status’)[‘ApplicantIncome’].mean().plot.bar()

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

bins=[0,2500,4000,6000,81000]
group=[‘Low’,’Average’,’High’,’Very high’]
train[‘Income_bin’]=pd.cut(train[‘ApplicantIncome’],bins,labels=group)
Income_bin=pd.crosstab(train[‘Income_bin’],train[‘Loan_Status’])
Income_bin.div(Income_bin.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True)
plt.xlabel(‘ApplicantIncome’)
P=plt.ylabel(‘Percentage’)

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

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

bins=[0,1000,3000,42000]
group=[‘Low’,’Average’,’High’]
train[‘Coapplicant_Income_bin’]=pd.cut(train[‘CoapplicantIncome’],bins,labels=group)
Coapplicant_Income_bin=pd.crosstab(train[‘Coapplicant_Income_bin’],train[‘Loan_Status’])
Coapplicant_Income_bin.div(Coapplicant_Income_bin.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True)
plt.xlabel(‘CoapplicantIncome’)
P=plt.ylabel(‘Percentage’)

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

Давайте объединим доход соискателя и доход со-заявителя и посмотрим, как суммарный доход влияет на Loan_Status.

train[‘Total_Income’]=train[‘ApplicantIncome’]+train[‘CoapplicantIncome’]
bins=[0,2500,4000,6000,81000]
group=[‘Low’,’Average’,’High’,’Very high’]
train[‘Total_Income_bin’]=pd.cut(train[‘Total_Income’],bins,labels=group)
Total_Income_bin=pd.crosstab(train[‘Total_Income_bin’],train[‘Loan_Status’])
Total_Income_bin.div(Total_Income_bin.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True)
plt.xlabel(‘Total_Income’)
P=plt.ylabel(‘Percentage’)

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

Давайте визуализируем переменную «Сумма займа».

bins=[0,100,200,700]
group=[‘Low’,’Average’,’High’]
train[‘LoanAmount_bin’]=pd.cut(train[‘LoanAmount’],bins,labels=group)
LoanAmount_bin=pd.crosstab(train[‘LoanAmount_bin’],train[‘Loan_Status’])
LoanAmount_bin.div(LoanAmount_bin.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True)
plt.xlabel(‘LoanAmount’)
P=plt.ylabel(‘Percentage’)

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

Давайте оставим корзины, которые мы создали для исследовательской части. Мы изменим переменную 3+ в зависимостях на 3, чтобы сделать ее числовой переменной. Мы также преобразуем категории целевой переменной в 0 и 1, чтобы найти ее корреляцию с числовыми переменными. Еще одна причина для этого - несколько моделей, например, логистическая регрессия, принимают только числовые значения в качестве входных данных. Мы заменим N на 0 и Y на 1.

train=train.drop([‘Income_bin’, ‘Coapplicant_Income_bin’, ‘LoanAmount_bin’, ‘Total_Income_bin’, ‘Total_Income’], axis=1)
train[‘Dependents’].replace(‘3+’, 3,inplace=True)
test[‘Dependents’].replace(‘3+’, 3,inplace=True)
train[‘Loan_Status’].replace(’N’, 0,inplace=True)
train[‘Loan_Status’].replace(‘Y’, 1,inplace=True)

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

matrix = train.corr()
f, ax = plt.subplots(figsize=(9,6))
sns.heatmap(matrix,vmax=.8,square=True,cmap=”BuPu”, annot = True)

Мы видим, что наиболее коррелирующими переменными являются (ApplicantIncome - LoanAmount) и (Credit_History - Loan_Status). LoanAmount также коррелирует с CoapplicantIncome.

Вменение отсутствующего значения

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

train.isnull().sum()
Loan_ID               0
Gender               13
Married               3
Dependents           15
Education             0
Self_Employed        32
ApplicantIncome       0
CoapplicantIncome     0
LoanAmount           22
Loan_Amount_Term     14
Credit_History       50
Property_Area         0
Loan_Status           0
dtype: int64

Отсутствуют значения в функциях «Пол», «В браке», «Иждивенцы», «Самостоятельная работа», «LoanAmount», «Loan_Amount_Term» и «Credit_History».

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

Мы можем рассмотреть эти методы для заполнения недостающих значений:

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

В функциях «Пол», «В браке», «Иждивенцы», «Credit_History» и «Self_Employed» очень мало пропущенных значений, поэтому мы можем заполнить их, используя режим функций.

train[‘Gender’].fillna(train[‘Gender’].mode()[0], inplace=True)
train[‘Married’].fillna(train[‘Married’].mode()[0], inplace=True)
train[‘Dependents’].fillna(train[‘Dependents’].mode()[0], inplace=True)
train[‘Self_Employed’].fillna(train[‘Self_Employed’].mode()[0], inplace=True)
train[‘Credit_History’].fillna(train[‘Credit_History’].mode()[0], inplace=True)

Теперь давайте попробуем найти способ заполнить недостающие значения в Loan_Amount_Term. Мы рассмотрим количество значений переменной срока ссуды.

train[‘Loan_Amount_Term’].value_counts()
360.0    512
180.0     44
480.0     15
300.0     13
84.0       4
240.0      4
120.0      3
36.0       2
60.0       2
12.0       1
Name: Loan_Amount_Term, dtype: int64

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

train[‘Loan_Amount_Term’].fillna(train[‘Loan_Amount_Term’].mode()[0], inplace=True)

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

train[‘LoanAmount’].fillna(train[‘LoanAmount’].median(), inplace=True)

Теперь проверим, все ли недостающие значения заполнены в наборе данных.

train.isnull().sum()
Loan_ID              0
Gender               0
Married              0
Dependents           0
Education            0
Self_Employed        0
ApplicantIncome      0
CoapplicantIncome    0
LoanAmount           0
Loan_Amount_Term     0
Credit_History       0
Property_Area        0
Loan_Status          0
dtype: int64

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

test[‘Gender’].fillna(train[‘Gender’].mode()[0], inplace=True)
test[‘Married’].fillna(train[‘Married’].mode()[0], inplace=True)
test[‘Dependents’].fillna(train[‘Dependents’].mode()[0], inplace=True)
test[‘Self_Employed’].fillna(train[‘Self_Employed’].mode()[0], inplace=True)
test[‘Credit_History’].fillna(train[‘Credit_History’].mode()[0], inplace=True)
test[‘Loan_Amount_Term’].fillna(train[‘Loan_Amount_Term’].mode()[0], inplace=True)
test[‘LoanAmount’].fillna(train[‘LoanAmount’].median(), inplace=True)

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

Как мы видели ранее в одномерном анализе, LoanAmount содержит выбросы, поэтому мы должны обрабатывать их, поскольку наличие выбросов влияет на распределение данных. Давайте посмотрим, что может случиться с набором данных с выбросами. Для набора данных образца:
1,1,2,2,2,2,3,3,3,4,4
Мы находим следующее: среднее значение, медиана, мода и стандартное отклонение < br /> Среднее значение = 2,58
Медиана = 2,5
Режим = 2
Стандартное отклонение = 1,08
Если мы добавим выброс в набор данных:
1,1, 2,2,2,2,3,3,3,4,4,400
Новые значения нашей статистики:
Среднее = 35,38
Медианное = 2,5
Режим = 2
Стандартное отклонение = 114,74
Видно, что наличие выбросов часто оказывает значительное влияние на среднее и стандартное отклонение и, следовательно, влияет на распределение. Мы должны предпринять шаги для удаления выбросов из наших наборов данных.
Из-за этих выбросов основная часть данных в сумме ссуды находится слева, а правый хвост длиннее. Это называется правильным перекосом. Один из способов устранить перекос - выполнить преобразование журнала. Поскольку мы используем преобразование журнала, оно не сильно влияет на меньшие значения, но уменьшает большие значения. Итак, мы получаем распределение, подобное нормальному.
Давайте визуализируем эффект преобразования журнала. Мы внесем аналогичные изменения в тестовый файл одновременно.

train[‘LoanAmount_log’]=np.log(train[‘LoanAmount’])
train[‘LoanAmount_log’].hist(bins=20)
test[‘LoanAmount_log’]=np.log(test[‘LoanAmount’])

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

Построение модели: Часть I

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

  • Логистическая регрессия - это алгоритм классификации. Он используется для прогнозирования двоичного результата (1/0, Да / Нет, Истина / Ложь) с учетом набора независимых переменных.
  • Логистическая регрессия - это оценка функции логита. Функция logit - это просто журнал шансов в пользу события.
  • Эта функция создает S-образную кривую с оценкой вероятности, которая очень похожа на искомую ступенчатую функцию.

Чтобы узнать больше о логистической регрессии, обратитесь к этой статье: https://www.analyticsvidhya.com/blog/2015/10/basics-logistic-regression/
Давайте отбросим переменную Loan_ID, поскольку в ней нет какое-либо влияние на статус кредита. Мы внесем те же изменения в набор тестовых данных, что и для набора данных для обучения.

train=train.drop(‘Loan_ID’,axis=1)
test=test.drop(‘Loan_ID’,axis=1)

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

Чтобы узнать больше о sklearn, обратитесь сюда: http://scikit-learn.org/stable/tutorial/index.html

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

X = train.drop(‘Loan_Status’,1)
y = train.Loan_Status

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

  • Рассмотрим переменную «Пол». Он имеет два класса: мужской и женский.
  • Поскольку логистическая регрессия принимает в качестве входных данных только числовые значения, мы должны преобразовать мужской и женский пол в числовые значения.
  • После того, как мы применим к этой переменной фиктивные переменные, она преобразует переменную «Пол» в две переменные (Gender_Male и Gender_Female), по одной для каждого класса, то есть Male и Female.
  • Gender_Male будет иметь значение 0, если пол женский, и значение 1, если пол мужской.
X = pd.get_dummies(X)
train=pd.get_dummies(train)
test=pd.get_dummies(test)

Теперь мы обучим модель на обучающем наборе данных и сделаем прогнозы для тестового набора данных. Но можем ли мы подтвердить эти прогнозы? Один из способов сделать это - разделить набор данных поезда на две части: обучение и проверка. Мы можем обучить модель на этой обучающей части и с ее помощью делать прогнозы для проверочной части. Таким образом, мы можем проверить наши прогнозы, поскольку у нас есть истинные прогнозы для части проверки (которых у нас нет для тестового набора данных).

Мы будем использовать функцию train_test_split из sklearn, чтобы разделить наш набор данных поезда. Итак, сначала давайте импортируем train_test_split.

from sklearn.model_selection import train_test_split
x_train, x_cv, y_train, y_cv = train_test_split(X,y, test_size=0.3)

Набор данных разделен на обучающую и проверочную части. Давайте импортируем LogisticRegression и precision_score из sklearn и подгоним модель логистической регрессии.

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
model = LogisticRegression()
model.fit(x_train, y_train)
LogisticRegression()

Здесь параметр C представляет собой инверсию силы регуляризации. Регуляризация накладывает штраф на увеличение величины значений параметров с целью уменьшения переобучения. Меньшие значения C указывают на более сильную регуляризацию. Чтобы узнать о других параметрах, обратитесь сюда: http: // scikit- learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html

Давайте спрогнозируем Loan_Status для набора проверки и вычислим его точность.

pred_cv = model.predict(x_cv)
accuracy_score(y_cv,pred_cv)
0.7891891891891892

Таким образом, наши прогнозы точны почти на 80%, то есть мы правильно определили 80% статуса кредита.

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

pred_test = model.predict(test)

Давайте импортируем файл отправки, который мы должны отправить в средство проверки решения.

submission = pd.read_csv(‘Dataset/sample_submission.csv’)
submission.head()

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

submission[‘Loan_Status’]=pred_test
submission[‘Loan_ID’]=test_original[‘Loan_ID’]

Помните, что нам нужны прогнозы в Y и N. Итак, давайте преобразуем 1 и 0 в Y и N.

submission[‘Loan_Status’].replace(0, ’N’, inplace=True)
submission[‘Loan_Status’].replace(1, ‘Y’, inplace=True)

Наконец, мы конвертируем отправку в формат .csv.

pd.DataFrame(submission, columns=[‘Loan_ID’,’Loan_Status’]).to_csv(‘Output/logistic.csv’)

Логистическая регрессия с использованием стратифицированной перекрестной проверки k-кратных значений

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

  • Подход с использованием проверочного набора
  • k-кратная перекрестная проверка
  • Оставьте одну перекрестную проверку (LOOCV)
  • Стратифицированная k-кратная перекрестная проверка

Если вы хотите узнать больше о методах проверки, обратитесь к этой статье: https://www.analyticsvidhya.com/blog/2018/05/improve-model-performance-cross-validation-in-python-r/

В этом разделе мы узнаем о стратифицированной k-кратной перекрестной проверке. Давайте разберемся, как это работает:

  • Стратификация - это процесс переупорядочивания данных, чтобы гарантировать, что каждая сгибка является хорошим представителем целого.
  • Например, в задаче двоичной классификации, где каждый класс составляет 50% данных, лучше всего расположить данные таким образом, чтобы в каждом сгибе каждый класс составлял примерно половину экземпляров.
  • Как правило, это лучший подход при работе как со смещением, так и с дисперсией.
  • Случайно выбранная складка может неадекватно представлять второстепенный класс, особенно в случаях, когда существует огромный дисбаланс классов.

Давайте импортируем StratifiedKFold из sklearn и подгоним модель.

from sklearn.model_selection import StratifiedKFold

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

i=1
mean = 0
kf = StratifiedKFold(n_splits=5,random_state=1)
for train_index,test_index in kf.split(X,y):
 print (‘\n{} of kfold {} ‘.format(i,kf.n_splits))
 xtr,xvl = X.loc[train_index],X.loc[test_index]
 ytr,yvl = y[train_index],y[test_index]
 model = LogisticRegression(random_state=1)
 model.fit(xtr,ytr)
 pred_test=model.predict(xvl)
 score=accuracy_score(yvl,pred_test)
 mean += score
 print (‘accuracy_score’,score)
 i+=1
 pred_test = model.predict(test)
 pred = model.predict_proba(xvl)[:,1]
print (‘\n Mean Validation Accuracy’,mean/(i-1))
1 of kfold 5 
accuracy_score 0.8048780487804879

2 of kfold 5 
accuracy_score 0.7642276422764228

3 of kfold 5 
accuracy_score 0.7804878048780488

4 of kfold 5 
accuracy_score 0.8455284552845529

5 of kfold 5 
accuracy_score 0.8032786885245902

Mean Validation Accuracy 0.7996801279488205

Средняя точность проверки для этой модели оказалась 0,80. Давайте визуализируем кривую roc.

from sklearn import metrics
fpr, tpr, _ = metrics.roc_curve(yvl, pred)
auc = metrics.roc_auc_score(yvl, pred)
plt.figure(figsize=(12,8))
plt.plot(fpr, tpr, label=”validation, auc=”+str(auc))
plt.xlabel(‘False Positive Rate’)
plt.ylabel(‘True Positive Rate’)
plt.legend(loc=4)
plt.show()

Мы получили auc значение 0,70.

submission[‘Loan_Status’]=pred_test
submission[‘Loan_ID’]=test_original[‘Loan_ID’]

Помните, что нам нужны прогнозы в Y и N. Итак, давайте преобразуем 1 и 0 в Y и N.

submission[‘Loan_Status’].replace(0, ’N’, inplace=True)
submission[‘Loan_Status’].replace(1, ‘Y’, inplace=True)
pd.DataFrame(submission, columns=[‘Loan_ID’,’Loan_Status’]).to_csv(‘Output/Log1.csv’)

Функциональная инженерия

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

  • Общий доход. Как обсуждалось в ходе двумерного анализа, мы объединим доход соискателя и доход со-заявителя. Если общий доход высок, шансы на одобрение кредита также могут быть высокими.
  • EMI - EMI - это ежемесячная сумма, которую заявитель должен выплачивать в счет погашения ссуды. Идея создания этой переменной заключается в том, что людям с высоким уровнем EMI может быть сложно выплатить ссуду. Мы можем рассчитать EMI, взяв соотношение суммы ссуды к сроку ссуды.
  • Балансовый доход - это доход, оставшийся после выплаты EMI. Идея создания этой переменной заключается в том, что если это значение велико, высока вероятность того, что человек выплатит ссуду, и, следовательно, увеличиваются шансы на одобрение ссуды.
train[‘Total_Income’]=train[‘ApplicantIncome’]+train[‘CoapplicantIncome’]
test[‘Total_Income’]=test[‘ApplicantIncome’]+test[‘CoapplicantIncome’]

Давайте проверим распределение общего дохода.

sns.distplot(train[‘Total_Income’])

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

train[‘Total_Income_log’] = np.log(train[‘Total_Income’])
sns.distplot(train[‘Total_Income_log’])
test[‘Total_Income_log’] = np.log(test[‘Total_Income’])

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

train[‘EMI’]=train[‘LoanAmount’]/train[‘Loan_Amount_Term’]
test[‘EMI’]=test[‘LoanAmount’]/test[‘Loan_Amount_Term’]

Давайте проверим распределение переменной EMI.

sns.distplot(train[‘EMI’])

train[‘Balance Income’] = train[‘Total_Income’]-(train[‘EMI’]*1000)
test[‘Balance Income’] = test[‘Total_Income’]-(test[‘EMI’]*1000)
sns.distplot(train[‘Balance Income’])

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

train=train.drop([‘ApplicantIncome’, ‘CoapplicantIncome’, ‘LoanAmount’, ‘Loan_Amount_Term’], axis=1)
test=test.drop([‘ApplicantIncome’, ‘CoapplicantIncome’, ‘LoanAmount’, ‘Loan_Amount_Term’], axis=1)

Построение модели: Часть II

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

  • Логистическая регрессия
  • Древо решений
  • Случайный лес
  • XGBoost

Давайте подготовим данные для ввода в модели.

X = train.drop(‘Loan_Status’,1)
y = train.Loan_Status

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

i=1
mean = 0
kf = StratifiedKFold(n_splits=5,random_state=1,shuffle=True)
for train_index,test_index in kf.split(X,y):
 print (‘\n{} of kfold {} ‘.format(i,kf.n_splits))
 xtr,xvl = X.loc[train_index],X.loc[test_index]
 ytr,yvl = y[train_index],y[test_index]
 model = LogisticRegression(random_state=1)
 model.fit(xtr,ytr)
 pred_test=model.predict(xvl)
 score=accuracy_score(yvl,pred_test)
 mean += score
 print (‘accuracy_score’,score)
 i+=1
 pred_test = model.predict(test)
 pred = model.predict_proba(xvl)[:,1]
print (‘\n Mean Validation Accuracy’,mean/(i-1))
1 of kfold 5 
accuracy_score 0.7967479674796748

2 of kfold 5 
accuracy_score 0.6910569105691057

3 of kfold 5 
accuracy_score 0.6666666666666666

4 of kfold 5 
accuracy_score 0.7804878048780488

5 of kfold 5 
accuracy_score 0.680327868852459

 Mean Validation Accuracy 0.7230574436891909
submission['Loan_Status']=pred_test
submission['Loan_ID']=test_original['Loan_ID']
submission['Loan_Status'].replace(0, 'N', inplace=True)
submission['Loan_Status'].replace(1, 'Y', inplace=True)
pd.DataFrame(submission, columns=['Loan_ID','Loan_Status']).to_csv('Output/Log2.csv')

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

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

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

Для получения подробного объяснения посетите https://www.analyticsvidhya.com/blog/2016/04/complete-tutorial-tree-based-modeling-scratch-in-python/#six.

Давайте подберем модель дерева решений с пятикратной перекрестной проверкой.

from sklearn import tree
i=1
mean = 0
kf = StratifiedKFold(n_splits=5,random_state=1,shuffle=True)
for train_index,test_index in kf.split(X,y):
    print ('\n{} of kfold {} '.format(i,kf.n_splits))
    xtr,xvl = X.loc[train_index],X.loc[test_index]
    ytr,yvl = y[train_index],y[test_index]
    model = tree.DecisionTreeClassifier(random_state=1)
    model.fit(xtr,ytr)
    pred_test=model.predict(xvl)
    score=accuracy_score(yvl,pred_test)
    mean += score
    print ('accuracy_score',score)
    i+=1
    pred_test = model.predict(test)
    pred = model.predict_proba(xvl)[:,1]
print ('\n Mean Validation Accuracy',mean/(i-1))
1 of kfold 5 
accuracy_score 0.7398373983739838

2 of kfold 5 
accuracy_score 0.6991869918699187

3 of kfold 5 
accuracy_score 0.7560975609756098

4 of kfold 5 
accuracy_score 0.7073170731707317

5 of kfold 5 
accuracy_score 0.6721311475409836

 Mean Validation Accuracy 0.7149140343862455
submission['Loan_Status']=pred_test
submission['Loan_ID']=test_original['Loan_ID']
submission['Loan_Status'].replace(0, 'N', inplace=True)
submission['Loan_Status'].replace(1, 'Y', inplace=True)
pd.DataFrame(submission, columns=['Loan_ID','Loan_Status']).to_csv('Output/DecisionTree.csv')

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

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

Для подробного объяснения посетите эту статью https://www.analyticsvidhya.com/blog/2016/04/complete-tutorial-tree-based-modeling-scratch-in-python/

from sklearn.ensemble import RandomForestClassifier
i=1
mean = 0
kf = StratifiedKFold(n_splits=5,random_state=1,shuffle=True)
for train_index,test_index in kf.split(X,y):
 print (‘\n{} of kfold {} ‘.format(i,kf.n_splits))
 xtr,xvl = X.loc[train_index],X.loc[test_index]
 ytr,yvl = y[train_index],y[test_index]
 model = RandomForestClassifier(random_state=1, max_depth=10)
 model.fit(xtr,ytr)
 pred_test=model.predict(xvl)
 score=accuracy_score(yvl,pred_test)
 mean += score
 print (‘accuracy_score’,score)
 i+=1
 pred_test = model.predict(test)
 pred = model.predict_proba(xvl)[:,1]
print (‘\n Mean Validation Accuracy’,mean/(i-1))
1 of kfold 5 
accuracy_score 0.8292682926829268

2 of kfold 5 
accuracy_score 0.8130081300813008

3 of kfold 5 
accuracy_score 0.7723577235772358

4 of kfold 5 
accuracy_score 0.8048780487804879

5 of kfold 5 
accuracy_score 0.7540983606557377

 Mean Validation Accuracy 0.7947221111555378

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

Настроим параметры max_depth и n_estimators. max_depth определяет максимальную глубину дерева, а n_estimators определяет количество деревьев, которые будут использоваться в модели случайного леса.

Поиск по сетке

from sklearn.model_selection import GridSearchCV
paramgrid = {‘max_depth’: list(range(1,20,2)), ‘n_estimators’: list(range(1,200,20))}
grid_search=GridSearchCV(RandomForestClassifier(random_state=1),paramgrid)
from sklearn.model_selection import train_test_split
x_train, x_cv, y_train, y_cv = train_test_split(X,y, test_size=0.3, random_state=1)
grid_search.fit(x_train,y_train)
GridSearchCV(estimator=RandomForestClassifier(random_state=1),
             param_grid={'max_depth': [1, 3, 5, 7, 9, 11, 13, 15, 17, 19],
                         'n_estimators': [1, 21, 41, 61, 81, 101, 121, 141, 161,
                                          181]})
grid_search.best_estimator_
RandomForestClassifier(max_depth=5, n_estimators=41, random_state=1)
i=1
mean = 0
kf = StratifiedKFold(n_splits=5,random_state=1,shuffle=True)
for train_index,test_index in kf.split(X,y):
    print ('\n{} of kfold {} '.format(i,kf.n_splits))
    xtr,xvl = X.loc[train_index],X.loc[test_index]
    ytr,yvl = y[train_index],y[test_index]
    model = RandomForestClassifier(random_state=1, max_depth=3, n_estimators=41)
    model.fit(xtr,ytr)
    pred_test = model.predict(xvl)
    score = accuracy_score(yvl,pred_test)
    mean += score
    print ('accuracy_score',score)
    i+=1
    pred_test = model.predict(test)
    pred = model.predict_proba(xvl)[:,1]
print ('\n Mean Validation Accuracy',mean/(i-1))
1 of kfold 5 
accuracy_score 0.8130081300813008

2 of kfold 5 
accuracy_score 0.8455284552845529

3 of kfold 5 
accuracy_score 0.8048780487804879

4 of kfold 5 
accuracy_score 0.7967479674796748

5 of kfold 5 
accuracy_score 0.7786885245901639

 Mean Validation Accuracy 0.8077702252432362
submission['Loan_Status']=pred_test
submission['Loan_ID']=test_original['Loan_ID']
submission['Loan_Status'].replace(0, 'N', inplace=True)
submission['Loan_Status'].replace(1, 'Y', inplace=True)
pd.DataFrame(submission, columns=['Loan_ID','Loan_Status']).to_csv('Output/RandomForest.csv')

Давайте теперь определим важность функции, то есть, какие функции наиболее важны для этой проблемы. Для этого мы будем использовать атрибут feature_importances_ из sklearn.

importances=pd.Series(model.feature_importances_, index=X.columns)
importances.plot(kind=’barh’, figsize=(12,8))

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

XGBOOST

XGBoost - это быстрый и эффективный алгоритм, который использовался победителями многих конкурсов по науке о данных. Это алгоритм повышения, и вы можете обратиться к статье ниже, чтобы узнать больше о повышении: https://www.analyticsvidhya.com/blog/2015/11/quick-introduction-boosting-algorithms-machine-learning/

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

  • n_estimator: указывает количество деревьев для модели.
  • max_depth: мы можем указать максимальную глубину дерева с помощью этого параметра.

GBoostError: не удалось загрузить библиотеку XGBoost (libxgboost.dylib). Если вы столкнулись с этой ошибкой в ​​macOS, запустите brew install libomp в Terminal

from xgboost import XGBClassifier
i=1 
mean = 0
kf = StratifiedKFold(n_splits=5,random_state=1,shuffle=True) 
for train_index,test_index in kf.split(X,y): 
 print(‘\n{} of kfold {}’.format(i,kf.n_splits)) 
 xtr,xvl = X.loc[train_index],X.loc[test_index] 
 ytr,yvl = y[train_index],y[test_index] 
 model = XGBClassifier(n_estimators=50, max_depth=4) 
 model.fit(xtr, ytr) 
 pred_test = model.predict(xvl) 
 score = accuracy_score(yvl,pred_test) 
 mean += score
 print (‘accuracy_score’,score)
 i+=1
 pred_test = model.predict(test)
 pred = model.predict_proba(xvl)[:,1]
print (‘\n Mean Validation Accuracy’,mean/(i-1))
1 of kfold 5
accuracy_score 0.7804878048780488

2 of kfold 5
accuracy_score 0.7886178861788617

3 of kfold 5
accuracy_score 0.7642276422764228

4 of kfold 5
accuracy_score 0.7804878048780488

5 of kfold 5
accuracy_score 0.7622950819672131

 Mean Validation Accuracy 0.7752232440357191
submission['Loan_Status']=pred_test
submission['Loan_ID']=test_original['Loan_ID']
submission['Loan_Status'].replace(0, 'N', inplace=True)
submission['Loan_Status'].replace(1, 'Y', inplace=True)
pd.DataFrame(submission, columns=['Loan_ID','Loan_Status']).to_csv('Output/XGBoost.csv')

SPSS Modeler

Чтобы создать поток SPSS Modeler и построить с его помощью модель машинного обучения, следуйте приведенным ниже инструкциям:



Зарегистрируйтесь в учетной записи IBM Cloud, чтобы попробовать это руководство -



Заключение

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