KNN 搭配交叉驗證,哪幾組程式碼能正確執行?
KNN 連題組第二題:研究人員在對 digits 資料集進行分類時,決定使用 KNN 並搭配交叉驗證來評估模型準確率。他們撰寫了四組不同的程式碼來進行 KNN 訓練與交叉驗證,但不確定哪幾組程式碼能正確執行並輸出準確率。請問哪幾組程式碼能正確使用 KNN 搭配交叉驗證,對 digits 資料集進行訓練並輸出準確率?
程式碼 A:使用 StratifiedKFold(n_splits=5, shuffle=True),scoring="accuracy"
程式碼 B:同 A,但 scoring="f1"
程式碼 C:使用 cv=5(整數),scoring="accuracy"
程式碼 D:同 C,但 scoring="f1"
郵遞區號辨識專案的研究人員要用 KNN 分類手寫數字(digits 資料集,10 個類別:0-9),搭配 5 折交叉驗證評估準確率。他們寫了四組程式碼,兩組用 StratifiedKFold、兩組用整數 cv=5,差別在評估指標:accuracy 或 f1。
問你:四組程式碼中,哪幾組能不出錯地執行並輸出準確率?
一句話總結
能正確執行的只有程式碼 A 和程式碼 C(使用 scoring="accuracy"):digits 是 10 類別的多分類問題,scoring="f1" 的預設是二元分類用法,對多類別問題會報錯或警告,必須改成 "f1_macro" 或 "f1_weighted" 才能正常執行。
先感受問題:f1 指標的「二元預設陷阱」
郵遞區號辨識系統要分辨 0-9 共 10 種數字,這是標準的多分類(multiclass)問題。研究人員用 sklearn 的 cross_val_score 評估 KNN,試了兩種評估指標:accuracy 和 f1。
accuracy 很直觀:答對幾題除以總題數,10 類別也適用。
f1 就有問題了:sklearn 的 cross_val_score 傳入 scoring="f1" 時,預設假設這是「二元分類」問題(只有正例和負例兩類)。一旦偵測到是多類別問題,就會拋出錯誤:「Target is multiclass but average='binary'. Please choose another average setting, one of [None, 'micro', 'macro', 'weighted']。」
程式碼 B 和 D 都是 scoring="f1" 的版本,兩個都會出錯。
f1 指標用錯場景,為什麼會出錯?
- f1 預設是 average='binary':sklearn 的 f1_score 函數,average 參數預設是 'binary',只適合兩類別(0 和 1)的分類問題。
- digits 是 10 類別(0-9):每個樣本的 y 值可以是 0、1、2、...、9,不是二元問題,使用 average='binary' 的 f1 會立刻報錯。
- StratifiedKFold 和 cv=5 都是交叉驗證策略,沒有問題:StratifiedKFold 是更精確的版本,確保每折的類別比例一致;cv=5 是整數,sklearn 預設用 StratifiedKFold 處理分類問題,兩者都合法。
- 多分類的 f1 需要指定平均方式:scoring="f1_macro"(各類別 f1 取算術平均)或 scoring="f1_weighted"(按類別樣本數加權平均)才能在多分類問題上正確運行。
- accuracy 沒有二元/多元的限制:accuracy 是「答對/全部」,對任意類別數的問題都適用,不需要指定平均方式。
只有 accuracy 能在多分類上直接用
逐一分析四組程式碼:
- 程式碼 A(StratifiedKFold + accuracy):兩個設定都正確,能正常執行輸出準確率。
- 程式碼 B(StratifiedKFold + "f1"):f1 預設 average='binary',digits 是 10 分類,報錯:ValueError。
- 程式碼 C(cv=5 + accuracy):cv=5 整數在分類問題上等同 StratifiedKFold(5),accuracy 沒問題,能正常執行。
- 程式碼 D(cv=5 + "f1"):和 B 同樣問題,f1 在多分類上無法使用,報錯。
能正確執行的是 A 和 C,共同特點是都用 scoring="accuracy"。
這就是選項 B 講的:程式碼 A、程式碼 C。
技術版:sklearn 的 f1 指標與多分類問題
中級考試大概率會考程式碼跟公式,所以這部分你還是要學。但如果現在學起來很痛苦,可以先跳過,等讀完其他題目回頭再來。
想像你要評估一個能辨識 10 種動物的 AI。accuracy 就像「幾題答對除以總題數」,不管有幾種動物都能算。
f1 分數原本是為「二選一」的問題設計的(例如:是不是貓?),它需要明確定義哪個是「正例」哪個是「負例」。如果你有 10 種動物,你得先說清楚「以哪種動物為正例計算 f1」,或者「10 種各算一個 f1 再取平均」。直接說「算 f1」而不指定平均方式,就像問「10 個人的平均身高多少?」但你既不說是算術平均還是加權平均,系統就無法計算。
| 白話說法 | 程式碼 |
|---|---|
| 使用 StratifiedKFold,準確率(正確) | cv = StratifiedKFold(n_splits=5, shuffle=True) cross_val_score(model, X, y, cv=cv, scoring="accuracy") |
| 使用整數 cv,準確率(正確) | cross_val_score(model, X, y, cv=5, scoring="accuracy") |
| f1 在多分類(會出錯) | scoring="f1" ← ValueError(average='binary' 但是多類別) |
| 多分類 f1 的正確寫法 | scoring="f1_macro" 或 scoring="f1_weighted" |
| digits 的類別數 | len(np.unique(y)) → 10(0-9) |
- scoring="accuracy"
- 答對比例,適用任何分類問題,不需要指定平均方式
- scoring="f1"
- 預設 average='binary',只適用二元分類,多分類會報 ValueError
- scoring="f1_macro"
- 對每個類別各算 f1,取算術平均,各類別權重相等,適合類別不平衡時關注少數類
- scoring="f1_weighted"
- 按樣本數加權的 f1 平均,多數類影響較大,適合整體效能評估
- StratifiedKFold
- 每折保持類別比例的 K-Fold,cv=整數在分類問題上 sklearn 預設也用 Stratified
f1_macro 的計算:
對每個類別 c ∈ {0,1,...,9},計算二元 f1_c(以 c 為正例)
f1_macro = (f1_0 + f1_1 + ... + f1_9) / 10
f1_weighted 的計算:
f1_weighted = Σ_c (support_c × f1_c) / total_samples
其中 support_c = 類別 c 的樣本數
程式碼 B 和 D 的錯誤:
sklearn 內部呼叫 f1_score(y_true, y_pred, average='binary')
当 y_true 包含 10 個類別時,拋出:
ValueError: Target is multiclass but average='binary'.
Please choose another average setting, one of
[None, 'micro', 'macro', 'samples', 'weighted']
正確的多分類 scoring 字串(不用記全部,記住 f1_macro 和 f1_weighted 最常用):
"f1_macro" / "f1_weighted" / "f1_micro" / "f1_samples"
- scoring="f1" 在多分類問題上為什麼會出錯?需要改成什麼?
- StratifiedKFold 和 cv=整數有什麼差異?什麼時候優先用 StratifiedKFold?
- f1_macro 和 f1_weighted 有什麼差異?各適合什麼場景?
- 如果把 digits 換成二元分類問題(0 和非 0),scoring="f1" 還會出錯嗎?
- cross_val_score 的 scores 陣列代表什麼?為什麼要看 scores.mean() 而不只是 scores[-1]?
為什麼其他選項是錯的
字面在說什麼:A、B、C、D 四組程式碼都能正確執行並輸出準確率。
為什麼不對:程式碼 B 和 D 使用 scoring="f1",sklearn 的 f1 預設是 average='binary',不適用於 10 類別的 digits 資料集,執行時會拋出 ValueError。四組全部能跑是錯誤的。
誰會選錯:沒注意到 f1 有「二元/多類別」的限制,以為 f1 是萬用的評估指標,任何分類問題都能用 scoring="f1"。
字面在說什麼:使用 StratifiedKFold 的 A 和 B 能跑,cv=5 整數的 C 和 D 不行。
為什麼不對:cv=5 整數在分類問題上,sklearn 預設用 StratifiedKFold(5) 處理,效果等同程式碼 A 的設定(不含 shuffle=True,但功能上完全合法)。選項 C 的問題不在 cv=5,而在 scoring="accuracy" 完全沒問題。選項 D 的問題不在 cv=5,而在 scoring="f1"。
誰會選錯:誤以為 cv=整數是「不精確」或「錯誤」的用法,以為只有明確指定 StratifiedKFold 物件才算「正確」,忽視了 sklearn 會自動把整數 cv 轉換成 Stratified 交叉驗證。
字面在說什麼:使用 cv=5 整數的 C 和 D 能跑,StratifiedKFold 的 A 和 B 有問題。
為什麼不對:StratifiedKFold 完全合法且是更明確的寫法,程式碼 A 絕對能正確執行。選項 D 裡的程式碼 D(cv=5 + scoring="f1")同樣因為 f1 的多分類問題而出錯,和程式碼 B 一樣。
誰會選錯:誤以為 StratifiedKFold 有什麼問題,或者搞錯了哪組用了什麼 scoring,沒仔細對應每組程式碼的設定。
同個考點下次怎麼變形
直覺:如果要在多分類問題上使用 f1,scoring 應該怎麼寫?
答案:使用 scoring="f1_macro"(各類別等權重)或 scoring="f1_weighted"(按樣本數加權)。digits 資料集各類別樣本數相當均衡,兩者結果相近;類別不平衡時差異會很大,選 weighted 較常見。
直覺:明確傳入 StratifiedKFold(shuffle=True) 和直接傳 cv=5,有什麼實質差異?
答案:主要差異是 shuffle 參數。cv=5 整數使用的是 StratifiedKFold(n_splits=5),預設 shuffle=False,折的分配按樣本原始順序。加上 shuffle=True 會先隨機打亂再切折,讓每折的樣本更具代表性,通常比不 shuffle 更穩定。時間序列資料不應 shuffle。
直覺:scoring="precision" 和 scoring="recall" 在多分類問題上也會出錯嗎?
答案:是的,同樣問題。scoring="precision" 和 scoring="recall" 預設都是 average='binary',多分類問題需要改成 "precision_macro" / "precision_weighted" 或 "recall_macro" / "recall_weighted"。記住:任何有 f1/precision/recall 的 scoring 字串,在多分類問題上都需要加上 "_macro" 或 "_weighted"。
直覺:cross_val_score 回傳的 scores 陣列和 scores.mean() 各代表什麼?
答案:scores 是每一折的評估結果(5 折就有 5 個值),代表模型在不同子集上的效能。scores.mean() 是 5 折的平均效能,是最終報告的標準指標。scores.std() 代表模型穩定性,std 小代表效能穩定,std 大代表對資料分布敏感,可能過擬合。
直覺:KNeighborsClassifier(n_neighbors=3) 的 n_neighbors 怎麼選最合適?
答案:通常用網格搜尋(GridSearchCV)或交叉驗證掃描多個 n_neighbors 值,選出在驗證集上準確率最高的 K。經驗法則:K = sqrt(n_samples),digits 資料集 1797 筆,K ≈ 42,但實際上小的 K(3-10)也常表現不錯。奇數 K 可避免二元分類時的平票。
想再往下看,這 5 個
- K 近鄰(KNN)本題主角,透過計算測試點與訓練集的距離、取最近 K 個鄰居投票決定類別,不需要訓練階段。
- 交叉驗證(Cross-Validation)本題使用的評估框架,StratifiedKFold 和 cv=整數都是實現方式,確保評估結果穩定可靠。
- F1 分數(F1 Score)本題的核心陷阱,多分類問題必須指定 average 參數(macro/weighted),直接用 scoring="f1" 在多類別上會報錯。
- 準確率(Accuracy)不受分類數量限制的通用指標,是本題中唯一能直接在多分類問題上使用的 scoring 選項。
- 資料不平衡(Data Imbalance)理解 f1_macro 和 f1_weighted 差異的關鍵概念,類別不平衡時兩者差異顯著,需要根據目的選擇。