Learning Objectives
- 用 numpy 算出混淆矩陣的四個數字,再算精確率、召回率與 F1
- 把模型的準確率和「全猜最多的類別」比較,說明準確率為什麼可能騙人
- 調整閾值,說明精確率與召回率怎麼互相牽動,並依錯誤的代價選擇閾值
- 讀懂 ROC 曲線與 AUC,說明它們和閾值的關係
準確率 85%,算好嗎?
假設學校想提早找出「這學期可能需要學業輔導」的同學,模型的準確率是 85%。聽起來不錯,但如果只有 15% 的同學真的需要輔導,那麼一個人都不抓、全部猜「不需要」,準確率也是 85%。
分類的錯誤有兩種,而且代價通常不一樣:
- 漏抓:真的需要協助的同學,模型說不需要。
- 誤報:不需要協助的同學,模型說需要。
準確率把這兩種錯誤混在一起算,看不出模型到底錯在哪裡。這一課要把它們拆開:先親手數出四個數字,再看怎麼用它們評估模型、怎麼選擇判定的門檻。
逐步看:用 numpy 數出四個數字
十位同學,1 表示「需要輔導」,我們稱它為正類(positive)。注意 tp、fn、fp、tn 每一行都是兩個布林陣列做 &(而且),再數有幾個 True。
混淆矩陣與三個指標
把四個數字排成表,就是混淆矩陣(confusion matrix)。scikit-learn 的 confusion_matrix 輸出的排法是這樣(列是實際、欄是判定,類別由小到大):
| 判定 0 | 判定 1 | |
|---|---|---|
| 實際 0 | TN | FP(誤報) |
| 實際 1 | FN(漏抓) | TP |
| 指標 | 公式 | 回答的問題 |
|---|---|---|
| 精確率(precision) | TP/(TP+FP) | 模型說「是」的那些,有多少真的是? |
| 召回率(recall) | TP/(TP+FN) | 真的是的那些,模型抓到多少? |
| F1 | 2×精確率×召回率/(精確率+召回率) | 想要一個數字同時反映兩者時用 |
要記住精確率和召回率的差別,就看分母:精確率的分母是模型判定的人,召回率的分母是實際符合的人。
正類是誰由你決定:同一份資料,把「需要輔導」當正類或把「不需要」當正類,這三個數字都會不同。scikit-learn 的二元分類預設把標籤 1 當正類(pos_label=1)。
逐步看:準確率和基準比一比
這裡用 risk.csv:1000 位同學的學業與生活紀錄,at_risk 是 1 表示需要輔導(程式產生的模擬資料,不是任何學校的真實資料)。看輸出的兩個準確率,再看混淆矩陣和報告。
閾值:判定的門檻可以調
羅吉斯迴歸輸出的是機率,predict 只是用 0.5 當門檻把它變成 0 或 1。這個門檻叫閾值(threshold),0.5 只是預設值,不是一定要用它。
- 閾值調低:比較容易判定為正類。漏抓變少(召回率上升),但誤報通常變多(精確率通常下降)。
- 閾值調高:只有很有把握才判定。誤報變少,但漏抓變多。
沒有一個閾值同時對兩者都最好,要看哪一種錯誤的代價比較高。學業預警如果只是多一封關心的信,漏抓的代價就比誤報高,閾值可以低一些;如果每次判定都要安排老師面談一小時,誤報的代價就不小。
逐步看:同一個模型,換閾值
模型完全不用重新訓練,只是用不同的門檻看同一批機率。注意每一輪的判定人數、精確率、召回率怎麼變。
ROC 曲線與 AUC
每個閾值都對應一組結果。把所有閾值的結果一次畫出來,就是 ROC 曲線:
- 橫軸是假陽性率(false positive rate)=FP/(FP+TN):沒事的人被誤報的比例。
- 縱軸是召回率:需要協助的人被抓到的比例。
曲線越往左上角越好。隨機亂猜會落在對角線上。AUC 是曲線下的面積:它等於「隨機抽一位正類、一位負類,模型給正類的分數比較高」的機率。AUC 0.5 是亂猜,1.0 是可以完全分開。
AUC 不用先決定閾值,適合比較模型能不能把兩類排開;但實際上線時還是要選一個閾值,AUC 高不代表在你選的閾值下召回率或精確率就夠用。
逐步看:畫 ROC 曲線
roc_curve 要的是分數(機率),不是 0 或 1 的判定,這是最常見的錯誤。
實作:寫一個 metrics 函數
完成 metrics(y_true, y_pred):用 numpy 算出 TP、FP、FN,回傳 (精確率, 召回率, F1),每個數字四捨五入到小數 3 位。
分母是 0 時(例如沒有人被判定為 1)要回傳 0.0,不可以出現除以零的錯誤。第三個測試就是這種情況。
小測驗:分類的評估
下面程式會印出什麼?
import numpy as np
y = np.array([1, 1, 0, 0, 0, 1])
p = np.array([1, 0, 0, 1, 0, 1])
tp = ((y == 1) & (p == 1)).sum()
fp = ((y == 0) & (p == 1)).sum()
print(int(tp), int(fp))
資料裡 95% 是負類,模型的準確率是 95%。最恰當的解讀是?
垃圾郵件過濾器把垃圾郵件當正類。使用者最不能接受「正常信被丟進垃圾桶」,你最該盯著哪個指標?
其他都不變,把閾值從 0.5 調低到 0.2,下面哪一個一定成立?
confusion_matrix 印出 [[204, 8], [29, 9]]。模型漏抓了幾位實際是正類的人?
模型的 AUC 是 0.5,代表什麼?
重點整理
- 準確率一定要和基準(全猜最多的類別)比較;資料不平衡時,它可能幾乎沒有資訊。
- 混淆矩陣拆出 TP、FP、FN、TN。精確率看模型判定的人裡有多少對,召回率看實際符合的人被抓到多少,F1 是兩者的調和平均。
- 閾值可以調:調低時召回率上升、精確率通常下降。要用哪個閾值,看兩種錯誤的代價。
- ROC 曲線畫出所有閾值的結果,AUC 衡量排序能力,兩者都要傳分數而不是判定結果。
- 這一課的數字都來自切一次的測試資料,250 筆裡差一兩筆就會讓指標變動不少。下一課用交叉驗證得到更穩定的估計,也解決第 8 課留下的問題:不能用測試資料挑超參數。