web-dev-qa-db-ja.com

さまざまなバイナリ分類子のROCAUCスコアを比較し、Pythonで統計的有意性を評価するにはどうすればよいですか? (p値、信頼区間)

Pythonのさまざまなバイナリ分類子を比較したいと思います。そのために、ROC AUCスコアを計算し、95%信頼区間(CI)、およびを測定します。 p値統計的有意性にアクセスします。

以下は、バイナリ分類データセットで3つの異なるモデルをトレーニングし、ROC曲線をプロットし、AUCスコアを計算するscikit-learnの最小限の例です。

これが私の具体的な質問です:

  1. テストセットのROCAUCスコアの95%信頼区間(CI)を計算する方法は? (例:ブートストラップを使用)。
  2. (テストセットで)AUCスコアを比較し、p値を測定して統計を評価する方法意義? (帰無仮説は、モデルに違いがないというものです。帰無仮説を棄却するということは、AUCスコアの差が統計的に有意であることを意味します。)

import numpy as np

np.random.seed(2018)

from sklearn.datasets import load_breast_cancer
from sklearn.metrics import roc_auc_score, roc_curve
from sklearn.model_selection import train_test_split

from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import RandomForestClassifier
from sklearn.neural_network import MLPClassifier
import matplotlib
import matplotlib.pyplot as plt

data = load_breast_cancer()

X = data.data
y = data.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=17)

# Naive Bayes Classifier
nb_clf = GaussianNB()
nb_clf.fit(X_train, y_train)
nb_prediction_proba = nb_clf.predict_proba(X_test)[:, 1]

# Ranodm Forest Classifier
rf_clf = RandomForestClassifier(n_estimators=20)
rf_clf.fit(X_train, y_train)
rf_prediction_proba = rf_clf.predict_proba(X_test)[:, 1]

# Multi-layer Perceptron Classifier
mlp_clf = MLPClassifier(alpha=1, hidden_layer_sizes=150)
mlp_clf.fit(X_train, y_train)
mlp_prediction_proba = mlp_clf.predict_proba(X_test)[:, 1]


def roc_curve_and_score(y_test, pred_proba):
    fpr, tpr, _ = roc_curve(y_test.ravel(), pred_proba.ravel())
    roc_auc = roc_auc_score(y_test.ravel(), pred_proba.ravel())
    return fpr, tpr, roc_auc


plt.figure(figsize=(8, 6))
matplotlib.rcParams.update({'font.size': 14})
plt.grid()
fpr, tpr, roc_auc = roc_curve_and_score(y_test, rf_prediction_proba)
plt.plot(fpr, tpr, color='darkorange', lw=2,
         label='ROC AUC={0:.3f}'.format(roc_auc))
fpr, tpr, roc_auc = roc_curve_and_score(y_test, nb_prediction_proba)
plt.plot(fpr, tpr, color='green', lw=2,
         label='ROC AUC={0:.3f}'.format(roc_auc))
fpr, tpr, roc_auc = roc_curve_and_score(y_test, mlp_prediction_proba)
plt.plot(fpr, tpr, color='crimson', lw=2,
         label='ROC AUC={0:.3f}'.format(roc_auc))
plt.plot([0, 1], [0, 1], color='navy', lw=1, linestyle='--')
plt.legend(loc="lower right")
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('1 - Specificity')
plt.ylabel('Sensitivity')
plt.show()

enter image description here

8
mhrtsh

95%信頼区間のブートストラップ

データの複数のリサンプリングで分析を繰り返したいと考えています。一般的なケースでは、データxから必要な統計を決定する関数f(x)があり、次のようにbootstrap:

def bootstrap(x, f, nsamples=1000):
    stats = [f(x[np.random.randint(x.shape[0], size=x.shape[0])]) for _ in range(nsamples)]
    return np.percentile(stats, (2.5, 97.5))

これにより、95%信頼区間のいわゆるプラグイン推定値が得られます(つまり、bootstrap分布)のパーセンタイルを取得するだけです)。

あなたの場合、あなたはこのようなより具体的な関数を書くことができます

def bootstrap_auc(clf, X_train, y_train, X_test, y_test, nsamples=1000):
    auc_values = []
    for b in range(nsamples):
        idx = np.random.randint(X_train.shape[0], size=X_train.shape[0])
        clf.fit(X_train[idx], y_train[idx])
        pred = clf.predict_proba(X_test)[:, 1]
        roc_auc = roc_auc_score(y_test.ravel(), pred.ravel())
        auc_values.append(roc_auc)
    return np.percentile(auc_values, (2.5, 97.5))

ここで、clfはパフォーマンスをテストする分類子であり、X_trainy_trainX_testy_testはあなたのコードのようです。

これにより、次の信頼区間が得られます(3桁に四捨五入、1000 bootstrapサンプル):

  • ナイーブベイズ:0.986 [0.980 0.988](信頼区間の推定値、下限値、上限値)
  • ランダムフォレスト:0.983 [0.974 0.989]
  • 多層パーセプトロン:0.974 [0.223 0.98]

偶然のパフォーマンスに対してテストするための並べ替えテスト

順列テストは、技術的には観測シーケンスのすべての順列を調べ、並べ替えられたターゲット値を使用してroc曲線を評価します(機能は並べ替えられません)。これは、いくつかの観測がある場合は問題ありませんが、より多くの観測がある場合は非常にコストがかかります。したがって、順列の数をサブサンプリングし、単純にいくつかのランダム順列を実行するのが一般的です。ここで、実装は、テストする特定のものにもう少し依存します。次の関数は、roc_auc値に対してそれを行います

def permutation_test(clf, X_train, y_train, X_test, y_test, nsamples=1000):
    idx1 = np.arange(X_train.shape[0])
    idx2 = np.arange(X_test.shape[0])
    auc_values = np.empty(nsamples)
    for b in range(nsamples):
        np.random.shuffle(idx1)  # Shuffles in-place
        np.random.shuffle(idx2)
        clf.fit(X_train, y_train[idx1])
        pred = clf.predict_proba(X_test)[:, 1]
        roc_auc = roc_auc_score(y_test[idx2].ravel(), pred.ravel())
        auc_values[b] = roc_auc
    clf.fit(X_train, y_train)
    pred = clf.predict_proba(X_test)[:, 1]
    roc_auc = roc_auc_score(y_test.ravel(), pred.ravel())
    return roc_auc, np.mean(auc_values >= roc_auc)

この関数は、分類子をclfとして再び受け取り、シャッフルされていないデータのAUC値とp値(つまり、シャッフルされていないデータにあるもの以上のAUC値を観測する確率)を返します。

これを1000個のサンプルで実行すると、3つの分類子すべてのp値が0になります。これらはサンプリングのために正確ではありませんが、これらの分類子のすべてが偶然よりも優れたパフォーマンスを発揮することを示していることに注意してください。

分類器間の差異の並べ替え検定

これははるかに簡単です。 2つの分類子が与えられると、すべての観測値の予測があります。このように、予測と分類子の間の割り当てをシャッフルするだけです。

def permutation_test_between_clfs(y_test, pred_proba_1, pred_proba_2, nsamples=1000):
    auc_differences = []
    auc1 = roc_auc_score(y_test.ravel(), pred_proba_1.ravel())
    auc2 = roc_auc_score(y_test.ravel(), pred_proba_2.ravel())
    observed_difference = auc1 - auc2
    for _ in range(nsamples):
        mask = np.random.randint(2, size=len(pred_proba_1.ravel()))
        p1 = np.where(mask, pred_proba_1.ravel(), pred_proba_2.ravel())
        p2 = np.where(mask, pred_proba_2.ravel(), pred_proba_1.ravel())
        auc1 = roc_auc_score(y_test.ravel(), p1)
        auc2 = roc_auc_score(y_test.ravel(), p2)
        auc_differences(auc1 - auc2)
    return observed_difference, np.mean(auc_differences >= observed_difference)

このテストと1000のサンプルでは、​​3つの分類子の間に有意差は見つかりませんでした。

  • ナイーブベイズvsランダムフォレスト:diff = 0.0029、p(diff>)= 0.311
  • 単純ベイズvsMLP:diff = 0.0117、p(diff>)= 0.186
  • ランダムフォレストとMLP:diff = 0.0088、p(diff>)= 0.203

ここで、diffは2つの分類子間のroc曲線の差を示し、p(diff>)は、シャッフルされたデータセットでより大きな差を観測する経験的確率です。

5
Ingo

以下のコードを使用して、ニューラルネットのAUCと漸近正規分布信頼区間を計算できます。

tf.contrib.metrics.auc_with_confidence_intervals(
labels,
predictions,
weights=None,
alpha=0.95,
logit_transformation=True,
metrics_collections=(),
updates_collections=(),
name=None)
0
RAJAT GUPTA