中級科目三程式實戰 · 混淆矩陣

四個選項全都是真的指標:
從混淆矩陣求 Recall

這一題只有一個 2×2 矩陣和四個小數,看起來很單純。但它的四個選項全部都是真實存在的評估指標——0.82 是 Precision、0.95 是 NPV、0.10 是 FPR、0.90 才是 Recall。分不清楚就會選到別的。

閱讀模式

00題目

二元分類以 1 為正類,scikit-learn 的 confusion_matrix 依序以真實類別為列、預測類別為欄,得到下列矩陣:此模型對正類的 Recall 為何?

import numpy as np  # 把 NumPy 拿進來,取個綽號叫 np

cm = np.array([     # 把下面的表格做成 2×2 陣列,存進 cm
    [90, 10],       # 第 0 列:真實類別是 0 的那 100 筆
    [ 5, 45]        # 第 1 列:真實類別是 1 的那 50 筆
])                  # 表格到此結束

先說混淆矩陣是什麼

模型做完預測之後,我們要知道它到底表現如何。最原始的做法是把每一筆的「真正答案」和「模型猜的」對起來數一數——混淆矩陣就是這張統計表

二元分類只有四種可能的結果:猜是、真的是猜是、其實不是猜不是、其實是猜不是、真的不是
這四種各發生幾次,就是矩陣裡的四個數字。所有分類指標——Recall、Precision、F1、Accuracy——全部都是從這四個數字算出來的。

本題的矩陣長這樣

模型預測 0模型預測 1(正類)這一列合計
真實是 090 TN 真陰性10 FP 偽陽性100
真實是 1(正類)5 FN 偽陰性45 TP 真陽性50
這一欄合計9555150

對角線(90 和 45)是猜對的,另外兩格(10 和 5)是猜錯的。總共 150 筆,猜對 135 筆。

先點開看:混淆矩陣是什麼?TP / FP / FN / TN 怎麼記?正類是哪一類?

不熟 AI 專有名詞?你需要先認識下列名詞

點擊後出現漸進式說明:白話說明 → 說清楚一點 → 常見錯誤與考點。

這張表本身
混淆矩陣TP / FP / FN / TN正類 positive classX 與 y
從四格算出來的指標
Recall 召回率Precision 精確率F1 分數Accuracy 準確率
容易被拿來當陷阱的指標
特異度 SpecificityNPV 陰性預測值FPR 偽陽性率ROC-AUC
背後的觀念
判定門檻型一/型二錯誤類別不平衡macro / weighted 平均訓練折與驗證折

不熟 Python?你需要先認識下列名詞

點擊後出現漸進式說明

把工具拿進來
importfrom模組 module函式庫 library
存東西、取名字
= 指派變數多重指派
呼叫與設定
函式 function( ) 呼叫括號參數關鍵字參數括號內的 =
資料的裝法
[ ] list 串列( ) tuple 元組" " 字串True / False縮排
設計圖與機器
類別 class實例化大小寫命名慣例
物件與方法(點的用法)
. 點運算子方法 method屬性 attribute
NumPy
NumPy 陣列shape 形狀.ravel() 攤平

01逐行拆解

題目只給了前 6 行(建立矩陣)。要真的算出答案,完整程式是這 12 行:

先認識一個符號:點 .

這是物件導向的寫法。np.array(...)cm.ravel() 都用到它。

np.array(...)      點的左邊是模組(numpy 的綽號) 唸「裡面的」
cm.ravel()         點的左邊是物件(做出來的陣列)  唸「它的」

cm.ravel()         括號 = 方法,叫它做一件事
cm.shape           括號 = 屬性,看一格資料

想深入的話點這裡:點 . 到底是什麼?方法和函式差在哪?屬性和方法差在哪?

第 1 行把 NumPy 拿進來,順便取個綽號
import numpy as np  # 把 NumPy 拿進來,取個綽號叫 np

NumPy 是 Python 做數值運算的函式庫,專門處理成排成列的數字。as np取綽號——之後寫 np. 就等於 numpy.,少打五個字。

這個綽號不是規定,但全世界都這樣寫(就像 pandas 一律叫 pd)。看到別人的程式碼出現 np.,你就知道那是 NumPy。

第 3–6 行把混淆矩陣寫成二維陣列
cm = np.array([     # 把下面的表格做成 2×2 陣列,存進 cm
    [90, 10],       # 第 0 列:真實類別是 0 的那 100 筆
    [ 5, 45]        # 第 1 列:真實類別是 1 的那 50 筆
])                  # 表格到此結束

兩層方括號是重點:外層的 [ ] 裝著整個表格,內層的每一組 [ ]一列

np.array([
    [90, 10],   ← 第 0 列:真實是 0 的那 100 筆
    [ 5, 45]    ← 第 1 列:真實是 1 的那 50 筆
])
      ↑    ↑
   第0欄  第1欄
  預測0  預測1
列和欄的方向就是這題的第一個陷阱。scikit-learn 的規則固定是列=真實、欄=預測,而且類別由小到大排。
有些教科書畫成相反的方向,那樣讀的話 FP 和 FN 會對調,Recall 和 Precision 也會互換。

中間的空白([ 5, 45] 的那個空格)只是為了讓數字對齊好看,程式不在意。

第 8 行把四個格子拆進四個變數
tn, fp, fn, tp = cm.ravel()  # 攤平成 [90, 10, 5, 45],依序裝進四個變數

這一行做了兩件事:

動作做什麼
cm.ravel()把 2×2 的矩陣攤平成一條[90, 10, 5, 45],順序是從左到右、由上到下
tn, fp, fn, tp =多重指派:四個數字分別裝進四個箱子
cm.ravel()  →  [ 90 ,  10 ,   5 ,  45 ]
                  ↓     ↓     ↓     ↓
                 tn    fp    fn    tp
這個順序只在二元分類、標籤是 0 / 1、且用 sklearn 的方向時才成立。三分類的混淆矩陣是 3×3,攤平會有 9 個數字,就不能這樣解包了。
第 10 行算出答案
recall = tp / (tp + fn)  # 45 ÷ (45 + 5) = 0.90,括號不能省

\[ \text{Recall} = \frac{TP}{TP + FN} = \frac{45}{45 + 5} = \frac{45}{50} = 0.90 \]

分母是「真正的正類總共有幾個」,也就是混淆矩陣第 1 列加起來(45 + 5 = 50)。
Recall 問的是:這 50 個真正的正類裡面,模型抓到了幾個?答案是 45 個,所以 45 ÷ 50 = 0.90。

括號不能省。如果寫成 tp / tp + fn,Python 會先做除法再加法,變成 (45/45) + 5 = 6.0——完全錯誤。

第 12 行印出結果
print("Recall =", recall)   # Recall = 0.9

逗號分隔的多個東西會被 print 用空格接起來印出。所以螢幕上會看到 Recall = 0.9

其實可以更省事

scikit-learn 有現成的函式,不用自己數格子:

from sklearn.metrics import recall_score  # 從 sklearn 的 metrics 模組把 recall_score 這個函式拿過來

recall = recall_score(y_true, y_pred)          # 預設把標籤 1 當正類
recall_0 = recall_score(y_true, y_pred, pos_label=0)  # 改成看負類
考試時題目通常只給你矩陣(沒有 y_truey_pred),所以還是得會自己從四格算。但實務上請用 recall_score,少數錯格子。

02四個格子分別是什麼

兩個字母的密碼

第二個字母 P / N =「模型猜什麼」;第一個字母 T / F =「這個判斷對不對」。

所以 False Negative 拆開唸就是「模型說不是(Negative),但這個判斷是錯的(False)」→ 實際上是正類,卻被漏掉了。

用機場安檢記最快

代號安檢情境後果本題
TP說有違禁品,真的有抓對了45
FP說有違禁品,其實沒有誤報,旅客白被攔一次10
FN說沒有,其實有漏掉了,最可怕5
TN說沒有,真的沒有正常放行90

對應到統計學:FP 是型一錯誤(大驚小怪)、FN 是型二錯誤(視而不見)。兩者的取捨是所有分類問題的核心。

從四格可以算出哪些東西

下面這張表把常見指標一次列完。注意每個指標的分母不一樣——分母選錯就是答錯

指標公式分母是什麼本題
Recall 召回率TP / (TP + FN)真正的正類總數(第 1 0.90
Precision 精確率TP / (TP + FP)模型判為正類的總數(第 1 0.82
特異度TN / (TN + FP)真正的負類總數(第 0 列)0.90
NPVTN / (TN + FN)模型判為負類的總數(第 0 欄)0.95
FPRFP / (FP + TN)真正的負類總數0.10
Accuracy(TP + TN) / 全部全部 150 筆0.90
F1Recall 與 Precision 的調和平均0.86
記憶關鍵:Recall 看「列」,Precision 看「欄」。
Recall 的分母是橫的一整列(真的有幾個),Precision 的分母是直的一整欄(模型說有幾個)。這一句記住,這類題目幾乎不會錯。
互動實驗室:混淆矩陣計算機改數字看指標怎麼變
也可以直接改下面四個數字
模型預測 0
模型預測 1(正類)
真實 0
TN 真陰性
說沒事、真的沒事
FP 偽陽性
誤報(型一錯誤)
真實 1
FN 偽陰性
漏掉(型二錯誤)
TP 真陽性
抓對了

03四個選項全都是真的指標

這才是這一題真正的設計。它沒有任何一個選項是亂寫的——每一個都是你用同一個矩陣、換一個公式就會算出來的數字:

選項它其實是怎麼算出來的問的是什麼
A 0.82Precision 精確率45 / (45 + 10) = 45/55模型說是正類的,有幾成是真的
B 0.95NPV 陰性預測值90 / (90 + 5) = 90/95模型說沒事的,有幾成真的沒事
C 0.10FPR 偽陽性率10 / (10 + 90) = 10/100沒事的人裡,有幾成被誤報
D 0.90Recall 召回率 ← 正解45 / (45 + 5) = 45/50真的是正類的,有幾成被抓到
最容易掉進去的是 A(0.82)。因為 Recall 和 Precision 的分子都是 TP(45),只有分母不同——一個加 FN、一個加 FP。手一滑就加錯那一格。
這時候「Recall 看列、Precision 看欄」這句口訣就是保命符。

順帶一提:這裡有個巧合

本題的 Accuracy 也剛好是 0.90(猜對 135 ÷ 全部 150),跟 Recall 一樣。但這純粹是這組數字的巧合,不是通則。

可以用上面的計算機驗證:把 FP 改成 30、其他不動,Accuracy 會掉到 0.794,Recall 卻完全不變(因為 FP 不在 Recall 的公式裡)。

04Recall 與 Precision 的取捨

兩者為什麼會互相拉扯

模型真正輸出的是機率(例如「有 82% 像正類」),要變成 0 / 1 得先畫一條線——這條線就是判定門檻,預設 0.5。

門檻四格怎麼變指標怎麼變
調低(寧可錯殺)更多筆被判為正類 → FN 減少FP 增加Recall 上升Precision 下降
調高(寧可放過)更少筆被判為正類 → FP 減少FN 增加Precision 上升Recall 下降
混淆矩陣是「某一個門檻下」的快照。門檻一改,四個數字全部會變,所有指標也跟著變——所以看到任何指標,都要問一句「這是在哪個門檻下算的」。
互動實驗室:拖門檻看四格怎麼連動Recall 與 Precision 的拉鋸
TN
0
說沒事、真沒事
FP 誤報
0
型一錯誤
FN 漏掉
0
型二錯誤
TP
0
抓對了

那到底該看哪一個

優先看什麼時候例子
Recall漏掉的代價高癌症篩檢、機場安檢、設備故障預警、詐騙偵測
Precision誤報的代價高垃圾郵件過濾、推薦系統、發布逮捕令
F1兩邊都要顧、不知道偏哪邊一般模型比較、競賽評分
ROC-AUC想跳過門檻、只看排序能力模型之間的整體比較
醫療情境幾乎一定優先看 Recall——把惡性誤判成良性(漏診)比虛驚一場嚴重太多,所以實務上常刻意把門檻往下調,寧可多做幾次複檢。

05Accuracy 為什麼常常騙人

Accuracy 是最直覺的指標——猜對幾成。但它有一個致命弱點:當兩類數量差很多時,它會被多數類綁架

經典例子:詐騙偵測中 1000 筆交易只有 10 筆是詐騙。一個「全部都說正常」的模型:
Accuracy = 990 / 1000 = 99% 看起來完美
Recall = 0 / 10 = 0%  一筆詐騙都沒抓到
互動實驗室:不平衡資料下的 Accuracy 陷阱拉少數類比例看差距
總共 1000 筆
「全部猜多數類」的笨模型
99.0%
Accuracy
Recall 0.0% 一筆少數類都沒抓到
該看的指標
少數類 Recall
少數類 Precision
F1 / PR-AUC
所以看到高 Accuracy 的第一個動作,是先問兩類各有幾筆
本題負類 100、正類 50(2:1),還算平衡,所以 Accuracy 0.90 有參考價值——但如果變成 990:10,那個數字就完全不能看了。

06三個最容易被扣分的地方

一、矩陣的方向弄反

scikit-learn 固定是列=真實、欄=預測。但有些教科書、有些簡報畫成相反。方向一反,FP 和 FN 就對調:

左下角那一格是算 Recall 會變成
sklearn 方向FN(真的是 1、卻猜 0)= 545 / 50 = 0.90
轉置後FP(真的是 0、卻猜 1)= 545 / 55 = 0.82(其實算成 Precision)
保命做法:看題目怎麼說。本題白紙黑字寫了「依序以真實類別為列、預測類別為欄」,就照這個讀。實務上則一律用 tn, fp, fn, tp = cm.ravel() 或直接呼叫 recall_score

二、搞錯誰是正類

本題明講「以 1 為正類」,所以 Recall 算的是第 1 列。但如果題目改成「以 0 為正類」,同一個矩陣的答案就變成:

以 0 為正類 → Recall = TN / (TN + FP) = 90 / (90 + 10) = 0.90
(這其實就是特異度)

在 sklearn 裡,預設把標籤比較大的那一個當正類;要改的話用 pos_label=0

這一點在醫療資料特別容易翻船。例如 scikit-learn 的乳癌資料集是 0 = 惡性、1 = 良性——直覺會以為 1 是「有病」,其實相反。算 Recall 前一定要先確認正類是誰。

三、把 Recall 和 Precision 的分母加錯

兩者分子都是 TP,只差在分母多加哪一格:

Recall    = TP / (TP + FN)    ← 加的是「漏掉的」  看
Precision = TP / (TP + FP)    ← 加的是「誤報的」  看
口訣:Recall 看列(真的有幾個),Precision 看欄(模型說有幾個)。

四(加分題):多分類怎麼辦

三類以上時混淆矩陣變成 3×3,ravel() 就不能用了,而且每一類都有自己的 Recall。這時候要看 classification_report,並注意 macro 平均與 weighted 平均的差別。

              precision    recall  f1-score   support
      0 負類     0.9474    0.9000    0.9231       100
      1 正類     0.8182    0.9000    0.8571        50
    accuracy                         0.9000       150
   macro avg     0.8828    0.9000    0.8901       150
weighted avg     0.9043    0.9000    0.9011       150

上面這張表是本題資料實跑 classification_report 的結果。可以對照看:正類那一列的 recall 就是 0.9000,precision 是 0.8182——正好是選項 D 和 A。

回到題目:現在再作答一次

四個格子、四個公式都拆過了。先不要往下捲,回頭把同一題再做一次——這次應該不只是選對,而是說得出另外三個選項各自是什麼指標。

二元分類以 1 為正類,scikit-learn 的 confusion_matrix 依序以真實類別為列、預測類別為欄,得到下列矩陣:此模型對正類的 Recall 為何?

import numpy as np  # 把 NumPy 拿進來,取個綽號叫 np

cm = np.array([     # 把下面的表格做成 2×2 陣列,存進 cm
    [90, 10],       # 第 0 列:真實類別是 0 的那 100 筆
    [ 5, 45]        # 第 1 列:真實類別是 1 的那 50 筆
])                  # 表格到此結束

07自我檢測

七題,答錯會直接告訴你錯在哪。

08重點整理

  1. import numpy as np 是把 NumPy 拿進來並取綽號,之後寫 np. 就好。
  2. 兩層方括號=二維陣列:外層裝整個表格,內層每一組是一列。
  3. cm.ravel() 把 2×2 攤平成 [tn, fp, fn, tp],再用多重指派拆進四個變數。
  4. scikit-learn 的混淆矩陣固定是「列=真實、欄=預測」,類別由小到大排。方向反了 FP 和 FN 就對調。
  5. 第二個字母是模型猜什麼(P/N),第一個字母是猜對了沒(T/F)。所以 FN = 模型說不是、但說錯了 = 漏掉。
  6. Recall = TP / (TP + FN),看「列」——真正的正類裡抓到幾成。本題 45 / 50 = 0.90
  7. Precision = TP / (TP + FP),看「欄」——模型說是正類的裡面有幾成是真的。本題 45 / 55 = 0.82。
  8. 本題四個選項全是真實指標:0.82 Precision、0.95 NPV、0.10 FPR、0.90 Recall。
  9. 混淆矩陣是某一個門檻下的快照。門檻調低 → Recall 上升、Precision 下降;調高則相反。
  10. Accuracy 在類別不平衡時會騙人。看到高 Accuracy 先問兩類各有幾筆,並改看少數類的 Recall / Precision / F1。
  11. 算 Recall 前一定要先確認誰是正類。sklearn 預設把標籤大的那個當正類,要改用 pos_label
完整程式碼