6 min read

Полнота (Recall)

Полнота (Recall)
Фото: Aleksandra Boguslawska / Unsplash

Полнота (Отзыв, чувствительность, истинная положительная скорость) – это мера того, насколько правильно наша Модель (Model) идентифицирует истинно положительные наблюдения. Термин тесно связан с Матрицей ошибок (Confusion Matrix). Например, для всех пациентов, которые на самом деле имеют сердечные заболевания, полнота говорит нам, сколько из них мы правильно определили как имеющих сердечные заболевания.

Введение

Спросите любого специалиста по Машинному обучению (ML) о самых запутанных концепциях в их учебном пути, и неизменно ответ склоняется в сторону темы "Доля правильных ответов (Accuracy) и полнота". Разницу между ними на самом деле легко запомнить, но только после того, как мы действительно поймем, что означает каждый термин. Довольно часто эксперты склонны давать запутанные объяснения, которые еще больше запутывают новичков. Итак, давайте поставим точки над 'i'.

Доля правильных ответов и полнота

Для любой модели соответствие реальности чрезвычайно важно. Это включает в себя достижение баланса между Недообучением (Underfitting) и Переобучением (Overfitting), или, другими словами, Дилемма смещения-дисперсии (Bias-Variance Trade-off).

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

Но как это сделать? Мы рассмотрим метрики оценки классификации, сосредоточившись на Accuracy и полноте, а также узнаем, как вычислить эти метрики, освоив набор данных и простой алгоритм классификации. Итак, давайте начнем!

Постановка задачи

Я твердо верю в обучение на практике. Поэтому на протяжении всей этой статьи мы будем говорить в практическом плане – с помощью набора данных.

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

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

Как всегда, мы начнем с импорта необходимых библиотек и пакетов:

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report
from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
import seaborn as sns
%matplotlib inline

Затем давайте посмотрим на данные и целевые переменные, с которыми мы имеем дело:

data_file_path = 'https://www.dropbox.com/s/88u0lwlme4xz6iz/heart.csv?dl=1'
data_df = pd.read_csv(data_file_path)

#To get information on the number of entries and the datatypes of the features
data_df.head()

Давайте проверим, есть ли у нас пропущенные значения:

data_df.isnull().sum()

Пропущенных значений нет:

age         0
sex         0
cp          0
trestbps    0
chol        0
fbs         0
restecg     0
thalach     0
exang       0
oldpeak     0
slope       0
ca          0
thal        0
target      0
dtype: int64

Теперь мы можем посмотреть, сколько пациентов на самом деле страдают от сердечных заболеваний (1), а сколько нет (0):

Давайте продолжим, разделив наши обучающие и тестовые данные на Предикторы (Predictor Variable) и Целевую переменную (Target Variable). Поскольку мы используем kNN, необходимо также подвергнуть датасет Стандартизации (Standartization):

y = data_df["target"].values
x = data_df.drop(["target"], axis = 1)

# Стандартизация обязательна для kNN
ss = StandardScaler()
x = ss.fit_transform(x)

# Разделение датасета на тренировочную и тестовую части
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size = 0.3) # 70% training and 30% test

Интуиция, стоящая за выбором наилучшего значения k, выходит за рамки этой статьи, но мы должны знать, что можем определить оптимальное значение k, когда получим самый высокий тестовый балл для этого значения. Для этого мы можем оценить результаты обучения и тестирования до 20 ближайших соседей:

train_score = []
test_score = []
k_vals = []

for k in range(1, 21):
    k_vals.append(k)
    knn = KNeighborsClassifier(n_neighbors = k)
    knn.fit(X_train, y_train)
    
    tr_score = knn.score(X_train, y_train)
    train_score.append(tr_score)
    
    te_score = knn.score(X_test, y_test)
    test_score.append(te_score)

Чтобы оценить максимальный тестовый балл и связанные с ним значения k, выполните следующую команду:

max_test_score = max(test_score)
test_scores_ind = [i for i, v in enumerate(test_score) if v == max_test_score]
print('Максимальный тестовый скор {} and k = {}'.format(max_test_score * 100, list(map(lambda x: x + 1, test_scores_ind))))

Таким образом, мы получили оптимальное значение k, равное 10 с оценкой 85,7:

Максимальный тестовый скор: 85.71428571428571 and k = [10]

Мы доработаем одно из этих значений и соответствующим образом подгоним модель:

knn = KNeighborsClassifier(10)

knn.fit(X_train, y_train)
knn.score(X_test, y_test)

Мы получаем хороший результат, на первый взгляд:

0.8571428571428571

Но как мы оцениваем, является ли эта модель "хорошей" или нет? Для этого мы используем матрицу ошибок:

y_pred = knn.predict(X_test)
confusion_matrix(y_test, y_pred)
pd.crosstab(y_test, y_pred, rownames = ['Фактические'], colnames =['Предсказанные'], margins = True)

Матрица путаницы помогает нам понять, насколько верны были наши прогнозы и насколько они соответствуют реальным значениям:

Мы уже знаем, что наши тестовые данные состояли из 91 Наблюдения (Observation). Это значение 4-й строки и 4-го столбца в конце. Мы также замечаем, что существуют некоторые фактические и прогнозируемые значения. Фактические значения – это количество точек данных, которые первоначально были разделены на 0 или 1. Прогнозируемые значения – это количество точек данных, которые наша модель kNN предсказала как 0 или 1.

Фактические значения:

  • Пациенты, у которых на самом деле нет сердечных заболеваний = 41
  • Пациенты, у которых на самом деле есть болезнь сердца = 50

Прогнозируемые значения таковы:

  • Число пациентов, которым было предсказано, что у них нет сердечных заболеваний = 40
  • Количество пациентов, у которых было предсказано наличие заболевания сердца = 51

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

  • Случаи, когда у пациентов на самом деле не было болезни сердца и наша модель так и предсказывала, называются истинно отрицательными предсказаниями (True Negatives). Их 33.
  • Случаи, когда у пациентов действительно есть болезнь сердца, и наша модель также предсказывает, что она есть, называются Истинно позитивное (True Positives). В нашей матрице таких значений 43.
  • Однако есть некоторые случаи, когда у пациента на самом деле нет сердечных заболеваний, но наша модель предсказала, что они есть. Этот вид ошибки является ошибкой типа I, и мы называем значения Ложно позитивными (False Positives). Для нашей матрицы Ложных срабатываний 8.
  • Точно так же есть случаи, когда у пациента действительно есть болезнь сердца, но наша модель предсказала, что это не так. Этот вид ошибки является Ошибкой типа II, и мы называем значения Ложно отрицательными (False Negatives). Для нашей матрицы таких 7.

Что такое ДПО?

Итак, теперь мы подошли к сути этой статьи. Что такое Доля правильных ответов (ДПР)? И какое отношение к этому имеет все вышеперечисленное обучение?

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

$$ДПО\space{=}\space{\frac{TP}{TP + FP}},\space{где}$$
$$TP\space{–}\space{истинно}\space{позитивные}\space{предсказания}$$
$$FP\space{–}\space{ложно}\space{позитивные}\space{предсказания}$$

Какова Accuracy нашей модели? Она равна примерно 84%. Важно, чтобы мы не начинали лечить пациента, у которого на самом деле нет сердечного заболевания, из-за результатов модели.

Что такое полнота?

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

$$Полнота\space{=}\space{\frac{TP}{TP + FN}},\space{где}$$
$$TP\space{–}\space{истинно}\space{позитивные}\space{предсказания}$$
$$FN\space{–}\space{ложно}\space{отрицательные}\space{предсказания}$$

Для нашей модели, полнота равна 86%. Он также определяет, насколько точно наша модель способна идентифицировать соответствующие данные. Мы называем это чувствительностью, или истинной положительной скоростью. Что делать, если у  есть болезнь сердца, но ему не дают никакого лечения, потому что наша модель предсказала это? Именно такой ситуации мы и хотели бы избежать!

Самая простая метрика для понимания – доля правильных ответов

Теперь мы подошли к одной из самых простых метрик – доля правильных ответов, отношения общего числа правильных предсказаний и общего числа предсказаний. Рассчитывается по формуле:

$$ДПО\space{=}\space{\frac{TP}{TP + FN}},\space{где}$$
$$TP\space{–}\space{истинно}\space{позитивные}\space{предсказания}$$
$$FN\space{–}\space{ложно}\space{отрицательные}\space{предсказания}$$

Для нашей модели ДПО равна ~83%. Использование accuracy в качестве определяющей метрики имеет смысл, но желательно использовать ДПО и полноту. Могут быть и другие ситуации, когда наша accuracy очень высока, но полнота низка. В идеале для нашей модели мы хотели бы полностью избежать любых ситуаций, когда у пациента есть болезнь сердца, но наша модель классифицирует его как не имеющего ее.

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

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

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

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

Автор оригинальной статьи: Purva Huilgol