優(yōu)全指南:從指標選擇到交叉驗證)
從標題“機器學習10”來看這應該是系列教程的第十篇。到了這個階段基礎算法、數(shù)據(jù)預處理、特征工程這些內(nèi)容基本都講過了再往后應該聊點真正影響模型上線效果的東西。我打算把這一篇定位在模型評估與調(diào)優(yōu)上——訓練出幾個模型不難難的是搞清楚哪個模型靠譜、為什么靠譜、上線之后能不能穩(wěn)住。這一篇就把這些事兒掰開揉碎講清楚適合已經(jīng)掌握基本建模流程、正在往實戰(zhàn)方向進階的讀者。1. 模型評估為什么值得單獨拿出來講很多同學在第九篇左右的時候已經(jīng)能跑通一個完整的建模流程讀數(shù)據(jù)、做清洗、訓練模型、看準確率。但說實話大多數(shù)人卡在下一步——換了個模型之后準確率反而低了加了特征之后效果沒提升調(diào)了半天參數(shù)也不知道有沒有過擬合。這些問題看著是調(diào)參問題本質(zhì)上是評估體系沒搭好。評估這件事最常被誤解的一點是準確率高了就代表模型好。實際項目里根本不是這么回事。拿分類問題來說如果正樣本只占5%一個把所有樣本都判成負類的模型準確率照樣能到95%。這種模型上線就是災難。所以評估要做的不只是算一個數(shù)字而是用一套完整的體系去回答幾個問題這個模型的預測能力到底怎么樣它在哪類樣本上容易出錯我該用哪個指標來衡量它訓練集上的表現(xiàn)到底可不可信這一篇我會按實戰(zhàn)流程來講從設計評估方案、選指標、做交叉驗證到診斷過擬合、調(diào)超參數(shù)一條線走下來。里面的代碼都用常見的庫來寫思路適用于任何建模框架。2. 先搞清楚要回答什么問題再談評估方案2.1 業(yè)務目標決定評估指標選什么指標不是看哪個數(shù)字好看而是看業(yè)務上最不能接受哪類錯誤。拿一個信用風險模型來說把高風險用戶誤判成低風險損失的是真金白銀把低風險用戶誤判成高風險損失的是客戶體驗。兩類錯誤成本不一樣評估指標的權(quán)重就應該不一樣。再比如做商品推薦系統(tǒng)的排序模型用戶不在乎你推薦了多少個在乎的是前排有沒有他想要的。這時候用召回率、精確率反而不如用排序類指標合適。所以我開頭講的第一個點永遠是建模之前先跟業(yè)務方確認哪些錯誤是致命的哪些錯誤只是麻煩。這個確認結(jié)果直接決定了下游所有評估工作的走向。2.2 先把數(shù)據(jù)切分方案定下來評估方案里最基礎但最容易出錯的一步是數(shù)據(jù)切分。很多人常規(guī)做法是直接拿一份數(shù)據(jù)7成訓練3成測試跑完拉到。但實際業(yè)務里有幾個坑一是時序類數(shù)據(jù)不能隨機切按時間順序切分才合理不然就是拿未來的數(shù)據(jù)教過去成績虛高二是有分組結(jié)構(gòu)的數(shù)據(jù)比如同一個用戶的多條行為記錄如果隨機切同一個用戶同時出現(xiàn)在訓練集和測試集里模型等于開卷考試。正確的做法是先看數(shù)據(jù)的生成方式再決定切分策略。沒有時間依賴和個體依賴的問題可以用常規(guī)的隨機切分有時序依賴的按時間點前80%訓練后20%測試有用戶分組的按用戶維度切分保證一個用戶的所有記錄只出現(xiàn)在一邊。這個原則很多人一開始不當回事到上線才發(fā)現(xiàn)問題代價大得多。3. 指標選擇與計算別只看準確率3.1 分類指標逐個過分類問題里準確率是最直觀的指標但也是最容易誤導人的。除了前面說的樣本不平衡場景還有一個問題是準確率對概率閾值敏感。很多模型輸出的是概率值默認按0.5切分但業(yè)務場景里可能0.3就該觸發(fā)預警了。所以真正能看模型本身能力的是跟閾值無關的指標比如AUC。精確率和召回率是一對需要放在一起看的指標。精確率是“你判為正類的樣本里有多大比例真是正類”召回率是“所有正類樣本里你抓回來多少”。這兩個天然互斥調(diào)一個閾值拉高精確率召回率必然降。實際項目里我習慣的做法是畫出P-R曲線看曲線下的面積和形狀再結(jié)合業(yè)務成本選一個平衡點。F1值是精確率和召回率的調(diào)和平均數(shù)適合兩者都重要的時候用。但注意F1值的前提是精確率和召回率同等重要如果業(yè)務上偏向某一方F1反而不如加權(quán)版本實用。AUC這個指標值得多說一句。它的直觀含義是隨機抽一個正樣本和一個負樣本模型給正樣本打分高于負樣本的概率。這個性質(zhì)決定了AUC不受樣本不平衡比例影響也不受閾值選擇影響非常適合做模型能力基線比較。但是AUC只看排序不管絕對分數(shù)兩個模型AUC一樣在實際觸發(fā)閾值下的表現(xiàn)可能差很遠所以AUC用于選模型階段閾值選擇還得另算。3.2 回歸指標速查回歸問題也有類似的問題。均方誤差MSE對大誤差敏感因為誤差被平方放大適合我特別不希望出現(xiàn)大偏差的場景。平均絕對誤差MAE更穩(wěn)健適合有離群值的時候用。R2乍看像“解釋了多少方差”但樣本量小的時候R2很容易虛高。我自己的習慣是回歸任務里MSE和MAE一起報告再畫一張預測值和真實值的散點圖看形狀。只看任何一個數(shù)值指標都可能漏掉系統(tǒng)性偏差的問題。3.3 一個動手算指標的例子用一段常見的分類模型代碼來演示完整流程from sklearn.metrics import ( accuracy_score, precision_score, recall_score, f1_score, roc_auc_score, roc_curve ) # y_true 為真實標簽y_pred 為閾值0.5下的預測標簽 # y_prob 為模型輸出的正類概率 acc accuracy_score(y_true, y_pred) prec precision_score(y_true, y_pred) rec recall_score(y_true, y_pred) f1 f1_score(y_true, y_pred) auc roc_auc_score(y_true, y_prob) print(f準確率: {acc:.4f}) print(f精確率: {prec:.4f}) print(f召回率: {rec:.4f}) print(fF1值: {f1:.4f}) print(fAUC: {auc:.4f})跑完這些代碼你對模型的認識就從“一個數(shù)字”變成“一組畫像”。比如AUC有0.92但召回率只有0.4這說明模型排序能力很強但閾值切太高了把很多正樣本壓在了0.5以下。這時候正確的動作不是放棄模型而是調(diào)閾值。4. 交叉驗證實操從單次切分到穩(wěn)健評估4.1 為什么要做K折交叉驗證單次切分的缺點是評估結(jié)果方差太大這次運氣好分到的測試集容易準確率就高下次分到的難準確率就崩。K折交叉驗證把數(shù)據(jù)分成K份輪流拿1份做測試、其余做訓練所有樣本都有機會當測試集最終結(jié)果取K次平均。這樣做出來的評估更接近模型的真實水平。K怎么選也有講究。K太小比如2或3每次訓練數(shù)據(jù)不夠評估出來的模型跟最終用全量數(shù)據(jù)訓練的模型差距大。K太大比如20訓練集之間重疊度高評估結(jié)果的方差會變小但計算成本成倍增加。實際項目中5折和10折用得最多數(shù)據(jù)量小的時候用10折更穩(wěn)數(shù)據(jù)量大的時候5折就夠訓練太慢就降K。4.2 分層K折與分組K折普通的K折交叉驗證有個隱患如果正負樣本比例是9比1隨機分出來的某幾折里可能正樣本特別少甚至沒有那一折的評估就失真。解決辦法是分層K折每一折都保持和原始數(shù)據(jù)相同的類別比例這也是默認該用的方案。再往前一步如果數(shù)據(jù)里有用戶ID這種分組結(jié)構(gòu)就要用分組K折保證同一個組的所有樣本被分到同一折避免信息泄漏。這個細節(jié)在Kaggle競賽里很常見業(yè)務項目里反而容易被忽略。4.3 交叉驗證代碼模板from sklearn.model_selection import StratifiedKFold from sklearn.ensemble import RandomForestClassifier import numpy as np skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) auc_list [] acc_list [] for train_idx, test_idx in skf.split(X, y): X_train, X_test X.iloc[train_idx], X.iloc[test_idx] y_train, y_test y.iloc[train_idx], y.iloc[test_idx] model RandomForestClassifier(n_estimators100, random_state42) model.fit(X_train, y_train) y_prob model.predict_proba(X_test)[:, 1] y_pred (y_prob 0.5).astype(int) auc_list.append(roc_auc_score(y_test, y_prob)) acc_list.append(accuracy_score(y_test, y_pred)) print(fAUC均值: {np.mean(auc_list):.4f} ± {np.std(auc_list):.4f}) print(f準確率均值: {np.mean(acc_list):.4f} ± {np.std(acc_list):.4f})注意看輸出里我加了均值和標準差。標準差是很多人不看的但它恰恰告訴你評估穩(wěn)不穩(wěn)。如果5折的AUC分別是0.91、0.90、0.92、0.86、0.85均值0.888但方差挺大說明模型在部分數(shù)據(jù)子集上表現(xiàn)明顯偏差需要進一步查是不是某些特征在某些群體上失效了。提示交叉驗證的結(jié)果用于選模型和估參數(shù)確定最終模型之后最好再用一份完全沒碰過的測試集做一次最終驗收這樣報告出來的性能才有說服力。5. 過擬合診斷學習曲線和驗證曲線的實際用法5.1 怎么判斷模型是真的好還是背下來了訓練集表現(xiàn)很好、測試集表現(xiàn)差是建模里最經(jīng)典的現(xiàn)象。問題在于很多人看到這個現(xiàn)象只知道“噢過擬合了”但不知道怎么定位是哪層環(huán)節(jié)出的問題。我的排查順序是這樣先看訓練集分數(shù)和測試集分數(shù)的差距。如果訓練集分數(shù)高但測試集分數(shù)低典型過擬合優(yōu)先考慮降低模型復雜度、加正則化或者加數(shù)據(jù)。如果訓練集分數(shù)也不高那就是欠擬合模型的容量不夠或者特征沒到位這時候調(diào)參沒意義應該先換更強的模型或者做特征工程。5.2 畫學習曲線學習曲線能直觀回答“再加數(shù)據(jù)還有沒有用”的問題。橫軸是訓練樣本量縱軸是模型得分同時畫訓練集得分和交叉驗證得分兩條曲線。from sklearn.model_selection import learning_curve train_sizes, train_scores, cv_scores learning_curve( model, X, y, cv5, train_sizesnp.linspace(0.1, 1.0, 8), scoringroc_auc, random_state42 ) train_mean train_scores.mean(axis1) cv_mean cv_scores.mean(axis1)如果兩條曲線隨著樣本量增加逐漸靠攏說明當前瓶頸是數(shù)據(jù)量繼續(xù)收集數(shù)據(jù)有效果。如果兩條曲線始終并排保持較大差距說明模型的方差太大單純加數(shù)據(jù)效果有限得靠正則化或降低復雜度來解決。這個判斷直接影響你要不要花費巨大精力去擴數(shù)據(jù)。5.3 用驗證曲線觀察參數(shù)敏感度驗證曲線用來觀察某個超參數(shù)對模型性能的影響。以隨機森林的樹深度為例from sklearn.model_selection import validation_curve param_range range(3, 15) train_scores, test_scores validation_curve( RandomForestClassifier(n_estimators100, random_state42), X, y, param_namemax_depth, param_rangeparam_range, cv5, scoringroc_auc )畫出來的曲線里如果某個參數(shù)在很寬的范圍內(nèi)性能都差不多說明模型對這個參數(shù)不敏感你不需要花大力氣去精調(diào)它。如果曲線有個明顯的峰峰值兩邊都在掉這個參數(shù)就值得放進調(diào)優(yōu)范圍里細摳。很多同學一上來就全參數(shù)網(wǎng)格搜索其實先跑一遍驗證曲線把敏感參數(shù)篩出來能省大量時間。6. 超參數(shù)調(diào)優(yōu)網(wǎng)格搜索、隨機搜索與貝葉斯優(yōu)化的取舍6.1 網(wǎng)格搜索為什么慢網(wǎng)格搜索的原理是把每個參數(shù)的候選值列出來笛卡爾積組合后全部跑一遍交叉驗證。參數(shù)少的時候好用參數(shù)一多就指數(shù)爆炸。假設有4個參數(shù)各給10個候選值那就是一萬個組合每個組合跑5折交叉驗證五萬次訓練時間成本直接失控。所以網(wǎng)格搜索適合參數(shù)少、每個參數(shù)候選值也少的情況更多時候我拿它做最后一輪的精細搜索而不是從一開始就用它探路。6.2 隨機搜索是性價比之選隨機搜索的思路是每個參數(shù)按分布隨機采樣采樣一定次數(shù)后取效果最好的組合。它不保證覆蓋所有組合但實踐中有個很有意思的現(xiàn)象在許多參數(shù)組合里真正對性能影響大的往往只有一兩個參數(shù)隨機采樣更容易碰到這些關鍵參數(shù)附近的區(qū)域。在同樣的預算下隨機搜索找到好參數(shù)組合的概率遠高于網(wǎng)格搜索。我的習慣是先用隨機搜索把大概區(qū)域摸出來再用網(wǎng)格搜索在小范圍內(nèi)精調(diào)。兩層配合效果比單獨用任何一種都好。6.3 貝葉斯優(yōu)化的進階玩法貝葉斯優(yōu)化跟前面兩種搜索的邏輯不一樣它會把之前跑過的參數(shù)組合和對應效果作為先驗建立一個代理模型預測哪些參數(shù)組合最有可能帶來高分數(shù)然后優(yōu)先去試這些組合。思路很像一個有經(jīng)驗的人調(diào)參前幾次隨機試后面越來越有方向?,F(xiàn)在很多庫都封裝了貝葉斯優(yōu)化最常見的比如Optuna。用起來比GridSearchCV順手的地方在于它可以定義搜索的迭代預算還能中途剪枝把表現(xiàn)差的參數(shù)組合提前終止節(jié)省大量訓練時間。import optuna def objective(trial): n_estimators trial.suggest_int(n_estimators, 50, 300) max_depth trial.suggest_int(max_depth, 3, 20) min_samples_split trial.suggest_int(min_samples_split, 2, 10) model RandomForestClassifier( n_estimatorsn_estimators, max_depthmax_depth, min_samples_splitmin_samples_split, random_state42 ) cv_scores cross_val_score(model, X, y, cv5, scoringroc_auc) return cv_scores.mean() study optuna.create_study(directionmaximize) study.optimize(objective, n_trials50)跑完之后直接看study.best_params拿最優(yōu)參數(shù)。注意調(diào)參之后必須重新評估一次不要在調(diào)參過程中用到的交叉驗證結(jié)果上直接報最終性能那屬于“看著答案考試”分數(shù)虛高。注意調(diào)參的本質(zhì)是在有限的數(shù)據(jù)上尋找最優(yōu)復雜度。如果數(shù)據(jù)量特別小調(diào)參過細反而會把噪聲學進去導致驗證分數(shù)高但上線性能差。參數(shù)差不多能用就行別追求極致的交叉驗證分數(shù)。7. 常見問題與排查技巧實錄7.1 為什么加了特征準確率反而下降典型特征工程誤區(qū)。新特征跟已有特征高度相關模型把權(quán)重分散到冗余特征上等于引入了噪聲。另外某些特征只在訓練集里有效在測試集里分布完全不一樣直接把模型帶偏。排查方式很簡單加了特征之后先看訓練集表現(xiàn)有沒有明顯提升如果訓練集沒提升、測試集還在掉說明特征是噪聲如果訓練集提升了但測試集掉了說明過擬合。7.2 交叉驗證分數(shù)漂移很大怎么處理5折的結(jié)果一折0.95一折0.80通常不是模型的問題而是數(shù)據(jù)里存在某些特定子群體模型沒學好。我會把這幾個表現(xiàn)差的折的數(shù)據(jù)單獨拉出來看它們的類別分布、特征分布跟其他折有沒有明顯差異有時候是某個時間段的數(shù)據(jù)或者某個渠道的數(shù)據(jù)質(zhì)量差定位之后針對處理比盲目調(diào)參有效得多。7.3 線上預測效果和線下評測差距大這種情況最頭疼。常見原因有幾個一是訓練數(shù)據(jù)和線上真實輸入的數(shù)據(jù)分布不一致也就是沒有做特征分布的監(jiān)控二是數(shù)據(jù)泄漏線下切分時把本該是未來的信息漏進去了導致線下虛高三是線上使用的特征管線跟訓練時不一致可能少處理了一個字段或者歸一化參數(shù)不一樣。排查順序建議先檢查推理代碼的數(shù)據(jù)預處理流程再重新審查數(shù)據(jù)切分邏輯最后才是懷疑分布漂移。7.4 一個我踩過的調(diào)參坑有次隨機森林模型線下AUC很好看上線后每天凌晨效果暴跌。排查了很久發(fā)現(xiàn)訓練的時候?qū)θ笔е底隽斯潭ㄖ堤畛涞€上凌晨時段的數(shù)據(jù)缺失率高很多填充方式不變但特征分布變了。后來在評估階段專門加了一個模擬線上數(shù)據(jù)分布的驗證集每次訓練完都拿它做一次驗收。從那之后我再也不敢只用隨機切分的交叉驗證分數(shù)做上線決策。8. 實操心得我的評估與調(diào)優(yōu)完整流程最后分享一下我實際項目的固定流程這套流程跑熟了之后每次建模心里都很有底。第一步先花時間跟業(yè)務方確認核心指標搞清楚哪種錯誤成本高。第二步按數(shù)據(jù)生成方式設計切分策略洗數(shù)據(jù)前就把驗證方案定好。第三步在基準模型上跑交叉驗證拿到評估基線。第四步畫學習曲線判斷瓶頸在數(shù)據(jù)還是模型。第五步用驗證曲線篩敏感參數(shù)再用隨機搜索或者貝葉斯優(yōu)化找最優(yōu)區(qū)域。第六步在最優(yōu)區(qū)域用小范圍網(wǎng)格精調(diào)。第七步用留出測試集做最終驗收同時檢查特征分布和預測分布是否有異常。第八步記錄實驗日志包括數(shù)據(jù)版本、參數(shù)組合、指標結(jié)果和當時判斷后續(xù)復盤全靠它。這個流程最大的價值是每一步都留下可追溯的決策依據(jù)。我不看玄學式的調(diào)參也不看拍腦袋的模型對比一切改動都建立在評估結(jié)果之上。評估體系搭起來了建模項目的交付質(zhì)量和效率都會明顯提升。后續(xù)有精力的話還可以往自動化機器學習的方向擴展把參數(shù)搜索和評估流程封裝成標準模板每次新項目直接套用。