- 技術 💻
Precision(適合率)・再現率・正解率・F1の違い|陽性1%で実測
結論
正解率(accuracy)は「全体のうち当たった割合」、適合率(precision)は「陽性と予測したうち本当に陽性だった割合」、再現率(recall)は「本当の陽性のうち拾えた割合」、F1スコアは適合率と再現率の調和平均です。陽性が少ないデータでは、正解率が高くても何も見つけていないことがあります。
この記事でわかること
- 陽性1%で正解率99.0%・検出0件になった混同行列の実物
- しきい値0.9〜0.02で適合率と再現率が入れ替わる実測表
- macro・micro・weighted平均とPR曲線・ROC AUCの違い
4つの指標は、どれも「混同行列」という4つの数(TP・FP・FN・TN)から計算します。どの数を分母にするかが違うだけです。
flowchart LR
C{実際は陽性} -->|予測も陽性| TP[TP 当たり]
C -->|予測は陰性| FN[FN 見逃し]
D{実際は陰性} -->|予測は陽性| FP[FP 誤報]
D -->|予測も陰性| TN[TN 正しく除外]
よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| 適合率と再現率の違い | 分母が違う。適合率は「予測した陽性」、再現率は「本当の陽性」で割る | 適合率・再現率の節 |
| accuracy(正解率)とは | 全件のうち予測が当たった割合。陽性が少ないと高く出すぎる | 正解率の節 |
| precision とは | 適合率。誤報(FP)の少なさを表す | 適合率の節 |
| recall とは | 再現率・感度。見逃し(FN)の少なさを表す | 再現率の節 |
| F1スコアとは | 適合率と再現率の調和平均。低いほうに引っぱられる | F1スコアの節 |
| 混同行列の読み方 | scikit-learn は行が正解、列が予測。並びは tn, fp, fn, tp |
混同行列の節 |
| 適合率と再現率のトレードオフ | しきい値を下げると再現率が上がり、適合率が下がる | しきい値の節 |
| macro・micro・weighted の違い | クラスを同じ重さで平均するか、件数で重みを付けるか | 多クラスの節 |
| どの指標を使うか | 見逃しが痛いなら再現率、誤報が痛いなら適合率 | 判断表の節 |
確認環境:Ubuntu 24.04.5 LTS の上で、作業用の venv に scikit-learn 1.9.1(NumPy 2.5.4・SciPy 1.18.1・Python 3.13.18)を入れて実行しました。データは外部から取得せず、make_classification で作った乱数のデータ(陽性1%・2万件など)と、scikit-learn に同梱の乳がんデータ(load_breast_cancer・569件)だけを使っています。モデルはロジスティック回帰(LogisticRegression)、比較用に「常に多いほうのクラスを答える」DummyClassifier を使いました。表の数値はすべて手元で実行した出力で、乱数の種(random_state)を固定しているため同じ版なら同じ値になります。指標の定義は scikit-learn 1.9.1 の公式ドキュメント(Metrics and scoring)と Google の機械学習集中講座、図は mermaid 11.16.0 で描画を確かめています(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Precision(適合率)・再現率・正解率・F1の違い|陽性1%で実測」
正解率・適合率・再現率・F1スコアの違い(一覧表)
この節では、4つの指標の式・意味・向いている場面が一覧で分かります。
4つとも「0〜1(0〜100%)で、1に近いほど良い」指標ですが、何を数えているかがまったく違います。 式は、混同行列の4つの数で書けます。
| 指標 | 英語 | 式 | ひとことで | 低いと起きること |
|---|---|---|---|---|
| 正解率 | accuracy | (TP + TN) ÷ 全件 |
全体で何割当たったか | 全体に外れが多い |
| 適合率 | precision | TP ÷ (TP + FP) |
「陽性」と言ったものの当たりの割合 | 誤報が多い |
| 再現率 | recall(感度・真陽性率) | TP ÷ (TP + FN) |
本当の陽性を何割拾えたか | 見逃しが多い |
| F1スコア | F1 score(F値) | 2 × 適合率 × 再現率 ÷ (適合率 + 再現率) |
適合率と再現率の釣り合い | どちらか(または両方)が低い |
| 特異度 | specificity(真陰性率) | TN ÷ (TN + FP) |
本当の陰性を何割正しく外せたか | 陰性を陽性と言いすぎ |
| 偽陽性率 | FPR | FP ÷ (FP + TN) |
陰性のうち誤報になった割合(1 − 特異度) | (高いと)誤報が多い |
適合率と再現率は分母だけが違う
見分ける問い:「分母は予測側か、正解側か」
10件で手計算してみる
陽性4件・陰性6件のデータで、モデルが次のように答えたとします。
y_true = [1, 1, 1, 1, 0, 0, 0, 0, 0, 0] # 正解(1が陽性)
y_pred = [1, 1, 1, 0, 1, 0, 0, 0, 0, 0] # 予測
- TP(陽性を陽性と当てた)=3、FN(陽性を見逃した)=1、FP(陰性を陽性と誤った)=1、TN(陰性を陰性と当てた)=5
- 正解率 = (3+5) ÷ 10 = 0.80
- 適合率 = 3 ÷ (3+1) = 0.75、再現率 = 3 ÷ (3+1) = 0.75
- F1 = 2×0.75×0.75 ÷ (0.75+0.75) = 0.75、特異度 = 5 ÷ 6 = 0.833
scikit-learn で同じ値になることを確かめました(後の節に出力があります)。Google の機械学習集中講座の練習問題(TP5・TN6・FP3・FN2 の再現率は 5÷7≒0.714、TP3・TN4・FP2・FN1 の適合率は 3÷5=0.6)も、同じ式で答えが出ます。
混同行列とは(TP・FP・FN・TNの読み方)
この節では、混同行列の4つのマスの意味と、scikit-learn の出力のどこを見ればよいかが分かります。
混同行列(confusion matrix)は、「正解」と「予測」の組み合わせごとに件数を数えた表です。 2クラスなら2行2列の4マスになり、ここから全部の指標が計算できます。
| 予測:陰性(0) | 予測:陽性(1) | |
|---|---|---|
| 正解:陰性(0) | TN(真陰性)正しく外した | FP(偽陽性)誤報・空振り |
| 正解:陽性(1) | FN(偽陰性)見逃し | TP(真陽性)当たり |
名前は「予測が当たったか(True/False)」+「予測が何だったか(Positive/Negative)」の順に読みます。FP は「Positive と予測したが False(外れ)」、つまり誤報です。
scikit-learn の出力は「行が正解、列が予測」
先ほどの10件を confusion_matrix に渡した実際の出力です。
from sklearn.metrics import confusion_matrix
print(confusion_matrix(y_true, y_pred))
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
print("tn fp fn tp", tn, fp, fn, tp)
[[5 1]
[1 3]]
tn fp fn tp 5 1 1 3
scikit-learn の公式ドキュメントは、行が正解のクラスに対応すると書き、「Wikipedia などは軸の向きが違うことがある」と注意しています。教科書や記事によって、左上が TP の表(陽性が先)と、左上が TN の表(scikit-learn の並び)があるので、表を見たら最初に軸のラベルを確かめます。
並びは tn, fp, fn, tp
2クラスで .ravel() すると、この順で4つの数が出る。
TP は最後
陽性を先にする
labels=[1, 0] を渡すと [[3 1] [1 5]] になり、左上が TP になった。
教科書の並びに合わせる
割合で見る
normalize='true' で行ごとの割合になる。対角が各クラスの再現率(0.833 と 0.75)。
件数の差が大きいとき
文字のラベル
"spam"・"ham" は辞書順に並ぶ(ham が先)。labels= で並びを決める。
取り違えに注意
>>> confusion_matrix(y_true, y_pred, labels=[1, 0])
[[3 1]
[1 5]]
>>> confusion_matrix(y_true, y_pred, normalize='true')
[[0.83333333 0.16666667]
[0.25 0.75 ]]
多クラスでも同じで、3クラスなら3行3列になり、対角線が当たり、それ以外が「どのクラスをどのクラスと間違えたか」になります(多クラスの節に実物があります)。
正解率(accuracy)とは:陽性1%で99%なのに検出0件
この節では、正解率の意味と、陽性が少ないデータで正解率が当てにならない理由を、実際に作ったデータで分かります。
正解率(accuracy)は、全件のうち予測が当たった割合 (TP + TN) ÷ 全件 です。 クラスの件数がだいたい同じなら分かりやすい指標ですが、陽性がまれなデータでは「全部陰性」と答えるだけで高い値になります。
実験:陽性1%のデータで「全部陰性」と答える
2万件のうち陽性がちょうど1%(200件)のデータを乱数で作り、半分(1万件、陽性100件)をテストにしました。比べたのは、学習せずに常に多いほうのクラス(陰性)を答える DummyClassifier と、ロジスティック回帰です。
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.dummy import DummyClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score
X, y = make_classification(n_samples=20000, n_features=20, n_informative=5,
weights=[0.99], flip_y=0.0, random_state=0)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.5, stratify=y, random_state=0)
dummy = DummyClassifier(strategy="most_frequent").fit(X_tr, y_tr)
print(confusion_matrix(y_te, dummy.predict(X_te)))
pos rate 0.01 n pos 200
test n 10000 pos 100
[[9900 0]
[ 100 0]]
dummy: tn=9900 fp=0 fn=100 tp=0 acc=0.9900 prec=0.0000 rec=0.0000 f1=0.0000
logreg: tn=9892 fp=8 fn=47 tp=53 acc=0.9945 prec=0.8689 rec=0.5300 f1=0.6584
正解率だけを見ると、何もしないモデル(99.00%)と学習したモデル(99.45%)の差は0.45ポイントしかありません。ところが再現率は 0 と 0.53、F1 は 0 と 0.66 で、まったく別のモデルだと分かります。陽性がまれなデータ(不正検知・故障予知・病気の検査など)では、正解率を主な指標にしないのが基本です。
正解率の代わりに見る数
| 指標 | 何もしないモデル | ロジスティック回帰 | メモ |
|---|---|---|---|
| 正解率 | 0.9900 | 0.9945 | 差がほとんど出ない |
| 再現率 | 0.0000 | 0.5300 | 見逃しの多さが分かる |
| F1スコア | 0.0000 | 0.6584 | 陽性の扱いの良さ |
| balanced accuracy | 0.5000 | 0.7646 | 各クラスの再現率の平均。偶然は0.5 |
| MCC(マシューズ相関係数) | 0.0000 | 0.6762 | 4マス全部を使う。偶然は0 |
balanced accuracy は、scikit-learn の公式ドキュメントで「各クラスの再現率の macro 平均」と定義され、2クラスでは感度(再現率)と特異度の平均です。何もしないモデルはちょうど 0.5 になり、偶然との差が読みやすくなります。
適合率(precision)とは:誤報の少なさ
この節では、適合率の意味と、適合率を重視する場面、検索での読み方が分かります。
適合率(precision)は、モデルが「陽性」と答えたもののうち、本当に陽性だった割合 TP ÷ (TP + FP) です。 誤報(FP)が増えると下がります。上の実験のロジスティック回帰は、陽性と答えた61件のうち53件が本当の陽性で、適合率は 53÷61=0.869 でした。
- 適合率を重視する場面:迷惑メールの自動振り分け(大事なメールが迷惑メール扱いになると困る)、自動でアカウントを止める処理、人が確認する件数に上限がある警報。
- 注意:陽性と1件も答えなければ分母が0になり、値が決まりません。scikit-learn は警告を出して 0 とします(エラーの節に原文があります)。
検索での適合率と再現率
適合率と再現率は、もともと情報検索の評価で使われてきた言葉です。検索結果の集合を「結果」、本当に探していた文書の集合を「正解」とすると、次のように読めます。
- 適合率 = 結果のうち正解だった件数 ÷ 結果の件数(出したものにハズレが少ないか)
- 再現率 = 結果のうち正解だった件数 ÷ 正解の件数(探したいものを漏れなく出せたか)
全部の文書を返せば再現率は1になりますが、適合率は下がります。Web 検索は上位の適合率、法律・特許・文献の調査は再現率を重視する、というように用途で優先が変わります。RAG(検索して生成する仕組み)の評価でも Context Precision・Context Recall という同じ考え方の指標があり、関連記事「RagasでRAGを評価する|指標の意味とPythonコード」で扱っています。
再現率(recall)とは:感度・特異度・偽陽性率との関係
この節では、再現率の意味と、医療の「感度」「特異度」、ROC 曲線で使う「偽陽性率」との対応が分かります。
再現率(recall)は、本当の陽性のうち、モデルが陽性と当てられた割合 TP ÷ (TP + FN) です。 見逃し(FN)が増えると下がります。上の実験のロジスティック回帰は、陽性100件のうち53件を拾い、47件を見逃したので再現率は 0.53 です。
| 呼び方 | 式 | 主に使う分野 |
|---|---|---|
| 再現率(recall) | TP ÷ (TP + FN) |
機械学習・情報検索 |
| 感度(sensitivity)・真陽性率(TPR) | 再現率と同じ式 | 医療の検査・ROC 曲線 |
| 特異度(specificity)・真陰性率 | TN ÷ (TN + FP) |
医療の検査 |
| 偽陽性率(FPR) | FP ÷ (FP + TN) = 1 − 特異度 |
ROC 曲線の横軸 |
- 再現率を重視する場面:病気のふるい分け検査(見逃すと治療が遅れる)、不正利用の検知、製品の欠陥検査、セキュリティの侵入検知。
- 注意:全部を陽性と答えれば再現率は必ず1になります。再現率だけを目標にすると、誤報だらけのモデルでも高得点になるため、適合率か偽陽性率と組で見ます。
実データ(乳がんデータ)で陽性を取り違える
scikit-learn 同梱の乳がんデータ(569件)では、0 が悪性(malignant)、1 が良性(benign)です。recall_score は何も指定しないと 1 を陽性として計算するため、「悪性の見逃し」を測ったつもりで「良性の再現率」を見てしまうことがあります。
['malignant' 'benign'] [212 357]
[[ 61 3]
[ 4 103]]
precision recall f1-score support
malignant 0.938 0.953 0.946 64
benign 0.972 0.963 0.967 107
accuracy 0.959 171
recall default pos_label=1 (benign) 0.9626168224299065
recall malignant pos_label=0 0.953125
悪性64件のうち3件を良性と判定しており、悪性の再現率は 0.953 です。pos_label=0 を渡さないと 0.963(良性の再現率)が出ます。「どちらを陽性にしたか」は、指標を出す前に必ず決めて書き残します。
F1スコアとは:調和平均を使う理由とFβスコア
この節では、F1スコアの式と、ふつうの平均ではなく調和平均を使う理由、Fβスコアでの重み付けが分かります。
F1スコア(F値、F-measure)は、適合率と再現率の調和平均 2 × P × R ÷ (P + R) です。 混同行列で書くと 2TP ÷ (2TP + FP + FN) になり、TN を使いません。
調和平均にする理由
調和平均は、2つの値のうち小さいほうに強く引っぱられます。適合率1.0・再現率0.01(1件だけ自信をもって当てたモデル)の場合を比べると、違いがはっきりします。
| 適合率 | 再現率 | ふつうの平均 | F1(調和平均) |
|---|---|---|---|
| 1.00 | 0.01 | 0.505 | 0.0198 |
| 0.60 | 0.90 | 0.750 | 0.720 |
| 0.75 | 0.75 | 0.750 | 0.750 |
ふつうの平均なら0.505と「半分くらいできている」ように見えるモデルが、F1では0.02になります。両方がそろって高いときだけ高くなるのが F1 の特徴です。
Fβスコアで「どちらを重く見るか」を決める
F1 は適合率と再現率を同じ重さで扱います。重さを変えたいときは Fβスコアを使い、β が1より大きいと再現率、小さいと適合率を重く見ます。適合率0.6・再現率0.9のモデルで、scikit-learn の fbeta_score を実行した結果です。
P 0.6 R 0.9
beta=0.5 0.6429 ← 適合率を重く見る(低いほうの0.6に近づく)
beta=1 0.72 ← F1
beta=2 0.8182 ← 再現率を重く見る(高いほうの0.9に近づく)
F2(再現率重視)が向く
病気のふるい分け検査
不正利用・侵入の検知
見逃しの損失が誤報より大きい
F0.5(適合率重視)が向く
迷惑メールの自動振り分け
おすすめ表示・自動の処分
誤報の損失が見逃しより大きい
F1 の弱点は、TN を使わないことと、「見逃し1件と誤報1件の損失が同じ」と暗に決めてしまうことです。損失の大きさが分かっているなら、F1 より、その費用で直接比べるほうが判断に合います。
scikit-learnで計算する(confusion_matrix・classification_report)
この節では、scikit-learn で4つの指標と混同行列を出すコードと、classification_report の読み方が分かります。
1つずつ出すなら accuracy_score・precision_score・recall_score・f1_score、まとめて見るなら classification_report を使います。 引数は、どれも「正解、予測」の順です。
from sklearn.metrics import (accuracy_score, precision_score, recall_score,
f1_score, fbeta_score, classification_report)
y_true = [1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
y_pred = [1, 1, 1, 0, 1, 0, 0, 0, 0, 0]
print("acc", accuracy_score(y_true, y_pred), "prec", precision_score(y_true, y_pred),
"rec", recall_score(y_true, y_pred), "f1", f1_score(y_true, y_pred))
print(classification_report(y_true, y_pred, digits=3))
acc 0.8 prec 0.75 rec 0.75 f1 0.75
precision recall f1-score support
0 0.833 0.833 0.833 6
1 0.750 0.750 0.750 4
accuracy 0.800 10
macro avg 0.792 0.792 0.792 10
weighted avg 0.800 0.800 0.800 10
1行はクラスごと
「1」の行が陽性の適合率・再現率・F1。「0」の行は陰性を陽性とみなしたときの値(0の再現率=特異度 0.833)。
2support は正解の件数
陽性4件・陰性6件。support が小さいクラスの値は、1件の差で大きく動く。
3下の3行は全体の値
accuracy は正解率。macro avg はクラスの単純平均、weighted avg は support で重みを付けた平均。
陽性1%の実験で、何もしないモデルの classification_report を出すと、陽性の行がすべて 0.000 になり、同時に警告が出ました(警告は3回出ています)。
UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.
precision recall f1-score support
0 0.990 1.000 0.995 9900
1 0.000 0.000 0.000 100
accuracy 0.990 10000
macro avg 0.495 0.500 0.497 10000
weighted avg 0.980 0.990 0.985 10000
weighted avg の F1 は 0.985 と高く見えますが、件数の多い陰性(9,900件)の値でほぼ決まっているためです。陽性がまれなときは、陽性の行(ここでは「1」)を必ず見ます。
しきい値で適合率と再現率が入れ替わる(トレードオフの実測)
この節では、判定のしきい値を動かすと適合率・再現率・F1・正解率がどう変わるかを、実際の数で分かります。
多くの分類モデルは「陽性らしさ」を0〜1の値(確率)で出し、しきい値(既定は0.5)以上を陽性とします。 しきい値を下げると陽性と答える件数が増え、再現率が上がる代わりに誤報が増えて適合率が下がります。陽性1%の実験のロジスティック回帰で、predict_proba の値に対してしきい値を変えた結果です(テスト1万件、陽性100件)。
p = model.predict_proba(X_te)[:, 1] # 陽性らしさ(0〜1)
for t in [0.9, 0.7, 0.5, 0.3, 0.2, 0.1, 0.05, 0.02]:
y_hat = (p >= t).astype(int)
print(t, precision_score(y_te, y_hat), recall_score(y_te, y_hat), f1_score(y_te, y_hat))
| しきい値 | TP/FP/FN | 適合率 | 再現率 | F1(正解率) |
|---|---|---|---|---|
| 0.9 | 28/0/72 | 1.000 | 0.280 | 0.438(0.9928) |
| 0.7 | 46/5/54 | 0.902 | 0.460 | 0.609(0.9941) |
| 0.5(既定) | 53/8/47 | 0.869 | 0.530 | 0.658(0.9945) |
| 0.3 | 65/17/35 | 0.793 | 0.650 | 0.714(0.9948) |
| 0.2 | 70/32/30 | 0.686 | 0.700 | 0.693(0.9938) |
| 0.1 | 79/77/21 | 0.506 | 0.790 | 0.617(0.9902) |
| 0.05 | 86/158/14 | 0.352 | 0.860 | 0.500(0.9828) |
| 0.02 | 91/370/9 | 0.197 | 0.910 | 0.324(0.9621) |
- しきい値を0.9から0.02へ下げると、再現率は0.28→0.91に上がり、適合率は1.00→0.20に下がりました。 片方を上げるともう片方が下がる、という関係がそのまま数に出ています。
- F1 が最も高かったのは0.3付近(0.714)で、既定の0.5ではありません。
precision_recall_curveで全部のしきい値を調べると、最大の F1 は 0.726(しきい値0.349、適合率0.823・再現率0.65)でした。 - 正解率は0.96〜0.995の間でしか動かず、どのしきい値がよいかの判断には使えませんでした。
「再現率0.8以上」のように条件で決める
実際の運用では「見逃しは2割まで」「1日に人が確認できるのは100件まで」のような条件が先にあることが多く、その条件の中で一番よいしきい値を選びます。同じデータで「再現率0.8以上の中で適合率が最大」を探すと、しきい値0.099・適合率0.506・再現率0.80でした。陽性と判定される件数は約160件で、そのうち約半分が誤報になる、と事前に見積もれます。
確率(スコア)を出す
predict_proba(X)[:, 1] か decision_function(X) で、陽性らしさの値を取り出す。predict の0・1では曲線を描けない。
曲線を計算する
precision_recall_curve(y, p) で、しきい値ごとの適合率・再現率を一度に得る。
条件を満たす点を選ぶ
「再現率0.8以上で適合率が最大」「F1 が最大」など、先に決めた条件で1点を選ぶ。
テストデータで一度だけ確かめる
選んだしきい値を固定し、最後にテストデータで評価する。テストデータで選び直さない。
precision_recall_curve の戻り値は、適合率・再現率がしきい値より1つ多く、末尾は「適合率1・再現率0」で対応するしきい値がありません(公式ドキュメント)。F1 の最大を探すときは、上のコードのように末尾を除いて計算します。
学習データだけでしきい値を選ぶ(TunedThresholdClassifierCV)
scikit-learn 1.5 から、交差検証でしきい値を選ぶ TunedThresholdClassifierCV があります。学習データだけでしきい値を決め、テストデータは最後の確認にだけ使えます。
from sklearn.model_selection import TunedThresholdClassifierCV
tuned = TunedThresholdClassifierCV(LogisticRegression(max_iter=1000),
scoring="f1", cv=5, random_state=0).fit(X_tr, y_tr)
print(tuned.best_threshold_, tuned.best_score_)
best_threshold_ 0.323 cv f1 0.738
[9886 14 35 65] test P 0.823 R 0.65 F1 0.726
学習データの交差検証で選んだしきい値は0.323で、テストデータの F1 は0.726でした。テストデータで直接探した最大値(0.726)と同じ値を、テストデータを見ずに得られています。
PR曲線・ROC曲線・AUCの違い(不均衡データでの見え方)
この節では、しきい値によらずモデルを比べる PR 曲線・ROC 曲線と、その面積(AP・ROC AUC)の違いが分かります。
PR 曲線は横軸に再現率・縦軸に適合率、ROC 曲線は横軸に偽陽性率・縦軸に再現率(真陽性率)をとり、しきい値を全部動かしたときの点を結んだ線です。 面積は、PR 曲線では平均適合率(AP、average_precision_score)、ROC 曲線では ROC AUC(roc_auc_score)として1つの数にできます。
| モデル | ROC AUC | 平均適合率(AP) |
|---|---|---|
| ロジスティック回帰 | 0.980 | 0.754 |
| でたらめなスコア(乱数) | 0.493 | 0.0097 |
- 同じモデルでも、ROC AUC は0.980とほぼ満点に見え、AP は0.754にとどまりました。 ROC 曲線の横軸(偽陽性率)は陰性9,900件で割るため、誤報が数十件増えても値がほとんど動かないからです。
- 実際に ROC 曲線上で「偽陽性率1%以下」の点を読むと、再現率は0.84でした。偽陽性率1%は陰性9,900件のうち最大99件の誤報で、見つけた陽性84件より誤報のほうが多くなり得ます。
- Google の機械学習集中講座(ROC と AUC の節)も、クラスが偏っているデータでは PR 曲線とその面積がモデルの比較に役立つ、と説明しています。
- でたらめなスコアの基準は、ROC AUC が0.5、AP が陽性の割合(ここでは0.01)です。AP は「陽性の割合と比べて何倍か」で読みます。
scikit-learn の average_precision_score は、各しきい値での適合率に再現率の増分を掛けて足し合わせる方式で、補間した方式は実装されていない、と公式ドキュメントに書かれています。台形で面積を求める auc(recall, precision) とは値が少し変わることがあります。
陽性がまれなデータのモデル比較
ROC AUC は陽性と陰性の数が近いときや、順位付けの良さを比べるときに向く
多クラスのmacro・micro・weighted平均の違い
この節では、3クラス以上のときに適合率・再現率・F1をどう1つにまとめるかと、3つの平均の違いが実際の数で分かります。
多クラスでは、クラスごとに「そのクラスを陽性、それ以外を陰性」として指標を出し、それを平均します。 平均の仕方が3通りあり、scikit-learn では average= で選びます。件数が 80%・15%・5% の3クラスのデータを作り、ロジスティック回帰で分類した結果です(テスト3,000件)。
[2385 456 159] ← 各クラスの正解の件数
[[2333 47 5]
[ 151 301 4]
[ 125 32 2]]
precision recall f1-score support
0 0.894 0.978 0.934 2385
1 0.792 0.660 0.720 456
2 0.182 0.013 0.024 159
accuracy 0.879 3000
macro avg 0.623 0.550 0.559 3000
weighted avg 0.841 0.879 0.853 3000
| average | 計算のしかた | 適合率/再現率/F1 | 向く場面 |
|---|---|---|---|
macro |
クラスごとの値を単純に平均 | 0.623/0.550/0.559 | 少ないクラスも同じ重さで見たい |
micro |
全クラスの TP・FP・FN を合計してから計算 | 0.879/0.879/0.879 | 1件1件を同じ重さで見たい |
weighted |
クラスごとの値を正解の件数で重み付け | 0.841/0.879/0.853 | 件数の比率どおりに見たい |
None |
平均せずクラスごとに返す | F1:0.934/0.720/0.024 | どのクラスが弱いか調べる |
- クラス2(全体の5%)は159件中2件しか当たっておらず、F1は0.024でした。 macro の F1(0.559)はこの弱さを反映しますが、micro(0.879)と weighted(0.853)ではほとんど見えません。
- micro の3つの値がすべて正解率(0.879)と同じになっているのは偶然ではありません。scikit-learn の公式ドキュメントは「全ラベルを含めた多クラスの micro 平均は、適合率・再現率・F がすべて正解率と同じになる」と書いています。
- 混同行列を見ると、クラス2の125件がクラス0と判定されていました。平均の値だけでなく、混同行列でどこに流れているかを見ると、直す方向(データを増やす・重みを付ける)が決まります。
- 1まず average=None でクラスごとに見る弱いクラスがあるかどうかを先に確かめる
- 2少ないクラスも大事なら macro病名の分類・問い合わせの種類など
- 3件数の比率どおりの成績が知りたいなら weighted全体の業務量に対する当たりの割合
- 4micro は1ラベルの多クラスなら正解率と同じ複数ラベル(マルチラベル)では意味が変わる
どの指標を使うか(用途別の判断表)
この節では、スパム判定・病気の検査・検索・不正検知などの用途ごとに、どの指標を主にするかが分かります。
指標は「見逃し(FN)と誤報(FP)のどちらの損失が大きいか」で選びます。 Google の機械学習集中講座も、再現率は偽陰性の費用が偽陽性より高いとき、適合率は陽性の予測が正しいことが特に大事なときに使う、と整理しています。
| 用途 | 痛いのは | 主に見る指標 | あわせて見る |
|---|---|---|---|
| 迷惑メールの自動振り分け | 誤報(大事なメールが消える) | 適合率・F0.5 | 再現率(取りこぼしの量) |
| 病気のふるい分け検査 | 見逃し | 再現率(感度)・F2 | 特異度・適合率(再検査の件数) |
| 確定診断・自動の処分 | 誤報(誤った処置) | 適合率・特異度 | 再現率 |
| 不正利用・侵入の検知 | 見逃し(ただし確認の人手に上限) | 再現率と、そのときの適合率 | AP・1日の判定件数 |
| Web 検索・おすすめ表示 | 上位のハズレ | 上位の適合率(precision@k) | 再現率 |
| 法律・特許・文献の調査 | 見逃し | 再現率 | 確認する件数 |
| クラスの数がほぼ同じ分類 | 特になし | 正解率 | 混同行列・macro F1 |
| モデル同士の比較(しきい値未定) | — | AP(陽性がまれ)・ROC AUC | 使うしきい値での値 |
- 1陽性はどちらか決めて書いたかpos_label と、陽性の割合(不均衡の度合い)を報告に入れる
- 2何もしないモデルと比べたかDummyClassifier の正解率・F1 を並べる。陽性1%なら正解率99%が基準
- 3しきい値と、そのときの件数を出したか「1日に何件が陽性判定になり、そのうち何件が誤報か」まで書く
- 4混同行列を添えたか率だけでなく件数を見せると、陽性の少なさや取り違えに気づける
よくある間違い(データ漏れ・テストデータで調整・陽性の取り違え)
この節では、指標の値が実際より良く見えてしまう典型的な間違いを、実際に再現した数で分かります。
データ漏れ:分ける前に特徴量を選ぶ
評価用のデータの情報が学習に混ざることを、データ漏れ(data leakage、リーク)と呼びます。 正解と無関係な乱数だけのデータ(200件・特徴量5,000個、正解も乱数)で、「全データで正解と相関の高い20個を選んでから交差検証」と「選ぶ処理も交差検証の中に入れる(Pipeline)」を比べました。
rs = np.random.RandomState(0)
X = rs.randn(200, 5000); y = rs.randint(0, 2, 200) # 正解と無関係な乱数
X_sel = SelectKBest(f_classif, k=20).fit_transform(X, y) # 全データで選んでしまう
print(cross_val_score(LogisticRegression(max_iter=1000), X_sel, y, cv=cv).mean())
pipe = make_pipeline(SelectKBest(f_classif, k=20), LogisticRegression(max_iter=1000))
print(cross_val_score(pipe, X, y, cv=cv).mean()) # 各分割の学習側だけで選ぶ
leaky acc 0.81
pipeline acc 0.425
当てられるはずのない乱数のデータで、漏れのある手順は正解率0.81を出しました。正しい手順では0.425(偶然の0.5前後)です。標準化・欠損値の補完・主成分分析(PCA)なども、全データで fit してから分けると同じ種類の漏れになります。PCA の手順は関連記事「主成分分析(PCA)とは|手順・寄与率・負荷量の読み方をPythonで実演」で扱っています。
テストデータでしきい値を選ぶ
テストデータで F1 が最大になるしきい値を選び、その F1 を報告すると、新しいデータでの値より高く出ます。陽性2%のデータを30通りの乱数で作り、「テスト2,000件(陽性は約40件)で選んで報告した F1」と「同じしきい値を新しい52,000件に使った F1」を比べました。
| 30回の平均 | 値 |
|---|---|
| テストデータで選んで報告した F1 | 0.390 |
| 同じしきい値の、新しいデータでの F1 | 0.341 |
| 差の平均(最大) | 0.050(0.238) |
| 報告のほうが高かった回数 | 23回/30回 |
陽性が少ないほど、テストデータに偶然合ったしきい値を選びやすくなります。しきい値は検証用データか交差検証(前の節の TunedThresholdClassifierCV)で選び、テストデータは最後に一度だけ使います。
陽性が少ないと、1件で値が大きく動く
陽性1%・2万件のデータで5分割の交差検証をすると、1つの分割の陽性は40件です。再現率は1件で2.5ポイント動き、5回の値は次のようにばらつきました。
test_accuracy [0.995 0.995 0.996 0.995 0.995] mean 0.995 std 0.0
test_precision [0.833 0.957 1. 0.862 0.917] mean 0.914 std 0.061
test_recall [0.625 0.55 0.575 0.625 0.55 ] mean 0.585 std 0.034
test_f1 [0.714 0.698 0.73 0.725 0.688] mean 0.711 std 0.016
test_roc_auc [0.965 0.967 0.997 0.987 0.943] mean 0.972 std 0.019
test_average_precision [0.727 0.727 0.896 0.747 0.77 ] mean 0.773 std 0.063
正解率はどの分割でもほぼ同じですが、適合率は0.833〜1.0、AP は0.727〜0.896まで動きました。2つのモデルの差が0.02程度なら、このばらつきの中に収まってしまいます。差が偶然かどうかの考え方は、関連記事「p値とは|意味・有意水準・よくある誤解をPythonで実演」で扱っています。
評価の手順
時系列のデータは、未来のデータで学習しないよう時間の順に分ける
よくあるエラー/警告の原文と直し方
この節では、scikit-learn で指標を計算したときに出るエラー・警告の原文と、その原因・直し方が分かります。
すべて scikit-learn 1.9.1 で実際に出た文言です。
| 原文 | 原因 | 直し方 |
|---|---|---|
UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 due to no predicted samples. Use `zero_division` parameter to control this behavior. |
陽性と1件も予測していないため、適合率の分母が0 | しきい値・モデルを見直す。値の決め方は zero_division=0 か 1 で明示する |
UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. |
classification_report で、予測が0件のクラスがある |
どのクラスが0件かを混同行列で確かめる |
ValueError: pos_label=1 is not a valid label. It should be one of ['ham' 'spam'] |
ラベルが文字なのに、陽性を指定していない(既定は1) | pos_label="spam" を渡す |
ValueError: Classification metrics can't handle a mix of binary and continuous targets |
予測に確率(0.8 など)をそのまま渡した | predict の結果か、しきい値で0・1にした値を渡す。確率は AUC 系の関数へ |
ValueError: Target is multiclass but average='binary'. Please choose another average setting, one of [None, 'micro', 'macro', 'weighted']. |
3クラス以上なのに average を指定していない |
average="macro" などを渡す |
| (エラーは出ない)悪性の再現率のつもりが良性の値 | 陽性が0のデータで pos_label を指定していない |
pos_label=0 を渡す。classification_report で両方を見る |
| (エラーは出ない)混同行列の並びが思っていたのと逆 | ラベルは昇順(文字は辞書順)に並ぶ | labels=[1, 0] などで並びを指定する |
zero_division の結果は、手元では次のとおりでした(正解が [1, 0, 0]、予測が全部0)。
>>> precision_score([1,0,0], [0,0,0]) # 警告つきで 0.0
>>> precision_score([1,0,0], [0,0,0], zero_division=0) # 0.0(警告なし)
>>> precision_score([1,0,0], [0,0,0], zero_division=1) # 1.0(警告なし)
zero_division=1 にすると「何も予測しないモデル」の適合率が満点になります。値が決まらない状態であることに変わりはないので、警告を消す目的だけで1にしないようにします。
よくある質問
この節では、正解率・適合率・再現率・F1スコアについて、よく出る疑問への短い答えが分かります。
Q. 「精度」は accuracy と precision のどちらですか? A. 文書によって違います。Google の機械学習集中講座の日本語版は accuracy を「精度」と訳し、precision を「適合率」としています。一方で、precision の訳語に「精度」を当てる資料もあります。誤解を避けるには、英語を併記するか「正解率(accuracy)」「適合率(precision)」と書きます。
Q. 再現率と感度は同じですか?
A. 同じ式 TP ÷ (TP + FN) です。機械学習・検索では再現率、医療の検査では感度、ROC 曲線では真陽性率(TPR)と呼ばれます。
Q. F値とF1スコアは同じですか? A. 日本語の「F値」は、多くの場合 F1スコア(β=1)を指します。β を変えたものは F2・F0.5 のように数を付けて区別します。
Q. 適合率と再現率は、どちらが大事ですか? A. 誤報と見逃しのどちらの損失が大きいかで決まります。見逃しが痛い検査・不正検知は再現率、誤報が痛い自動処理・迷惑メールの振り分けは適合率を主にし、もう片方も最低ラインを決めて見ます。
Q. F1スコアはいくつなら良いのですか? A. 決まった合格点はありません。陽性の割合・何もしないモデル・いま使っている仕組みと比べて判断します。陽性1%の実験では、何もしないモデルの F1 は0、ロジスティック回帰は0.66でした。
Q. 正解率(accuracy)を使ってよいのはどんなときですか? A. クラスの件数がだいたい同じで、見逃しと誤報の損失も同じくらいのときです。その場合でも混同行列を一緒に見ると、どちらに偏って外しているかが分かります。
Q. ROC AUC が0.98なのに、使ってみると誤報だらけです。 A. 陽性がまれなデータでは、ROC AUC が高く出やすくなります。手元の実験でも ROC AUC 0.980 に対し AP は0.754でした。使うしきい値での適合率と、1日あたりの誤報の件数で確かめます。
Q. しきい値は0.5でなければいけませんか? A. いいえ。0.5は既定の値にすぎず、陽性1%の実験では F1 が最大になるしきい値は0.3前後でした。検証用データで、業務の条件に合うしきい値を選びます。
Q. 多クラス分類では、どの平均を報告すればよいですか?
A. まずクラスごとの値(average=None)を見て、少ないクラスも大事なら macro、件数の比率どおりの成績なら weighted を報告します。どれを使ったかは必ず書き添えます。
Q. ニューラルネットワークや LSTM のモデルでも同じ指標を使えますか? A. 使えます。指標はモデルの種類によらず、正解と予測(または確率)があれば計算できます。モデルの仕組みは関連記事「ニューラルネットワークとは|仕組み・学習・AttentionをNumPyで実演」と関連記事「LSTMとは|仕組み・3つのゲート・RNNとの違いをPythonで実演」で扱っています。
Q. 数値を予測する回帰でも適合率・再現率を使いますか? A. 使いません。回帰では、予測と正解の差を使う平均絶対誤差(MAE)・二乗平均平方根誤差(RMSE)・決定係数(R²)などで評価します。適合率・再現率は「陽性か陰性か」のような分類のための指標です。
まとめ
- 正解率は全体の当たりの割合、適合率は「陽性と言ったもの」の当たりの割合、再現率は「本当の陽性」を拾えた割合、F1はその2つの調和平均。違いは分母。
- 陽性1%のデータでは、何もしないモデルが正解率99.0%・検出0件になった。陽性がまれなら、正解率ではなく再現率・適合率・F1・AP と混同行列で見る。
- しきい値を0.9→0.02に下げると、再現率は0.28→0.91、適合率は1.00→0.20に動いた。しきい値は既定の0.5に固定せず、業務の条件で検証用データから選ぶ。
- 多クラスでは macro が少ないクラスの弱さを映し、micro は正解率と同じになる。まず
average=Noneでクラスごとに見る。 - 全データでの特徴量選択は、乱数のデータでも正解率0.81を出した。分割→Pipeline→検証用でしきい値→テストは最後に1回、の順を守る。
参考・出典
確認日はいずれも 2026年10月29日です。
- scikit-learn「3.4. Metrics and scoring: quantifying the quality of predictions」(1.9.1):https://scikit-learn.org/stable/modules/model_evaluation.html
- scikit-learn「precision_recall_curve」:https://scikit-learn.org/stable/modules/generated/sklearn.metrics.precision_recall_curve.html
- scikit-learn「confusion_matrix」:https://scikit-learn.org/stable/modules/generated/sklearn.metrics.confusion_matrix.html
- scikit-learn「classification_report」:https://scikit-learn.org/stable/modules/generated/sklearn.metrics.classification_report.html
- scikit-learn「TunedThresholdClassifierCV」:https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.TunedThresholdClassifierCV.html
- scikit-learn「Common pitfalls and recommended practices」(データ漏れ):https://scikit-learn.org/stable/common_pitfalls.html
- Google for Developers「分類: 精度、再現率、適合率、関連指標」(機械学習集中講座):https://developers.google.com/machine-learning/crash-course/classification/accuracy-precision-recall?hl=ja
- Google for Developers「分類: ROC と AUC」(機械学習集中講座):https://developers.google.com/machine-learning/crash-course/classification/roc-and-auc?hl=ja
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Precision(適合率)・再現率・正解率・F1の違い|陽性1%で実測」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。