Пишем скоринговую модель на Python |
||
МЕНЮ Искусственный интеллект Поиск Регистрация на сайте Помощь проекту ТЕМЫ Новости ИИ Искусственный интеллект Разработка ИИГолосовой помощник Городские сумасшедшие ИИ в медицине ИИ проекты Искусственные нейросети Слежка за людьми Угроза ИИ ИИ теория Внедрение ИИКомпьютерные науки Машинное обуч. (Ошибки) Машинное обучение Машинный перевод Реализация ИИ Реализация нейросетей Создание беспилотных авто Трезво про ИИ Философия ИИ Big data Работа разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика
Генетические алгоритмы Капсульные нейросети Основы нейронных сетей Распознавание лиц Распознавание образов Распознавание речи Техническое зрение Чат-боты Авторизация |
2018-08-18 19:02 Кредитный скоринг – одна из наиболее распространенных задач среди множества проблем, решаемых финансовыми аналитиками. В данной статье мы разберемся с ключевыми шагами для написания собственной скоринговой модели на Python. Два наиболее важных вопроса кредитования:
В качестве библиотек и функций импортируем следующие: import numpy as np import pandas as pd import matplotlib import matplotlib.pyplot as plt import seaborn as sns from imblearn.pipeline import make_pipeline as imb_make_pipeline from imblearn.under_sampling import RandomUnderSampler from imblearn.ensemble import BalancedBaggingClassifier, EasyEnsemble from sklearn.preprocessing import Imputer, RobustScaler, FunctionTransformer from sklearn.ensemble import RandomForestClassifier, VotingClassifier, GradientBoostingClassifier from sklearn.model_selection import train_test_split, cross_val_score, cross_val_predict from sklearn.metrics import (roc_auc_score, confusion_matrix, accuracy_score, roc_curve, precision_recall_curve, f1_score) from sklearn.pipeline import make_pipeline Знакомство с данными df = pd.read_csv("loans.csv") print(df.dtypes) В результате можно видеть, что единственным категориальным признаком является признак purpose: credit_policy int64 purpose object int_rate float64 installment float64 log_annual_inc float64 dti float64 fico int64 days_with_cr_line float64 revol_bal int64 revol_util float64 inq_last_6mths float64 delinq_2yrs float64 pub_rec float64 not_fully_paid int64 Определим число пустых ячеек в каждом столбце: print(df.isnull().sum()) В таблице имеется небольшое число пропусков для шести признаков: credit_policy 0 purpose 0 int_rate 0 installment 0 log_annual_inc 4 dti 0 fico 0 days_with_cr_line 29 revol_bal 0 revol_util 62 inq_last_6mths 29 delinq_2yrs 29 pub_rec 29 not_fully_paid 0 Рассмотрим как сбалансирована выборка относительно оплаченных кредитов: pos = df[df["not_fully_paid"] == 1].shape[0] neg = df[df["not_fully_paid"] == 0].shape[0] plt.figure(figsize=(8, 6)) sns.countplot(df["not_fully_paid"]) plt.xticks((0, 1), ["Оплачено полностью", "Оплачено не полностью"]) plt.xlabel("") plt.ylabel("Число заемщиков") Полученная диаграмма показывает, что выборка не сбалансирована относительно целевой переменной not_fully_paid. Начальные соображения и предобработка данных
1) Создадим переменные признака purpose вместо используемых категориальных значений. 2) Разобьем данные на обучающую (80%) и тестовую (20%) выборки. 3) Чтобы выбросы в данных оказывали меньшее влияние на обучение, стандартизируем выборку при помощи метода RobustScaler. Этот метод центрует данные вокруг медианы и масштабирует их с использованием межквартильного диапазона. Стратегии работы с пропущенными значениями
Начнем с создания бинарных функций для отсутствующих значений, а затем вычислим показатель AUC для различных моделей на обучающей выборке: df = pd.get_dummies(df, columns=["purpose"], drop_first=True) for feature in df.columns: if np.any(np.isnan(df[feature])): df["is_" + feature + "_missing"] = np.isnan(df[feature]) * 1 Выделим в качестве целевой переменной признак not_fully_paid и разобьем данные на обучающую и тестовую выборки: X = df.loc[:, df.columns != "not_fully_paid"].values y = df.loc[:, df.columns == "not_fully_paid"].values.flatten() X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=True, random_state=0, stratify=y) print("Оригинальные размеры данных: ", X_train.shape, X_test.shape) В результате получаем: Оригинальные размеры данных: (7662, 24) (1916, 24) Рассмотрим в нашем примере самый простой вариант – выкинем из выборки записи с пропусками: train_indices_na = np.max(np.isnan(X_train), axis=1) test_indices_na = np.max(np.isnan(X_test), axis=1) X_train_dropna, y_train_dropna = X_train[~train_indices_na, :][:, :-6], y_train[~train_indices_na] X_test_dropna, y_test_dropna = X_test[~test_indices_na, :][:, :-6], y_test[~test_indices_na] print("После выкидывания NA: ", X_train_dropna.shape, X_test_dropna.shape) В результате запуска увидим: После выкидывания NA: (7610, 18) (1906, 18) Построим классификатор случайного леса и определим показатель AUC для данной модели: rf_clf = RandomForestClassifier(n_estimators=500, max_features=0.25, criterion="entropy", class_weight="balanced") pip_baseline = make_pipeline(RobustScaler(), rf_clf) scores = cross_val_score(pip_baseline, X_train_dropna, y_train_dropna, scoring="roc_auc", cv=10) print("Среднее значение AUC базовой модели {}".format(scores.mean())) Запуск кода дает: Среднее значение AUC базовой модели 0.662. Проверим, улучшают ли бинарные функции качество нашей модели: rf_clf.fit(RobustScaler().fit_transform(Imputer(strategy="median").fit_transform(X_train)), y_train) importances = rf_clf.feature_importances_ indices = np.argsort(rf_clf.feature_importances_)[::-1] plt.figure(figsize=(12, 6)) plt.bar(range(1, 25), importances[indices], align="center") plt.xticks(range(1, 25), df.columns[df.columns != "not_fully_paid"][indices], rotation=90) plt.title("Значимость признаков") Вы можете попробовать и более сложные стратегии, однако для такого малого числа потерь данных при использовании различных подходов обработки пропусков обычно наблюдаются сопоставимые результаты. Рассматривая полученную диаграмму, можно заметить, что добавление двоичных функций в этом конкретном случае не приводит к приросту производительности модели. Таким образом для нашей выборки их можно удалить: X_train = X_train[:, :-6] X_test = X_test[:, :-6] Стратегии работы с несбалансированными выборками
В качестве примера сравним результат прошлой модели случайного леса с теми, где мы либо сбалансируем выборку, удалив часть мажоритарных записей (under-sample подход), либо воспользуемся синтетическим подходом: rf_clf = RandomForestClassifier(n_estimators=500, max_features=0.25, criterion="entropy", class_weight="balanced") pip_orig = make_pipeline(Imputer(strategy="mean"), RobustScaler(), rf_clf) scores = cross_val_score(pip_orig, X_train, y_train, scoring="roc_auc", cv=10) print("AUC оригинальной модели: ", scores.mean()) pip_undersample = imb_make_pipeline(Imputer(strategy="mean"), RobustScaler(), RandomUnderSampler(), rf_clf) scores = cross_val_score(pip_undersample, X_train, y_train, scoring="roc_auc", cv=10) print("AUC модели без большей части мажоритарных примеров: ", scores.mean()) resampled_rf = BalancedBaggingClassifier(base_estimator=rf_clf, n_estimators=10, random_state=0) pip_resampled = make_pipeline(Imputer(strategy="mean"), RobustScaler(), resampled_rf) scores = cross_val_score(pip_resampled, X_train, y_train, scoring="roc_auc", cv=10) print("AUC модели EasyEnsemble: ", scores.mean()) Результат работы скрипта: AUC оригинальной модели: 0.663 AUC модели без большей части мажоритарных примеров: 0.658 AUC модели EasyEnsemble 0.671 Можно видеть, что в случае рассматриваемой выборки простое исключение экземпляров мажоритарного класса не приводит к улучшению качества предсказания модели. В то же время применение синтетического подхода, например, EasyEnsemble, позволяют сбалансировать выборку и улучшить предсказательные возможности модели. resampled_rf.fit(X_train_dropna, y_train_dropna) print(y_test_dropna[-3], y_test_dropna[-2]) print(resampled_rf.predict([X_test_dropna[-3]]), resampled_rf.predict([X_test_dropna[-2]])) Вывод: 1 0 [1] [0] Для улучшения предсказания скоринговых моделей существует гораздо больше сложных концепций, чем мы можем изложить в рамках одной публикации. С некоторыми из них в продолжение работы с этой статьей на примере того же датасета вы можете ознакомиться здесь. Источник: sfeducation.ru Комментарии: |
|