K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

ROC-AUC

中級

03.3.2

ROC-AUC

最終更新 2020-01-29 読了時間 4 分
まとめ
ROC 曲線は、分類器の確率に対する閾値を 1 から 0 へ動かしながら、各点での偽陽性率(FPR、陰性を誤って陽性と言った割合)を横軸、真陽性率(TPR、陽性を正しく陽性と言った割合)を縦軸に打った軌跡です。その下の面積が AUC で、1.0 なら「どの陽性サンプルも、どの陰性サンプルより高い確率を付けている」完全な並び、0.5 なら順位がランダムと同じです。閾値を決める前の「並べる力」を 1 つの数にするので、閾値 0.5 の Accuracy で差が出ないモデルどうしも比べられます。読み終えると、RocCurveDisplay.from_predictions と roc_auc_score で曲線と面積を出し、AUC 0.95 のモデルでも閾値を別に決めなければ運用できない理由を説明できるようになります。

感度と特異度 を先に読んでおくと、ROC 曲線の縦軸 TPR が感度、横軸 FPR が「1 − 特異度」そのものだと分かり、曲線の 1 点が 1 つの閾値に対応する理由がつかめます。


直感:陽性と陰性をどれだけ正しく並べられるか #

AUC には「ランダムに選んだ陽性 1 件と陰性 1 件を比べたとき、陽性の方に高い確率を付けている割合」という意味があります。乳がん診断で悪性 10 件・良性 10 件に確率を付け、悪性 10 件がすべて良性 10 件より上に並べば 100 組すべてで勝ち、AUC は 1.0 です。並びが完全にばらばらなら半分の組で勝つだけなので 0.5 になります。ROC 曲線はこの並びを図にしたもので、確率の高い順に 1 件ずつ陽性と判定していき、悪性を拾えば縦(TPR)に、良性を拾えば横(FPR)に 1 歩進むと描けます。良性より先に悪性を拾いきれば左上に張り付いた ROC 曲線になり、AUC が 1.0 に近づきます。

式で書くと何を描いているか #

閾値 \(\theta\) ごとに、混同行列の 4 マスから 2 つの比率を計算します。TPR は \(TP / (TP + FN)\) で感度(Recall)と同じ、FPR は \(FP / (FP + TN)\) で「1 − 特異度」です。\(\theta = 1\) では誰も陽性と判定しないので (FPR, TPR) = (0, 0)、\(\theta = 0\) では全員が陽性なので (1, 1) になり、ROC 曲線はこの 2 点を必ず通ります。AUC はこの曲線を FPR について 0 から 1 まで積分した面積で、roc_auc_score は台形則で計算します。AUC = 0.5 は対角線(ランダム予測)、AUC < 0.5 は予測が逆向きで、確率を \(1 - p\) に反転すれば \(1 - \mathrm{AUC}\) の分類器になります。


何が起きているか(ステップで見る) #

コードを読む前に、この手法が図の上で何をしているかを追ってみましょう。「再生」を押すか、スライダーを動かすと 1 手順ずつ進みます。

見るポイント:閾値を動かすと TP・FP・FN が同時に変わる。どれを重視するかで最適な閾値は変わる。ROC 曲線はこの閾値の動きをすべてつなげた 1 本の線で、AUC はその線の下の面積。

Python で ROC 曲線と AUC を出す #

まずは環境を確認し、必要なライブラリを導入します。

1
2
python --version        # 例: Python 3.13.0
pip install scikit-learn matplotlib

次のコードは乳がん診断データ 569 件を 7:3 に分け、標準化 + ロジスティック回帰で確率を出し、roc_auc_score で AUC を表示したうえで ROC 曲線を描いて保存します。図表は static/images/eval/classification/rocauc に保存され、generate_eval_assets.py から再利用できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
from __future__ import annotations

from pathlib import Path

import matplotlib.pyplot as plt
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import RocCurveDisplay, roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline, make_pipeline
from sklearn.preprocessing import StandardScaler

def build_pipeline(random_state: int = 42) -> Pipeline:
    """ロジスティック回帰で ROC 曲線を計算するためのパイプラインを構築する。"""
    return make_pipeline(
        StandardScaler(),
        LogisticRegression(max_iter=2000, solver="lbfgs", random_state=random_state),
    )

def plot_roc_curve() -> None:
    """ROC 曲線と AUC を計算し、可視化してファイルに保存する。"""
    features, labels = load_breast_cancer(return_X_y=True)
    x_train, x_test, y_train, y_test = train_test_split(
        features,
        labels,
        test_size=0.3,
        stratify=labels,
        random_state=42,
    )

    pipeline = build_pipeline()
    pipeline.fit(x_train, y_train)
    probabilities = pipeline.predict_proba(x_test)[:, 1]

    auc = roc_auc_score(y_test, probabilities)
    print(f"ROC-AUC: {auc:.3f}")

    figure, axis = plt.subplots(figsize=(5, 5))
    RocCurveDisplay.from_predictions(
        y_test,
        probabilities,
        name="Logistic Regression",
        ax=axis,
    )
    axis.plot([0, 1], [0, 1], "--", color="grey", alpha=0.5, label="Random")
    axis.set_xlabel("False Positive Rate")
    axis.set_ylabel("True Positive Rate")
    axis.set_title("ROC Curve (Breast Cancer Dataset)")
    axis.legend(loc="lower right")

    figure.tight_layout()
    output_dir = Path("static/images/eval/classification/rocauc")
    output_dir.mkdir(parents=True, exist_ok=True)
    figure.savefig(output_dir / "roc_curve.png", dpi=150)
    plt.close(figure)

if __name__ == "__main__":
    plot_roc_curve()
ROC 曲線

ROC 曲線の面積(AUC)がモデルの判別性能を表す

実行結果の読み方 #

乳がんデータでは AUC が 0.99 前後と出て、曲線はほぼ左上の角に張り付きます。これは「悪性のほぼ全件に、良性のどれより高い確率を付けられた」ことを意味します。ただし AUC は並びだけを見るので、閾値を 0.5 にしたときの FPR・TPR は曲線上のどこか 1 点にすぎず、「見逃しを 2% 以下」のような要件を満たす閾値は roc_curve が返す thresholds から別に選ぶ必要があります。

閾値と ROC 曲線 #

スライダーで閾値を動かすと、曲線上の点が左下(閾値 1)から右上(閾値 0)へ移動し、TPR と FPR がどの順で増えるかを確認できます。


閾値調整で確認したいこと #

医療や不正検知のように見逃しのコストが大きい場面では、ROC 曲線上で TPR を 0.95 以上に保ちながら許容できる FPR の点を探し、その点の閾値を roc_curve の thresholds から取ります。AUC が 0.9 を超えるモデルは曲線が左上に寄っています。閾値を 0.5 から動かしても TPR と FPR の両方が極端に悪化しにくく、動作点を選ぶ余地が広いです。複数モデルの比較では AUC が高い方が全体の並べる力は高いと期待できますが、差が 0.01 程度なら PR-AUC や業務上の指標(1 日あたりの誤警報件数など)も並べて判断します。閾値を変更すると Precision と Recall のバランスも変わるので、ROC-AUC と PR-AUC を合わせて確認すると意思決定がしやすくなります。

多クラス分類ではAUCの計算方法に注意が必要です。scikit-learnの roc_auc_score はデフォルトでOvR(One-vs-Rest)の平均AUCを返しますが、クラス間の識別性能に大きな差がある場合は各クラスのAUCを個別に確認してください。


使いどころと落とし穴 #

最初に見るのはデータの偏りで、陽性率が 5% を切るような不均衡では、陰性を大量に正しく当てるだけで FPR が小さくなり、ROC-AUC が 0.9 でも Precision が 0.2 ということが起きます。次に class_weight="balanced" や sample_weight を試し、AUC が改善するか確認します。閾値はチームで決める値なので、ROC 曲線をダッシュボードに置いて FPR と TPR の組を見ながら合意を取り、最後にノートブックで random_state を固定して、モデル更新のたびに同じ手順で AUC を比べられるようにしておきます。

クラス不均衡が強い場合(例: 陽性率5%未満)はROC-AUCに加えてPR-AUC(Precision-Recall曲線の面積)も確認してください。ROC-AUCは不均衡データで見かけ上良いスコアが出やすいですが、PR-AUCは少数クラスへの性能を直接反映します。


まとめ #

ROC 曲線は閾値を 1 から 0 へ動かしたときの (FPR, TPR) の軌跡で、AUC はその面積、つまり「陽性 1 件と陰性 1 件を比べて陽性に高い確率を付けている割合」です。乳がんデータでは roc_auc_score が 0.99 前後を返し、曲線は左上に張り付きます。AUC は閾値を使わないので Accuracy で差が出ないモデルも比べられますが、運用に使う閾値は roc_curve の thresholds から FPR・TPR の要件に合わせて別に決めます。陽性率が 5% を切る不均衡では ROC-AUC が楽観的になるので、平均適合率 と 適合率・再現率 を並べて判断します。