Learning Objectives
- 判斷資料不平衡時哪些指標有意義,並和基準比較
- 用調閾值、class_weight、複製少數類別處理不平衡,並避免複製造成的資料洩漏
- 分群組計算錯誤率、召回率與 AUC,看出整體數字藏住的差異
- 說明小群組的數字為什麼浮動大,以及訓練資料不具代表性時會發生什麼事
兩個整體數字看不到的問題
第 13 課看到,risk.csv 只有約 15% 的同學需要輔導,模型的準確率 0.852 幾乎只比全猜「不需要」多一點點。這是第一個問題:資料不平衡(imbalanced data),少數類別的表現被多數類別蓋掉了。
第二個問題更不容易發現。這份資料來自兩個校區,A 校區有 800 筆、B 校區只有 200 筆。模型的整體分數主要反映人數多的 A 校區;B 校區的表現可能差很多,但整體數字看不出來。訓練資料不能代表某個群組時造成的這類問題,就是這一課要談的資料偏誤(data bias)。
這一課只處理技術面:怎麼量出來、數字可不可信、怎麼訓練會不一樣。risk.csv 是程式產生的模擬資料,不是任何學校的真實資料,兩個校區的差異是刻意放進去的,不要拿來推論真實世界的學校或學生。
逐步看:不平衡時,先看基準
和第 13 課相同的切分與模型,這次把重點放在基準和召回率。
三種常見的處理方式
| 方法 | 做法 | 要注意 |
|---|---|---|
| 調閾值 | 模型不變,把判定的門檻調低 | 閾值要用訓練資料的交叉驗證決定,不可以看測試資料 |
| 類別權重 | class_weight="balanced":訓練時少數類別的錯誤算重一點 |
輸出的機率不再代表實際比例 |
| 重新抽樣 | 複製少數類別,或減少多數類別 | 只能處理訓練資料,否則會洩漏 |
三種方法都是在交換:多抓一些正類,代價是多誤報一些。準確率通常會下降,這是預期內的;評估時測試資料要維持原本的比例,因為上線時遇到的就是原本的比例。
少數類別本身有多少資訊也很重要。如果特徵真的分不開兩類,再怎麼調整也只是換一種錯法。做得到的話,多收集少數類別的資料是最直接的做法。
逐步看:比較調閾值與類別權重
看三行輸出的精確率與召回率。show 函數用同一份測試資料評估,只是為了少寫幾次 print。
逐步看:複製少數類別的陷阱
把少數類別複製到和多數類別一樣多,再訓練決策樹。兩段程式只差在先複製還是先切分。
資料偏誤:把錯誤分群組算
整體的錯誤率是各群組依人數加權的平均,人數少的群組幾乎影響不了它。所以評估時要分群組各算一次:
- 錯誤率:這個群組有多少判定是錯的。
- 召回率與假陽性率:錯誤的種類,是漏抓多還是誤報多。
- AUC:模型能不能把這個群組裡的兩類排開,不受閾值影響。
要注意群組的基礎比例可能不同(這份資料 A 校區約 11%、B 校區約 34% 需要輔導),所以同一個閾值下,兩個群組的錯誤率和判定比例本來就可能不一樣。AUC 不受基礎比例直接影響,適合先用來判斷「模型在這個群組裡有沒有學到東西」。
逐步看:A、B 校區分開看
同一個模型、同一份測試資料,用 campus 欄把測試資料分成兩份各算一次。campus 沒有放進特徵。
逐步看:小群組的數字會浮動
上面 B 校區的 AUC 0.631 可信嗎?測試資料裡 B 校區只有 46 筆,其中正類 17 筆。換幾個 random_state,每次重新切分、重新訓練,看數字怎麼變。
為什麼小群組要多切幾次
測試資料裡的 B 校區只有約 50 筆、正類不到 20 筆,有幾筆排對或排錯,AUC 就差很多。所以回報群組指標時:
- 寫出每個群組的筆數與正類數;
- 多試幾個切分或用交叉驗證,回報平均與範圍,不要只報一次;
- 差距比浮動小的時候,不要說兩個群組有差別。
那 B 校區平均為什麼比較低?可能的原因有兩個:一是訓練資料裡 B 校區只占兩成,二是兩個校區的規律不同。模型只有一組權重,會比較貼近人數多的那一邊。這可以用不同的訓練方式驗證,下面就來比較。
逐步看:怎麼訓練差很多
比較三種訓練資料:全部資料、只用 A 校區、只用 B 校區。為了減少切分造成的浮動,這裡用 5 折的 cross_val_predict,每一筆都由沒看過它的模型預測。最後印出兩個校區各自訓練的係數。
量到差異之後,技術上能做什麼
| 做法 | 什麼時候有用 | 限制 |
|---|---|---|
| 多收集該群組的資料 | 訓練資料裡人數太少 | 最根本,但要花時間 |
| 分群組訓練 | 規律真的不同,而且每組資料夠多 | 每組資料變少;預測時要知道這筆屬於哪一組 |
| 加入能解釋差異的特徵 | 差異來自沒有測量到的因素 | 要先知道缺什麼,也要能取得 |
| 換能表達交互作用的模型(例如樹模型) | 同一個特徵在不同群組的作用不同 | 仍需要該群組有足夠的資料 |
上面 B 校區單獨訓練的 AUC(0.851)比用全部資料(0.715)高很多,是因為這份模擬資料刻意讓兩校規律不同,而且 B 校區的規律很清楚。真實資料不一定如此:小群組單獨訓練也可能因為資料太少而更差,所以要用交叉驗證實際比較,不要假設。
新資料的來源和訓練資料不同時(換一間學校、換一個學期),也要重新檢查。模型在原本的資料上表現好,不代表在新群體上也一樣,上面只用 A 校區預測 B 校區就是這個情況。
實作:分群組回報錯誤率與召回率
完成 group_report(y_true, y_pred, group):用 numpy 對每個群組印出一行:
A 6 筆,錯誤率 0.333 召回率 0.5
群組依字母順序(np.unique),錯誤率與召回率四捨五入到小數 3 位。
小測驗:不平衡與資料偏誤
資料有 1% 是正類。下面哪個組合最適合當主要的評估指標?
要用複製少數類別處理不平衡,正確的順序是?
下面程式會印出什麼?
import numpy as np
y = np.array([1, 0, 0, 1])
p = np.array([1, 0, 1, 0])
g = np.array(["A", "A", "B", "B"])
print(float((y[g == "A"] != p[g == "A"]).mean()), float((y[g == "B"] != p[g == "B"]).mean()))
切一次資料後,群組 X(測試資料 30 筆)的 AUC 比群組 Y(300 筆)低 0.1。最恰當的下一步是?
只用 A 校區資料訓練的模型,在 A 校區的 AUC 是 0.85、在 B 校區是 0.536。這最能說明什麼?
重點整理
- 不平衡:準確率要和基準比,看召回率、精確率與 AUC。處理方法有調閾值、
class_weight、重新抽樣;評估時測試資料維持原本的比例。 - 重新抽樣只能在切分之後、只處理訓練資料,否則是資料洩漏(這一課的召回率從 0.316 變成假的 1.0)。
- 分群組評估:整體數字主要反映人數多的群組,要分開算錯誤率、召回率、假陽性率與 AUC,也要注意基礎比例不同。
- 小群組的數字會浮動:寫出筆數,多切幾次或用交叉驗證,回報平均與範圍。
- 訓練資料不具代表性時,模型在沒學過的群組上可能接近亂猜(這份資料 AUC 0.536)。
- 這一課只談怎麼量與怎麼訓練。數字差異要怎麼取捨、該不該這樣使用模型,屬於公平性的價值討論,不是這一課能回答的,可以看 AI 素養軌第 5 課。
- 下一課回到特徵本身:類別欄、缺值怎麼處理,以及怎麼用 Pipeline 把前處理和模型接起來,避免這一課和上一課看到的洩漏。