はじめに
2値分類のモデルを評価する際に、ROC曲線がよく用いられます。 特に、その曲線下の面積(Area under the curve, AUC)が指標として使われます。 その他にも、混同行列から算出されるPrecisionとRecallからなるPR曲線があります。
ネガティブケースにデータが偏っている場合は、ROC曲線よりPR曲線が適していると言われてます。 じゃあ、偏ってるときはPR曲線使えばいいかと思いましたが、そもそもROC曲線でAUCを比較することとPR曲線でAUCを比較することは等価なのかが気になりました。 結果は、等価ではありませんでした。 以下では、そのことを例を使って見ていきます。
問題設定
陽性、陰性の数がそれぞれ1つ以上のデータを用意する。 そのデータに対して、陽性を当てる確率をのパターン付与する。 このとき、以下は成立するか?
ここで、はからなるROC曲線、PR曲線のAUCとする。
反例
陽性2つ、陰性8つのデータを使用します。
確率の方は、numpy.random.rand()
でseedを振って算出します。
実際は、適切な例を見つけるためにseedを振って探索してますが、コードは割愛します。
import random import matplotlib.pyplot as plt import numpy as np import pandas as pd from sklearn.metrics import (average_precision_score, precision_recall_curve, roc_auc_score, roc_curve) # 10点のデータと2種類の確率の振り方A,Bを準備 np.random.seed(98) probA = np.random.rand(10) np.random.seed(63) probB = np.random.rand(10) df = pd.DataFrame({ 'class': [1]*2 + [0]*8, 'probA': probA, 'probB': probB }) # A,Bに対して、ROC曲線、PR曲線とそのAUCを算出 rocA = roc_curve(df['class'], df['probA']) rocB = roc_curve(df['class'], df['probB']) prA = precision_recall_curve(df['class'], df['probA']) prB = precision_recall_curve(df['class'], df['probB']) roc_aucA = roc_auc_score(df['class'], df['probA']) roc_aucB = roc_auc_score(df['class'], df['probB']) pr_aucA = average_precision_score(df['class'], df['probA']) pr_aucB = average_precision_score(df['class'], df['probB']) # ROC曲線、PR曲線を可視化 fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10, 4)) ax1.plot(rocA[0], rocA[1], label='ROC A (AUC = %.2f)' % roc_aucA) ax1.plot(rocB[0], rocB[1], label='ROC B (AUC = %.2f)' % roc_aucB) ax1.legend() ax1.set_title('ROC curve') ax1.set_xlabel('FPR') ax1.set_ylabel('TPR') ax1.grid() ax2.plot(prA[1], prA[0], label='PR A (AUC = %.2f)' % pr_aucA) ax2.plot(prB[1], prB[0], label='PR B (AUC = %.2f)' % pr_aucB) ax2.legend() ax2.set_title('PR curve') ax2.set_xlabel('Recall') ax2.set_ylabel('Precision') ax2.grid()
結果は、かつとなっています。 つまり、ROC曲線のAUCが高くても、PR曲線のAUCが低い場合があります。
考察
との確率を表示させます。
display(df[['class', 'probA']].sort_values( 'probA', ascending=False).reset_index(drop=True)) display(df[['class', 'probB']].sort_values( 'probB', ascending=False).reset_index(drop=True))
- PR曲線について
の場合、一番大きい確率ではずしているので、PR曲線がまで移動します。 この段階で、のAUCは最大でも、と低い値です。 また、一番大きい確率で当てているは、に移動します。 そのため、のAUCは最小でもと大きい値になります。
- ROC曲線について
の場合、最初こそ外してますが、番目までですべての陽性が出尽くしたので、AUCが大きい値になります。 一方、は最初当ててますが、次の陽性は番目と遅く、となるのが遅れてAUCが少し低めに出ています。
おわりに
最初は、今回の問題が正しいと思って証明を考えてましたが、よくわからなかったのでコード書いたら、すんなりと反例が出てきました。 こうやって、調べたいことをぱっとコード書いて正しそうか確かめられるようになりたいなと思いました。
今回は、反例を構成しましたが、ある条件をつければ上記の問題が正しくなることが知られています。 暇があるときに、その証明をブログで紹介しようかなと思います。