はじめに
はぁ... 安西こころさん... 尊い...、nikkieです😇
scikit-learnにまつわる小ネタです。
2値分類における混同行列を読めるようになりたい!
目次
- はじめに
- 目次
- 2値分類における混同行列
- scikit-learnのconfusion_matrixは書籍でよく見る説明から、行どうし・列どうしで入れ替わってる〜〜!!🤯
- confusion_matrixの返り値からprecisionやrecallを求めるコード
- 終わりに
2値分類における混同行列
2値分類なのでラベルは0か1です。
正解の0/1の並び(y_true
)と、モデルが推論した0/1の並び(y_pred
)があったときに、2×2の行列でサンプルをセグメント分けして数を出します。
いろんな書籍で取り上げられている話題だと思いますが、今回は『評価指標入門』13章 3.3の図3.2 (p.114) を参考にしました。
- ラベルが0=負例=N(egative)
- ラベルが1=正例=P(ositive)
推論ラベル=P | 推論ラベル=N | |
---|---|---|
正解ラベル=P | TP | FN |
正解ラベル=N | FP | TN |
TP, FN, FP, TNの命名規則は以下です。
- 2文字目(PまたはN)は推論ラベル
- 推論ラベルが正解ラベルと一致しているかどうかで1文字目が決まる
意味としては
- TP: 推論ラベルがPで正解ラベルと一致(
y_true
もy_pred
も1) - FP: 推論ラベルがPで正解ラベルと一致しない(
y_true
は0、y_pred
は1) - FN: 推論ラベルがNで正解ラベルと一致しない(
y_true
は1、y_pred
は0) - TN: 推論ラベルがNで正解ラベルと一致(
y_true
もy_pred
も0)
となります。
scikit-learnのconfusion_matrix
は書籍でよく見る説明から、行どうし・列どうしで入れ替わってる〜〜!!🤯
書籍で多く見る説明は完全に理解しているのですが、scikit-learnの返す混同行列とはギャップがあるよな〜と、脳内での変換に難しさを感じていました。
なので、この機に徹底的に理解しちゃいます!
返り値(Returns)に注目すると、以下のようにあります。
Confusion matrix whose i-th row and j-th column entry indicates the number of samples with true label being i-th class and predicted label being j-th class.
confusion_matrix
が返すndarray
について
i=0,1
, j=0,1
としたときの [i, j]
要素は
i
行 (i-th row): 正解ラベルがi
のサンプル数 (true label being i-th class)j
列 (j-th column): 推論ラベルがj
のサンプル数 (predicted label being j-th class)
よって、表で表すと
推論ラベル=N | 推論ラベル=P | |
---|---|---|
正解ラベル=N | TN | FP |
正解ラベル=P | FN | TP |
となります。
これはドキュメント中のサンプルコード(以下に引用)とも一致しますね。
>>> tn, fp, fn, tp = confusion_matrix([0, 1, 0, 1], [1, 1, 1, 0]).ravel() >>> (tn, fp, fn, tp) (0, 2, 1, 1)
サンプルコード内訳2
>>> confusion_matrix([0, 1, 0, 1], [1, 1, 1, 0]) array([[0, 2], [1, 1]]) >>> # tn=0, fp=2, fn=1, tp=1
この記事のコードの動作環境
- Python 3.10.9
- scikit-learn 1.2.2
confusion_matrix
の返り値からprecisionやrecallを求めるコード
precision
📌定義はtp / (tp + fp)
。
https://scikit-learn.org/stable/modules/generated/sklearn.metrics.precision_score.html
confusion_matrix
が返す混同行列では
tp
は[1][1]
(1列目が推論ラベル=P。1行目は正解もP)fp
は[0][1]
(0行目は正解がNなのでPという推論は誤っている)
ですから、上のサンプルコードを使うと
>>> cm = confusion_matrix([0, 1, 0, 1], [1, 1, 1, 0]) >>> cm[1][1] / (cm[1][1] + cm[0][1]) # tp / (tp + fp) 0.3333333333333333 >>> from sklearn.metrics import precision_score >>> precision_score([0, 1, 0, 1], [1, 1, 1, 0]) # 検算 0.3333333333333333
recall
📌定義はtp / (tp + fn)
。
https://scikit-learn.org/stable/modules/generated/sklearn.metrics.recall_score.html
confusion_matrix
が返す混同行列では
tp
は[1][1]
(1行目は正解ラベルP。1列目が推論ラベル=P)fn
は[1][0]
(0列目は推論ラベル=Nなので、Nという推論は誤っている)
ですから
>>> cm[1][1] / (cm[1][1] + cm[1][0]) # tp / (tp + fn) 0.5 >>> from sklearn.metrics import recall_score >>> recall_score([0, 1, 0, 1], [1, 1, 1, 0]) # 検算 0.5
終わりに
scikit-learnが返すconfusion_matrix
も完全に理解した!
この返り値は2値分類では
- 行は正解ラベル0,1
- 列は推論ラベル0,1
となっています。つまり
array([[TN, FP], [FN, TP]])
書籍などで見かけることが多いのは
- 行は正解ラベル1,0
- 列は推論ラベル1,0
で、scikit-learnでは行と列がそれぞれの中で入れ替わっているので、読み替えに苦労していました。
TP, FN, FP, TNの命名規則は
- 推論ラベルで2文字目のPかNかが決まる
- 1文字目(T/F)は推論ラベルと正解ラベルが一致しているかどうか
今後はこのエントリをショートカットに使います!