LoginSignup
1
0

scikit-learnでAUCを計算する

Last updated at Posted at 2024-01-20

閾値を適用してAUCを扱いたい場合のメモ。

閾値は0.5ではない

sklearnがAUCを算出するときのカットオフ(閾値)は、「デフォルトで0.5」だと思っていたら違った。

例えば、以下のような簡単な例(二値分類)を計算する。

import numpy as np
from sklearn import metrics
y_true = [0, 0, 1, 1]
y_pred = [0.4, 0.3, 0.75, 0.8]
fpr, tpr, thresholds = metrics.roc_curve(y_true, y_pred, pos_label=1)
print(metrics.auc(fpr, tpr))# 1.0

上の例では、当然、ラベル1である確率を出しているので、感度1.0, 特異度1.0でAUCは1.0。
仮に、カットオフを0.5として考えても、当然、AUCは1.0。

y_true = [0, 0, 1, 1]
y_pred = [0.4, 0.3, 0.75, 0.8]
y_pred = [1 if p >= 0.5 else 0 for p in y_pred] # [0,0,1,1] cutoff=0.5
fpr, tpr, thresholds = metrics.roc_curve(y_true, y_pred, pos_label=1)
print(metrics.auc(fpr, tpr))# 1.0

では、次のように、y_pred = [0.5, 0.6, 0.75, 0.8]とするとどうか。
この例では、カットオフを0.5にした場合、感度1.0、特異度0.0であるため、AUCは0.5になる。

y_true = [0, 0, 1, 1]
y_pred = [0.5, 0.6, 0.75, 0.8]
y_pred = [1 if p >= 0.5 else 0 for p in y_pred] # [1,1,1,1]
fpr, tpr, thresholds = metrics.roc_curve(y_true, y_pred, pos_label=1)
print(metrics.auc(fpr, tpr))# 0.5

ところが、閾値で予測結果をラベル化せずに、予測確率をそのまま渡すとどうか。
すると、AUC1.0。

y_true = [0, 0, 1, 1]
y_pred = [0.5, 0.6, 0.75, 0.8]
fpr, tpr, thresholds = metrics.roc_curve(y_true, y_pred, pos_label=1)
print(metrics.auc(fpr, tpr))# 1.0

sklearnは、自動的に最適な閾値でAUCを算出してくれているのか?と頭の中が一瞬混乱するが、しかしこれは当然のことで、scikit-learnのauc関数は、細かく刻んだ閾値ごとにfprとtprを算出して、曲線化面積を計算しているのだから、閾値云々の話ではないわけだ。

ここで、ひとつ疑問が残る。

sklearnが算出してくれたAUCを実現する閾値をどのように求めればよいかがわからない。
作ったモデルを実装するときに、最適な閾値を設定できない問題が発生する。
また、その閾値のときの混同行列がわからないため、感度と特異度も求められない。

ここで、カットオフ0.5で決め打ちしてしまうと、先の例のように、
感度1.0、特異度0.0、なのに、AUC1.0!?

ということで、任意の閾値を適用して評価指標を得る際は、「metrics.auc(fpr, tpr)」関数で得られるAUCを達成するための最適な閾値をROCから求めなければならない。
そして、カットオフを決めた後、予測確率をラベル化してから、諸々の評価指標を算出する。

Youden's index

カットオフを先に計算するためには、Youden's indexが(ユーデンインデックス)よく用いられている。

このように求める。

y_true = [0, 0, 1, 1]
y_pred = [0.5, 0.6, 0.75, 0.8]
fpr, tpr, thresholds = metrics.roc_curve(y_true, y_pred, pos_label=1)
youden_indices = tpr-fpr
index = np.argmax(tpr - fpr)
youden_cutoff = thresholds[index]
# youden indexで予測確率をクラスラベル化
y_pred = [1 if p >= youden_cutoff else 0 for p in y_pred] # [0,0,1,1]
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()# 評価指標は混同行列からも計算できる
print('youden index',youden_cutoff)# 0.75
print(classification_report(y_true, y_pred))

閾値でクラスラベル化してROCを計算すると、ROCカーブが角ばるが、仕方ない。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0