Серия инструментов с открытым исходным кодом для решения реальных задач

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

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

Потенциальная аудитория:

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

В этом блоге вы узнаете:

  • Создайте систему показателей, используя машинное обучение с помощью Python.
  • Набор навыков: Логистическая регрессия, Градиентный бустинг, Вес доказательства (WOE), Информационная ценность (IV), Биннинг, Биннинг хи-квадрат

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

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

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

Введение:

Код и данные для этого блога доступны здесь:



Инструмент с открытым исходным кодом: Жаба

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

Набор данных: набор данных клиентов кредитных карт по умолчанию

Описание можно найти на Kaggle. Исходный набор данных можно найти на UCI.

Есть 25 переменных:

  • ID: идентификатор клиента.
  • 23 функции включают: ограничение баланса, пол, образование, брак, возраст, погашение, сумма выписки по счету, сумма предыдущего платежа.
  • Ярлык: платеж по умолчанию (1=да, 0=нет)

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

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

Так что все зависит от вашей системы показателей!

Давай начнем.

1. Предварительная обработка данных

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

В этом блоге мы будем использовать только CSV-файл.

Установка и импорт пакетов

import pkg_resources
import pip
installedPackages = {pkg.key for pkg in pkg_resources.working_set}
required = { 'pandas','numpy', 'matplotlib', 'seaborn','toad','pickle','sklearn'}
missing = required - installedPackages
if missing:
    !pip install pandas
    !pip install numpy
    !pip install matplotlib
    !pip install seaborn
    !pip install toad
    !pip install pickle
    !pip install sklearn

import pandas as pd
from sklearn.metrics import roc_auc_score,roc_curve,auc,precision_recall_curve
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression 
from sklearn.ensemble import GradientBoostingClassifier

import numpy as np
import glob
import math
import seaborn as sns 
import matplotlib.pyplot as plt
import toad
import pickle

Загрузите данные и проверьте скорость метки по умолчанию

# use pandas to load the csv file
data = pd.read_csv('UCI_Credit_Card.csv')
# check the size of the data
data.shape
# check few lines
data.head()
#use the world 'label'
data['label']=data['default.payment.next.month']
data=data.drop(columns=['default.payment.next.month'])
#check the fraud proportion of the data
target_info(data['label'])
# set an exclude list for the scorecard package Toad
exclude_list = ['ID','label']

Коэффициент мошенничества 22% (люди по умолчанию), это довольно высокий уровень по умолчанию в исторических данных.

Тренировочный и тестовый сплит

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

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

# use the ID column to split the train-test data
data.ID.describe()
train = data_split(data,start = 0, end=22500,date_col='ID')
test = data_split(data,start = 22500, end=172792,date_col='ID')

2. Фильтрация функций

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

###feature filtering by missing value, IV & corrrelation:
##If the missing value rate is greater than the threshold, delete the feature
##If the correlation coefficient is greater than the threshold, delete the feature
##If the IV is smaller than the threshold, delete the features

train_selected, drop_lst= toad.selection.select(frame = train,
                                                target=train['label'], 
                                                empty = 0.7, 
                                                iv = 0.02, corr = 1, 
                                                return_drop=True, 
                                                exclude=exclude_list)
print("keep:",train_selected.shape[1],
      "drop empty:",len(drop_lst['empty']),
      "drop iv:",len(drop_lst['iv']),
      "drop corr:",len(drop_lst['corr']))

Таким образом, мы оставим в общей сложности 23 функции и исключим 2 функции ['SEX', 'MARIAGE'], потому что у них низкий IV.

  • Весность доказательств (WOE) — описывает взаимосвязь между прогностической переменной и бинарной целевой переменной.
  • Информационная ценность (IV) — измеряет силу этих отношений на основе WOE. На отраслевом уровне исключаются функции с IV ниже 0,02.

# output the iv table to a dataframe
def output_iv_importance(train_selected,label_col):
    feat_import_iv = toad.quality(train_selected,label_col,iv_only=True)
    feat_import_iv=feat_import_iv['iv']
    feat_import_iv = feat_import_iv.reset_index()
    feat_import_iv.columns = ['name','iv']
    return feat_import_iv

df_iv=output_iv_importance(train_selected,'label')
df_iv.head(30)

Это IV рейтинг по всем характеристикам. Мы видим, что PAY_0 имеет самый высокий IV, что логично, поскольку эта функция указывает самый последний статус погашения. ОБРАЗОВАНИЕ и ВОЗРАСТ имеют низкий IV по сравнению со статусом и суммой платежа.

3. Биннинг функций

Биннинг признаков — это преобразование непрерывной или числовой переменной в категориальный признак.

Преимущества объединения функций:

  1. Он упрощаетмодель логистической регрессии и снижает риск переобучения модели.
  2. Логистическая регрессия — это обобщенная линейная модель, и ее выразительные возможности ограничены; Объединение признаков может привнести нелинейность в модель, что может улучшить выразительность модели и помочь лучше подобрать модель.
  3. Дискретизированные функции очень устойчивы к аномальным данным: например, значение функции равно 1, если возраст › 30 лет, и 0 иначе. Если функции не дискретизированы, аномальная точка данных «300 лет» повлияет на подгонку модели.
  4. Он может обрабатывать нулевые данные как отдельный класс.

Этапы объединения функций:

Шаг 1. Инициализация: c = toad.transform.Combiner()

Шаг 2. Учебный биннинг:

c.fit(dataframe, 
      y = 'target', 
      method = 'chi', 
      min_samples = 0.05, 
      n_bins = None, 
      empty_separate = False)
  • y: целевой столбец
  • method: метод биннинга, поддерживает chi (бинирование хи-квадрат), dt (бинирование дерева решений), kmean , квантиль, шаг (бинирование с одинаковым размером шага)
  • min_samples: каждое поле содержитнаименьшее количество образцов, которое может быть числом или пропорцией.
  • n_bins: количество бинов; Если невозможно разделить такое количество ящиков, будет разделено максимальное количество ящиков.
  • empty_separate: разделять ли пустые поля отдельно.

Шаг 3. проверить узлы бинирования: c.export()

Шаг 4. Настроить биннинг вручную: c.load(dict)

Шаг 5. Применить результаты объединения: c.transform(dataframe, labels=False)

  • labels: следует ли преобразовывать результаты биннинга в метки блоков. Если False, выведите 0, 1, 2… (дискретные переменные сортируются в соответствии с пропорцией), а если True, выведите( -inf, 0], (0,10], (10, inf).
import time
start = time.time()
combiner = toad.transform.Combiner()
# use the filtered features for training
# Use the stable chi-square binning, 
# specifying that each bin has at least 5% data to ensure stability
# empty values ​​will be automatically assigned to the best bin
combiner.fit(X=train_selected,
             y=train_selected['label'],
             method='chi',
             min_samples = 0.05,
             exclude=exclude_list)
end = time.time()
print((end-start)/60)

#output binning
bins = combiner.export()

Результат биндинга:

#apply binning
train_selected_bin = combiner.transform(train_selected)
test_bin = combiner.transform(test[train_selected_bin.columns])

#Fine tune bins
from toad.plot import  bin_plot,badrate_plot
bin_plot(train_selected_bin,x='PAY_AMT1',target='label')
bin_plot(test_bin,x='PAY_AMT1',target='label')

На этом графике гистограмма представляет долю данных в соответствующей ячейке; красная линия представляет собой долю постоянных клиентов.

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

Этот график выглядит нормально, если есть внезапный скачок или падение, нам нужно использовать c.set_rules(dict) для объединения биннинга.

Например.

#setting rules
rule = {'PAY_AMT1':[['0', 'nan'],['1'], ['2'], ['3']]}

#Adjust binning
c.set_rules(rule)

4. Преобразование в WOE и расчет PSI

Преобразование WOE выполняется после завершения биннинга.

Шаги следующие:

  1. Используйте настроенный выше Combiner c для преобразования данных
  2. Инициализировать преобразование woe t:t= toad.transform.WOETransformer()
  3. Обучениеt: t.fit_transform обучает и выводит преобразованные данные для набора поездов.
  4. target: данные целевого столбца (не имя столбца)
  5. исключить: столбцы, которые не нужно преобразовывать WOE. Примечание. Все столбцы будут преобразованы, включая столбцы, которые не были объединены в группы, а столбцы, которые не нужно преобразовывать с помощью WOE, будут удалены с помощью исключения, особенно целевой столбец.
  6. Преобразование данных теста/OOT: transer.transform
##transform to WOE
t=toad.transform.WOETransformer()
#transform training set
train_woe = t.fit_transform(X=train_selected_bin,
                            y=train_selected_bin['label'], 
                            exclude=exclude_list)
#transform testing set
test_woe = t.transform(test_bin)

final_data_woe = pd.concat([train_woe,test_woe])

Рассчитать фунт на квадратный дюйм

PSI (индекс стабильности населения) отражает стабильность распределения. Мы часто используем его для просмотра функций и оценки стабильности модели. На отраслевом уровне следует отказаться от функций с PSI выше 0,2.

#get the feature name
features_list = [feat for feat in train_woe.columns if feat not in exclude_list]
#calculate PSI using toad
psi_df = toad.metrics.PSI(train_woe[features_list], test_woe[features_list]).sort_values(0)
#put into a dataframe
psi_df = psi_df.reset_index()
psi_df = psi_df.rename(columns = {'index' : 'feature',0:'psi'})

# features less than 0.25
psi005 = list(psi_df[psi_df.psi<0.25].feature)
# features geater than 0.25
psi_remove = list(psi_df[psi_df.psi>=0.25].feature)

# keep exclude list
for i in exclude_list:
    if i in psi005:
        pass
    else:
       psi005.append(i) 
# remove features that are geater than 0.25
train_selected_woe_psi = train_woe[psi005]
off_woe_psi = test_woe[psi005]

# output our final data table
final_data_woe = pd.concat([train_selected_woe_psi,off_woe_psi])

5. Выходной финал IV

Этот шаг заключается в выводе IV после преобразования WOE, он немного отличается от IV необработанных признаков.

# output the IV
features_use = [feat for feat in final_data_woe.columns if feat not in exclude_list]
len(features_use)

df_iv=output_iv_importance(final_data_woe[features_use+['label']],'label')

Идея состоит в том, чтобы получить функции с самым высоким IV и самым низким PSI.

6. Настройка модели

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

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

  • Простая линейная связь: связь между переменными представляет собой линейную связь.
  • Хорошая интерпретируемость: влияние входных переменных на целевые переменные легко доступно
  • Дайте вероятности вместо дискриминационных классов: характеристическую информацию о покупателе (например, брак, возраст, историческую кредитную историю и т. д.) можно интегрировать и преобразовать в вероятность. значение, которое обеспечивает интуитивную основу для прогнозирования, хороший клиент или плохой. То есть, чем больше значение, тем меньше вероятность того, что клиент не выполнит обязательства в будущем.
  • Простота развертывания: тестирование, развертывание, мониторинг, настройка и т. д. относительно просты.
def check_train_test_auc(x_train,y_train,x_test,y_test):
    from sklearn.linear_model import LogisticRegression
    lr = LogisticRegression(random_state=42,C= 0.1, penalty='l2', solver='newton-cg')

  
    lr = LogisticRegression(class_weight='balanced')
    lr.fit(x_train, y_train)

    pred_train = lr.predict_proba(x_train)[:,1]
    from toad.metrics import KS, AUC

    print('train KS',KS(pred_train, y_train))
    print('train AUC',AUC(pred_train, y_train))
    
    pred_OOT =lr.predict_proba(x_test)[:,1]
    print('Test KS',KS(pred_OOT, y_test))
    print('Test AUC',AUC(pred_OOT, y_test))
    
    from sklearn.metrics import confusion_matrix, accuracy_score, roc_auc_score, plot_roc_curve, classification_report

    fig, ax = plt.subplots(figsize=(12, 8))
    plot_roc_curve(lr, x_test, y_test, color='blue', ax=ax)

#train & test
check_train_test_auc(x_train = train_woe[features_use],y_train=train_woe['label'],
                     x_test =test_woe[features_use] ,y_test = test_woe['label'])

Мы видим, что небольшой разницы между поездом AUC и тестовым AUC или поездом KS и тестом KS нет. Это означает, что наша модель не подходит.

Обучите GradientBoostingClassifier и проверьте таблицу важности функций

Чтобы увидеть, будет ли модель GBDT работать лучше, чем LR, и сравнить таблицу важности функций с IV.

def get_evaluation_scores(label, predictions):
    from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
    from sklearn.metrics import balanced_accuracy_score
    tp, fn, fp, tn = confusion_matrix(label,predictions,labels=[1,0]).reshape(-1)
    print('True Positive:',tp)
    print('True Negative:',tn)
    print('False Positive:',fp)
    print('False Negative:',fn)
    accuracy = (tp+tn)/(tp+fn+fp+tn)
    print('accuracy: ',accuracy)
    recall = tp/(tp+fn)
    print('(recall): ',recall)
    precision = tp/(tp+fp)
    print('(precision): ',precision)
    #f1 score = 2*(P*R)/(P+R)
    f1 = 2*precision*recall/(precision+recall)
    print('F1 score: ',f1)
    
    print(classification_report(label, predictions))
    
    print('balanced_accuracy_score: ',balanced_accuracy_score(label,predictions))
    return precision, recall

def evaluate_result(df_train,df_test,features_name):
    from sklearn.ensemble import AdaBoostClassifier, GradientBoostingClassifier, RandomForestClassifier, ExtraTreesClassifier
    import seaborn as sns
    import matplotlib.pyplot as plt
    start = time.time()
    x_train = df_train[features_name]
    y_train = df_train['label']

    x_test  = df_test[features_name]
    y_test  = df_test['label']

    model = GradientBoostingClassifier(n_estimators=250,random_state=0)
    model.fit(x_train,y_train)
    predictions = model.predict(x_test)
    get_evaluation_scores(label = y_test, predictions=predictions)
    feat_importances = pd.Series(model.feature_importances_, index=features_name)
    feat_importances=pd.DataFrame(feat_importances).reset_index()
    feat_importances.columns=['feature_name','feature_importance']
    feat_importances=feat_importances.sort_values(['feature_importance'],ascending=False)
    import matplotlib.pyplot as plt
    plt.figure(figsize=(15,15))

    sns_plot1=sns.barplot(feat_importances.feature_importance,feat_importances.feature_name,estimator=sum)
    plt.title("Features Importance",size=18)
    plt.ylabel('', size = 15)
    plt.tick_params(labelsize=18)
    return feat_importances,model,x_train,y_train,x_test,y_test

fet_importance_GBDT_reason,model,x_train,y_train,x_test,y_test = evaluate_result(df_train=train_woe,
                df_test=test_woe,
                features_name=features_use)

Как видно из таблицы важности функций, GBDT придает большое значение (64%) функции PAY_0.

def plot_roc_pre_recall_curve(labels, probs):
    from sklearn.metrics import precision_recall_curve
    # Get ROC curve FPR and TPR from true labels vs score values
    fpr, tpr, _ = roc_curve(labels, probs)

    # Calculate ROC Area Under the Curve (AUC) from FPR and TPR data points
    roc_auc = auc(fpr, tpr)

    # Calculate precision and recall from true labels vs score values
    precision, recall, _ = precision_recall_curve(labels, probs)

    plt.figure(figsize=(8, 3))

    plt.subplot(1,2,1)
    lw = 2
    plt.plot(fpr, tpr, color='darkorange', lw=lw, label='ROC curve (area = %0.4f)' % roc_auc)
    plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('ROC Curve')
    plt.legend(loc="lower right")
    plt.grid(True)

    plt.subplot(1,2,2)
    plt.step(recall, precision, color='orange', where='post')
    # plt.fill_between(recall, precision, step='post', alpha=0.5, color='orange')
    plt.xlabel('Recall')
    plt.ylabel('Precision')
    plt.ylim([0.0, 1.05])
    plt.xlim([0.0, 1.0])
    plt.title('Precision Recall Curve')
    plt.grid(True)

    left  = 0.125  # the left side of the subplots of the figure
    right = 0.9    # the right side of the subplots of the figure
    bottom = 0.1   # the bottom of the subplots of the figure 
    top = 0.9      # the top of the subplots of the figure
    wspace = 0.5   # the amount of width reserved for blank space between subplots
    hspace = 0.2   # the amount of height reserved for white space between subplots
    plt.subplots_adjust(left, bottom, right, top, wspace, hspace)
    plt.show()

probs = model.predict_proba(x_test)[:,1]
sns.set(font_scale = 1)
plot_roc_pre_recall_curve(y_test, probs)

Кривая ROC и Precision-Recall выглядит нормально.

7. Изготовление моделей

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

#prepare train & test data
x_train = train_woe[features_use]
y_train=train_woe['label']
x_test =test_woe[features_use] 
y_test = test_woe['label']

#Train LR
#lr = LogisticRegression(random_state=42,C= 0.1, penalty='l2', solver='newton-cg')
lr = LogisticRegression(class_weight = 'balanced')
lr.fit(x_train, y_train)

#check AUC
probs = lr.predict_proba(x_test)[:,1]
sns.set(font_scale = 1)
plot_roc_pre_recall_curve(y_test, probs)

AUC LR: 0,7835

AUC GBDT: 0,7892

Не большая разница между этими моделями. Так что можно использовать LR для создания системы показателей.

8. Настройка системы показателей

Следующие параметры являются наиболее важными для настройки системы показателей.

  • base_score = 1000, base_odds = 35 , pdo = 80, рейтинг = 2

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

# scorecard tuning
card = toad.ScoreCard(
    combiner = combiner,
    transer = t,
    class_weight = 'balanced',
    C=0.1,
    base_score = 1000,
    base_odds = 35 ,
    pdo = 80,
    rate = 2
)

card.fit(train_woe[features_use], train_woe['label'])

#inference on test data
test['CreditScore'] = card.predict(test)
test['CreditScore'].describe()

#output the scorecard
final_card_score=card.export()
len(final_card_score)

#transform the scorecard into dataframe and save to csv
keys = list(card.export().keys())
score_card_df = pd.DataFrame()
for n in keys:
    temp = pd.DataFrame.from_dict(final_card_score[n], orient='index')
    temp = temp.reset_index()
    temp.columns= ['binning','score']
    temp['variable'] = n
    temp = temp[['variable','binning','score']]
    score_card_df=score_card_df.append(temp)
score_card_df.head(30)

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

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

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

Например, мы можем установить уровень кредита следующим образом:

9. Анализ распределения

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

plt.figure(figsize=(12,10))
import random
import numpy
from matplotlib import pyplot as plt

w = 40
n = math.ceil((data['CreditScore'].max() - data['CreditScore'].min())/w)
#bins = numpy.linspace(-10, 10, 100)

plt.hist(data[data.label==1].CreditScore, alpha=0.5, label='Black',bins = n)
plt.hist(data[data.label==0].CreditScore, alpha=0.5, label='White',bins = n)
plt.legend(loc='upper left')
plt.title('Credit Score Distribution: Test Set',size=15)
plt.show()

Черный означает постоянных клиентов, а белый означает хороших клиентов.

Хорошая модель четко разделяет распределение черного и белого.

Идеальным распределением является форма улыбки.

  • Хорошие клиенты с высоким кредитным рейтингом — справа
  • Клиенты по умолчанию с низким кредитным рейтингом-› слева.

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

10. Настройка порога

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

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

Другими словами, ваша цель – найти порог с потерями ≤10% и охватом ≥70%.

def get_credit_level(
    test,
    target_score ='order_score',
    out_col = 'order_level',
    left_bound = -100,
    level_0 = 100,
    level_1 = 200,    
    level_2 = 250,    
    level_3 = 300,    
    level_4 = 350,    
    level_5 = 400,    
    level_6 = 450,
    level_7 = 500,
    level_8 = 800):
    level = []
    for i in range(len(test)):
        if (test[target_score][i]>left_bound) & (test[target_score][i]<=level_0):
            level.append(0)
        elif  (test[target_score][i]>level_0) & (test[target_score][i]<=level_1):
            level.append(1)
        elif  (test[target_score][i]>level_1) & (test[target_score][i]<=level_2):
            level.append(2)
        elif  (test[target_score][i]>level_2) & (test[target_score][i]<=level_3):
            level.append(3)
        elif  (test[target_score][i]>level_3) & (test[target_score][i]<=level_4):
            level.append(4)
        elif  (test[target_score][i]>level_4) & (test[target_score][i]<=level_5):
            level.append(5)
        elif  (test[target_score][i]>level_5) & (test[target_score][i]<=level_6):
            level.append(6)
        elif  (test[target_score][i]>level_6) & (test[target_score][i]<=level_7):
            level.append(7)
        elif  (test[target_score][i]>level_7 )& (test[target_score][i]<=level_8):
            level.append(8)
        
    test[out_col] = level
    return test

def plot_bts_level_loss(test, target_col):
    bts_level_df = test[target_col].value_counts()
    bts_level_df=pd.DataFrame(bts_level_df)
    df_label_level= test[test.label==1].groupby(target_col)['label'].count()/ test.groupby(target_col)['label'].count()
    df_label_level = pd.DataFrame(df_label_level)
    bts_level_df.sort_index().plot.bar(title='')
    df_label_level.plot()

test = get_credit_level(test,
                       target_score ='CreditScore',
                       out_col = 'CreditScore_level',
                      left_bound = -1000,
    level_0 = 250,
    level_1 = 300,    
    level_2 = 400,    
    level_3 = 500,    
    level_4 = 580,    
    level_5 = 630,    
    level_6 = 690,
    level_7 = 730,
    level_8 = 1000
                )
plot_bts_level_loss(test,target_col='CreditScore_level')
def get_loss_coverage(test,target_level):
    #level 5-Leve 8 Loss (percentage of default people)
    L5_loss = test[test[target_level]>=5 ].label.value_counts()/len(test[test[target_level]>=5 ])
    #level 5- level 8 Coverage (percentage of good people)
    L5_coverage=test[test[target_level]>=5 ].label.value_counts()[0]/test[test.label==0].shape[0]
    print("Level 5-Level 8: Loss is ",L5_loss[1], "; Coverage is ",L5_coverage)
    #level 6-level 8 Loss
    L6_loss=test[test[target_level]>=6 ].label.value_counts()/len(test[test[target_level]>=6 ])
    #level 6-level 8 Coverage
    L6_coverage=test[test[target_level]>=6].label.value_counts()[0]/test[test.label==0].shape[0]
    print("Level 6-Level 8: Loss is ",L6_loss[1], "; Coverage is ",L6_coverage)

На этом графике показано распределение каждого уровня кредита и коэффициента дефолта по кредиту на этом уровне.

Изучив каждый уровень, вы получите таблицу потерь и покрытия. Например, если вы отправите предложения о переводе остатка всем людям 7501 (L0-L8), вы понесете 21% потерь (1548/7501) .

Чтобы достичь цели (потери ≤10% и покрытие ≥70%), вам нужно выбрать L6-L8 с потерями 10,1% (347+120+34)/(2573+1570+796) и 75% покрытие (2226+1450+762)/5953.

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

11. Протестируйте нашу систему показателей вручную

Можем ли мы отправить клиенту предложение о переносе баланса со следующей информацией в следующем месяце?

Давайте вручную проверим систему показателей.

Вывод по жабе:

Проверив нашу таблицу уровней кредитоспособности:

Мы видим, что этот клиент является клиентом уровня 8 с Perfect Credit. Таким образом, мы можем выдать ему / ей предложение о переводе баланса.

Заключение:

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

В этом блоге обсуждаются следующие модные словечки ML:

Информационная ценность (IV), вес доказательств (WOE), индекс стабильности населения (PSI), AUC (площадь под ROC-кривой), KS (Колмогорова-Смирнова), логистическая регрессия (LR), GBDT (решение о повышении градиента). дерево)

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

Спасибо.

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