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

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

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

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

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

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

Импортируйте библиотеки и исследуйте набор данных

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns
from sklearn import preprocessing
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn import metrics
# create dataframe from csv file
heart_data = pd.read_csv('./data-science/heart.csv')
heart_data.head()

Информация об атрибутах:

0) возраст - 1) пол - 2) тип боли в груди (4 значения) - 3) артериальное давление в покое - 4) холесторальный уровень сыворотки в мг / дл - 5) уровень сахара в крови натощак ›120 мг / дл - 6) в состоянии покоя электрокарта. результаты (значения 0,1,2) - 7) достигнутая максимальная частота сердечных сокращений - 8) стенокардия, вызванная физической нагрузкой - 9) oldpeak - 10) наклон максимальной нагрузки при физической нагрузке - 11) количество крупных сосудов (0–3) - 12 ) thal: 0 = нормально; 1 = исправленный дефект; 2 = обратимый дефект - 13) цель: 0 = меньше вероятность сердечного приступа 1 = больше вероятность сердечного приступа

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

# Correlation ranges from -1 to +1. 
# Values closer to zero means there is no linear trend between the 2 variables. 
# Close to 1, more positive correlation, # –1 indicates a perfect negative corr.
corr = heart_data.corr()
plt.figure(figsize=(16,9))
ax = sns.heatmap(
    corr, 
    annot = True,     
    vmin=-1, vmax=1, center=0,
    cmap=sns.diverging_palette(10, 240, n=50),
    square=True
)
ax.set_xticklabels(
    ax.get_xticklabels(),
    rotation=45,
    horizontalalignment='right'
)

На карте корреляции мы видим, что функции fbs и chol имеют корреляцию с целью, близкую к 0 (без корреляции), поэтому мы не будем считать их важными в нашей модели и приступим к удалению. Мы фактически создадим новый фрейм данных (heart_part), чтобы сохранить оригинал на случай, если нам понадобится его повторно использовать в какой-то момент.

# Drop 2 features with poor correlation with target
heart_part=heart_data.drop(columns=['fbs', 'chol'])

Если бы мы хотели лучше визуализировать корреляцию -0,4 (отрицательная тенденция) между возрастом и максимальной частотой пульса (талах) по cp (тип боли в груди ):

# Chest pain type (cp) group
# Age VS maximum heart rate (thalach) = -0.4 trend
g = sns.FacetGrid(heart_data, col="cp")
g.map(sns.scatterplot, "age", "thalach", alpha=.7)
g.add_legend()

С помощью seaborn мы также можем визуализировать взаимосвязь между двумя переменными с разделением точек сигмовидной линией. Для этого воспользуемся методом lmplot с параметром logistic = True.

На изображении ниже мы видим цель (0–1) по отношению к функции талах.

Создайте нашу модель логистической регрессии

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

# X features and Y labels
X=heart_part.drop(columns=['target'])
Y=heart_part['target']
# Build train and test datasets
X_train,X_test,y_train,y_test=train_test_split(X,Y,random_state=0)

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

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

зачем нам решать эту проблему?

Давайте поговорим о «парадоксе точности» - парадоксальном открытии того, что точность не является хорошим показателем для прогнозных моделей при классификации в прогнозной аналитике. Например, если бы у нас был хороший показатель точности 90% для несбалансированных данных (с 90% выборками из класса 1 и только 10% из класса 2), наша модель посмотрела бы на данные и решила, что Лучшее, что он может сделать, это предсказать, что данные относятся к классу 1 !!, в этом случае измерение только точности приводит нас к вводящей в заблуждение позиции в классификации

как ее решить?

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

Итак, мы проверяем, чтобы образцы классов не были чрезмерно разбалансированными.

from collections import Counter
Counter(y_train)

Счетчик ({0: 105, 1: 122})

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

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

# Scale the X data
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

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

# build logistic regression model, train and predict test data
lr = LogisticRegression()
model = lr.fit(X_train, y_train)
lr_predict = lr.predict(X_test)

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

# import data modeling metrics
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report
lr_conf_matrix = confusion_matrix(y_test, lr_predict)
lr_acc_score = accuracy_score(y_test, lr_predict)
print("confusion matrix")
print(lr_conf_matrix)
print("\n")
print("Accuracy of Logistic Regression:",lr_acc_score*100,'\n')
print(classification_report(y_test,lr_predict))

Для лучшей визуализации нашей матрицы путаницы мы будем использовать Seaborn

df_cm = pd.DataFrame(lr_conf_matrix, columns=np.unique(y_test), index = np.unique(y_test))
df_cm.index.name = 'Real'
df_cm.columns.name = 'Predicted'
plt.figure(figsize = (10,7))
sns.set(font_scale=1.2)
sns.heatmap(df_cm, cmap="Blues", annot=True,annot_kws={"size": 14})

Хорошо, но что означают все эти показатели? Если вы читали предыдущий пост о линейной регрессии, следует ли вам распознать значение точности, которое в данном случае составляет 82,89%, и других значений?

Показатели для двоичной классификации

а) Матрица неточностей

Это таблица с 4 различными комбинациями прогнозируемых и фактических значений.

TN (истинно отрицательное): предсказано отрицательно, и это правда.

FP (ложная позиция / ошибка типа 1): предположительно положительный результат, но он неверен.

FN (ложное отрицание / ошибка типа 2): прогнозируемый отрицательный результат, и он является ложным.

TP (истинное положение): прогноз положительный, и это правда.

В нашем случае у нас, например, 39 ТП и 4 ФН. Это указывает на то, что наша модель предсказала 39 образцов из 43 возможных как возможные для сердечных приступов, в результате чего 4 образца не были идентифицированы как положительные (ошибка типа 2). В том случае, когда мы прогнозируем серьезные проблемы со здоровьем у пациентов, мы должны минимизировать это последнее значение (FN).

В то время как модель определила 24 из 33 образцов как отрицательные, в результате мы получили 9 образцов, которые не были правильно идентифицированы как отрицательные. Хотя частота ошибок выше, она не так серьезна, как в предыдущем случае (FN), поскольку было выявлено 9 выборок пациентов с вероятностью сердечного приступа, хотя этого не было (FP).

б) Точность = TP / (TP + FP)

Важно, если нам нужно избежать ложных срабатываний, в нашем случае мы смотрим на значение выше класса 1 (больше вероятность сердечного приступа) = ›39 / (39 + 9) = 0,8125

c) Отзыв (также называемый истинно положительный рейтинг или чувствительность) = TP / (TP + FN)

Важно, если нам нужно избегать ложноотрицательных результатов (в этом наше дело) = ›39 / (39 + 4) = 0,9069

d) F1-балл = 2.TP / (2. TP) + FN + FP)

Объединяет точность и отзыв в одном значении = ›2,39 / (2,39) + 4 + 9 = 0,8571

Важно помнить, что существует компромисс между точностью и отзывом (мы повышаем точность, меньше повторяем и наоборот)

вкратце есть:

больше задач, ориентированных на отзыв (серьезные последствия, если мы не обнаружим положительных результатов)

  • обнаружение болезней, поиск и извлечение правовой информации

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

  • рейтинг в поисковых системах, классификатор документов

д) Частота ложных срабатываний (Частота ложных срабатываний, выпадения) = FP / (FP + TN)

Обобщает, как часто прогнозируется положительный класс, когда фактический результат отрицательный.

f) Кривые ROC

Рабочая характеристика приемника (кривая ROC) суммирует компромисс между истинно положительной скоростью / отзывом (b) и ложноположительной скоростью (e ) для прогнозной модели с использованием различных пороговых значений вероятности. Его использование уместно, когда наблюдения сбалансированы между каждым классом.

Кривая ROC / AUC

Хорошо, достаточно показателей, чтобы задуматься, давайте вернемся к оценке нашей логистической модели. Мы сказали, что наши 2 класса достаточно сбалансированы, поэтому желательно увидеть производительность модели, взяв значения кривой ROC. Эй, остановись! Почему важно визуализировать эту кривую ROC?

  1. Кривые разных моделей можно сравнивать напрямую в целом или для разных пороговых значений.
  2. Площадь под кривой (AUC) можно использовать как сводку навыков модели.

Помните, что когда мы прогнозируем двоичный результат, это правильный прогноз (истинно положительный) или нет (ложный положительный). Такое же напряжение возникает с истинным негативом и ложным негативом. Итак, что такое умелая модель? Искусная модель назначит более высокую вероятность случайно выбранному реальному положительному событию, чем среднему отрицательному. Умелые модели обычно представлены кривыми, которые наклоняются к левому верхнему углу графика.

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

# obtain prediction for each row of X_test
predictions = model.predict_proba(X_test)
# build roc_curve values with y_test and probability 
# predictions of 1 value class (more chance heart attack)
fpr, recall, thresholds = roc_curve(y_test, predictions[:, 1])
# compute Area Under the Roc Curve
roc_auc = auc(fpr, recall)
# Plot Roc-Auc
plt.figure(figsize = (10,7))
plt.title('Receiver Operating Characteristic')
plt.plot(fpr,recall,'b',label='AUC = %0.2f' % roc_auc)
plt.legend(loc='lower right')
plt.plot([0,1], [0,1], 'r--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.0])
plt.xlabel('Fall-out')
plt.ylabel('Recall')
plt.show()

Красная пунктирная линия будет моделью без навыков, которая не может различать классы и представлена ​​в точке (0,5, 0,5), на каждом пороге она представлена ​​диагональной линией от нижнего левого угла графика до верхнего правого. и имеет AUC 0,5.

Модель с совершенными способностями представлена ​​в одной точке (0,1). Наша кривая ROC представлена ​​синей линией и превосходит случайную 0,5 AUC (красная пунктирная линия).

Как всегда, прилагается ссылка на github с полным блокнотом jupyter для логистической регрессии, чтобы вы могли проверить код самостоятельно.

Также подписывайтесь на меня в моем блоге о данных EmpowereDataScience

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

Ваши комментарии приветствуются