В этом руководстве мы рассмотрим несколько метрик классификации в scikit-learn Python и напишем наши собственные функции с нуля, чтобы понять математику, лежащую в основе некоторых из них.

Одной из основных областей прогнозного моделирования в науке о данных является классификация. Классификация состоит в попытке предсказать, к какому классу принадлежит конкретная выборка из совокупности. Например, если мы пытаемся предсказать, будет ли конкретный пациент повторно госпитализирован, два возможных класса: больничный (положительный) и не госпитализированный (отрицательный). Затем модель классификации пытается предсказать, будет ли каждый пациент госпитализирован или нет. Другими словами, классификация просто пытается предсказать, в какой сегмент (прогнозируемое положительное или прогнозируемое отрицательное) следует поместить конкретную выборку из совокупности, как показано ниже.

Обучая свою модель прогнозирования классификации, вы захотите оценить, насколько она хороша. Интересно, что есть много разных способов оценки производительности. Большинство специалистов по обработке данных, использующих Python для прогнозного моделирования, используют пакет Python под названием scikit-learn. Scikit-learn содержит множество встроенных функций для анализа производительности моделей. В этом руководстве мы рассмотрим некоторые из этих показателей и напишем наши собственные функции с нуля, чтобы понять математику, лежащую в основе некоторых из них. Если вы предпочитаете просто читать о показателях производительности, посмотрите мой предыдущий пост здесь.

В этом руководстве будут рассмотрены следующие показатели из sklearn.metrics:

  • confusion_matrix
  • precision_score
  • отзыв_score
  • precision_score
  • f1_score
  • roc_curve
  • roc_auc_score

Начиная

Чтобы увидеть образец набора данных и записную книжку jupyter, посетите мой github здесь. Мы напишем наши собственные функции с нуля, предполагая двухклассную классификацию. Обратите внимание, что вам нужно будет заполнить части, помеченные как # your code here

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

import pandas as pd
df = pd.read_csv('data.csv')
df.head()

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

thresh = 0.5
df['predicted_RF'] = (df.model_RF >= 0.5).astype('int')
df['predicted_LR'] = (df.model_LR >= 0.5).astype('int')
df.head()

confusion_matrix

Учитывая фактическую метку и прогнозируемую метку, первое, что мы можем сделать, это разделить наши образцы на 4 сегмента:

  • Истинно положительный - фактический = 1, прогнозируемый = 1
  • Ложноположительный результат - фактический = 0, прогнозируемый = 1
  • Ложноотрицательный - фактический = 1, прогнозируемый = 0
  • Истинно отрицательный - фактический = 0, прогнозируемый = 0

Эти сегменты могут быть представлены следующим изображением (исходный источник https://en.wikipedia.org/wiki/Precision_and_recall#/media/File:Precisionrecall.svg), и мы будем ссылаться на это изображение во многих расчетах ниже.

Эти сегменты также можно отобразить с помощью матрицы путаницы, как показано ниже:

Мы можем получить матрицу путаницы (как массив 2x2) из ​​scikit-learn, который принимает в качестве входных данных фактические метки и предсказанные метки.

from sklearn.metrics import confusion_matrix
confusion_matrix(df.actual_label.values, df.predicted_RF.values)

где было 5047 истинных положительных результатов, 2360 ложных срабатываний, 2832 ложных отрицательных и 5519 истинно отрицательных результатов. Давайте определим наши собственные функции для проверки confusion_matrix. Обратите внимание, что я заполнил первый, а вам нужно заполнить остальные 3.

def find_TP(y_true, y_pred):
    # counts the number of true positives (y_true = 1, y_pred = 1)
    return sum((y_true == 1) & (y_pred == 1))
def find_FN(y_true, y_pred):
    # counts the number of false negatives (y_true = 1, y_pred = 0)
    return # your code here
def find_FP(y_true, y_pred):
    # counts the number of false positives (y_true = 0, y_pred = 1)
    return # your code here
def find_TN(y_true, y_pred):
    # counts the number of true negatives (y_true = 0, y_pred = 0)
    return # your code here

Вы можете проверить соответствие ваших результатов с помощью

print('TP:',find_TP(df.actual_label.values, df.predicted_RF.values))
print('FN:',find_FN(df.actual_label.values, df.predicted_RF.values))
print('FP:',find_FP(df.actual_label.values, df.predicted_RF.values))
print('TN:',find_TN(df.actual_label.values, df.predicted_RF.values))

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

import numpy as np
def find_conf_matrix_values(y_true,y_pred):
    # calculate TP, FN, FP, TN
    TP = find_TP(y_true,y_pred)
    FN = find_FN(y_true,y_pred)
    FP = find_FP(y_true,y_pred)
    TN = find_TN(y_true,y_pred)
    return TP,FN,FP,TN
def my_confusion_matrix(y_true, y_pred):
    TP,FN,FP,TN = find_conf_matrix_values(y_true,y_pred)
    return np.array([[TN,FP],[FN,TP]])

Проверьте соответствие ваших результатов с

my_confusion_matrix(df.actual_label.values, df.predicted_RF.values)

Вместо того, чтобы сравнивать вручную, давайте проверим, что наши функции работали с использованием встроенных в Python assert и array_equal функций numpy.

assert  np.array_equal(my_confusion_matrix(df.actual_label.values, df.predicted_RF.values), confusion_matrix(df.actual_label.values, df.predicted_RF.values) ), 'my_confusion_matrix() is not correct for RF'
assert  np.array_equal(my_confusion_matrix(df.actual_label.values, df.predicted_LR.values),confusion_matrix(df.actual_label.values, df.predicted_LR.values) ), 'my_confusion_matrix() is not correct for LR'

Учитывая эти четыре сегмента (TP, FP, FN, TN), мы можем рассчитать многие другие показатели производительности.

precision_score

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

Мы можем получить оценку точности из scikit-learn, который принимает в качестве входных данных фактические метки и предсказанные метки.

from sklearn.metrics import accuracy_score
accuracy_score(df.actual_label.values, df.predicted_RF.values)

Ваш ответ должен быть 0,6705165630156111

Определите свою собственную функцию, которая дублирует accuracy_score, используя формулу выше.

def my_accuracy_score(y_true, y_pred):
    # calculates the fraction of samples predicted correctly
    TP,FN,FP,TN = find_conf_matrix_values(y_true,y_pred)  
    return # your code here
assert my_accuracy_score(df.actual_label.values, df.predicted_RF.values) == accuracy_score(df.actual_label.values, df.predicted_RF.values), 'my_accuracy_score failed on RF'
assert my_accuracy_score(df.actual_label.values, df.predicted_LR.values) == accuracy_score(df.actual_label.values, df.predicted_LR.values), 'my_accuracy_score failed on LR'
print('Accuracy RF: %.3f'%(my_accuracy_score(df.actual_label.values, df.predicted_RF.values)))
print('Accuracy LR: %.3f'%(my_accuracy_score(df.actual_label.values, df.predicted_LR.values)))

Используя точность в качестве показателя производительности, модель RF более точна (0,67), чем модель LR (0,62). Так должны ли мы остановиться на этом и сказать, что модель RF - лучшая модель? Нет! Точность не всегда является лучшим показателем для оценки моделей классификации. Например, предположим, что мы пытаемся предсказать то, что произойдет только 1 раз из 100. Мы могли бы построить модель с точностью 99%, сказав, что события никогда не было. Однако мы ловим 0% событий, которые нам небезразличны. Показатель 0% - это еще один показатель производительности, известный как отзыв.

отзыв_score

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

Мы можем получить оценку точности из scikit-learn, который принимает в качестве входных данных фактические метки и предсказанные метки.

from sklearn.metrics import recall_score
recall_score(df.actual_label.values, df.predicted_RF.values)

Определите свою собственную функцию, которая дублирует recall_score, используя формулу выше.

def my_recall_score(y_true, y_pred):
    # calculates the fraction of positive samples predicted correctly
    TP,FN,FP,TN = find_conf_matrix_values(y_true,y_pred)  
    return # your code here
assert my_recall_score(df.actual_label.values, df.predicted_RF.values) == recall_score(df.actual_label.values, df.predicted_RF.values), 'my_accuracy_score failed on RF'
assert my_recall_score(df.actual_label.values, df.predicted_LR.values) == recall_score(df.actual_label.values, df.predicted_LR.values), 'my_accuracy_score failed on LR'
print('Recall RF: %.3f'%(my_recall_score(df.actual_label.values, df.predicted_RF.values)))
print('Recall LR: %.3f'%(my_recall_score(df.actual_label.values, df.predicted_LR.values)))

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

precision_score

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

Мы можем получить оценку точности из scikit-learn, который принимает в качестве входных данных фактические метки и предсказанные метки.

from sklearn.metrics import precision_score
precision_score(df.actual_label.values, df.predicted_RF.values)

Определите свою собственную функцию, которая дублирует precision_score, используя формулу выше.

def my_precision_score(y_true, y_pred):
    # calculates the fraction of predicted positives samples that are actually positive
    TP,FN,FP,TN = find_conf_matrix_values(y_true,y_pred)  
    return # your code here
assert my_precision_score(df.actual_label.values, df.predicted_RF.values) == precision_score(df.actual_label.values, df.predicted_RF.values), 'my_accuracy_score failed on RF'
assert my_precision_score(df.actual_label.values, df.predicted_LR.values) == precision_score(df.actual_label.values, df.predicted_LR.values), 'my_accuracy_score failed on LR'
print('Precision RF: %.3f'%(my_precision_score(df.actual_label.values, df.predicted_RF.values)))
print('Precision LR: %.3f'%(my_precision_score(df.actual_label.values, df.predicted_LR.values)))

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

f1_score

Оценка f1 - это гармоническое среднее значение запоминания и точности, с более высокой оценкой как лучшей моделью. Оценка f1 рассчитывается по следующей формуле:

Мы можем получить оценку f1 из scikit-learn, который принимает в качестве входных данных фактические метки и предсказанные метки.

from sklearn.metrics import f1_score
f1_score(df.actual_label.values, df.predicted_RF.values)

Определите свою собственную функцию, которая дублирует f1_score, используя формулу выше.

def my_f1_score(y_true, y_pred):
    # calculates the F1 score
    recall = my_recall_score(y_true,y_pred)  
    precision = my_precision_score(y_true,y_pred)  
    return # your code here
assert my_f1_score(df.actual_label.values, df.predicted_RF.values) == f1_score(df.actual_label.values, df.predicted_RF.values), 'my_accuracy_score failed on RF'
assert my_f1_score(df.actual_label.values, df.predicted_LR.values) == f1_score(df.actual_label.values, df.predicted_LR.values), 'my_accuracy_score failed on LR'
print('F1 RF: %.3f'%(my_f1_score(df.actual_label.values, df.predicted_RF.values)))
print('F1 LR: %.3f'%(my_f1_score(df.actual_label.values, df.predicted_LR.values)))

До сих пор мы предполагали, что определили порог 0,5 для выбора того, какие образцы предсказываются как положительные. Если мы изменим этот порог, показатели производительности изменятся. Как показано ниже:

print('scores with threshold = 0.5')
print('Accuracy RF: %.3f'%(my_accuracy_score(df.actual_label.values, df.predicted_RF.values)))
print('Recall RF: %.3f'%(my_recall_score(df.actual_label.values, df.predicted_RF.values)))
print('Precision RF: %.3f'%(my_precision_score(df.actual_label.values, df.predicted_RF.values)))
print('F1 RF: %.3f'%(my_f1_score(df.actual_label.values, df.predicted_RF.values)))
print(' ')
print('scores with threshold = 0.25')
print('Accuracy RF: %.3f'%(my_accuracy_score(df.actual_label.values, (df.model_RF >= 0.25).astype('int').values)))
print('Recall RF: %.3f'%(my_recall_score(df.actual_label.values, (df.model_RF >= 0.25).astype('int').values)))
print('Precision RF: %.3f'%(my_precision_score(df.actual_label.values, (df.model_RF >= 0.25).astype('int').values)))
print('F1 RF: %.3f'%(my_f1_score(df.actual_label.values, (df.model_RF >= 0.25).astype('int').values)))

Как мы оцениваем модель, если мы не выбрали порог? Один очень распространенный метод - использование кривой рабочей характеристики приемника (ROC).

roc_curve и roc_auc_score

Кривые ROC ОЧЕНЬ помогают понять баланс между показателем истинно-положительных и ложноположительных результатов. Sci-kit learn имеет встроенные функции для кривых ROC и их анализа. Входными данными для этих функций (roc_curve и roc_auc_score) являются фактические метки и предсказанные вероятности (а не предсказанные метки). И roc_curve, и roc_auc_score являются сложными функциями, поэтому мы не будем заставлять вас писать эти функции с нуля. Вместо этого мы покажем вам, как использовать функции sci-kit learn, и объясним ключевые моменты. Начнем с использования roc_curve для построения графика ROC.

from sklearn.metrics import roc_curve
fpr_RF, tpr_RF, thresholds_RF = roc_curve(df.actual_label.values, df.model_RF.values)
fpr_LR, tpr_LR, thresholds_LR = roc_curve(df.actual_label.values, df.model_LR.values)

Функция roc_curve возвращает три списка:

  • пороговые значения = все уникальные вероятности предсказания в порядке убывания
  • fpr = частота ложных срабатываний (FP / (FP + TN)) для каждого порога
  • tpr = истинная положительная скорость (TP / (TP + FN)) для каждого порога

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

import matplotlib.pyplot as plt
plt.plot(fpr_RF, tpr_RF,'r-',label = 'RF')
plt.plot(fpr_LR,tpr_LR,'b-', label= 'LR')
plt.plot([0,1],[0,1],'k-',label='random')
plt.plot([0,0,1,1],[0,1,1,1],'g-',label='perfect')
plt.legend()
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.show()

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

  • модель, которая случайным образом угадывает метку, приведет к черной линии, и вы хотите иметь модель, у которой есть кривая над этой черной линией
  • ROC, который находится дальше от черной линии, лучше, поэтому RF (красный) выглядит лучше, чем LR (синий)
  • Хотя это и не видно напрямую, высокий порог дает точку в нижнем левом углу, а низкий порог дает точку в правом верхнем углу. Это означает, что при уменьшении порога вы получаете более высокий TPR за счет более высокого FPR.

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

from sklearn.metrics import roc_auc_score
auc_RF = roc_auc_score(df.actual_label.values, df.model_RF.values)
auc_LR = roc_auc_score(df.actual_label.values, df.model_LR.values)
print('AUC RF:%.3f'% auc_RF)
print('AUC LR:%.3f'% auc_LR)

Как видите, площадь под кривой для модели RF (AUC = 0,738) лучше, чем LR (AUC = 0,666). Когда я строю кривую ROC, мне нравится добавлять AUC к легенде, как показано ниже.

import matplotlib.pyplot as plt
plt.plot(fpr_RF, tpr_RF,'r-',label = 'RF AUC: %.3f'%auc_RF)
plt.plot(fpr_LR,tpr_LR,'b-', label= 'LR AUC: %.3f'%auc_LR)
plt.plot([0,1],[0,1],'k-',label='random')
plt.plot([0,0,1,1],[0,1,1,1],'g-',label='perfect')
plt.legend()
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.show()

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

Заключение

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

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