Построение кривой ROC для многоклассовой задачи

#python #machine-learning #scikit-learn #roc #auc

#python #машинное обучение #scikit-learn #ОКР #auc

Вопрос:

Я пытаюсь применить идею sklearn расширения ROC для мультикласса к моему набору данных. Моя ROC-кривая для каждого класса выглядит как нахождение прямой линии для каждого, разверните sklearn пример, показывающий колебания кривой.

Я привожу MWE ниже, чтобы показать, что я имею в виду:

 # all imports
import numpy as np
import matplotlib.pyplot as plt
from itertools import cycle
from sklearn import svm, datasets
from sklearn.metrics import roc_curve, auc
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import label_binarize
from sklearn.datasets import  make_classification
from sklearn.ensemble import RandomForestClassifier
# dummy dataset
X, y = make_classification(10000, n_classes=5, n_informative=10, weights=[.04, .4, .12, .5, .04])
train, test, ytrain, ytest = train_test_split(X, y, test_size=.3, random_state=42)

# random forest model
model = RandomForestClassifier()
model.fit(train, ytrain)
yhat = model.predict(test)
 

Затем следующая функция строит кривую ROC:

 def plot_roc_curve(y_test, y_pred):
  
  n_classes = len(np.unique(y_test))
  y_test = label_binarize(y_test, classes=np.arange(n_classes))
  y_pred = label_binarize(y_pred, classes=np.arange(n_classes))

  # Compute ROC curve and ROC area for each class
  fpr = dict()
  tpr = dict()
  roc_auc = dict()
  for i in range(n_classes):
    fpr[i], tpr[i], _ = roc_curve(y_test[:, i], y_pred[:, i])
    roc_auc[i] = auc(fpr[i], tpr[i])
  
  # Compute micro-average ROC curve and ROC area
  fpr["micro"], tpr["micro"], _ = roc_curve(y_test.ravel(), y_pred.ravel())
  roc_auc["micro"] = auc(fpr["micro"], tpr["micro"])

  # First aggregate all false positive rates
  all_fpr = np.unique(np.concatenate([fpr[i] for i in range(n_classes)]))

  # Then interpolate all ROC curves at this points
  mean_tpr = np.zeros_like(all_fpr)
  for i in range(n_classes):
    mean_tpr  = np.interp(all_fpr, fpr[i], tpr[i])

  # Finally average it and compute AUC
  mean_tpr /= n_classes

  fpr["macro"] = all_fpr
  tpr["macro"] = mean_tpr
  roc_auc["macro"] = auc(fpr["macro"], tpr["macro"])

  # Plot all ROC curves
  #plt.figure(figsize=(10,5))
  plt.figure(dpi=600)
  lw = 2
  plt.plot(fpr["micro"], tpr["micro"],
    label="micro-average ROC curve (area = {0:0.2f})".format(roc_auc["micro"]),
    color="deeppink", linestyle=":", linewidth=4,)

  plt.plot(fpr["macro"], tpr["macro"],
    label="macro-average ROC curve (area = {0:0.2f})".format(roc_auc["macro"]),
    color="navy", linestyle=":", linewidth=4,)

  colors = cycle(["aqua", "darkorange", "darkgreen", "yellow", "blue"])
  for i, color in zip(range(n_classes), colors):
    plt.plot(fpr[i], tpr[i], color=color, lw=lw,
        label="ROC curve of class {0} (area = {1:0.2f})".format(i, roc_auc[i]),)

  plt.plot([0, 1], [0, 1], "k--", lw=lw)
  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 (ROC) curve")
  plt.legend()
 

Вывод:

 plot_roc_curve(ytest, yhat)
 

введите описание изображения здесь

Вид изгиба прямой линии один раз. Я хотел бы видеть производительность модели при разных пороговых значениях, а не только при одном, рисунок, аналогичный иллюстрации sklearn для 3-х классов, показанной ниже:

введите описание изображения здесь

Ответ №1:

  • Дело в том, что вы используете predict() вместо predict_proba() / decision_function() для определения вашего y_hat . Это означает, что, учитывая, что пороговый вектор определяется количеством различных значений в y_hat (см. Здесь Для справки), у вас будет несколько пороговых значений для каждого класса, только для которых tpr fpr вычисляются и (что, в свою очередь, подразумевает, что ваши кривые оцениваются только в нескольких точках).
  • Действительно, подумайте о том, что говорится в документе для передачи y_scores в roc_curve() , либо о вероятностных оценках, либо о значениях решений. В примере из sklearn значения решений используются для вычисления баллов. Учитывая, что вы рассматриваете a RandomForestClassifier() , рассмотрение оценок вероятности в вашем y_hat должно быть правильным.
  • В чем тогда смысл бинаризации выходных данных с метками? Стандартное определение ROC представлено в терминах двоичной классификации. Чтобы перейти к многоклассовой задаче, вы должны преобразовать свою задачу в двоичный файл, используя подход OneVsAll, чтобы у вас было n_class несколько кривых ROC. (Заметьте, действительно, что as SVC() по умолчанию обрабатывает многоклассовые проблемы в режиме OvO, в примере им пришлось принудительно использовать OvA, применяя OneVsRestClassifier конструктор; с a RandomForestClassifier у вас нет такой проблемы, поскольку она по своей сути многоклассовая, см. Здесь Для справки). В этих терминах, как только вы переключитесь на predict_proba() , вы увидите, что нет особого смысла в бинаризующих предсказаниях меток.
      # all imports
     import numpy as np
     import matplotlib.pyplot as plt
     from itertools import cycle
     from sklearn import svm, datasets
     from sklearn.metrics import roc_curve, auc
     from sklearn.model_selection import train_test_split
     from sklearn.preprocessing import label_binarize
     from sklearn.datasets import  make_classification
     from sklearn.ensemble import RandomForestClassifier
     # dummy dataset
     X, y = make_classification(10000, n_classes=5, n_informative=10, weights=[.04, .4, .12, .5, .04])
     train, test, ytrain, ytest = train_test_split(X, y, test_size=.3, random_state=42)
    
     # random forest model
     model = RandomForestClassifier()
     model.fit(train, ytrain)
     yhat = model.predict_proba(test)
    
     def plot_roc_curve(y_test, y_pred):
         n_classes = len(np.unique(y_test))
         y_test = label_binarize(y_test, classes=np.arange(n_classes))
    
         # Compute ROC curve and ROC area for each class
         fpr = dict()
         tpr = dict()
         roc_auc = dict()
         thresholds = dict()
         for i in range(n_classes):
           fpr[i], tpr[i], thresholds[i] = roc_curve(y_test[:, i], y_pred[:, i], drop_intermediate=False)
         roc_auc[i] = auc(fpr[i], tpr[i])
    
         # Compute micro-average ROC curve and ROC area
         fpr["micro"], tpr["micro"], _ = roc_curve(y_test.ravel(), y_pred.ravel())
         roc_auc["micro"] = auc(fpr["micro"], tpr["micro"])
    
         # First aggregate all false positive rates
         all_fpr = np.unique(np.concatenate([fpr[i] for i in range(n_classes)]))
    
         # Then interpolate all ROC curves at this points
         mean_tpr = np.zeros_like(all_fpr)
         for i in range(n_classes):
           mean_tpr  = np.interp(all_fpr, fpr[i], tpr[i])
    
         # Finally average it and compute AUC
         mean_tpr /= n_classes
    
         fpr["macro"] = all_fpr
         tpr["macro"] = mean_tpr
         roc_auc["macro"] = auc(fpr["macro"], tpr["macro"])
    
         # Plot all ROC curves
         #plt.figure(figsize=(10,5))
         plt.figure(dpi=600)
         lw = 2
         plt.plot(fpr["micro"], tpr["micro"],
         label="micro-average ROC curve (area = {0:0.2f})".format(roc_auc["micro"]),
         color="deeppink", linestyle=":", linewidth=4,)
    
         plt.plot(fpr["macro"], tpr["macro"],
         label="macro-average ROC curve (area = {0:0.2f})".format(roc_auc["macro"]),
         color="navy", linestyle=":", linewidth=4,)
    
         colors = cycle(["aqua", "darkorange", "darkgreen", "yellow", "blue"])
         for i, color in zip(range(n_classes), colors):
           plt.plot(fpr[i], tpr[i], color=color, lw=lw,
           label="ROC curve of class {0} (area = {1:0.2f})".format(i, roc_auc[i]),)
    
         plt.plot([0, 1], [0, 1], "k--", lw=lw)
         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 (ROC) curve")
         plt.legend()
     

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

Комментарии:

1. А, понятно. Большое спасибо за объяснение.