Кривые ROC AUC сравнивают TPR и FPR для разных пороговых значений классификации для классификатора.

Кривые ROC AUC помогают нам выбрать лучшую модель для работы, оценивая, насколько хорошо модель различает классы.

Легенда:
ROC = рабочая кривая приемника
AUC = площадь под кривой
TPR = частота истинных положительных результатов
FPR = частота ложных срабатываний

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

Пример: прогнозирование того, какие SMS-сообщения являются СПАМом

Сначала загрузите набор данных из Kaggle.

Откройте его в фреймворке pandas.

import pandas as pd
df = pd.read_csv('~/Downloads/spam.csv', encoding='ISO-8859-1')
df.head(3)

Похоже, данные плохо отформатированы. Данные часто бывают такими - мы будем работать над этим.

Преобразуйте v1 в ваши ярлыки, y, и v2, в ваши функции, X. Ярлыки должны быть целыми числами, чтобы вводить их в модель, поэтому для if spam установлено значение 1, а для if ham установлено значение 0. Если вы не знаете, ham означает, что это не spam.

import numpy as np
y = np.array([(1 if i=='spam' else 0) for i in df.v1.tolist()])
X = np.array(df.v2.tolist())

X теперь является массивом строк, а y - массивом 1's и 0's.

Разделите данные на тестовые и обучающие наборы. Обратите внимание, что данные еще не векторизованы.

from sklearn.model_selection import StratifiedShuffleSplit
splitter = StratifiedShuffleSplit(
    n_splits=1, test_size=0.3, random_state=0
)
for train_index, test_index in splitter.split(X, y):
    X_train_pre_vectorize, X_test_pre_vectorize = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]

Установите векторизатор и преобразуйте тестовый и обучающий наборы.

from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer()
X_train = vectorizer.fit_transform(X_train_pre_vectorize)
X_test = vectorizer.transform(X_test_pre_vectorize)

Выберите классификатор и поместите его в комплект поезда. Я произвольно выбрал LogisticRegression.

from sklearn.linear_model import LogisticRegression
classifier = LogisticRegression()
classifier.fit(X_train, y_train)

Обычно именно здесь мы прогнозируем классы для набора тестов, но, поскольку нас просто интересует построение кривой ROC AUC, пропустите его.

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

y_score = classifier.predict_proba(X_test)
y_score = np.array(y_score)
print(y_score)

Следующий код может немного сбить с толку. Использование label_binarize() с 3 (или более) классами преобразовало бы одно y значение [2] в [0 0 1] или [0] в [1 0 0], но это не работает одинаково только с двумя классами. Поэтому мы вызываем hstack numpy, чтобы переформатировать вывод.

from sklearn.preprocessing import label_binarize
y_test_bin = label_binarize(y_test, neg_label=0, pos_label=1, classes=[0,1])
y_test_bin = np.hstack((1 - y_test_bin, y_test_bin))
print(y_test_bin)

Сгенерируйте кривую.

from sklearn.metrics import roc_curve, auc
import matplotlib.pyplot as plt
fpr = dict()
tpr = dict()
roc_auc = dict()
for i in [0,1]:
    # collect labels and scores for the current index
    labels = y_test_bin[:, i]
    scores = y_score[:, i]
    
    # calculates FPR and TPR for a number of thresholds
    fpr[i], tpr[i], thresholds = roc_curve(labels, scores)
    
    # given points on a curve, this calculates the area under it
    roc_auc[i] = auc(fpr[i], tpr[i])

На этом этапе мы могли рассчитать кривую ROC отдельно для классов 0 и 1. Но для простоты мы объединим их и сгенерируем единую кривую.

Отказ от ответственности: это имеет больше смысла, когда классы сбалансированы, или может скрыть тот факт, что модель плохо справляется с одним классом, если она хорошо справляется с другим. Но мы все равно сделаем это здесь, чтобы узнать, как это сделать.

Мы воспользуемся «микро-усреднением» и сгладим TPR для обоих классов вместе, аналогично FPR. .ravel() сделает это за нас. Так, например, [[1,0],[0,1]] становится [1,0,0,1]

fpr["micro"], tpr["micro"], _ = roc_curve(y_test_bin.ravel(), y_score.ravel())
roc_auc['micro'] = auc(fpr["micro"], tpr["micro"])

Теперь постройте кривую.

plt.figure()
lw = 2
plt.plot(fpr[1], tpr[1], color='darkorange',
         lw=lw, label='ROC curve (area = %0.2f)' % roc_auc[1])
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic example')
plt.legend(loc="lower right")
plt.show()

Отлично! Это довольно крутая кривая.

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

Что мы подразумеваем под «разными порогами классификации»?

Если вы привыкли использовать классификаторы sklearn из коробки, вы знаете, что .predict выводит предсказанный класс. Но вы можете не знать, что по умолчанию используется порог классификации 50%.

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

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

Какая интуиция говорит о том, что высокая кривая хороша?

Модель, которая правильно классифицирует большинство примеров и выводит вероятности предсказания, близкие либо к 0.0 (это не этот класс), либо к 1.0 (это этот класс), будет иметь кривую, подобную приведенной выше.

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

Например, если модель выводит вероятность предсказания 0.9 для SMS-сообщения, являющегося СПАМом, изменение порога с 0.5 на 0.6 не влияет на выводимый класс. Модель очень уверена в своих выведенных классах.

Напротив, если бы модель выводила большинство вероятностей прогноза в диапазоне от 0.3 до 0.7, то перемещение порога с 0.5 на 0.6 изменило бы полученные классы. Можно сказать, что такая модель не уверена в собственных предсказанных классификациях.

Почему ROC и AUC?

Кривая представляет собой «кривую ROC», которая отображает TPR и FPR при различных порогах.

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

Что такое TPR?

Истинно положительная оценка.

TPR - это количество TP, деленное на сумму TP и FN.

Пример 1: модель, которая предсказывает, является ли изображение собакой.
TP: правильно предсказала, что изображение собаки является собакой.
FN: неверно предсказал, что изображение собаки - это кошка.

Пример 2: модель, которая предсказывает, является ли сообщение СПАМом.
TP: правильно предсказывает, что СПАМ-сообщение является СПАМом.
FN : неверно предсказано, что СПАМ является HAM.

Что такое FPR?

Ложноположительный рейтинг.

FPR - это количество FP, деленное на сумму FP и TN.

Пример 1. Модель, которая предсказывает, изображена ли собака на изображении.
FP: неверно предсказано, что изображение кошки - это собака.
TN: правильно предсказал, что на изображении нет собаки.

Пример 2: модель, которая предсказывает, является ли сообщение СПАМом.
FP: неверно предсказывает, что сообщение HAM является СПАМом.
TN : правильно предсказал, что сообщение HAM - это HAM.

Что не соответствует кривой ROC AUC?

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

Выводы

  • Кривая ROC AUC может дать нам представление о прогностической способности модели различать классы.
  • Модели с более высокой AUC, как правило, более эффективны, чем модели с более низкой AUC.
  • ROC AUC не лучший вариант для сильно несбалансированных наборов данных.