血液検査で「陽性」と告げられたとき、本当に病気である確率はどれくらいでしょうか。スパムフォルダに振り分けられたメールのうち、本当に迷惑メールなのは何割でしょうか。クレジットカードの不正利用検知システムが「正常」と判断した取引のなかに、実は不正取引はどれくらい紛れ込んでいるでしょうか。
これらの問いに答えるには、「正しく当てた」「外した」を一括りに語るだけでは足りません。外し方には二種類あるからです。本当は陽性なのに見逃してしまう外し方と、本当は陰性なのに陽性と誤って警報を鳴らす外し方。この二つは、現場ではまったく違うコストを生みます。癌の見逃し(False Negative)は患者の生死に関わる一方、健常者への誤陽性(False Positive)は不要な精密検査を招くだけ。スパムフィルタなら逆で、正常なメールをスパム扱いする偽陽性こそが致命的です。
機械学習で分類器を作るとき、この「外し方の二種類」を正確に切り分けて評価する道具が 混同行列(confusion matrix) であり、そこから派生する TP / TN / FP / FN の4つの数です。さらに、これら4つから Accuracy(正解率)、Precision(適合率)、Recall(再現率)、F1スコア、そして閾値を動かしたときの軌跡を描く ROC曲線とAUC、不均衡データに強い PR曲線 といった現代の評価指標が組み上がっていきます。
この記事の射程は広いので、応用先を二つ具体的に挙げておきましょう。一つは 医療スクリーニング:罹患率1%の疾患を検出する場面で「全員を陰性と予測」しても Accuracy は 99% に達してしまう罠と、Recall・PR曲線による正しい評価。もう一つは 不正検知・異常検知:陽性が極端に少ない不均衡データで、なぜ ROC AUC が楽観的になりがちで、PR-AUC のほうが現実を映すか。これらは Kaggle のコンペティションから医療機器の認証、SOC(セキュリティ運用センター)のアラート設計まで、現代の分類問題の評価そのものです。
本記事の内容
- 陽性/陰性、True/False の直感的意味と、混同行列の見方
- TP / TN / FP / FN の正確な定義と、医療検査・スパム判定の具体例
- Accuracy / Precision / Recall / F1 の導出と、どれをいつ使うべきか
- Accuracy が嘘をつく不均衡データの罠(99% accuracy なのに役立たない例)
- ROC曲線・AUC の幾何学と確率解釈
- Precision-Recall 曲線と Average Precision(不均衡データに強い)
- 閾値最適化:F1最大、Recall制約付き Precision最大、Youden’s J
- 多クラスへの拡張:one-vs-rest と macro / micro / weighted 平均
sklearn.metricsによる Python 実装(合成データでの可視化)
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
なぜ「正解率だけでは足りない」のか
機械学習の入門書では、まず Accuracy(正解率)が登場します。テストデータ $N$ 件のうち、モデルが正しく予測できた割合。シンプルで分かりやすい指標で、確かに第一感としては使いやすい。しかし現実の問題では、Accuracy だけを追いかけると、しばしばとんでもないモデルを「優秀」と評価してしまいます。
例として、ある工場の製品の不良品検出を考えます。不良品率は1%。あなたは機械学習モデルを訓練し、テストデータ10,000件で評価したところ Accuracy = 99.0% という素晴らしい数値が出ました。一見、文句のつけようがない結果です。ところがフタを開けてみると、そのモデルは入力に関係なく常に「正常」と予測しているだけでした。正常品が9,900件、不良品が100件。「全部正常」と答えれば9,900件が当たり、Accuracy は確かに 99.0% に達します。しかし不良品は1件も検出できていないので、検査ライン上ではまったく役に立ちません。
このような事例は珍しいものではなく、不均衡データを扱うあらゆる分野で起きます。クレジットカード不正利用は全取引の1%未満。重篤な希少疾患の有病率は0.1%以下。地震予測における「地震発生」イベントの密度は時間軸上で極端に低い。これらの場面では、Accuracy 一辺倒の評価は問題の構造を見落とすのです。
そこで必要になるのが、「正解/不正解」をさらに どの方向にどう外したか で分解した分析です。陽性のものを陰性と予測したのか、陰性のものを陽性と予測したのか。この区別を体系化したのが、これから見ていく混同行列です。

ポジティブとネガティブ — まず用語を揃える
混同行列に踏み込む前に、用語を整理します。分類問題では、興味のあるクラスを ポジティブ(Positive)、それ以外を ネガティブ(Negative) と呼ぶのが慣例です。直訳すると「陽性」と「陰性」になるので、医療検査の場面で使われるのと同じ語感です。
ここで初心者がよく戸惑うのは、「ポジティブ=良いこと」というイメージとの食い違いです。例えば異常検知では、異常がポジティブクラス、正常がネガティブクラスになります。「異常」は嬉しい出来事ではないのに、なぜポジティブなのでしょうか。
答えは「ポジティブ/ネガティブの値判断とは無関係で、純粋に注目しているクラスかどうかを表すラベルにすぎない」からです。スパム検出ならスパムがポジティブ、癌診断なら罹患がポジティブ、不正検知なら不正がポジティブ。検出したい・検出すべき対象がポジティブ、というだけのことです。これは相対的な定義であって、問題設定によって動的に決まります。ある研究では「故障」がポジティブで、別の研究では同じ事象が「健全からの逸脱」と表現されてネガティブとして扱われる、ということも起こりえます。
この相対性はしばしば混乱の元になるので、新しい論文を読むときは「この研究ではどちらをポジティブと呼んでいるか」を最初に確認する癖をつけてください。多くの場合、データ件数が少ないクラス(少数派・希少事象)がポジティブとして扱われます。
ポジティブ/ネガティブの呼称が決まると、分類器の予測結果は「ポジティブと予測した/ネガティブと予測した」と「実際にポジティブだった/実際にネガティブだった」の組み合わせで、ちょうど4通りに分かれます。これを行列形式に整理したのが、いよいよこれから見る混同行列です。
混同行列とTP / TN / FP / FN
4つの象限の意味
混同行列は、テストデータの各サンプルを (実際のクラス, 予測クラス) のペアでカウントした 2×2 の表です。慣例として、行が実際のクラス、列が予測クラスを表します(書籍によって行と列が入れ替わっていることもあるので注意)。4つのセルがそれぞれ次の意味を持ちます。
| 略号 | 名称 | 意味 |
|---|---|---|
| TP | True Positive(真陽性) | 実際ポジティブ、予測もポジティブ(正解) |
| FN | False Negative(偽陰性) | 実際ポジティブ、予測はネガティブ(見逃し) |
| FP | False Positive(偽陽性) | 実際ネガティブ、予測はポジティブ(誤警報) |
| TN | True Negative(真陰性) | 実際ネガティブ、予測もネガティブ(正解) |
略号の覚え方は単純です。1文字目の T/F は「予測が当たったかどうか」(True = 当たった、False = 外した)、2文字目の P/N は「モデルが予測したクラス」(Positive を予測した、Negative を予測した)。例えば FP は「F = 予測が外れた、P = ポジティブと予測した」なので、「ポジティブと予測したが外れた = 本当はネガティブ」だと読み解けます。
医療検査の具体例
混同行列の数値感覚をつかむために、最も有名な例である医療スクリーニング検査を見ます。人口10万人を対象に、罹患率0.1%(実患者100人)、感度99%、特異度95%の検査を行う設定を考えます。感度は「実患者を陽性と判定する率」(Recall に相当)、特異度は「健常者を陰性と判定する率」です。
実患者100人のうち、感度99%なので99人が正しく陽性(TP=99)、1人を見逃します(FN=1)。健常者99,900人のうち、特異度95%なので94,905人が正しく陰性(TN=94,905)と判定されますが、5%にあたる4,995人が誤って陽性(FP=4,995)になります。混同行列は以下の通りです。
| 予測:陽性 | 予測:陰性 | 合計 | |
|---|---|---|---|
| 実:陽性 | TP = 99 | FN = 1 | 100 |
| 実:陰性 | FP = 4,995 | TN = 94,905 | 99,900 |
| 合計 | 5,094 | 94,906 | 100,000 |
ここから読み取れる事実は鮮烈です。検査で陽性と出た人は 5,094 人いますが、そのうち本当の患者は 99 人しかいません。陽性反応が出ても本当に病気である確率は 99 / 5,094 ≈ 1.9% に過ぎないのです。感度99%という素晴らしいスペックを持つ検査でも、罹患率が低いと「陽性 = 病気」とは到底言えない。この事実は 基準率の誤謬(base rate fallacy) として有名で、ベイズの定理の最も直感的な応用例でもあります。
この具体例は、なぜ単なる Accuracy だけでなく、陽性予測の精度(Precision) や 見逃し率(FN の少なさ) といった多面的な指標が必要なのかを明確に物語ります。次は、これらの指標を一つずつ定義式から見ていきましょう。
Accuracy / Precision / Recall / F1 — 4つの基本指標
ここからは混同行列の4つの数 TP, TN, FP, FN を組み合わせて、機械学習論文で必ず登場する評価指標を導出していきます。文献上の慣習として、TP などの記号は単に「TPに分類されたサンプル数」を意味する整数だと解釈してください($|TP|$ や $\#TP$ と書く流儀もあります)。
Accuracy(正解率)
最も素朴な指標がこれです。全サンプルのうち、モデルが正しく予測できた割合。
$$ \mathrm{Accuracy} = \frac{TP + TN}{TP + TN + FP + FN} $$
分子は「予測が当たった件数」(TP と TN の和)、分母は「全サンプル数」です。クラスが均衡している場合(ポジティブとネガティブが半々程度)には、これだけで十分にモデルの良し悪しが分かります。
一方で、すでに見たように、クラスが不均衡な場合には Accuracy は強くミスリードします。多数派クラスを答えるだけで高い Accuracy が出てしまうため、実用的な性能とはほぼ無関係になることが珍しくありません。Accuracy はクラスバランスを見てから採用すべき指標です。
Precision(適合率)
Precision は「モデルが陽性と予測したものの中で、実際に陽性だった割合」を表します。陽性予測の精度・信頼度です。
$$ \mathrm{Precision} = \frac{TP}{TP + FP} $$
分母は「陽性と予測した件数の合計」($TP + FP$)です。すべての陽性予測のうち、正解は TP、誤りは FP なので、TP / (TP+FP) で「陽性予測の的中率」を表しています。
Precision が重要になるのは、偽陽性(FP)のコストが高い場面です。例えば、迷惑メール(スパム)フィルタを考えてみてください。重要なビジネスメールを「スパム」と誤判定すること(FP)は、機会損失や信頼失墜を招きます。スパム判定を出すからには本当にスパムであってほしい。だから Precision を重視する設計になります。
別の例として、医療における「がん確定診断」の場面では、陽性と判定すると患者に大きな精神的負荷と侵襲的な追加検査がかかります。だから「陽性と言ったからには本物であってほしい」、つまり Precision を高く保ちたい。確信を持って陽性と言えることが Precision の意味です。
Recall(再現率/感度)
Recall は Precision と対をなす指標で、「実際に陽性であるものの中で、モデルが陽性と捉えられた割合」を表します。医学では 感度(sensitivity)、信号処理では 検出率 と呼ばれることもあります。
$$ \mathrm{Recall} = \frac{TP}{TP + FN} $$
分母は「実際の陽性件数の合計」($TP + FN$)です。実際の陽性のうち、見つけられたのは TP、見逃したのは FN なので、TP / (TP+FN) で「陽性事象の検出率」を表します。
Recall が重要になるのは、偽陰性(FN)のコストが高い場面です。癌のスクリーニング検査がその典型で、患者を見逃すこと(FN)は最悪の結果を招きます。多少の偽陽性は精密検査で除外できるが、見逃しは取り返しがつかない。だから Recall を最優先に設計します。
異常検知や障害検知も同様です。サーバの異常、製造ラインの不良、地震・津波の警報——これらは「鳴り過ぎ」よりも「鳴らさず見逃す」ほうがはるかに高くつきます。取りこぼさないことが Recall の意味です。

Precision と Recall のトレードオフ
Precision と Recall は、しばしば互いに引っ張り合う関係にあります。閾値を厳しくして「よほど怪しいときだけ陽性と判定」する設定にすれば、陽性予測の精度(Precision)は上がりますが、見逃し(FN)が増えて Recall は下がる。逆に閾値を緩めて「少しでも怪しければ陽性」とすれば、Recall は上がるが偽陽性(FP)が増えて Precision は下がる。
これは数学的にも分母を見れば明らかです。Precision の分母は $TP + FP$(陽性と予測した数)、Recall の分母は $TP + FN$(実際の陽性数)。閾値を緩めて陽性予測を増やすと、$TP$ も増えるが $FP$ も増える(Precision の分母が肥大)。一方で、増えた予測のなかから取りこぼしが減るので $FN$ が減って Recall は上がる。両方を同時に最大化するのは原理的に難しいのです。
このトレードオフを単一の数値にまとめる試みが、次の F1 スコアです。
F1 スコア — Precision と Recall の調和平均
F1 は Precision と Recall の 調和平均(harmonic mean) で定義されます。
$$ \mathrm{F1} = \frac{2}{\frac{1}{\mathrm{Precision}} + \frac{1}{\mathrm{Recall}}} = \frac{2 \cdot \mathrm{Precision} \cdot \mathrm{Recall}}{\mathrm{Precision} + \mathrm{Recall}} $$
二つ目の等号は調和平均の定義を整理しただけです。なぜわざわざ調和平均なのでしょうか。算術平均 $(P + R)/2$ ではいけないのでしょうか。
理由は 「片方が極端に低いとき、調和平均は厳しくその影響を反映する」 からです。例として $P = 0.9$(高い精度)、$R = 0.1$(見逃し多数)を考えます。算術平均は $0.5$ になり「まあまあ」という印象を与えますが、調和平均は
$$ \mathrm{F1} = \frac{2 \cdot 0.9 \cdot 0.1}{0.9 + 0.1} = \frac{0.18}{1.0} = 0.18 $$
となり、低い側の値に強く引きずられます。これが調和平均の性質で、Precision と Recall の両方が高くないと F1 は高くならないという厳しさが、不均衡データの評価で重宝されるのです。
逆にいえば、F1 は片方だけが高い「いびつな」分類器を排除する道具です。Precision = 1.0 だが Recall = 0.05 のような「ごく少数の確信できるものだけを陽性とした分類器」は、業務によっては有用ですが、F1 では低く評価されます。だから F1 は バランスの取れた分類器を選ぶ指標 だと理解するとよいでしょう。

F1 を一般化した F-beta スコア もよく使われます。
$$ F_\beta = (1 + \beta^2) \cdot \frac{\mathrm{Precision} \cdot \mathrm{Recall}}{\beta^2 \cdot \mathrm{Precision} + \mathrm{Recall}} $$
ここで $\beta > 1$ なら Recall を重視(見逃しを許さない)、$\beta < 1$ なら Precision を重視(誤警報を許さない)。$\beta = 2$ の $F_2$ は癌スクリーニングのような Recall 重視の応用で、$\beta = 0.5$ の $F_{0.5}$ はスパム検出のような Precision 重視の応用で使われることがあります。$\beta = 1$ の F1 は、Precision と Recall を対等に扱う中立的な指標です。
ここまでで4つの基本指標が揃いました。次は、これらの指標が実際にどれくらい食い違うのかを、Accuracy が嘘をつく代表的な事例で見てみましょう。
Accuracy の罠 — 不均衡データの危険性
冒頭でも触れた「全部陰性」モデルの例を、改めて指標の数値で確認します。テストデータ10,000件、内訳は陽性100件・陰性9,900件。分類器が常に「陰性」を返すとき、混同行列は
| 予測:陽性 | 予測:陰性 | |
|---|---|---|
| 実:陽性 | TP = 0 | FN = 100 |
| 実:陰性 | FP = 0 | TN = 9,900 |
となります。それぞれの指標を計算してみましょう。
- Accuracy = $(0 + 9900) / 10000 = 99.0\%$
- Precision = $0 / (0 + 0) = $ 未定義(陽性予測がない)
- Recall = $0 / (0 + 100) = 0\%$
- F1 = $0$(Precision または Recall が 0 なので調和平均も 0)
Accuracy だけが 99% という素晴らしい値を示し、他の指標は 完全に役立たないモデル であることを正しく告発しています。Recall = 0 は「実際の陽性を一つも捕まえられていない」ことを意味し、F1 = 0 は「バランスが完全に崩れている」ことを意味します。
この罠は、データセットの陽性比率が極端に低い場面(不正検知、希少疾患、設備故障、天文学的イベントなど)で頻発します。教科書的な機械学習コンペで Accuracy が指標として採用されることはまれで、不均衡データを扱うコンペでは AUC や F1、PR-AUC などが好まれるのはこのためです。

不均衡データの罠を避ける指針として、次のチェックリストを覚えておきましょう。
- クラス比率を確認する:陽性比率が10%を切るなら Accuracy は信用しない。
- Recall と Precision を両方見る:片方だけ高いモデルは要注意。
- F1 または F-beta を採用する:単一指標でランキングしたいときは。
- PR曲線・AUC を確認する:閾値非依存の総合評価には。
- クラスごとに評価する:多クラス分類なら、少数クラスの Recall を必ずチェック。
ここまでは「ある一つの閾値での評価」を見てきました。しかし多くの分類器は確率(スコア)を出力し、閾値で陽性/陰性を切り分けます。閾値を動かすと指標も動く——だったら、閾値を変えながら評価を眺める方法はないのか?という発想から、次の ROC 曲線と AUC が生まれます。
ROC曲線とAUC — 閾値非依存の総合評価
スコア出力と閾値
多くの分類器は「確率」や「スコア」を出力します。ロジスティック回帰なら $P(y=1 \mid \boldsymbol{x})$ という確率値、ニューラルネットなら最後のシグモイドの値、SVM なら符号付き距離。これらの連続値スコアを、ある 閾値 $t$ で切って 陽性/陰性を判定します。
$$ \hat{y}(\boldsymbol{x}) = \begin{cases} \text{Positive} & \text{if } s(\boldsymbol{x}) \geq t \\ \text{Negative} & \text{otherwise} \end{cases} $$
閾値 $t$ をどこに置くかで、TP / FP / TN / FN の数が変わり、結果として Accuracy・Precision・Recall も変わります。$t$ を下げれば陽性判定が増えて Recall は上がるが Precision は下がる、$t$ を上げれば逆。閾値の選択そのものが評価軸になっているのです。
TPR と FPR
ROC(Receiver Operating Characteristic)曲線は、閾値を変えながら描いた軌跡です。横軸に 偽陽性率(FPR)、縦軸に 真陽性率(TPR) を取ります。それぞれの定義は
$$ \mathrm{TPR} = \frac{TP}{TP + FN} = \mathrm{Recall} = \text{感度} $$
$$ \mathrm{FPR} = \frac{FP}{FP + TN} = 1 – \text{特異度} $$
です。TPR は Recall そのもので「実陽性のうち何割を捕まえたか」、FPR は「実陰性のうち何割を誤って陽性とみなしたか」です。両者は 分母にネガティブ側が混ざらない ことが Precision との大きな違いで、結果として ROC は クラスバランスに対して相対的に頑健 な性質を持ちます。
ROC曲線の形と読み方
閾値 $t$ を $+\infty$ から $-\infty$ へ動かしていくと、まず最初は「誰も陽性と判定しない」状態なので TP=0, FP=0、すなわち (FPR, TPR) = (0, 0)。閾値を下げると次第に陽性判定が増え、最後は「全員陽性」なので TP=陽性総数, FP=陰性総数、すなわち (FPR, TPR) = (1, 1) に至ります。この (0,0) から (1,1) への軌跡が ROC 曲線です。
理想的な分類器は、左上隅 (0, 1) を通る曲線、すなわち「FPR ゼロのまま TPR を1にできる」軌跡を描きます。逆にランダムな分類器(スコアが完全にランダム)は対角線 $\mathrm{TPR} = \mathrm{FPR}$ に沿って進みます。実用的な分類器は、その中間で対角線より上を通る曲線になります。曲線が左上に膨らんでいるほど良い、というのが ROC を読む基本です。

AUC(Area Under the Curve)
ROC 曲線は形が問題依存で読み解きにくいので、曲線の下の面積 AUC を単一の指標として使うのが一般的です。
$$ \mathrm{AUC} = \int_0^1 \mathrm{TPR}(\mathrm{FPR}) \, d\mathrm{FPR} $$
AUC は 0 〜 1 の値を取り、
- AUC = 1.0 → 完璧な分類器(FPR=0 で TPR=1 を実現)
- AUC = 0.5 → ランダム(対角線)
- AUC < 0.5 → 逆向きに予測している(符号を反転すれば良くなる)
となります。一般に AUC は 0.7〜0.9 の範囲が「実用的に良い」、0.9 を超えれば「素晴らしい」と言われます。
AUC の確率的解釈
AUC には美しい確率的解釈があります。陽性サンプルからランダムに1つ、陰性サンプルからランダムに1つを引いてきたとき、陽性サンプルのスコアが陰性サンプルより高い確率 が AUC と一致します。
$$ \mathrm{AUC} = \Pr\big(s(X_+) > s(X_-)\big) $$
ここで $X_+$ は陽性、$X_-$ は陰性からの無作為標本。直感的には、AUC は「分類器が陽性と陰性の 順序付け をどれだけ正しくできるか」を測っています。閾値の絶対値ではなく、スコアの 相対的な大小関係 だけが効くため、ROC・AUC は閾値非依存(threshold-free)な指標と呼ばれます。
この性質から、AUC は スコアの順序を変えない単調変換に対して不変 です。例えば確率 $p$ を $\log(p / (1-p))$ にロジット変換しても、$\sigma(z)$ にシグモイド変換しても、AUC は変わりません。順序さえ保たれれば値です。
AUC を2クラスの分布の重なりとして見る
AUC が高い/低いは、陽性スコアと陰性スコアの 分布の重なり具合 とほぼ同義です。両クラスの分布が完全に分離していれば AUC = 1、完全に重なっていれば AUC = 0.5。実用的な分類器は、この間にあって、重なり領域に位置するサンプルが「判定の難しいケース」になります。

ROC・AUC は便利な指標ですが、万能ではありません。特に 極端な不均衡データ では問題が生じます。陰性が圧倒的に多いとき、FPR の分母 $FP + TN$ は巨大になり、FP がそこそこ増えても FPR は微増にしか見えない。結果として ROC 曲線は楽観的に左上に張り付き、AUC が高く見えるのに 実際の陽性予測の精度(Precision)はひどく低い ことが起こります。この問題に正面から答えるのが、次の PR 曲線です。
Precision-Recall曲線(不均衡データに強い指標)
PR 曲線は、横軸に Recall、縦軸に Precision を取り、閾値を動かして描いた軌跡です。ROC が「陽性予測の取りこぼし(Recall)と誤警報(FPR)」を見るのに対し、PR は「陽性予測の取りこぼし(Recall)と的中率(Precision)」を見ます。
ROC との決定的な違いは、Precision の分母が陽性予測の総数 $TP + FP$ であり、陰性総数を直接含まないこと。これにより PR 曲線は 陰性が大量にあっても希釈されず、Precision の悪化を素直に映します。
PR曲線の読み方とAP(Average Precision)
理想的な分類器は右上隅 (Recall=1, Precision=1) を通ります。ランダム分類器のベースラインは 陽性比率に等しい水平線 Precision = $\pi$($\pi$ は陽性比率)になります。この点が ROC と決定的に違うところで、ROC の対角線は陽性比率に依存しませんが、PR のベースラインは陽性比率そのものです。
PR 曲線の下の面積を AP(Average Precision) または PR-AUC と呼びます。
$$ \mathrm{AP} = \int_0^1 \mathrm{Precision}(\mathrm{Recall}) \, d\mathrm{Recall} $$
これも 0〜1 の値で、不均衡データでは ROC AUC より厳しい(小さい)値が出ます。同じ分類器で、ROC AUC = 0.95、PR-AUC = 0.30 ということもよくあり、特に陽性が非常に少ない検出問題では PR-AUC を主要指標にする慣習が強くなっています。

どちらを使うべきか
経験則として、
- クラスバランスがほぼ均衡(陽性比率 30〜70%)→ ROC AUC でよい
- 不均衡(陽性比率 5% 以下) → PR-AUC が望ましい
- 両方を併用するのが最も安全
PR 曲線は陰性側の量を考慮しないので、「ネガティブを正しく除外する性能」を見たい場合には不向きです。一方、陽性検出が目的なら PR 曲線のほうが現場感覚に合います。
ROC・PR 両曲線は閾値全域を眺める指標ですが、実用上は どこかで閾値を固定 して運用する必要があります。次はその閾値選択の話題です。
閾値最適化 — どこで切るか
閾値ごとの指標の動き
スコアと閾値があれば、閾値を動かすたびに TP / FP / TN / FN の数が変わり、Accuracy・Precision・Recall・F1 すべてが変動します。一般的な傾向としては:
- 閾値を下げる → Recall ↑、Precision ↓、FP ↑
- 閾値を上げる → Precision ↑、Recall ↓、FN ↑
これらを同時にプロットすると、各指標が閾値とともにどう変動するかが一目で分かります。

F1 を最大化する閾値
業務制約が明確でない場合、まず F1 を最大化する閾値 を選ぶのが一つの標準です。検証データで閾値を細かく動かして F1 を計算し、最大値を取る点を採用します。これは Precision と Recall のバランスを自動的に取ってくれるシンプルな手法で、不均衡度が中程度の場面で広く使われます。
Youden’s J(ROC上の最適点)
ROC 曲線上で「左上の角から最も近い点」を選ぶ古典的な手法が Youden’s J です。
$$ J = \mathrm{TPR} – \mathrm{FPR} = \text{感度} + \text{特異度} – 1 $$
$J$ が最大の点を選ぶと、感度と特異度の和が最大になります。医療検査の設計で伝統的に使われてきた基準です。
業務制約付き最適化
業務によっては「Precision ≥ 0.9 を保証しつつ Recall を最大化」のような制約付き最適化が必要になります。これは PR 曲線の上で Precision = 0.9 のラインと交差する点を選び、その閾値を採用するというシンプルな操作で実現できます。逆に「Recall ≥ 0.95 を保証しつつ Precision を最大化」も同様です。
実務では、しばしば 偽陽性のコスト と 偽陰性のコスト が金額や時間で見積もれます。例えば「FP 1件あたり10万円の追加調査コスト、FN 1件あたり100万円の機会損失」と分かれば、
$$ \text{総コスト}(t) = c_{FP} \cdot FP(t) + c_{FN} \cdot FN(t) $$
を最小化する閾値 $t$ を選びます。この コスト感応的閾値選択 こそが、ビジネスにおける分類器運用の核です。
ここまでは2クラス分類に話を絞ってきました。実用では3クラス以上の多クラス問題も多く、混同行列も拡張する必要があります。次は多クラスへの拡張を見ていきます。
多クラス分類への拡張
多クラス混同行列
$K$ クラスの分類問題では、混同行列は $K \times K$ になります。行 $i$ 列 $j$ の要素は「実際にクラス $i$ で予測がクラス $j$」のサンプル数。対角成分が正解、対角外が誤分類です。
MNIST(手書き数字10クラス)のような例では、混同行列を眺めるだけで「どの数字とどの数字が混同されやすいか」が読み取れます。例えば「4と9」「3と5」「7と1」は字形が似ているため、対角外の値が大きくなる傾向があります。

one-vs-rest による2クラス指標の拡張
Precision、Recall、F1 は本来 2クラスの指標です。多クラスへ拡張するには、各クラスを「そのクラス vs その他」の2クラス問題と見立てて指標を計算します(one-vs-rest 方式)。クラス $c$ に対する Precision・Recall・F1 は
$$ \mathrm{Precision}_c = \frac{TP_c}{TP_c + FP_c}, \quad \mathrm{Recall}_c = \frac{TP_c}{TP_c + FN_c}, \quad \mathrm{F1}_c = \frac{2 P_c R_c}{P_c + R_c} $$
となります。これでクラスごとに $K$ 個の指標が得られます。
macro / micro / weighted 平均
$K$ 個のクラス別指標を一つの数にまとめる方法は3通りあります。
macro 平均: クラスごとの指標を単純平均します。
$$ \mathrm{F1}_\text{macro} = \frac{1}{K} \sum_{c=1}^{K} \mathrm{F1}_c $$
すべてのクラスを 対等に扱う ため、少数クラスの性能が直接効きます。少数クラスの検出性能を重視したい場合に使います。
weighted 平均: クラスごとの指標を クラスサイズで加重平均 します。
$$ \mathrm{F1}_\text{weighted} = \sum_{c=1}^{K} \frac{n_c}{N} \cdot \mathrm{F1}_c $$
ここで $n_c$ はクラス $c$ の実サンプル数、$N$ は全サンプル数。多数クラスが結果を支配します。「平均的なサンプルに対する性能」を見たいときに使います。
micro 平均: クラス別の集計を取らず、全クラスの TP / FP / FN をまず合算してから Precision・Recall・F1 を計算します。
$$ \mathrm{Precision}_\text{micro} = \frac{\sum_c TP_c}{\sum_c (TP_c + FP_c)}, \quad \mathrm{Recall}_\text{micro} = \frac{\sum_c TP_c}{\sum_c (TP_c + FN_c)} $$
multi-class 分類では、micro 平均は実は Accuracy と一致します(全予測のうち正解した割合)。サンプルを平等に扱う指標です。

どれを使うか
実務での使い分けは以下が目安です。
- 少数クラスを重視したい → macro
- 全サンプルに対する平均的な精度を見たい → micro(= Accuracy)
- 多数クラス寄りの評価で十分 → weighted
論文では macro F1 が「クラス間の公平性を見る指標」として、micro F1 が「全体性能の指標」として併記されることが多いので、両方を報告する習慣をつけるとよいでしょう。
理論を一通り押さえたところで、ここから Python による実装に入ります。scikit-learn の sklearn.metrics モジュールを使えば、これらの指標はすべて1行で計算できます。次のセクションで合成データを使った具体例を見ていきましょう。
Pythonによる実装
混同行列と基本指標
まず sklearn.metrics から confusion_matrix と各指標を呼び出し、シンプルな2クラス分類で確認します。
import numpy as np
from sklearn.metrics import (
confusion_matrix, accuracy_score,
precision_score, recall_score, f1_score, classification_report,
)
# 正解ラベル(10件中、陽性4件・陰性6件)
y_true = np.array([1, 0, 1, 1, 0, 0, 1, 0, 0, 0])
# モデル予測(陽性3件と予測:1件は外している)
y_pred = np.array([1, 0, 0, 1, 1, 0, 1, 0, 0, 0])
cm = confusion_matrix(y_true, y_pred)
print("混同行列:\n", cm)
# [[5 1] 行: 実際=0(陰性), 列: 予測=0,1
# [1 3]] 行: 実際=1(陽性)
# 個別指標
print(f"Accuracy = {accuracy_score(y_true, y_pred):.3f}")
print(f"Precision = {precision_score(y_true, y_pred):.3f}")
print(f"Recall = {recall_score(y_true, y_pred):.3f}")
print(f"F1 = {f1_score(y_true, y_pred):.3f}")
print("\n--- classification_report ---")
print(classification_report(y_true, y_pred, target_names=["陰性", "陽性"]))
confusion_matrix の戻り値は2次元配列で、行が実際のクラス、列が予測クラス と覚えてください(書籍によっては逆の場合があるので注意)。上の例なら左上 5 = TN、右上 1 = FP、左下 1 = FN、右下 3 = TP です。classification_report を使うと、クラスごとの Precision・Recall・F1 と support(サンプル数)、そして macro / weighted 平均がまとめて表示され、業務報告にそのまま使えます。
不均衡データで Accuracy の罠を再現
次に、罹患率1%の合成データを作り、「全員陰性」モデルが Accuracy 99% に到達するのに F1 = 0 となる実例を見ます。
import numpy as np
from sklearn.metrics import confusion_matrix, classification_report
rng = np.random.default_rng(seed=0)
N = 10000
prevalence = 0.01
y_true = (rng.uniform(size=N) < prevalence).astype(int) # 陽性が1%
# 全員陰性を予測するモデル
y_pred_const = np.zeros_like(y_true)
print("混同行列:")
print(confusion_matrix(y_true, y_pred_const))
print()
print(classification_report(y_true, y_pred_const, zero_division=0,
target_names=["陰性", "陽性"]))
実行すると Accuracy が約99%と表示される一方、陽性クラスの Precision・Recall・F1 はすべて 0、support(実陽性件数)は約100となります。zero_division=0 を指定するのは、Precision の分母が 0 になる(陽性予測が一件も無い)ときに警告ではなく 0 を返すためです。Accuracy だけを見ていたら絶対に気づけない壊滅的なモデルであることが、classification_report ではっきり可視化されます。
ROC曲線とAUCの可視化
ロジスティック回帰で2クラス分類モデルを訓練し、ROC曲線・AUCを描きます。sklearn.datasets.make_classification で合成データを作成します。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve, auc
X, y = make_classification(
n_samples=2000, n_features=20, n_informative=8,
weights=[0.5, 0.5], flip_y=0.05, random_state=0,
)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=0)
clf = LogisticRegression(max_iter=1000).fit(X_tr, y_tr)
y_score = clf.predict_proba(X_te)[:, 1] # 陽性クラスの確率
fpr, tpr, thresholds = roc_curve(y_te, y_score)
roc_auc = auc(fpr, tpr)
print(f"AUC = {roc_auc:.3f}")
plt.figure(figsize=(7, 6))
plt.plot(fpr, tpr, lw=2.4, label=f"ロジスティック回帰 (AUC={roc_auc:.3f})")
plt.plot([0, 1], [0, 1], "k--", lw=1.2, label="チャンスライン")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC曲線")
plt.legend(loc="lower right")
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
roc_curve は (FPR, TPR, thresholds) のタプルを返し、auc 関数が台形法で面積を積分してくれます。predict_proba の [:, 1] で陽性クラスの確率を取り出すのがポイントで、predict(0/1)ではROC曲線が引けません。実行すると AUC が約 0.93 前後の値となり、曲線が左上に膨らんで対角線から大きく離れた、実用的な分類器の振る舞いを確認できます。
PR曲線と Average Precision
陽性比率を 5% に下げた不均衡データで、ROC と PR の振る舞いを比較します。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import (
roc_curve, auc, precision_recall_curve, average_precision_score,
)
X, y = make_classification(
n_samples=5000, n_features=20, n_informative=6,
weights=[0.95, 0.05], flip_y=0.02, random_state=1, # 陽性5%
)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=0)
clf = LogisticRegression(max_iter=1000, class_weight="balanced").fit(X_tr, y_tr)
y_score = clf.predict_proba(X_te)[:, 1]
# ROC
fpr, tpr, _ = roc_curve(y_te, y_score)
roc_auc = auc(fpr, tpr)
# PR
prec, rec, _ = precision_recall_curve(y_te, y_score)
ap = average_precision_score(y_te, y_score)
baseline = y_te.mean()
fig, axes = plt.subplots(1, 2, figsize=(13, 5.5))
axes[0].plot(fpr, tpr, lw=2.4, color="#1f77b4", label=f"ROC AUC={roc_auc:.3f}")
axes[0].plot([0, 1], [0, 1], "k--", lw=1, label="チャンスライン")
axes[0].set(xlabel="FPR", ylabel="TPR", title="ROC(不均衡データでは楽観的)")
axes[0].legend(); axes[0].grid(alpha=0.3)
axes[1].plot(rec, prec, lw=2.4, color="#9467bd", label=f"AP={ap:.3f}")
axes[1].axhline(baseline, color="gray", linestyle="--", lw=1,
label=f"陽性比率ベースライン={baseline:.3f}")
axes[1].set(xlabel="Recall", ylabel="Precision", title="PR曲線(現実を映す)")
axes[1].legend(); axes[1].grid(alpha=0.3)
plt.tight_layout(); plt.show()
print(f"陽性比率: {baseline*100:.2f}%, ROC AUC: {roc_auc:.3f}, AP: {ap:.3f}")
このコードを実行すると、同じモデル・同じデータに対して ROC AUC は 0.95 前後と高く見える一方、AP は 0.5〜0.7 程度まで落ち込みます。これは ROC が陰性側の量(9,500人の健常者)に希釈されて FP が増えても FPR にあまり響かないのに対し、PR は陽性予測の的中率という現場感覚に直結する量を見ているためです。不均衡データの評価では AP / PR-AUC を主要指標にすべき という主張の根拠が、この数値差で実感できます。
閾値最適化(F1最大化)
スコアと正解ラベルから、F1 を最大化する閾値を探します。
import numpy as np
from sklearn.metrics import f1_score
# (前のコードのcontinuation: y_score, y_te が定義済みとする)
thresholds = np.linspace(0.01, 0.99, 200)
f1_list = [f1_score(y_te, (y_score >= t).astype(int)) for t in thresholds]
best_idx = int(np.argmax(f1_list))
best_t = thresholds[best_idx]
best_f1 = f1_list[best_idx]
print(f"F1最大の閾値: t={best_t:.3f}, F1={best_f1:.3f}")
# デフォルト閾値0.5での F1 と比較
default_f1 = f1_score(y_te, (y_score >= 0.5).astype(int))
print(f"デフォルト閾値 0.5 での F1 = {default_f1:.3f}")
不均衡データでは、デフォルトの閾値 0.5 は最適ではありません。多数派が陰性なので、確率0.5以上を陽性とするのは厳しすぎ、最適閾値はもっと低い(0.2〜0.3程度)になることがあります。出力を見ると、最適閾値での F1 がデフォルトより 0.05〜0.15 程度向上しており、閾値を動かすだけで実用性能が大きく改善することが分かります。
多クラス分類の混同行列と平均
最後に、3クラス分類で macro / micro / weighted F1 の挙動を確認します。
import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import (
confusion_matrix, classification_report, f1_score,
)
# 3クラス、ただし不均衡(A:80%, B:15%, C:5%)
X, y = make_classification(
n_samples=3000, n_features=20, n_informative=10,
n_classes=3, n_clusters_per_class=1,
weights=[0.80, 0.15, 0.05], random_state=2,
)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=0)
clf = LogisticRegression(max_iter=2000, multi_class="multinomial").fit(X_tr, y_tr)
y_pred = clf.predict(X_te)
print("=== 混同行列 ===")
print(confusion_matrix(y_te, y_pred))
print("\n=== classification_report ===")
print(classification_report(y_te, y_pred, target_names=["A(多数)", "B(中)", "C(少数)"]))
for avg in ["macro", "micro", "weighted"]:
print(f"F1 ({avg:8s}) = {f1_score(y_te, y_pred, average=avg):.3f}")
実行すると、クラスごとの F1 が出力され、続いて 3 種類の平均が表示されます。一般に、macro < weighted < micro の順に値が大きくなることが多く、これは少数クラスの低い F1 が macro では強く反映され、weighted ではクラスサイズで重みづけされて緩和され、micro では全体の正解率に丸まるためです。「平均 F1 = 0.90」とだけ報告すると、どの平均かで全く違う情報を伝えてしまうので、論文・報告では必ず平均化方法を明記することが重要です。
まとめ
本記事では、分類モデルの評価指標を、混同行列の4つの数 TP / TN / FP / FN から始めて、Accuracy・Precision・Recall・F1、ROC・AUC、PR曲線、閾値最適化、多クラス拡張まで体系的にたどりました。
- 混同行列の4象限: TP / FN / FP / TN。1文字目 T/F は予測が当たったか、2文字目 P/N はモデルが予測したクラス。FP は誤警報、FN は見逃し。
- Accuracy の罠: 不均衡データでは「全員陰性」モデルが Accuracy 99% を達成しうる。Precision・Recall・F1 で多面的に評価すべき。
- Precision vs Recall: 分母が違うだけ。Precision は陽性予測の的中率(FPを嫌う場面)、Recall は陽性事象の検出率(FNを嫌う場面)。両者はトレードオフ。
- F1 = 調和平均: 片方が低いと厳しく罰する。F-beta で重みを調整可能($F_2$ は Recall 重視、$F_{0.5}$ は Precision 重視)。
- ROC曲線とAUC: 閾値全域での (FPR, TPR) の軌跡。AUC は順序付け能力の確率的解釈を持つ。不均衡データでは楽観的になる。
- PR曲線とAP: 不均衡データの真の難しさを映す。陽性比率がベースライン。
- 閾値最適化: F1最大、Youden’s J、業務制約付き、コスト感応的選択など。
- 多クラス拡張: one-vs-rest で各クラスの指標を計算し、macro / micro / weighted で平均化。少数クラス重視なら macro、平均性能なら micro。
Python 実装では sklearn.metrics の confusion_matrix、classification_report、roc_curve、precision_recall_curve を組み合わせるだけで、ここで議論した全ての指標を実用的に計算できます。重要なのは 問題の構造に合った指標を選ぶ ことで、特に不均衡データでは Accuracy・ROC AUC に頼らず PR-AUC・F1・クラス別 Recall まで踏み込む癖をつけることです。
次のステップとして、以下の記事も参考にしてください。