Описание проекта
Из «Бета-Банка» стали уходить клиенты. Каждый месяц. Немного, но заметно. Банковские маркетологи посчитали: сохранять текущих клиентов дешевле, чем привлекать новых.
Нужно спрогнозировать, уйдёт клиент из банка в ближайшее время или нет. Вам предоставлены исторические данные о поведении клиентов и расторжении договоров с банком.
Постройте модель с предельно большим значением F1-меры. Чтобы сдать проект успешно, нужно довести метрику до 0.59. Проверьте F1-меру на тестовой выборке самостоятельно.
Дополнительно измеряйте AUC-ROC, сравнивайте её значение с F1-мерой.
Источник данных: https://www.kaggle.com/barelydedicated/bank-customer-churn-modeling
Описание атрибутов датафрейма:
RowNumber
— индекс строки в данныхCustomerId
— уникальный идентификатор клиентаSurname
— фамилияCreditScore
— кредитный рейтингGeography
— страна проживанияGender
— полAge
— возрастTenure
— сколько лет человек является клиентом банкаBalance
— баланс на счётеNumOfProducts
— количество продуктов банка, используемых клиентомHasCrCard
— наличие кредитной картыIsActiveMember
— активность клиентаEstimatedSalary
— предполагаемая зарплатаExited
— факт ухода клиента (целевой признак)
Оглавление
- 1 Подготовка данных
- 2 Исследование задачи
- 2.1 Изучение моделей без учёта дисбаланса
- 2.1.1 Разделение датафрейма на обучающие, валидационные и тестовые выборки
- 2.1.2 Разделение выборок на предназначенные для «деревянных» и логистических моделей с использованием порядкового и OHE декодирования категориальных признаков
- 2.1.3 Приведение данных к единому масштабу
- 2.1.4 Анализ полученных выборок
- 2.1.5 Создание функции и датафрейма для анализа метрик качества модели
- 2.1.6 Модель решающего дерева без балансировки классов
- 2.1.7 Модель случайного леса без балансировки классов
- 2.1.8 Проверка на мультиколлинеарность выборок для логистических моделей
- 2.1.9 Модель логистической регрессии без балансировки классов
- 2.1.10 Результаты использования моделей без балансировки классов
- 2.2 Исследование баланса классов
- 2.2.1 Модель решающего дерева с балансировкой весов классов внутри модели
- 2.2.2 Модель случайного леса с балансировкой весов классов внутри модели
- 2.2.3 Модель логистической регрессии с балансировкой весов классов внутри модели
- 2.2.4 Результаты использования моделей с балансировкой весов классов внутри модели
- 2.3 Итоги исследования задачи
- 2.1 Изучение моделей без учёта дисбаланса
- 3 Борьба с дисбалансом
- 3.1 Балансировка обучающей выборкой путем увеличения количества объектов класса «1»
- 3.1.1 Модель решающего дерева со сблансированной обучающей выборкой, полученной путем увеличения количества объектов класса «1»
- 3.1.2 Модель случайного леса со сблансированной обучающей выборкой, полученной путем увеличения количества объектов класса «1»
- 3.1.3 Модель логистической регрессии со сблансированной обучающей выборкой, полученной путем увеличения количества объектов класса «1»
- 3.1.4 Результаты использования моделей со сблансированной обучающей выборкой, полученной путем увеличения количества объектов класса «1»
- 3.2 Балансировка классов за счет уменьшения обучающей выборки класса «0»
- 3.2.1 Модель решающего дерева со сблансированной обучающей выборкой, полученной путем уменьшения количества объектов класса «0»
- 3.2.2 Модель случайного леса со сблансированной обучающей выборкой, полученной путем уменьшения количества объектов класса «0»
- 3.2.3 Модель логистической регрессии со сблансированной обучающей выборкой, полученной путем уменьшения количества объектов класса «0»
- 3.3 Сравнение показателей качества моделей с разными способами балансировки обучающих «деревянными» и логистических выборок и без балансировки
- 3.1 Балансировка обучающей выборкой путем увеличения количества объектов класса «1»
- 4 Тестирование модели
- 5 Чек-лист готовности проекта
Подготовка данных¶
Загрузка библиотек и их компонентов¶
# Импорт библиотек и их модулей
import pandas as pd
import numpy as nm
# Обработка данных
import re
from sklearn.preprocessing import(
OrdinalEncoder,
LabelEncoder,
OneHotEncoder,
StandardScaler
)
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle
# Модели классификации
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
# Показатели качества моделей
from sklearn.metrics import(
mean_absolute_error,
accuracy_score,
f1_score,
roc_curve,
roc_auc_score
)
# Визуализация графиков
import seaborn as sns
import matplotlib.pyplot as plt
Анализ данных¶
# Чтение данных
try:
data = pd.read_csv('/datasets/Churn.csv')
except:
data = pd.read_csv('datasets/Churn.csv')
# Вывод данных для анализа
print(data.info())
data.head()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 10000 entries, 0 to 9999 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 RowNumber 10000 non-null int64 1 CustomerId 10000 non-null int64 2 Surname 10000 non-null object 3 CreditScore 10000 non-null int64 4 Geography 10000 non-null object 5 Gender 10000 non-null object 6 Age 10000 non-null int64 7 Tenure 9091 non-null float64 8 Balance 10000 non-null float64 9 NumOfProducts 10000 non-null int64 10 HasCrCard 10000 non-null int64 11 IsActiveMember 10000 non-null int64 12 EstimatedSalary 10000 non-null float64 13 Exited 10000 non-null int64 dtypes: float64(3), int64(8), object(3) memory usage: 1.1+ MB None
RowNumber | CustomerId | Surname | CreditScore | Geography | Gender | Age | Tenure | Balance | NumOfProducts | HasCrCard | IsActiveMember | EstimatedSalary | Exited | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 15634602 | Hargrave | 619 | France | Female | 42 | 2.0 | 0.00 | 1 | 1 | 1 | 101348.88 | 1 |
1 | 2 | 15647311 | Hill | 608 | Spain | Female | 41 | 1.0 | 83807.86 | 1 | 0 | 1 | 112542.58 | 0 |
2 | 3 | 15619304 | Onio | 502 | France | Female | 42 | 8.0 | 159660.80 | 3 | 1 | 0 | 113931.57 | 1 |
3 | 4 | 15701354 | Boni | 699 | France | Female | 39 | 1.0 | 0.00 | 2 | 0 | 0 | 93826.63 | 0 |
4 | 5 | 15737888 | Mitchell | 850 | Spain | Female | 43 | 2.0 | 125510.82 | 1 | 1 | 1 | 79084.10 | 0 |
Выявлено:
- Менее 1% объектов содержат пропуски в атрибуте
Tenure
. Удаление этих объектов не приведёт к потере качества данных датафрейма, а все оставшиеся объекты можно использовать в машиностроению обучении. - Названия атрибутов не соответствуют «змеиному стилю». Их требуется привести к нижнему регистру.
- Датафрейм содержит 3 категориальных признака, требующих дополнительного анализа и возможной конвертации в численные типы данных для использовании в машинное обучении.
data.describe()
RowNumber | CustomerId | CreditScore | Age | Tenure | Balance | NumOfProducts | HasCrCard | IsActiveMember | EstimatedSalary | Exited | |
---|---|---|---|---|---|---|---|---|---|---|---|
count | 10000.00000 | 1.000000e+04 | 10000.000000 | 10000.000000 | 9091.000000 | 10000.000000 | 10000.000000 | 10000.00000 | 10000.000000 | 10000.000000 | 10000.000000 |
mean | 5000.50000 | 1.569094e+07 | 650.528800 | 38.921800 | 4.997690 | 76485.889288 | 1.530200 | 0.70550 | 0.515100 | 100090.239881 | 0.203700 |
std | 2886.89568 | 7.193619e+04 | 96.653299 | 10.487806 | 2.894723 | 62397.405202 | 0.581654 | 0.45584 | 0.499797 | 57510.492818 | 0.402769 |
min | 1.00000 | 1.556570e+07 | 350.000000 | 18.000000 | 0.000000 | 0.000000 | 1.000000 | 0.00000 | 0.000000 | 11.580000 | 0.000000 |
25% | 2500.75000 | 1.562853e+07 | 584.000000 | 32.000000 | 2.000000 | 0.000000 | 1.000000 | 0.00000 | 0.000000 | 51002.110000 | 0.000000 |
50% | 5000.50000 | 1.569074e+07 | 652.000000 | 37.000000 | 5.000000 | 97198.540000 | 1.000000 | 1.00000 | 1.000000 | 100193.915000 | 0.000000 |
75% | 7500.25000 | 1.575323e+07 | 718.000000 | 44.000000 | 7.000000 | 127644.240000 | 2.000000 | 1.00000 | 1.000000 | 149388.247500 | 0.000000 |
max | 10000.00000 | 1.581569e+07 | 850.000000 | 92.000000 | 10.000000 | 250898.090000 | 4.000000 | 1.00000 | 1.000000 | 199992.480000 | 1.000000 |
Все атрибуты отличаются друг от друга размерностью. Требуется их сбалансировать для улучшения качества моделей.
#
attr = 'Surname'
print(attr, ':', len(data[attr].unique()))
print(data[attr].unique())
print()
#
attr = 'Gender'
print(attr, ':', len(data[attr].unique()))
print(data[attr].unique())
print()
#
attr = 'Geography'
print(attr, ':', len(data[attr].unique()))
print(data[attr].unique())
print()
Surname : 2932 ['Hargrave' 'Hill' 'Onio' ... 'Kashiwagi' 'Aldridge' 'Burbidge'] Gender : 2 ['Female' 'Male'] Geography : 3 ['France' 'Spain' 'Germany']
Атрибуты Gender
и Geography
предлагается преобразовать методом порядкового кодирования. Атрибут Surname
предлагается удалить.
Выводы из анализа данных
Выявлено:
- Менее 1% объектов содержат пропуски в атрибуте
Tenure
. Удаление этих объектов не приведёт к потере качества данных датафрейма, а все оставшиеся обёртывания можно использовать в машиностроению обучении. - Названия атрибутов не соответствуют «змеиному стилю». Их требуется привести к нижнему регистру.
- Датафрейм содержит 3 категориальных признака. Атрибуты
Gender
иGeography
предлагается преобразовать методом порядкового кодирования. АтрибутSurname
предлагается удалить из датафрейма в силу его предполагаемой недостаточной значимости. - Все атрибуты отличаются друг от друга размерностью. Требуется их сбалансировать для улучшения качества моделей.
Предварительная обработка данных¶
# Приведение названий атрибутов к "змеиному стилю"
#data.columns = data.columns.str.lower()
data.columns = [re.sub(r'(?<!^)(?=[A-Z])', '_', i).lower() for i in data.columns]
data.columns
Index(['row_number', 'customer_id', 'surname', 'credit_score', 'geography', 'gender', 'age', 'tenure', 'balance', 'num_of_products', 'has_cr_card', 'is_active_member', 'estimated_salary', 'exited'], dtype='object')
# Удаление объектов с отсутствующими значениями признака "tenure"
data = data.loc[data['tenure'].notnull()]
# Удаление атрибута "surname"
data = data.drop('surname', axis=1)
# Удаление объектов с пустыми атрибутом "tenure"
data = data.loc[data['tenure'].notnull()]
# Проверка атрибутов датафрейма после их изменения
data.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 9091 entries, 0 to 9998 Data columns (total 13 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 row_number 9091 non-null int64 1 customer_id 9091 non-null int64 2 credit_score 9091 non-null int64 3 geography 9091 non-null object 4 gender 9091 non-null object 5 age 9091 non-null int64 6 tenure 9091 non-null float64 7 balance 9091 non-null float64 8 num_of_products 9091 non-null int64 9 has_cr_card 9091 non-null int64 10 is_active_member 9091 non-null int64 11 estimated_salary 9091 non-null float64 12 exited 9091 non-null int64 dtypes: float64(3), int64(8), object(2) memory usage: 994.3+ KB
Выводы из предварительной обработки данных
Проведены следующие работы:
- Названия атрибутов приведены к «змеиному стилю».
- Удален признак
surname
. - Удалены объекты с отсутствующими значениями признака
tenure
.
Итоги подготовки данных¶
Анализ данных выявил:
- Удалены менее 1% объектов, содержащих пропуски в атрибуте
Tenure
. Удаление этих объектов не приведёт к потере качества данных датафрейма, а все оставшиеся обёртывания можно использовать в машиностроению обучении. - Названия атрибутов приведены к «змеиному стилю».
- Датафрейм содержит 3 категориальных признака. Атрибут
Surname
удален из датафрейма в силу его предполагаемой недостаточной значимости. АтрибутыGender
иGeography
будут преобразованы методом порядкового кодирования после разделения датафрейма на выборки. - Все атрибуты отличаются друг от друга размерностью. После разделения датафрейма на выборки все атрибуты будут сбалансированы по размерности.
Исследование задачи¶
Изучение моделей без учёта дисбаланса¶
Не смотря на то, что все параметры далее будут переведены в числовые значения, в данной задаче требуется классификация по целевому признаку. Поэтому для решения задачи подойдут модели DecisionTreeClassifier
, RandomForestClassifier
и LogisticRegression
. Для решение НЕ подходят модели регрессии DecisionTreeRegressor
, RandomForestRegressor
и LinearRegression
.
Разделение датафрейма на обучающие, валидационные и тестовые выборки¶
# Разделение данных на выборки с целевыми данными и признаками
target = data['exited']
features = data.drop(['exited'], axis=1)
# Разделение выборок на обучающие и валидационные
features_train, features_test, target_train, target_test = train_test_split(
features,
target,
test_size=.4,
stratify=target,
random_state=12345
)
features_test, features_valid, target_test, target_valid = train_test_split(
features_test,
target_test,
test_size=.5,
stratify=target_test,
random_state=12345
)
print('Размеры выборок:')
print('features. :', features.shape)
print('target. :', target.shape)
print('features_train:', features_train.shape)
print('target_train :', target_train.shape)
print('features_test :', features_test.shape)
print('target_test :', target_test.shape)
print('features_valid:', features_valid.shape)
print('target_valid :', target_valid.shape)
print('\nfeatures_test:')
features_test.head()
Размеры выборок: features. : (9091, 12) target. : (9091,) features_train: (5454, 12) target_train : (5454,) features_test : (1818, 12) target_test : (1818,) features_valid: (1819, 12) target_valid : (1819,) features_test:
row_number | customer_id | credit_score | geography | gender | age | tenure | balance | num_of_products | has_cr_card | is_active_member | estimated_salary | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
7493 | 7494 | 15683276 | 610 | Spain | Female | 37 | 10.0 | 140363.95 | 2 | 1 | 1 | 129563.86 |
5460 | 5461 | 15668894 | 661 | Germany | Male | 41 | 5.0 | 122552.48 | 2 | 0 | 1 | 120646.40 |
9126 | 9127 | 15666095 | 753 | Spain | Male | 51 | 4.0 | 79811.72 | 2 | 0 | 1 | 68260.27 |
9801 | 9802 | 15578878 | 569 | Spain | Female | 30 | 3.0 | 139528.23 | 1 | 1 | 1 | 33230.37 |
9101 | 9102 | 15660980 | 597 | Spain | Male | 38 | 6.0 | 115702.67 | 2 | 1 | 1 | 25059.05 |
Разделение выборок на предназначенные для «деревянных» и логистических моделей с использованием порядкового и OHE декодирования категориальных признаков¶
# Порядковое кодирование категориальных признаков "gender" и "geography"
# для "деревянных" моделей
encoder = OrdinalEncoder(categories='auto', handle_unknown='ignore')
encoder.fit(features_train)
features_train_wooden = pd.DataFrame(
encoder.transform(features_train),
columns=features_train.columns,
index=features_train.index
)
features_valid_wooden = pd.DataFrame(
encoder.transform(features_valid),
columns=features_valid.columns,
index=features_valid.index
)
features_test_wooden = pd.DataFrame(
encoder.transform(features_test),
columns=features_test.columns,
index=features_test.index
)
print('features_test_wooden:')
features_test_wooden.head()
features_test_wooden:
row_number | customer_id | credit_score | geography | gender | age | tenure | balance | num_of_products | has_cr_card | is_active_member | estimated_salary | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
7493 | 0.0 | 0.0 | 205.0 | 2.0 | 0.0 | 19.0 | 10.0 | 0.0 | 1.0 | 1.0 | 1.0 | 0.0 |
5460 | 0.0 | 0.0 | 256.0 | 1.0 | 1.0 | 23.0 | 5.0 | 0.0 | 1.0 | 0.0 | 1.0 | 0.0 |
9126 | 0.0 | 0.0 | 348.0 | 2.0 | 1.0 | 33.0 | 4.0 | 0.0 | 1.0 | 0.0 | 1.0 | 0.0 |
9801 | 0.0 | 0.0 | 164.0 | 2.0 | 0.0 | 12.0 | 3.0 | 0.0 | 0.0 | 1.0 | 1.0 | 0.0 |
9101 | 0.0 | 0.0 | 192.0 | 2.0 | 1.0 | 20.0 | 6.0 | 0.0 | 1.0 | 1.0 | 1.0 | 0.0 |
# OneHotEncoder кодирование категориальных признаков "gender" и "geography"
# для логистических моделей
def OneHotEncoder_TrainValidTestTransform(features_train,
features_valid,
features_test,
column_name
):
ohe = OneHotEncoder(
categories='auto',
#drop='first',
handle_unknown='ignore',
sparse=False
)
ohe.fit(pd.DataFrame(features_train[column_name]))
# features_train
features_train = features_train.join(
pd.DataFrame(
ohe.transform(pd.DataFrame(features_train[column_name])),
columns=ohe.get_feature_names([column_name])
)
)
features_train_logistic = features_train.drop(column_name, axis=1)
# features_valid
features_valid = features_valid.join(
pd.DataFrame(
ohe.transform(pd.DataFrame(features_valid[column_name])),
columns=ohe.get_feature_names([column_name])
)
)
features_valid_logistic = features_valid.drop(column_name, axis=1)
# features_test
features_test = features_test.join(
pd.DataFrame(
ohe.transform(pd.DataFrame(features_test[column_name])),
columns=ohe.get_feature_names([column_name])
)
)
features_test_logistic = features_test.drop(column_name, axis=1)
return features_train_logistic, features_valid_logistic, features_test_logistic
"gender"
features_train_logistic, features_valid_logistic, features_test_logistic = OneHotEncoder_TrainValidTestTransform(
features_train,
features_valid,
features_test,
'gender')
"geography"
features_train_logistic, features_valid_logistic, features_test_logistic = OneHotEncoder_TrainValidTestTransform(
features_train_logistic,
features_valid_logistic,
features_test_logistic,
'geography')
print('features_test_logistic:')
features_test_logistic.head()
features_test_logistic:
row_number | customer_id | credit_score | age | tenure | balance | num_of_products | has_cr_card | is_active_member | estimated_salary | gender_Female | gender_Male | geography_France | geography_Germany | geography_Spain | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
7493 | 7494 | 15683276 | 610 | 37 | 10.0 | 140363.95 | 2 | 1 | 1 | 129563.86 | NaN | NaN | NaN | NaN | NaN |
5460 | 5461 | 15668894 | 661 | 41 | 5.0 | 122552.48 | 2 | 0 | 1 | 120646.40 | NaN | NaN | NaN | NaN | NaN |
9126 | 9127 | 15666095 | 753 | 51 | 4.0 | 79811.72 | 2 | 0 | 1 | 68260.27 | NaN | NaN | NaN | NaN | NaN |
9801 | 9802 | 15578878 | 569 | 30 | 3.0 | 139528.23 | 1 | 1 | 1 | 33230.37 | NaN | NaN | NaN | NaN | NaN |
9101 | 9102 | 15660980 | 597 | 38 | 6.0 | 115702.67 | 2 | 1 | 1 | 25059.05 | NaN | NaN | NaN | NaN | NaN |
# Проверка наличия разных значений аттрибутов,
# созданных путем OHE кодирования, на предмет наличия пропусков
features_test_logistic['gender_Female'].unique()
array([nan, 1., 0.])
# Замена пропусков в новых аттрибутах на 0
features_train_logistic = features_train_logistic.fillna(0)
features_valid_logistic = features_valid_logistic.fillna(0)
features_test_logistic = features_test_logistic.fillna(0)
features_test_logistic['gender_Female'].unique()
array([0., 1.])
Приведение данных к единому масштабу¶
# Приведение данных к единому масштабу
scaler = StandardScaler()
# для "деревянных" моделей
scaler.fit(features_train_wooden)
features_train_wooden = pd.DataFrame(
scaler.transform(features_train_wooden),
index=features_train_wooden.index,
columns=features_train_wooden.columns
)
features_valid_wooden = pd.DataFrame(
scaler.transform(features_valid_wooden),
index=features_valid_wooden.index,
columns=features_valid_wooden.columns
)
features_test_wooden = pd.DataFrame(
scaler.transform(features_test_wooden),
index=features_test_wooden.index,
columns=features_test_wooden.columns
)
# для логистических моделей
scaler.fit(features_train_logistic)
features_train_logistic = pd.DataFrame(
scaler.transform(features_train_logistic),
index=features_train_logistic.index,
columns=features_train_logistic.columns
)
features_valid_logistic = pd.DataFrame(
scaler.transform(features_valid_logistic),
index=features_valid_logistic.index,
columns=features_valid_logistic.columns
)
features_test_logistic = pd.DataFrame(
scaler.transform(features_test_logistic),
index=features_test_logistic.index,
columns=features_test_logistic.columns
)
Анализ полученных выборок¶
# Анализ атрибутов тестовой выборки
# для "деревянных" моделей
print(features_test_wooden.info())
print()
features_test_wooden.describe()
<class 'pandas.core.frame.DataFrame'> Int64Index: 1818 entries, 7493 to 4661 Data columns (total 12 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 row_number 1818 non-null float64 1 customer_id 1818 non-null float64 2 credit_score 1818 non-null float64 3 geography 1818 non-null float64 4 gender 1818 non-null float64 5 age 1818 non-null float64 6 tenure 1818 non-null float64 7 balance 1818 non-null float64 8 num_of_products 1818 non-null float64 9 has_cr_card 1818 non-null float64 10 is_active_member 1818 non-null float64 11 estimated_salary 1818 non-null float64 dtypes: float64(12) memory usage: 249.2 KB None
row_number | customer_id | credit_score | geography | gender | age | tenure | balance | num_of_products | has_cr_card | is_active_member | estimated_salary | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 |
mean | -1.731733 | -1.731733 | 0.029663 | -0.050233 | 0.047846 | 0.017180 | -0.010780 | -0.956339 | -0.036475 | -0.024180 | -0.008073 | -1.731495 |
std | 0.000000 | 0.000000 | 0.995222 | 0.977624 | 0.995012 | 1.033504 | 1.011652 | 0.000000 | 0.973909 | 1.010950 | 1.000530 | 0.010144 |
min | -1.731733 | -1.731733 | -2.561551 | -0.918068 | -1.089426 | -2.005133 | -1.748894 | -0.956339 | -0.910943 | -1.554765 | -1.036226 | -1.731733 |
25% | -1.731733 | -1.731733 | -0.660474 | -0.918068 | -1.089426 | -0.661078 | -1.053112 | -0.956339 | -0.910943 | -1.554765 | -1.036226 | -1.731733 |
50% | -1.731733 | -1.731733 | 0.039373 | -0.918068 | 0.917915 | -0.181059 | -0.009440 | -0.956339 | -0.910943 | 0.643184 | 0.965040 | -1.731733 |
75% | -1.731733 | -1.731733 | 0.715717 | 0.283550 | 0.917915 | 0.490968 | 0.686341 | -0.956339 | 0.789359 | 0.643184 | 0.965040 | -1.731733 |
max | -1.731733 | -1.731733 | 2.086686 | 1.485169 | 0.917915 | 4.139115 | 1.730013 | -0.956339 | 4.189962 | 0.643184 | 0.965040 | -1.299197 |
# Анализ атрибутов тестовой выборки
# для логистических моделей
print(features_test_logistic.info())
print()
features_test_logistic.describe()
<class 'pandas.core.frame.DataFrame'> Int64Index: 1818 entries, 7493 to 4661 Data columns (total 15 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 row_number 1818 non-null float64 1 customer_id 1818 non-null float64 2 credit_score 1818 non-null float64 3 age 1818 non-null float64 4 tenure 1818 non-null float64 5 balance 1818 non-null float64 6 num_of_products 1818 non-null float64 7 has_cr_card 1818 non-null float64 8 is_active_member 1818 non-null float64 9 estimated_salary 1818 non-null float64 10 gender_Female 1818 non-null float64 11 gender_Male 1818 non-null float64 12 geography_France 1818 non-null float64 13 geography_Germany 1818 non-null float64 14 geography_Spain 1818 non-null float64 dtypes: float64(15) memory usage: 291.8 KB None
row_number | customer_id | credit_score | age | tenure | balance | num_of_products | has_cr_card | is_active_member | estimated_salary | gender_Female | gender_Male | geography_France | geography_Germany | geography_Spain | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 | 1818.000000 |
mean | 0.062226 | 0.022237 | 0.029394 | 0.024595 | -0.010780 | 0.008403 | -0.036475 | -0.024180 | -0.008073 | -0.015442 | -0.427996 | -0.437109 | -0.430849 | -0.266873 | -0.294005 |
std | 1.015332 | 0.980033 | 0.995982 | 1.043167 | 1.011652 | 0.985896 | 0.973909 | 1.010950 | 1.000530 | 0.990842 | 0.578798 | 0.646456 | 0.608029 | 0.614191 | 0.548372 |
min | -1.728157 | -1.740931 | -3.126112 | -2.004168 | -1.748894 | -1.222967 | -0.910943 | -1.554765 | -1.036226 | -1.730254 | -0.584549 | -0.648030 | -0.609131 | -0.404530 | -0.402071 |
25% | -0.792920 | -0.817757 | -0.657523 | -0.660843 | -1.053112 | -1.222967 | -0.910943 | -1.554765 | -1.036226 | -0.858026 | -0.584549 | -0.648030 | -0.609131 | -0.404530 | -0.402071 |
50% | 0.084398 | 0.007659 | 0.040348 | -0.181084 | -0.009440 | 0.330659 | -0.910943 | 0.643184 | 0.965040 | -0.007500 | -0.584549 | -0.648030 | -0.609131 | -0.404530 | -0.402071 |
75% | 0.962845 | 0.869769 | 0.714783 | 0.490579 | 0.686341 | 0.802382 | 0.789359 | 0.643184 | 0.965040 | 0.809042 | -0.584549 | -0.648030 | -0.609131 | -0.404530 | -0.402071 |
max | 1.743543 | 1.713618 | 2.081881 | 5.096265 | 1.730013 | 2.323255 | 4.189962 | 0.643184 | 0.965040 | 1.740143 | 1.710721 | 1.543139 | 1.641683 | 2.472008 | 2.487121 |
Выборки подготовлены для использования в моделях. Для балансировки в дальнейшем выбоки будут дополнительно обработаны.
Создание функции и датафрейма для анализа метрик качества модели¶
# Функция анализ метрик качества модели
def output_of_quality_metrics(target_train,
target_test,
predictions_test,
probabilities_test,
quality_metrics,
model,
sample,
class_weight,
max_depth,
n_estimators
):
predictions_train = pd.Series(target_train.median(), index=target_train.index)
# Показатель ROC-AUC
probabilities_one_test = probabilities_test[:, 1]
fpr, tpr, thresholds = roc_curve(target_test, probabilities_one_test)
# Построение кривой AUC
plt.figure()
plt.plot(fpr, tpr, linestyle='-')
plt.plot([0, 1], [0, 1], linestyle='--')
plt.xlim(0, 1)
plt.ylim(0, 1)
plt.xlabel('False Pisitive Rate')
plt.xlabel('True Pisitive Rate')
plt.xlabel('True Pisitive Rate')
plt.title('ROC-кривая')
plt.show()
# Площадь "AUC-ROC"
auc_roc = roc_auc_score(target_test, probabilities_one_test)
print('AUC-ROC:', auc_roc)
# Метрики для оценки моделей классификации (не регрессии)
accuracy = accuracy_score(target_test, predictions_test)
f1 = f1_score(target_test, predictions_test)
print('Accuracy:', accuracy)
print('F1:', f1)
print()
# Альтернативные метрики
result = target_test * predictions_test
print('TP:', len(result.loc[result == 1]))
result = target_test + predictions_test
print('TN:', len(result.loc[result == 0]))
result = target_test - predictions_test
print('FP:', len(result.loc[result == -1]))
result = target_test - predictions_test
print('FN:', len(result.loc[result == 1]))
quality_metrics = quality_metrics.append({
'model': model,
'sample': sample,
'auc_roc': auc_roc,
'accuracy': accuracy,
'f1': f1,
'class_weight': class_weight,
'max_depth': max_depth,
'n_estimators': n_estimators
}, ignore_index=True)
return quality_metrics
# Датафрейм с результатами анализа моделей
quality_metrics = pd.DataFrame(columns=['model',
'sample',
'auc_roc',
'accuracy',
'f1',
'class_weight',
'max_depth',
'n_estimators'
]
)
quality_metrics
model | sample | auc_roc | accuracy | f1 | class_weight | max_depth | n_estimators |
---|
Модель решающего дерева без балансировки классов¶
# Функция поиска оптимальных параметров для решающего дерева
def DecisionTreeClassifier_SelectionOfParametrs(
features_train, target_train,
features_test, target_test,
class_weight, balance_type,
quality_metrics
):
# Поиск оптимальных параметров
depth_f1 = pd.DataFrame(columns=['depth', 'f1'])
for depth in range(0, 30, 1):
if depth > 0:
model = DecisionTreeClassifier(
class_weight=class_weight,
max_depth=depth,
random_state=12345
)
model.fit(features_train, target_train)
predictions_test = model.predict(features_test)
depth_f1 = depth_f1.append(pd.DataFrame({
'depth': [depth],
'f1': [f1_score(target_test, predictions_test)]
}), ignore_index=True)
print('max_depth:', depth)
print('f1:', f1_score(target_test, predictions_test))
print()
# Модель решающего дерева с оптимальными параметрами
depth = int(depth_f1.loc[depth_f1['f1'] == depth_f1['f1'].max(), 'depth'])
model = DecisionTreeClassifier(
max_depth=depth,
random_state=12345
)
model.fit(features_train, target_train)
predictions_test = model.predict(features_test)
probabilities_test = model.predict_proba(features_test)
# Анализ метрик качества моделей
quality_metrics = output_of_quality_metrics(target_train, target_test,
predictions_test, probabilities_test,
quality_metrics,
'DecisionTreeClassifier', balance_type,
class_weight, depth, None
)
print('\nmax_depth:', depth)
return quality_metrics
# Модель решающего дерева без балансировки классов
quality_metrics = DecisionTreeClassifier_SelectionOfParametrs(
features_train_wooden, target_train,
features_valid_wooden, target_valid,
None, 'none', quality_metrics
)
max_depth: 1 f1: 0.0 max_depth: 2 f1: 0.49913941480206536 max_depth: 3 f1: 0.4795737122557726 max_depth: 4 f1: 0.47368421052631576 max_depth: 5 f1: 0.4990757855822551 max_depth: 6 f1: 0.4549019607843137 max_depth: 7 f1: 0.5536547433903577 max_depth: 8 f1: 0.4612736660929432 max_depth: 9 f1: 0.5058479532163743 max_depth: 10 f1: 0.4444444444444444 max_depth: 11 f1: 0.43467336683417085 max_depth: 12 f1: 0.39151398264223725 max_depth: 13 f1: 0.31947840260798693 max_depth: 14 f1: 0.42424242424242425 max_depth: 15 f1: 0.37965485921889197 max_depth: 16 f1: 0.32885375494071145 max_depth: 17 f1: 0.3675777568331763 max_depth: 18 f1: 0.37447698744769875 max_depth: 19 f1: 0.3460076045627377 max_depth: 20 f1: 0.3931987247608927 max_depth: 21 f1: 0.40585774058577406 max_depth: 22 f1: 0.36221837088388215 max_depth: 23 f1: 0.34383954154727797 max_depth: 24 f1: 0.34383954154727797 max_depth: 25 f1: 0.34383954154727797 max_depth: 26 f1: 0.34383954154727797 max_depth: 27 f1: 0.34383954154727797 max_depth: 28 f1: 0.34383954154727797 max_depth: 29 f1: 0.34383954154727797
AUC-ROC: 0.7024737159535972 Accuracy: 0.8422210005497526 F1: 0.5536547433903577 TP: 178 TN: 1354 FP: 94 FN: 193 max_depth: 7
Модель случайного леса без балансировки классов¶
# Функция поиска оптимального параметра n_estimators случайного леса
def RandomForestClassifier_SelectionOfParametrs_NEstimatorsItrator(
features_train, target_train,
features_test, target_test,
class_weight, depth, n1, n2, n3
):
n_estimators_f1 = pd.DataFrame(columns=['n_estimators', 'f1'])
for n in range(n1, n2, n3):
model = RandomForestClassifier(
class_weight=class_weight,
n_estimators=n,
max_depth=depth,
random_state=12345
)
model.fit(features_train, target_train)
predictions_test = model.predict(features_test)
n_estimators_f1 = n_estimators_f1.append(pd.DataFrame({
'n_estimators': [n],
'f1': [f1_score(target_test, predictions_test)]
}), ignore_index=True)
print('n_estimators:', n)
print('f1:', f1_score(target_test, predictions_test))
print()
result = n_estimators_f1.loc[n_estimators_f1['f1'] == n_estimators_f1['f1'].max(), 'n_estimators']
#print(result)
if len(result) > 1:
result = int(result.loc[result == result.max()])
else:
result = int(result)
return result
# Функция поиска оптимальных параметров для случайного леса
def RandomForestClassifier_SelectionOfParametrs(
features_train, target_train,
features_test, target_test,
class_weight, balance_type, quality_metrics
):
# Поиск max_depth
depth_f1 = pd.DataFrame(columns=['depth', 'f1'])
for depth in range(1, 30, 1):
model = RandomForestClassifier(
class_weight=class_weight,
n_estimators=20,
max_depth=depth,
random_state=12345
)
model.fit(features_train, target_train)
predictions_test = model.predict(features_test)
depth_f1 = depth_f1.append(pd.DataFrame({
'depth': [depth],
'f1': [f1_score(target_test, predictions_test)]
}), ignore_index=True)
print('max_depth:', depth)
print('f1:', f1_score(target_test, predictions_test))
print()
depth = int(depth_f1.loc[depth_f1['f1'] == depth_f1['f1'].max(), 'depth'])
# Поиск n_estimators крупными шагами
n_estimators = RandomForestClassifier_SelectionOfParametrs_NEstimatorsItrator(
features_train, target_train,
features_test, target_test,
class_weight, depth, 50, 252, 10
)
# Модель случайного леса с оптимальными параметрами
model = RandomForestClassifier(
n_estimators=n_estimators,
max_depth=depth,
random_state=12345
)
model.fit(features_train, target_train)
predictions_test = model.predict(features_test)
probabilities_test = model.predict_proba(features_test)
# Анализ метрик качества моделей
quality_metrics = output_of_quality_metrics(target_train, target_test,
predictions_test, probabilities_test,
quality_metrics,
'RandomForestClassifier', balance_type,
class_weight, depth, n_estimators
)
print('\nmax_depth:', depth)
print('n_estimators:', n_estimators)
return quality_metrics
# Модель случайного леса без балансировки классов
quality_metrics = RandomForestClassifier_SelectionOfParametrs(
features_train_wooden, target_train,
features_valid_wooden, target_valid,
None, 'none', quality_metrics
)
max_depth: 1 f1: 0.0 max_depth: 2 f1: 0.10714285714285715 max_depth: 3 f1: 0.17031630170316303 max_depth: 4 f1: 0.389937106918239 max_depth: 5 f1: 0.4244897959183674 max_depth: 6 f1: 0.47924528301886793 max_depth: 7 f1: 0.48393194706994325 max_depth: 8 f1: 0.5446009389671361 max_depth: 9 f1: 0.5277777777777778 max_depth: 10 f1: 0.5116279069767441 max_depth: 11 f1: 0.43043478260869567 max_depth: 12 f1: 0.48575305291723203 max_depth: 13 f1: 0.38337182448036955 max_depth: 14 f1: 0.4021024967148489 max_depth: 15 f1: 0.4502762430939226 max_depth: 16 f1: 0.39124087591240886 max_depth: 17 f1: 0.4987012987012987 max_depth: 18 f1: 0.519893899204244 max_depth: 19 f1: 0.4135021097046414 max_depth: 20 f1: 0.46036585365853655 max_depth: 21 f1: 0.46713286713286717 max_depth: 22 f1: 0.45108695652173914 max_depth: 23 f1: 0.47058823529411764 max_depth: 24 f1: 0.4509283819628647 max_depth: 25 f1: 0.4862023653088041 max_depth: 26 f1: 0.46965699208443273 max_depth: 27 f1: 0.46965699208443273 max_depth: 28 f1: 0.46965699208443273 max_depth: 29 f1: 0.46965699208443273 n_estimators: 50 f1: 0.4936479128856625 n_estimators: 60 f1: 0.5159010600706714 n_estimators: 70 f1: 0.5185185185185185 n_estimators: 80 f1: 0.532399299474606 n_estimators: 90 f1: 0.5228070175438596 n_estimators: 100 f1: 0.51985559566787 n_estimators: 110 f1: 0.5178571428571428 n_estimators: 120 f1: 0.5240641711229945 n_estimators: 130 f1: 0.5186500888099467 n_estimators: 140 f1: 0.5222024866785079 n_estimators: 150 f1: 0.5202108963093146 n_estimators: 160 f1: 0.5283687943262411 n_estimators: 170 f1: 0.5222024866785079 n_estimators: 180 f1: 0.5186500888099467 n_estimators: 190 f1: 0.5098743267504487 n_estimators: 200 f1: 0.5179856115107913 n_estimators: 210 f1: 0.5197132616487455 n_estimators: 220 f1: 0.5197132616487455 n_estimators: 230 f1: 0.5242369838420108 n_estimators: 240 f1: 0.5215827338129496 n_estimators: 250 f1: 0.516245487364621
AUC-ROC: 0.8082921326562523 Accuracy: 0.8532160527762507 F1: 0.532399299474606 TP: 152 TN: 1400 FP: 48 FN: 219 max_depth: 8 n_estimators: 80
Проверка на мультиколлинеарность выборок для логистических моделей¶
# Проверка на мультиколлинеарность
# тренировочных выборок для логистических моделей
sns.heatmap(features_train_logistic.join(target_train).corr())
features_train_logistic.join(target_train).corr()
row_number | customer_id | credit_score | age | tenure | balance | num_of_products | has_cr_card | is_active_member | estimated_salary | gender_Female | gender_Male | geography_France | geography_Germany | geography_Spain | exited | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
row_number | 1.000000 | 0.005898 | 0.005712 | 0.007946 | -0.011205 | 0.007941 | 0.027162 | 0.002995 | 0.001504 | -0.010913 | -0.460272 | -0.497288 | -0.477358 | -0.303382 | -0.317613 | -0.000772 |
customer_id | 0.005898 | 1.000000 | 0.015712 | 0.008529 | -0.027959 | -0.001875 | -0.006575 | -0.014382 | 0.019827 | 0.008934 | -0.019446 | 0.017972 | -0.014917 | -0.001245 | 0.019618 | -0.013277 |
credit_score | 0.005712 | 0.015712 | 1.000000 | -0.006208 | 0.008835 | 0.006499 | 0.024159 | 0.005841 | 0.034242 | 0.000839 | 0.000568 | 0.011944 | -0.009777 | 0.014734 | 0.014214 | -0.016024 |
age | 0.007946 | 0.008529 | -0.006208 | 1.000000 | -0.011521 | 0.021097 | -0.026788 | -0.030624 | 0.083881 | -0.017381 | -0.007526 | 0.001229 | 0.017243 | -0.009995 | -0.019946 | 0.281278 |
tenure | -0.011205 | -0.027959 | 0.008835 | -0.011521 | 1.000000 | 0.006254 | -0.009793 | 0.039019 | -0.027909 | 0.028676 | 0.008976 | 0.001430 | 0.017221 | -0.011342 | 0.002470 | -0.005569 |
balance | 0.007941 | -0.001875 | 0.006499 | 0.021097 | 0.006254 | 1.000000 | -0.285302 | -0.016341 | 0.002558 | -0.000291 | -0.006299 | -0.003205 | -0.017244 | -0.007464 | 0.017476 | 0.106216 |
num_of_products | 0.027162 | -0.006575 | 0.024159 | -0.026788 | -0.009793 | -0.285302 | 1.000000 | 0.008951 | 0.005011 | 0.017998 | -0.011565 | -0.010363 | -0.002647 | -0.004415 | -0.020389 | -0.043919 |
has_cr_card | 0.002995 | -0.014382 | 0.005841 | -0.030624 | 0.039019 | -0.016341 | 0.008951 | 1.000000 | -0.002109 | -0.005875 | -0.002348 | 0.010606 | 0.013534 | -0.023826 | 0.017588 | -0.014600 |
is_active_member | 0.001504 | 0.019827 | 0.034242 | 0.083881 | -0.027909 | 0.002558 | 0.005011 | -0.002109 | 1.000000 | -0.020044 | 0.014988 | -0.021859 | 0.003096 | 0.004073 | -0.018021 | -0.147341 |
estimated_salary | -0.010913 | 0.008934 | 0.000839 | -0.017381 | 0.028676 | -0.000291 | 0.017998 | -0.005875 | -0.020044 | 1.000000 | 0.005538 | 0.008935 | 0.003077 | 0.012168 | 0.002582 | 0.006600 |
gender_Female | -0.460272 | -0.019446 | 0.000568 | -0.007526 | 0.008976 | -0.006299 | -0.011565 | -0.002348 | 0.014988 | 0.005538 | 1.000000 | -0.378805 | 0.293737 | 0.192073 | 0.189316 | -0.013787 |
gender_Male | -0.497288 | 0.017972 | 0.011944 | 0.001229 | 0.001430 | -0.003205 | -0.010363 | 0.010606 | -0.021859 | 0.008935 | -0.378805 | 1.000000 | 0.319642 | 0.215141 | 0.215352 | 0.013093 |
geography_France | -0.477358 | -0.014917 | -0.009777 | 0.017243 | 0.017221 | -0.017244 | -0.002647 | 0.013534 | 0.003096 | 0.003077 | 0.293737 | 0.319642 | 1.000000 | -0.246412 | -0.244914 | 0.006210 |
geography_Germany | -0.303382 | -0.001245 | 0.014734 | -0.009995 | -0.011342 | -0.007464 | -0.004415 | -0.023826 | 0.004073 | 0.012168 | 0.192073 | 0.215141 | -0.246412 | 1.000000 | -0.162650 | -0.001808 |
geography_Spain | -0.317613 | 0.019618 | 0.014214 | -0.019946 | 0.002470 | 0.017476 | -0.020389 | 0.017588 | -0.018021 | 0.002582 | 0.189316 | 0.215352 | -0.244914 | -0.162650 | 1.000000 | -0.006246 |
exited | -0.000772 | -0.013277 | -0.016024 | 0.281278 | -0.005569 | 0.106216 | -0.043919 | -0.014600 | -0.147341 | 0.006600 | -0.013787 | 0.013093 | 0.006210 | -0.001808 | -0.006246 | 1.000000 |
В обучающей выборке целевой показатель exited
имеет максимальную Положительную зависть с атрибутом age
, а отрицательную с is_active_member
и чуть меньшую с gender
. Все аттрибуты не достаточно коррелируют между собой для того, чтобы их исключать из выборки.
# Проверка на мультиколлинеарность
# тестовых выборок
sns.heatmap(features_valid_logistic.join(target_valid).corr())
features_valid_logistic.join(target_valid).corr()
row_number | customer_id | credit_score | age | tenure | balance | num_of_products | has_cr_card | is_active_member | estimated_salary | gender_Female | gender_Male | geography_France | geography_Germany | geography_Spain | exited | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
row_number | 1.000000 | -0.004687 | -0.021238 | -0.022875 | -0.028467 | -0.007524 | -0.049267 | -0.017128 | 0.026583 | 0.032802 | -0.428833 | -0.472871 | -0.462637 | -0.310754 | -0.296507 | -0.060179 |
customer_id | -0.004687 | 1.000000 | -0.019811 | 0.012808 | -0.006061 | -0.009157 | 0.045024 | -0.064256 | -0.009686 | 0.010066 | -0.009566 | 0.013437 | 0.003232 | -0.016932 | 0.019044 | 0.003639 |
credit_score | -0.021238 | -0.019811 | 1.000000 | -0.013809 | -0.008568 | -0.024891 | 0.006992 | -0.011907 | 0.030364 | 0.017494 | 0.050082 | -0.018401 | 0.029116 | 0.030111 | -0.031060 | -0.040755 |
age | -0.022875 | 0.012808 | -0.013809 | 1.000000 | -0.010143 | 0.054375 | -0.050313 | -0.010073 | 0.090122 | 0.003412 | -0.006570 | 0.039086 | -0.008604 | -0.010327 | 0.071635 | 0.280302 |
tenure | -0.028467 | -0.006061 | -0.008568 | -0.010143 | 1.000000 | -0.036796 | 0.061575 | 0.017838 | -0.016184 | -0.013928 | 0.031951 | -0.010001 | 0.011865 | 0.018325 | -0.006511 | -0.051494 |
balance | -0.007524 | -0.009157 | -0.024891 | 0.054375 | -0.036796 | 1.000000 | -0.309930 | -0.022514 | -0.036199 | 0.042752 | 0.031474 | -0.003727 | 0.002136 | 0.020740 | 0.013885 | 0.127003 |
num_of_products | -0.049267 | 0.045024 | 0.006992 | -0.050313 | 0.061575 | -0.309930 | 1.000000 | -0.007011 | 0.031366 | -0.003514 | -0.014212 | 0.028259 | 0.002495 | 0.000553 | 0.017730 | -0.085182 |
has_cr_card | -0.017128 | -0.064256 | -0.011907 | -0.010073 | 0.017838 | -0.022514 | -0.007011 | 1.000000 | -0.027230 | 0.008695 | 0.017583 | 0.027662 | 0.021351 | -0.001293 | 0.035147 | 0.005733 |
is_active_member | 0.026583 | -0.009686 | 0.030364 | 0.090122 | -0.016184 | -0.036199 | 0.031366 | -0.027230 | 1.000000 | -0.022181 | -0.012466 | -0.032888 | -0.018167 | -0.033954 | -0.004539 | -0.148687 |
estimated_salary | 0.032802 | 0.010066 | 0.017494 | 0.003412 | -0.013928 | 0.042752 | -0.003514 | 0.008695 | -0.022181 | 1.000000 | -0.018907 | 0.009388 | -0.004455 | -0.002596 | -0.003195 | 0.040447 |
gender_Female | -0.428833 | -0.009566 | 0.050082 | -0.006570 | 0.031951 | 0.031474 | -0.014212 | 0.017583 | -0.012466 | -0.018907 | 1.000000 | -0.101855 | 0.425875 | 0.351241 | 0.253830 | 0.004754 |
gender_Male | -0.472871 | 0.013437 | -0.018401 | 0.039086 | -0.010001 | -0.003727 | 0.028259 | 0.027662 | -0.032888 | 0.009388 | -0.101855 | 1.000000 | 0.498107 | 0.258706 | 0.338808 | 0.037867 |
geography_France | -0.462637 | 0.003232 | 0.029116 | -0.008604 | 0.011865 | 0.002136 | 0.002495 | 0.021351 | -0.018167 | -0.004455 | 0.425875 | 0.498107 | 1.000000 | -0.071101 | -0.069711 | 0.035337 |
geography_Germany | -0.310754 | -0.016932 | 0.030111 | -0.010327 | 0.018325 | 0.020740 | 0.000553 | -0.001293 | -0.033954 | -0.002596 | 0.351241 | 0.258706 | -0.071101 | 1.000000 | -0.045695 | 0.003171 |
geography_Spain | -0.296507 | 0.019044 | -0.031060 | 0.071635 | -0.006511 | 0.013885 | 0.017730 | 0.035147 | -0.004539 | -0.003195 | 0.253830 | 0.338808 | -0.069711 | -0.045695 | 1.000000 | 0.007349 |
exited | -0.060179 | 0.003639 | -0.040755 | 0.280302 | -0.051494 | 0.127003 | -0.085182 | 0.005733 | -0.148687 | 0.040447 | 0.004754 | 0.037867 | 0.035337 | 0.003171 | 0.007349 | 1.000000 |
В тестовой выборке, как и в целевой, показатель exited
имеет максимальную положительную зависть с атрибутом age
, а отрицательную с is_active_member
и чуть меньшую с gender
. Все аттрибуты не достаточно коррелируют между собой для того, чтобы их исключать из выборки.
Модель логистической регрессии без балансировки классов¶
# Функция поиска оптимальных параметров для логистической регрессии
def LogisticRegression_SelectionOfParametrs(
features_train, target_train,
features_test, target_test,
class_weight, balance_type, quality_metrics
):
# Пробное обучение методом логистической регрессии
model = LogisticRegression(
solver='liblinear',
class_weight=class_weight,
random_state=12345
)
model.fit(features_train, target_train)
predictions_test = model.predict(features_test)
probabilities_test = model.predict_proba(features_test)
# Анализ метрик качества моделей
quality_metrics = output_of_quality_metrics(target_train, target_test,
predictions_test, probabilities_test,
quality_metrics, 'LogisticRegression', balance_type,
class_weight, None, None
)
return quality_metrics
# Модель логистической регрессии без балансировки классов
quality_metrics = LogisticRegression_SelectionOfParametrs(
features_train_logistic, target_train,
features_valid_logistic, target_valid,
None, 'none', quality_metrics
)
AUC-ROC: 0.7442182543819154 Accuracy: 0.8042880703683343 F1: 0.25523012552301255 TP: 61 TN: 1402 FP: 46 FN: 310
Результаты использования моделей без балансировки классов¶
# Таблица изменения значений показателей качества моделей
quality_metrics.sort_values('f1', ascending=False)
model | sample | auc_roc | accuracy | f1 | class_weight | max_depth | n_estimators | |
---|---|---|---|---|---|---|---|---|
0 | DecisionTreeClassifier | none | 0.702474 | 0.842221 | 0.553655 | None | 7 | None |
1 | RandomForestClassifier | none | 0.808292 | 0.853216 | 0.532399 | None | 8 | 80 |
2 | LogisticRegression | none | 0.744218 | 0.804288 | 0.255230 | None | None | None |
Среди моделей с несбалансированными выборками лидирует DecisionTreeClassifier
.
Исследование баланса классов¶
print('Уникальные классы целевого признака:')
print(target_train.unique())
print()
print('Количество объектов каждого класса целевого признака:')
for i in target_train.unique():
print(f'Класс "{i}" =', len(target_train.loc[target_train == i]))
Уникальные классы целевого признака: [0 1] Количество объектов каждого класса целевого признака: Класс "0" = 4342 Класс "1" = 1112
print('Отношение класса "1." к классу "0." целевого признака:')
class_ratio = len(target_train.loc[target_train == 0]) / len(target_train.loc[target_train == 1])
class_ratio
Отношение класса "1." к классу "0." целевого признака:
3.9046762589928057
Котчество объектов класса 0
целевого признака превышает количество объектов класса 1
в 3.9046762589928057 раз.
Модель решающего дерева с балансировкой весов классов внутри модели¶
# Модель решающего дерева с балансировкой весов классов внутри модели
quality_metrics = DecisionTreeClassifier_SelectionOfParametrs(
features_train_wooden, target_train,
features_valid_wooden, target_valid,
'balanced', 'balance', quality_metrics
)
max_depth: 1 f1: 0.4771033013844515 max_depth: 2 f1: 0.49688149688149696 max_depth: 3 f1: 0.49688149688149696 max_depth: 4 f1: 0.5203045685279187 max_depth: 5 f1: 0.5340453938584779 max_depth: 6 f1: 0.5047318611987381 max_depth: 7 f1: 0.48648648648648657 max_depth: 8 f1: 0.310580204778157 max_depth: 9 f1: 0.31124807395993837 max_depth: 10 f1: 0.3560311284046693 max_depth: 11 f1: 0.32882414151925077 max_depth: 12 f1: 0.3127035830618893 max_depth: 13 f1: 0.328125 max_depth: 14 f1: 0.32026143790849676 max_depth: 15 f1: 0.2953020134228188 max_depth: 16 f1: 0.31171786120591577 max_depth: 17 f1: 0.31358885017421606 max_depth: 18 f1: 0.3180778032036613 max_depth: 19 f1: 0.30624263839811544 max_depth: 20 f1: 0.30568720379146924 max_depth: 21 f1: 0.27175208581644816 max_depth: 22 f1: 0.30492196878751504 max_depth: 23 f1: 0.30419161676646705 max_depth: 24 f1: 0.30419161676646705 max_depth: 25 f1: 0.30419161676646705 max_depth: 26 f1: 0.30419161676646705 max_depth: 27 f1: 0.30419161676646705 max_depth: 28 f1: 0.30419161676646705 max_depth: 29 f1: 0.30419161676646705
AUC-ROC: 0.7995887998689521 Accuracy: 0.8510170423309511 F1: 0.4990757855822551 TP: 135 TN: 1413 FP: 35 FN: 236 max_depth: 5
Модель случайного леса с балансировкой весов классов внутри модели¶
# Модель случайного леса с балансировкой весов классов внутри модели
quality_metrics = RandomForestClassifier_SelectionOfParametrs(
features_train_wooden, target_train,
features_valid_wooden, target_valid,
'balanced', 'balance', quality_metrics
)
max_depth: 1 f1: 0.5258323765786452 max_depth: 2 f1: 0.5110619469026548 max_depth: 3 f1: 0.5714285714285715 max_depth: 4 f1: 0.5399568034557235 max_depth: 5 f1: 0.5841121495327102 max_depth: 6 f1: 0.5645390070921986 max_depth: 7 f1: 0.5469613259668508 max_depth: 8 f1: 0.45619834710743806 max_depth: 9 f1: 0.3090128755364807 max_depth: 10 f1: 0.3858267716535433 max_depth: 11 f1: 0.2672413793103448 max_depth: 12 f1: 0.22119815668202764 max_depth: 13 f1: 0.17966903073286053 max_depth: 14 f1: 0.19134396355353075 max_depth: 15 f1: 0.2945054945054945 max_depth: 16 f1: 0.24713958810068654 max_depth: 17 f1: 0.06683804627249358 max_depth: 18 f1: 0.18571428571428572 max_depth: 19 f1: 0.17433414043583534 max_depth: 20 f1: 0.2901785714285714 max_depth: 21 f1: 0.1141439205955335 max_depth: 22 f1: 0.16097560975609757 max_depth: 23 f1: 0.19811320754716982 max_depth: 24 f1: 0.28764044943820233 max_depth: 25 f1: 0.2630385487528345 max_depth: 26 f1: 0.2602739726027397 max_depth: 27 f1: 0.2602739726027397 max_depth: 28 f1: 0.2602739726027397 max_depth: 29 f1: 0.2602739726027397 n_estimators: 50 f1: 0.5876543209876544 n_estimators: 60 f1: 0.57465495608532 n_estimators: 70 f1: 0.5761006289308176 n_estimators: 80 f1: 0.5789473684210527 n_estimators: 90 f1: 0.5768742058449808 n_estimators: 100 f1: 0.5772151898734177 n_estimators: 110 f1: 0.5692695214105794 n_estimators: 120 f1: 0.5760598503740648 n_estimators: 130 f1: 0.581772784019975 n_estimators: 140 f1: 0.5760598503740648 n_estimators: 150 f1: 0.5743073047858943 n_estimators: 160 f1: 0.5678073510773131 n_estimators: 170 f1: 0.5670886075949367 n_estimators: 180 f1: 0.569974554707379 n_estimators: 190 f1: 0.5641677255400254 n_estimators: 200 f1: 0.565891472868217 n_estimators: 210 f1: 0.5695876288659794 n_estimators: 220 f1: 0.5636363636363636 n_estimators: 230 f1: 0.5692108667529107 n_estimators: 240 f1: 0.565891472868217 n_estimators: 250 f1: 0.5673575129533678
AUC-ROC: 0.8214434632395645 Accuracy: 0.8449697636063771 F1: 0.4337349397590362 TP: 108 TN: 1429 FP: 19 FN: 263 max_depth: 5 n_estimators: 50
Модель логистической регрессии с балансировкой весов классов внутри модели¶
# Модель логистической регрессии с балансировкой весов классов внутри модели
quality_metrics = LogisticRegression_SelectionOfParametrs(
features_train_logistic, target_train,
features_valid_logistic, target_valid,
'balanced', 'balance', quality_metrics
)
AUC-ROC: 0.7483823770308707 Accuracy: 0.685541506322155 F1: 0.47329650092081027 TP: 257 TN: 990 FP: 458 FN: 114
Результаты использования моделей с балансировкой весов классов внутри модели¶
# Таблица изменения значений показателей
# качества всех уже протестированных моделей
quality_metrics.sort_values('f1', ascending=False)
model | sample | auc_roc | accuracy | f1 | class_weight | max_depth | n_estimators | |
---|---|---|---|---|---|---|---|---|
0 | DecisionTreeClassifier | none | 0.702474 | 0.842221 | 0.553655 | None | 7 | None |
1 | RandomForestClassifier | none | 0.808292 | 0.853216 | 0.532399 | None | 8 | 80 |
3 | DecisionTreeClassifier | balance | 0.799589 | 0.851017 | 0.499076 | balanced | 5 | None |
5 | LogisticRegression | balance | 0.748382 | 0.685542 | 0.473297 | balanced | None | None |
4 | RandomForestClassifier | balance | 0.821443 | 0.844970 | 0.433735 | balanced | 5 | 50 |
2 | LogisticRegression | none | 0.744218 | 0.804288 | 0.255230 | None | None | None |
Использование параметра class_weight='balanced'
, балансирующей веса классов, в модели логистической регрессии привело к следющим изменениям показателей качества модели:
- Две из трех моделей без балансировки весов классов имеют лучшее значение показателя
F1
. ЛидерDecisionTreeClassifier
со значениемF1
равным 0.553655. - Максимальное значение параметра
AUC-ROC
равное 0.821443 у сбалансированной моделиRandomForestClassifier
. - Максимальное значение показателя
Accuracy
равное 0.853216 у несбалансированной моделиRandomForestClassifier
.
Итоги исследования задачи¶
Разные модели в сбалансированном и несбалансированном состоянии показали разные значения настолько, что требуется долнительная проверка эффективности сбалансированных моделей.
Борьба с дисбалансом¶
Для борьбы с дисбалансом будет применено два подхода с использование в каждом по 3 модели. В рамках первого подхода будет увеличен размер выборки за счет увеличения количества объектов меньшего класса 1
целевого показателя в 3.9046762589928057 раз. Множитель был получен в предыдущем разделе и сохранен в переменной class_ratio
. В рамках второго подхода будет уменьшен размер выборки за счет уменьшения количества объектов класса большего класса 0
целевого показателя в 3.9046762589928057 раз.
С целью достижения результата будет создана универсальная функция для изменения размера выборок.
# Универсальная функция изменения размера выборки
def changesizasample(features, target, class_change, change_type, class_ratio):
# fetures - аттрибуты
# target - целевой признак
# class_change - класс (0, 1), размер которого требуется изменить
# change_type - тип изменения выборки True (увеличение), False (уменьшение)
# fraction - множитель для изменения
features_zeros = features[target == 0]
features_ones = features[target == 1]
target_zeros = target[target == 0]
target_ones = target[target == 1]
if change_type == True:
if class_change == 0:
features_downloadsample = pd.concat([features_zeros] * int(class_ratio) + [features_ones])
target_downloadsample = pd.concat([target_zeros] * int(class_ratio) + [target_ones])
if class_change == 1:
features_downloadsample = pd.concat([features_zeros] + [features_ones] * int(class_ratio))
target_downloadsample = pd.concat([target_zeros] + [target_ones] * int(class_ratio))
if change_type == False:
if class_change == 0:
features_zeros = features_zeros.sample(frac=class_ratio, random_state=12345)
target_zeros = target_zeros.sample(frac=class_ratio, random_state=12345)
if class_change == 1:
features_ones = features_ones.sample(frac=class_ratio, random_state=12345)
target_ones = target_ones.sample(frac=class_ratio, random_state=12345)
features_downloadsample = pd.concat([features_zeros] + [features_ones])
target_downloadsample = pd.concat([target_zeros] + [target_ones])
features_downloadsample, target_downloadsample = shuffle(
features_downloadsample, target_downloadsample, random_state=12345
)
return features_downloadsample, target_downloadsample
Балансировка обучающей выборкой путем увеличения количества объектов класса «1»¶
В рамках этого подхода будет увеличен размер выборки за счет увеличения количества объектов меньшего класса 1
целевого показателя в 3.9046762589928057 раз.
# Увеличение обучающей выборки за счет объектов класса "1" для "деревянных" моделей
features_train_changesizasample, target_train_changesizasample = changesizasample(
features_train_wooden,
target_train,
1, # класс целевой обучающей выборки
True, # увеличение выборки
class_ratio # кратность изменения (множитель/делитель)
)
print('Изменение размера выборок:')
print(features_train_wooden.shape, '->', features_train_changesizasample.shape)
print(target_train.shape, '->', target_train_changesizasample.shape)
Изменение размера выборок: (5454, 12) -> (7678, 12) (5454,) -> (7678,)
Размер обучающей выборки увеличен с 5454 до 7678 объектов за счет объектов класса 1
.
Модель решающего дерева со сблансированной обучающей выборкой, полученной путем увеличения количества объектов класса «1»¶
# Модель решающего дерева со сблансированной обучающей выборкой,
# полученной путем увеличения количества объектов класса "1"
quality_metrics = DecisionTreeClassifier_SelectionOfParametrs(
features_train_changesizasample, target_train_changesizasample,
features_valid_wooden, target_valid,
None, 'up_sample', quality_metrics
)
max_depth: 1 f1: 0.4771033013844515 max_depth: 2 f1: 0.49688149688149696 max_depth: 3 f1: 0.5266362252663623 max_depth: 4 f1: 0.5220883534136546 max_depth: 5 f1: 0.5584905660377358 max_depth: 6 f1: 0.5307017543859649 max_depth: 7 f1: 0.500669344042838 max_depth: 8 f1: 0.3313840155945419 max_depth: 9 f1: 0.3465491923641703 max_depth: 10 f1: 0.3421633554083885 max_depth: 11 f1: 0.3361344537815126 max_depth: 12 f1: 0.3031624863685932 max_depth: 13 f1: 0.34346103038309117 max_depth: 14 f1: 0.3880952380952381 max_depth: 15 f1: 0.28538011695906434 max_depth: 16 f1: 0.37249283667621774 max_depth: 17 f1: 0.35268346111719606 max_depth: 18 f1: 0.3076923076923077 max_depth: 19 f1: 0.3367003367003367 max_depth: 20 f1: 0.3462686567164179 max_depth: 21 f1: 0.30861244019138756 max_depth: 22 f1: 0.36444444444444446 max_depth: 23 f1: 0.36444444444444446 max_depth: 24 f1: 0.36444444444444446 max_depth: 25 f1: 0.36444444444444446 max_depth: 26 f1: 0.36444444444444446 max_depth: 27 f1: 0.36444444444444446 max_depth: 28 f1: 0.36444444444444446 max_depth: 29 f1: 0.36444444444444446
AUC-ROC: 0.7736630876680913 Accuracy: 0.8070368334249588 F1: 0.5584905660377358 TP: 222 TN: 1246 FP: 202 FN: 149 max_depth: 5
Модель случайного леса со сблансированной обучающей выборкой, полученной путем увеличения количества объектов класса «1»¶
# Модель случайного леса со сблансированной обучающей выборкой,
# полученной путем увеличения количества объектов класса "1"
quality_metrics = RandomForestClassifier_SelectionOfParametrs(
features_train_changesizasample, target_train_changesizasample,
features_valid_wooden, target_valid,
None, 'up_sample', quality_metrics
)
max_depth: 1 f1: 0.19093078758949883 max_depth: 2 f1: 0.546218487394958 max_depth: 3 f1: 0.5482625482625482 max_depth: 4 f1: 0.556701030927835 max_depth: 5 f1: 0.557840616966581 max_depth: 6 f1: 0.5904255319148937 max_depth: 7 f1: 0.5045317220543807 max_depth: 8 f1: 0.5395232120451693 max_depth: 9 f1: 0.5260347129506008 max_depth: 10 f1: 0.28169014084507044 max_depth: 11 f1: 0.47213114754098356 max_depth: 12 f1: 0.47027027027027024 max_depth: 13 f1: 0.3058823529411765 max_depth: 14 f1: 0.4860335195530726 max_depth: 15 f1: 0.2007042253521127 max_depth: 16 f1: 0.4027777777777778 max_depth: 17 f1: 0.4514003294892916 max_depth: 18 f1: 0.3966101694915255 max_depth: 19 f1: 0.43956043956043955 max_depth: 20 f1: 0.4354587869362364 max_depth: 21 f1: 0.42928452579034937 max_depth: 22 f1: 0.30268199233716475 max_depth: 23 f1: 0.36684303350970016 max_depth: 24 f1: 0.35251798561151076 max_depth: 25 f1: 0.36524822695035464 max_depth: 26 f1: 0.38095238095238093 max_depth: 27 f1: 0.39583333333333337 max_depth: 28 f1: 0.39583333333333337 max_depth: 29 f1: 0.39583333333333337 n_estimators: 50 f1: 0.5730824891461649 n_estimators: 60 f1: 0.5616045845272206 n_estimators: 70 f1: 0.5785813630041725 n_estimators: 80 f1: 0.5854341736694678 n_estimators: 90 f1: 0.577524893314367 n_estimators: 100 f1: 0.5677233429394813 n_estimators: 110 f1: 0.5647743813682679 n_estimators: 120 f1: 0.5623188405797103 n_estimators: 130 f1: 0.5589519650655022 n_estimators: 140 f1: 0.5647743813682679 n_estimators: 150 f1: 0.5643994211287988 n_estimators: 160 f1: 0.5628571428571428 n_estimators: 170 f1: 0.5602240896358543 n_estimators: 180 f1: 0.5702364394993045 n_estimators: 190 f1: 0.5643153526970955 n_estimators: 200 f1: 0.5764546684709068 n_estimators: 210 f1: 0.574496644295302 n_estimators: 220 f1: 0.561307901907357 n_estimators: 230 f1: 0.5632653061224488 n_estimators: 240 f1: 0.5636856368563686 n_estimators: 250 f1: 0.5641025641025642
AUC-ROC: 0.8320296793793093 Accuracy: 0.8372732270478285 F1: 0.5854341736694678 TP: 209 TN: 1314 FP: 134 FN: 162 max_depth: 6 n_estimators: 80
Модель логистической регрессии со сблансированной обучающей выборкой, полученной путем увеличения количества объектов класса «1»¶
# Увеличение обучающей выборки за счет объектов класса "1" для логистических" моделей
features_train_changesizasample, target_train_changesizasample = changesizasample(
features_train_logistic,
target_train,
1, # класс целевой обучающей выборки
True, # увеличение выборки
class_ratio # кратность изменения (множитель/делитель)
)
print('Изменение размера выборок:')
print(features_train_logistic.shape, '->', features_train_changesizasample.shape)
print(target_train.shape, '->', target_train_changesizasample.shape)
Изменение размера выборок: (5454, 15) -> (7678, 15) (5454,) -> (7678,)
# Модель логистической регрессии со сблансированной обучающей выборкой,
# полученной путем увеличения количества объектов класса "1"
quality_metrics = LogisticRegression_SelectionOfParametrs(
features_train_changesizasample, target_train_changesizasample,
features_valid_logistic, target_valid,
None, 'up_sample', quality_metrics
)
AUC-ROC: 0.747809042307635 Accuracy: 0.7416162726772952 F1: 0.48237885462555063 TP: 219 TN: 1130 FP: 318 FN: 152
Результаты использования моделей со сблансированной обучающей выборкой, полученной путем увеличения количества объектов класса «1»¶
# Результаты использования моделей со сблансированной обучающей выборкой,
# полученной путем увеличения количества объектов класса "1"
quality_metrics.sort_values('f1', ascending=False)
model | sample | auc_roc | accuracy | f1 | class_weight | max_depth | n_estimators | |
---|---|---|---|---|---|---|---|---|
7 | RandomForestClassifier | up_sample | 0.832030 | 0.837273 | 0.585434 | None | 6 | 80 |
6 | DecisionTreeClassifier | up_sample | 0.773663 | 0.807037 | 0.558491 | None | 5 | None |
0 | DecisionTreeClassifier | none | 0.702474 | 0.842221 | 0.553655 | None | 7 | None |
1 | RandomForestClassifier | none | 0.808292 | 0.853216 | 0.532399 | None | 8 | 80 |
3 | DecisionTreeClassifier | balance | 0.799589 | 0.851017 | 0.499076 | balanced | 5 | None |
8 | LogisticRegression | up_sample | 0.747809 | 0.741616 | 0.482379 | None | None | None |
5 | LogisticRegression | balance | 0.748382 | 0.685542 | 0.473297 | balanced | None | None |
4 | RandomForestClassifier | balance | 0.821443 | 0.844970 | 0.433735 | balanced | 5 | 50 |
2 | LogisticRegression | none | 0.744218 | 0.804288 | 0.255230 | None | None | None |
Балансировка классов за счет уменьшения обучающей выборки класса «0»¶
В рамках этого подхода будет уменьшен размер выборки за счет уменьшения количества объектов класса большего класса 0
целевого показателя в 3.9046762589928057 раз.
# Балансировка классов за счет уменьшения обучающей выборки класса "0"
features_train_changesizasample, target_train_changesizasample = changesizasample(
features_train_wooden,
target_train,
0, # класс целевой обучающей выборки
False, # уменьшение выборки
class_ratio/100 # кратность изменения (множитель/делитель)
)
print('Изменение размера выборок:')
print(features_train_wooden.shape, '->', features_train_changesizasample.shape)
print(target_train.shape, '->', target_train_changesizasample.shape)
Изменение размера выборок: (5454, 12) -> (1282, 12) (5454,) -> (1282,)
Размер обучающей выборки уменьшен с 5454 до 1282 объектов за счет объектов класса 0
.
Модель решающего дерева со сблансированной обучающей выборкой, полученной путем уменьшения количества объектов класса «0»¶
# Модель решающего дерева со сблансированной обучающей выборкой,
# полученной путем уменьшения количества объектов класса "0"
quality_metrics = DecisionTreeClassifier_SelectionOfParametrs(
features_train_changesizasample, target_train_changesizasample,
features_valid_wooden, target_valid,
None, 'down_sample', quality_metrics
)
max_depth: 1 f1: 0.33881278538812787 max_depth: 2 f1: 0.33881278538812787 max_depth: 3 f1: 0.398406374501992 max_depth: 4 f1: 0.3150912106135987 max_depth: 5 f1: 0.4414029084687767 max_depth: 6 f1: 0.4218040233614536 max_depth: 7 f1: 0.342668863261944 max_depth: 8 f1: 0.3871338311315336 max_depth: 9 f1: 0.362657091561939 max_depth: 10 f1: 0.3701863354037267 max_depth: 11 f1: 0.3186119873817035 max_depth: 12 f1: 0.37016949152542367 max_depth: 13 f1: 0.37016949152542367 max_depth: 14 f1: 0.37016949152542367 max_depth: 15 f1: 0.37016949152542367 max_depth: 16 f1: 0.37016949152542367 max_depth: 17 f1: 0.37016949152542367 max_depth: 18 f1: 0.37016949152542367 max_depth: 19 f1: 0.37016949152542367 max_depth: 20 f1: 0.37016949152542367 max_depth: 21 f1: 0.37016949152542367 max_depth: 22 f1: 0.37016949152542367 max_depth: 23 f1: 0.37016949152542367 max_depth: 24 f1: 0.37016949152542367 max_depth: 25 f1: 0.37016949152542367 max_depth: 26 f1: 0.37016949152542367 max_depth: 27 f1: 0.37016949152542367 max_depth: 28 f1: 0.37016949152542367 max_depth: 29 f1: 0.37016949152542367
AUC-ROC: 0.6727357001384939 Accuracy: 0.6410115448048378 F1: 0.4414029084687767 TP: 258 TN: 908 FP: 540 FN: 113 max_depth: 5
Модель случайного леса со сблансированной обучающей выборкой, полученной путем уменьшения количества объектов класса «0»¶
# Модель случайного леса со сблансированной обучающей выборкой,
# полученной путем уменьшения количества объектов класса "0"
quality_metrics = RandomForestClassifier_SelectionOfParametrs(
features_train_changesizasample, target_train_changesizasample,
features_valid_wooden, target_valid,
None, 'down_sample', quality_metrics
)
max_depth: 1 f1: 0.33881278538812787 max_depth: 2 f1: 0.33881278538812787 max_depth: 3 f1: 0.3699360341151386 max_depth: 4 f1: 0.3830016137708446 max_depth: 5 f1: 0.4225352112676057 max_depth: 6 f1: 0.3807439824945295 max_depth: 7 f1: 0.41290322580645156 max_depth: 8 f1: 0.3970189701897019 max_depth: 9 f1: 0.41963578780680927 max_depth: 10 f1: 0.3775351014040561 max_depth: 11 f1: 0.33097345132743367 max_depth: 12 f1: 0.3131067961165048 max_depth: 13 f1: 0.34974533106960953 max_depth: 14 f1: 0.2860192102454643 max_depth: 15 f1: 0.28688524590163933 max_depth: 16 f1: 0.28260869565217395 max_depth: 17 f1: 0.2835332606324973 max_depth: 18 f1: 0.2835332606324973 max_depth: 19 f1: 0.2835332606324973 max_depth: 20 f1: 0.2835332606324973 max_depth: 21 f1: 0.2835332606324973 max_depth: 22 f1: 0.2835332606324973 max_depth: 23 f1: 0.2835332606324973 max_depth: 24 f1: 0.2835332606324973 max_depth: 25 f1: 0.2835332606324973 max_depth: 26 f1: 0.2835332606324973 max_depth: 27 f1: 0.2835332606324973 max_depth: 28 f1: 0.2835332606324973 max_depth: 29 f1: 0.2835332606324973 n_estimators: 50 f1: 0.4423337856173677 n_estimators: 60 f1: 0.4512110726643599 n_estimators: 70 f1: 0.4298469387755101 n_estimators: 80 f1: 0.42156268927922463 n_estimators: 90 f1: 0.44010416666666663 n_estimators: 100 f1: 0.42412935323383083 n_estimators: 110 f1: 0.4306709265175719 n_estimators: 120 f1: 0.42454602379461487 n_estimators: 130 f1: 0.42848061029879214 n_estimators: 140 f1: 0.43305573350416404 n_estimators: 150 f1: 0.43147208121827413 n_estimators: 160 f1: 0.42785445420326224 n_estimators: 170 f1: 0.4328453214513049 n_estimators: 180 f1: 0.42794210195091253 n_estimators: 190 f1: 0.4256378344741755 n_estimators: 200 f1: 0.4259028642590287 n_estimators: 210 f1: 0.4235294117647059 n_estimators: 220 f1: 0.4217687074829933 n_estimators: 230 f1: 0.41982864137086906 n_estimators: 240 f1: 0.4197681513117755 n_estimators: 250 f1: 0.4185478950579622
AUC-ROC: 0.7694552203243435 Accuracy: 0.5640461792193513 F1: 0.4512110726643599 TP: 326 TN: 700 FP: 748 FN: 45 max_depth: 5 n_estimators: 60
Модель логистической регрессии со сблансированной обучающей выборкой, полученной путем уменьшения количества объектов класса «0»¶
# Уменьшение обучающей выборки за счет объектов класса "0" для логистических моделей
features_train_changesizasample, target_train_changesizasample = changesizasample(
features_train_logistic,
target_train,
0, # класс целевой обучающей выборки
False, # уменьшение выборки
class_ratio/100 # кратность изменения (множитель/делитель)
)
print('Изменение размера выборок:')
print(features_train_wooden.shape, '->', features_train_changesizasample.shape)
print(target_train.shape, '->', target_train_changesizasample.shape)
Изменение размера выборок: (5454, 12) -> (1282, 15) (5454,) -> (1282,)
# Модель логистической регрессии со сблансированной обучающей выборкой,
# полученной путем уменьшения количества объектов класса "0"
quality_metrics = LogisticRegression_SelectionOfParametrs(
features_train_changesizasample, target_train_changesizasample,
features_valid_logistic, target_valid,
None, 'down_sample', quality_metrics
)
AUC-ROC: 0.743058554600825 Accuracy: 0.2886201209455745 F1: 0.36003956478733923 TP: 364 TN: 161 FP: 1287 FN: 7
Сравнение показателей качества моделей с разными способами балансировки обучающих «деревянными» и логистических выборок и без балансировки¶
# Показатели качества моделей с разными способами балансировки
# обучающих "деревянными" и логистических выборок и без нее
quality_metrics.sort_values('f1', ascending=False)
model | sample | auc_roc | accuracy | f1 | class_weight | max_depth | n_estimators | |
---|---|---|---|---|---|---|---|---|
7 | RandomForestClassifier | up_sample | 0.832030 | 0.837273 | 0.585434 | None | 6 | 80 |
6 | DecisionTreeClassifier | up_sample | 0.773663 | 0.807037 | 0.558491 | None | 5 | None |
0 | DecisionTreeClassifier | none | 0.702474 | 0.842221 | 0.553655 | None | 7 | None |
1 | RandomForestClassifier | none | 0.808292 | 0.853216 | 0.532399 | None | 8 | 80 |
3 | DecisionTreeClassifier | balance | 0.799589 | 0.851017 | 0.499076 | balanced | 5 | None |
8 | LogisticRegression | up_sample | 0.747809 | 0.741616 | 0.482379 | None | None | None |
5 | LogisticRegression | balance | 0.748382 | 0.685542 | 0.473297 | balanced | None | None |
10 | RandomForestClassifier | down_sample | 0.769455 | 0.564046 | 0.451211 | None | 5 | 60 |
9 | DecisionTreeClassifier | down_sample | 0.672736 | 0.641012 | 0.441403 | None | 5 | None |
4 | RandomForestClassifier | balance | 0.821443 | 0.844970 | 0.433735 | balanced | 5 | 50 |
11 | LogisticRegression | down_sample | 0.743059 | 0.288620 | 0.360040 | None | None | None |
2 | LogisticRegression | none | 0.744218 | 0.804288 | 0.255230 | None | None | None |
print('Лучшее значение показателей "F1", "Accuracy", "AUC-ROC":')
quality_metrics.loc[
(quality_metrics['f1'] == quality_metrics['f1'].max()) |
(quality_metrics['accuracy'] == quality_metrics['accuracy'].max()) |
(quality_metrics['auc_roc'] == quality_metrics['auc_roc'].max())
]
Лучшее значение показателей "F1", "Accuracy", "AUC-ROC":
model | sample | auc_roc | accuracy | f1 | class_weight | max_depth | n_estimators | |
---|---|---|---|---|---|---|---|---|
1 | RandomForestClassifier | none | 0.808292 | 0.853216 | 0.532399 | None | 8 | 80 |
7 | RandomForestClassifier | up_sample | 0.832030 | 0.837273 | 0.585434 | None | 6 | 80 |
Анализ показателей качества выявил наилучшую комбинацию модели и выборки. Это модель RandomForestClassifier
с параметрами n_estimators=8
, max_depth=6
, random_state=12345
с использованием сбалансированной обучающей выборки с увеличенным в 3 раза количеством объектов класса 1
. У этой комбинации самое лучшее значение показателя F1
, равного 0.585434, и AUC-ROC
, равного 0.832030. Только значение показателя Accuracy
незначительно уступает этой же модели, но без балансировки.
Тестирование модели¶
Проведение тестирования¶
# Увеличение обучающей выборки за счет объектов класса "1" для "деревянных" моделей
features_train_changesizasample, target_train_changesizasample = changesizasample(
features_train_wooden,
target_train,
1, # класс целевой обучающей выборки
True, # увеличение выборки
class_ratio # кратность изменения (множитель/делитель)
)
print(features_train_changesizasample.shape)
print(target_train_changesizasample.shape)
(7678, 12) (7678,)
# Параметры модели
depth_f1 = int(quality_metrics.loc[
quality_metrics['f1'] == quality_metrics['f1'].max(),
'max_depth'
])
n_estimators_f1 = int(quality_metrics.loc[
quality_metrics['f1'] == quality_metrics['f1'].max(),
'n_estimators'
])
# Создание, обучение и предсказание модели
model = RandomForestClassifier(
class_weight=None,
n_estimators=n_estimators_f1,
max_depth=depth_f1,
random_state=12345
)
model.fit(features_train_changesizasample, target_train_changesizasample)
predictions_test = model.predict(features_test_wooden)
probabilities_test = model.predict_proba(features_test_wooden)
# Анализ метрик качества моделей
quality_metrics = output_of_quality_metrics(target_train, target_test,
predictions_test, probabilities_test,
quality_metrics,
'RandomForestClassifier', 'up_sample_test',
None, depth_f1, n_estimators_f1
)
# Параметры модели
print('\nmax_depth:', depth_f1)
print('n_estimators:', n_estimators_f1)
AUC-ROC: 0.8533791448801032 Accuracy: 0.845984598459846 F1: 0.6195652173913043 TP: 228 TN: 1310 FP: 137 FN: 143 max_depth: 6 n_estimators: 80
Значение показателя F1
равно 0.6195652173913043, что больше требуемого 0.59, следовательно, цель проекта достигнута. Кривая ROC
сильно выгнута вврех и значение показателя AUC-ROC
равно 0.8533791448801032. Это означает, что модель существенно отличается от случайной в лучшую сторону.
Выводы тестирования¶
В тестировании использована модель RandomForestClassifier
с параметрами n_estimators=80
, max_depth=6
, random_state=12345
и class_weight=None
с использованием сбалансированной обучающей выборки с увеличенным в 3 раза количеством объектов класса 1
. По результатам её использования:
- Показатель
F1
равен 0.6195652173913043, что больше требуемого 0.59. - Кривая
ROC
сильно выгнута вврех. Это означает, что модель существенно отличается от случайной в лучшую сторону. - Значение показателя
AUC-ROC
стремиться к идеальному, оно равно 0.8533791448801032. Значение хоть и не является идальным, оно значительно больше случайного значения, равного 0.5.
Все задачи проекта выполнены.
Выводы проекта¶
В рамках данного проекта проведены следующие работы:
- Подготовлены данные. В том числе, загружены библиотеки и их компоненты, проанализированы и предварительно обработаны данные.
- Исследована задача проекта. Изучены модели без учёта дисбаланс. Подобраны параметров моделей решающего дерева, случайного леса и логистической регрессии для улучшения их качества. Исследован баланс классов.
- Проведена борьба с дисбалансом классов за счет увеличения обучающей выборки класса «1», а также за счет уменьшения обучающей выборки класса «0». Сравнены показатели качества моделей с разными способами балансировки обучающей выборки и без балансировки.
- Протестирована выбранная модель и приведены выводы тестирования.
Рзультаты проведённой работы в виде анализа метрик качества всех моделей со сбалансированными разным способом классами в выборках и без балансировки представлены в следующей таблице.
# Рзультаты анализа метрик качества
# всех моделей с разными выборками
quality_metrics
model | sample | auc_roc | accuracy | f1 | class_weight | max_depth | n_estimators | |
---|---|---|---|---|---|---|---|---|
0 | DecisionTreeClassifier | none | 0.702474 | 0.842221 | 0.553655 | None | 7 | None |
1 | RandomForestClassifier | none | 0.808292 | 0.853216 | 0.532399 | None | 8 | 80 |
2 | LogisticRegression | none | 0.744218 | 0.804288 | 0.255230 | None | None | None |
3 | DecisionTreeClassifier | balance | 0.799589 | 0.851017 | 0.499076 | balanced | 5 | None |
4 | RandomForestClassifier | balance | 0.821443 | 0.844970 | 0.433735 | balanced | 5 | 50 |
5 | LogisticRegression | balance | 0.748382 | 0.685542 | 0.473297 | balanced | None | None |
6 | DecisionTreeClassifier | up_sample | 0.773663 | 0.807037 | 0.558491 | None | 5 | None |
7 | RandomForestClassifier | up_sample | 0.832030 | 0.837273 | 0.585434 | None | 6 | 80 |
8 | LogisticRegression | up_sample | 0.747809 | 0.741616 | 0.482379 | None | None | None |
9 | DecisionTreeClassifier | down_sample | 0.672736 | 0.641012 | 0.441403 | None | 5 | None |
10 | RandomForestClassifier | down_sample | 0.769455 | 0.564046 | 0.451211 | None | 5 | 60 |
11 | LogisticRegression | down_sample | 0.743059 | 0.288620 | 0.360040 | None | None | None |
12 | RandomForestClassifier | up_sample_test | 0.853379 | 0.845985 | 0.619565 | None | 6 | 80 |
Чек-лист готовности проекта¶
Поставьте ‘x’ в выполненных пунктах. Далее нажмите Shift+Enter.
- [x] Jupyter Notebook открыт
- [x] Весь код выполняется без ошибок
- [x] Ячейки с кодом расположены в порядке исполнения
- [x] Выполнен шаг 1: данные подготовлены
- [x] Выполнен шаг 2: задача исследована
- [x] Исследован баланс классов
- [x] Изучены модели без учёта дисбаланса
- [x] Написаны выводы по результатам исследования
- [x] Выполнен шаг 3: учтён дисбаланс
- [x] Применено несколько способов борьбы с дисбалансом
- [x] Написаны выводы по результатам исследования
- [x] Выполнен шаг 4: проведено тестирование
- [x] Удалось достичь F1-меры не менее 0.59
- [x] Исследована метрика AUC-ROC