partial AUCでprecision重視の評価

Dec 23, 2017  

機械学習の判別器を評価する際、F値やAUCはよく使われていると思います。 しかしながら、実応用の分野によっては、検出出来ないこと(false negative)よりも、誤検出(false positive)の方が問題視されることがよくあります。 そういったprecision重視にしたいケースで使える指標がpartial AUCです。 一般的には、false positiveに制約をもたせて用いるようです。AUCの面積を求める際に、そのfalse positive rateの下限値以上の部分のみ使います。

簡単に実装するため、scikit learnのaucのコードをオーバーライドして使っています。

import numpy as np
from sklearn.metrics import roc_auc_score, roc_curve, auc
from sklearn.metrics.base import _average_binary_score

# method overriding
def roc_auc_score(y_true, y_score, average="macro", sample_weight=None, max_fpr=None):
    def _binary_roc_auc_score(y_true, y_score, sample_weight=None, max_fpr=max_fpr):
        fpr, tpr, tresholds = roc_curve(y_true, y_score, sample_weight=sample_weight)

        if max_fpr:
            idx = np.where(fpr <= max_fpr)[0]

            idx_last = idx.max()
            idx_next = idx_last + 1
            xc = [fpr[idx_last], fpr[idx_next]]
            yc = [tpr[idx_last], tpr[idx_next]]
            tpr = np.r_[tpr[idx], np.interp(max_fpr, xc, yc)]
            fpr = np.r_[fpr[idx], max_fpr]
            partial_roc = auc(fpr, tpr, reorder=True)

            # standardize result to lie between 0.5 and 1
            min_area = max_fpr**2/2
            max_area = max_fpr
            return 0.5*(1+(partial_roc-min_area)/(max_area-min_area))

        return auc(fpr, tpr, reorder=True)

    return _average_binary_score(_binary_roc_auc_score, y_true, y_score, average, sample_weight=sample_weight)

以下のような感じで試してみると、AUCが同じスコアであっても、precisionが高い方がpAUCでは比較的高い値になることが分かります。 よって、pAUCを指標に学習やパラメータチューニングを行えば、precision重視のものが出来上がります。

import matplotlib.pylab as plt
import numpy as np

y_true = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0])
y_pred_each_fp = {
    "many": np.array([0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]),
    "few": np.array([1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0]),
    "no": np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0]),
}


MAX_FALSE_POSITIVE_RATE = 0.3

plt.figure(figsize=(17, 5))
for fi, (fp_type, y_pred) in enumerate(y_pred_each_fp.items()):
    plt.subplot(1, 3, fi+1)
    fpr, tpr, _ = roc_curve(y_true, y_pred)
    plt.plot(fpr, tpr, color='r', lw=2)
    plt.plot([0, 1], [0, 1], color='k', lw=1, linestyle='--')
    plt.plot([MAX_FALSE_POSITIVE_RATE, MAX_FALSE_POSITIVE_RATE], [0, 1], color='k', lw=1, linestyle='-.')
    plt.xlabel('false positive rate')
    plt.ylabel('true positive rate')
    plt.title("{fp_type} false positives (auc:{auc:1.2f}  pauc:{pauc:1.2f})".format(fp_type=fp_type,
        auc=roc_auc_score(y_true, y_pred), pauc=roc_auc_score(y_true, y_pred, max_fpr=MAX_FALSE_POSITIVE_RATE)))

pauc