這一題只有一個 2×2 矩陣和四個小數,看起來很單純。但它的四個選項全部都是真實存在的評估指標——0.82 是 Precision、0.95 是 NPV、0.10 是 FPR、0.90 才是 Recall。分不清楚就會選到別的。
二元分類以 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 筆 ]) # 表格到此結束
模型做完預測之後,我們要知道它到底表現如何。最原始的做法是把每一筆的「真正答案」和「模型猜的」對起來數一數——混淆矩陣就是這張統計表。
| 模型預測 0 | 模型預測 1(正類) | 這一列合計 | |
|---|---|---|---|
| 真實是 0 | 90 TN 真陰性 | 10 FP 偽陽性 | 100 |
| 真實是 1(正類) | 5 FN 偽陰性 | 45 TP 真陽性 | 50 |
| 這一欄合計 | 95 | 55 | 150 |
對角線(90 和 45)是猜對的,另外兩格(10 和 5)是猜錯的。總共 150 筆,猜對 135 筆。
點擊後出現漸進式說明:白話說明 → 說清楚一點 → 常見錯誤與考點。
點擊後出現漸進式說明。
題目只給了前 6 行(建立矩陣)。要真的算出答案,完整程式是這 12 行:
.這是物件導向的寫法。np.array(...) 和 cm.ravel() 都用到它。
np.array(...) 點的左邊是模組(numpy 的綽號) 唸「裡面的」 cm.ravel() 點的左邊是物件(做出來的陣列) 唸「它的」 cm.ravel() 有括號 = 方法,叫它做一件事 cm.shape 沒括號 = 屬性,看一格資料
import numpy as np # 把 NumPy 拿進來,取個綽號叫 np
NumPy 是 Python 做數值運算的函式庫,專門處理成排成列的數字。as np 是取綽號——之後寫 np. 就等於 numpy.,少打五個字。
這個綽號不是規定,但全世界都這樣寫(就像 pandas 一律叫 pd)。看到別人的程式碼出現 np.,你就知道那是 NumPy。
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
中間的空白([ 5, 45] 的那個空格)只是為了讓數字對齊好看,程式不在意。
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
recall = tp / (tp + fn) # 45 ÷ (45 + 5) = 0.90,括號不能省\[ \text{Recall} = \frac{TP}{TP + FN} = \frac{45}{45 + 5} = \frac{45}{50} = 0.90 \]
括號不能省。如果寫成 tp / tp + fn,Python 會先做除法再加法,變成 (45/45) + 5 = 6.0——完全錯誤。
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_true/y_pred),所以還是得會自己從四格算。但實務上請用 recall_score,少數錯格子。所以 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 |
| NPV | TN / (TN + FN) | 模型判為負類的總數(第 0 欄) | 0.95 |
| FPR | FP / (FP + TN) | 真正的負類總數 | 0.10 |
| Accuracy | (TP + TN) / 全部 | 全部 150 筆 | 0.90 |
| F1 | Recall 與 Precision 的調和平均 | — | 0.86 |
這才是這一題真正的設計。它沒有任何一個選項是亂寫的——每一個都是你用同一個矩陣、換一個公式就會算出來的數字:
| 選項 | 它其實是 | 怎麼算出來的 | 問的是什麼 |
|---|---|---|---|
| A 0.82 | Precision 精確率 | 45 / (45 + 10) = 45/55 | 模型說是正類的,有幾成是真的 |
| B 0.95 | NPV 陰性預測值 | 90 / (90 + 5) = 90/95 | 模型說沒事的,有幾成真的沒事 |
| C 0.10 | FPR 偽陽性率 | 10 / (10 + 90) = 10/100 | 沒事的人裡,有幾成被誤報 |
| D 0.90 | Recall 召回率 ← 正解 | 45 / (45 + 5) = 45/50 | 真的是正類的,有幾成被抓到 |
本題的 Accuracy 也剛好是 0.90(猜對 135 ÷ 全部 150),跟 Recall 一樣。但這純粹是這組數字的巧合,不是通則。
可以用上面的計算機驗證:把 FP 改成 30、其他不動,Accuracy 會掉到 0.794,Recall 卻完全不變(因為 FP 不在 Recall 的公式裡)。
模型真正輸出的是機率(例如「有 82% 像正類」),要變成 0 / 1 得先畫一條線——這條線就是判定門檻,預設 0.5。
| 門檻 | 四格怎麼變 | 指標怎麼變 |
|---|---|---|
| 調低(寧可錯殺) | 更多筆被判為正類 → FN 減少、FP 增加 | Recall 上升、Precision 下降 |
| 調高(寧可放過) | 更少筆被判為正類 → FP 減少、FN 增加 | Precision 上升、Recall 下降 |
| 優先看 | 什麼時候 | 例子 |
|---|---|---|
| Recall | 漏掉的代價高 | 癌症篩檢、機場安檢、設備故障預警、詐騙偵測 |
| Precision | 誤報的代價高 | 垃圾郵件過濾、推薦系統、發布逮捕令 |
| F1 | 兩邊都要顧、不知道偏哪邊 | 一般模型比較、競賽評分 |
| ROC-AUC | 想跳過門檻、只看排序能力 | 模型之間的整體比較 |
Accuracy 是最直覺的指標——猜對幾成。但它有一個致命弱點:當兩類數量差很多時,它會被多數類綁架。
scikit-learn 固定是列=真實、欄=預測。但有些教科書、有些簡報畫成相反。方向一反,FP 和 FN 就對調:
| 左下角那一格是 | 算 Recall 會變成 | |
|---|---|---|
| sklearn 方向 | FN(真的是 1、卻猜 0)= 5 | 45 / 50 = 0.90 |
| 轉置後 | FP(真的是 0、卻猜 1)= 5 | 45 / 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。
0 = 惡性、1 = 良性——直覺會以為 1 是「有病」,其實相反。算 Recall 前一定要先確認正類是誰。兩者分子都是 TP,只差在分母多加哪一格:
Recall = TP / (TP + FN) ← 加的是「漏掉的」 看列 Precision = TP / (TP + FP) ← 加的是「誤報的」 看欄
三類以上時混淆矩陣變成 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 筆 ]) # 表格到此結束
七題,答錯會直接告訴你錯在哪。
import numpy as np 是把 NumPy 拿進來並取綽號,之後寫 np. 就好。cm.ravel() 把 2×2 攤平成 [tn, fp, fn, tp],再用多重指派拆進四個變數。pos_label。混淆矩陣是中級科目三「機器學習技術與應用」的高頻考點,也會跨到科目二的模型評估。最常見的四種問法:① 給矩陣求某個指標(本題這種,選項通常全是真指標);② 給情境問「該優先看 Recall 還是 Precision」——漏掉的代價高就看 Recall;③ 問「Accuracy 高但模型沒用」是什麼情況——類別不平衡;④ 問 FP / FN 對應到型一還是型二錯誤。把「Recall 看列、Precision 看欄」和「第二個字母是模型猜什麼」這兩句記熟,這一類幾乎都是送分題。
兩者的分子都是 TP,只差分母。Recall 的分母是「真正的正類總數」(TP + FN),也就是混淆矩陣橫的那一列加起來,它問的是「該抓的抓到幾成」。Precision 的分母是「模型判為正類的總數」(TP + FP),也就是直的那一欄加起來,它問的是「抓到的裡面有幾成是真的」。一句口訣:Recall 看列、Precision 看欄。用情境記也可以:機場安檢在意 Recall(違禁品不能漏),垃圾郵件過濾在意 Precision(重要信件不能被誤殺)。
不是,這純粹是巧合。本題 Accuracy = (90 + 45) / 150 = 0.90,剛好跟 Recall 一樣。可以自己驗證:把 FP 從 10 改成 30、其他不動,Accuracy 會掉到 0.794,但 Recall 完全不變仍是 0.90——因為 FP 根本不在 Recall 的公式裡。兩個指標的分母完全不同(Recall 的分母是 50,Accuracy 的分母是 150),會相等只是數字湊巧。
scikit-learn 的 confusion_matrix(y_true, y_pred) 固定是列=真實類別、欄=預測類別,而且類別由小到大排(0 在前、1 在後)。所以 cm[1, 0] 是「真的是 1、卻被預測成 0」= FN。但有些教科書會畫成相反方向,方向一反 FP 和 FN 就對調,Recall 和 Precision 也會互換。考試時看題目怎麼定義;實務上一律用 tn, fp, fn, tp = cm.ravel()(二元分類專用)或直接呼叫 recall_score,不要自己數格子。
都是真實存在的指標,用同一個矩陣換公式就會算出來。0.95 是 NPV(陰性預測值)= TN / (TN + FN) = 90 / 95 = 0.947,意思是「模型說沒事的那些人裡,有幾成真的沒事」,它是負類版的 Precision。0.10 是 FPR(偽陽性率)= FP / (FP + TN) = 10 / 100 = 0.10,意思是「真正沒事的人裡,有幾成被誤報」,它是 ROC 曲線的橫軸,也等於 1 − 特異度。這題的四個選項全部都是真指標,所以不能靠刪去法,只能真的算。
模型輸出的其實是機率,門檻是把機率切成類別的那條線,預設 0.5。調低門檻(例如 0.3)會讓更多筆被判為正類,於是 FN 減少、Recall 上升,但 FP 增加、Precision 下降;調高門檻則相反。醫療篩檢、安檢這種「漏掉代價很高」的情境通常會刻意把門檻往下調,寧可多做幾次複檢。要注意的是:混淆矩陣只是某一個門檻下的快照,門檻一改四個數字全變。如果想跳過門檻、只比較模型的排序能力,就要改看 ROC-AUC。
因為類別不平衡。假設 1000 筆交易只有 10 筆是詐騙,一個「全部都說正常」的模型 Accuracy 就有 990/1000 = 99%,但它的 Recall 是 0——一筆詐騙都沒抓到。多數類的數量會把 Accuracy 撐得很好看。所以看到高 Accuracy 的第一個動作是先問「兩類各有幾筆」,如果差很多,就改看少數類的 Recall、Precision、F1,以及 PR-AUC。本題負類 100、正類 50 還算平衡,Accuracy 才有參考價值。
混淆矩陣會變成 3×3(或更大),ravel() 那招就不能用了,而且每一個類別都有自己的 Recall。這時候直接用 classification_report(y_true, y_pred),它會列出每一類的 precision / recall / f1 與樣本數。最下面還會有三種平均:macro 是各類直接平均(少數類跟多數類一樣重要)、weighted 是依樣本數加權(反映整體)、micro 是把所有類別的 TP/FP/FN 加總後才算(二元分類時等於 Accuracy)。要挑哪一種取決於你在意的是「每一類都要顧」還是「整體表現」。