險(xiǎn)預(yù)測實(shí)戰(zhàn):從特征工程到模型融合的完整方案)
簡介在生物特征與機(jī)器學(xué)習(xí)結(jié)合的競賽場景中糖尿病遺傳風(fēng)險(xiǎn)預(yù)測是一道經(jīng)典且充滿挑戰(zhàn)的題目。其核心并非單純依賴血糖、BMI等強(qiáng)生理指標(biāo)而需從家族史、SNP位點(diǎn)等弱特征中挖掘組合模式。本文從技術(shù)原理出發(fā)解析為何梯度提升樹如LightGBM優(yōu)于深度學(xué)習(xí)處理結(jié)構(gòu)化表格數(shù)據(jù)并系統(tǒng)闡述缺失值分檔處理、家族史聚合、風(fēng)險(xiǎn)位點(diǎn)凈得分等特征構(gòu)造方法。同時(shí)針對AUC評估指標(biāo)下的類別不平衡與過擬合問題給出基于GroupKFold的驗(yàn)證策略與參數(shù)調(diào)優(yōu)路徑。通過多模型融合與交叉驗(yàn)證預(yù)測可顯著提升排序能力。該方案適用于醫(yī)療健康領(lǐng)域的風(fēng)險(xiǎn)預(yù)測任務(wù)尤其適合基因數(shù)據(jù)與臨床指標(biāo)混合的場景為參賽者或研究者提供了一套可復(fù)用的工程實(shí)踐框架。 天池上跑過糖尿病遺傳風(fēng)險(xiǎn)預(yù)測的朋友應(yīng)該知道這道題看著不算難但真做起來坑挺多。我當(dāng)時(shí)把大半個(gè)假期砸在這上面最后運(yùn)氣不錯(cuò)拿了前三源碼和說明文檔一直存在網(wǎng)盤里。最近有學(xué)弟問怎么入門這類“生物特征機(jī)器學(xué)習(xí)”的比賽我就把整套方案從頭到尾捋一遍包括數(shù)據(jù)預(yù)處理、特征構(gòu)造、模型調(diào)參、模型融合這些關(guān)鍵環(huán)節(jié)以及我實(shí)際踩過的坑。如果你想找的是那種直接復(fù)制就能跑的完整代碼這篇文可能不是最快的路徑但如果你想知道每個(gè)步驟為什么要這么做、遇到問題怎么排查這篇應(yīng)該能幫到你。我盡量不寫那種“這里導(dǎo)入庫、那里處理缺失值”的流水賬而是把每個(gè)設(shè)計(jì)決策背后的邏輯講清楚。比如為什么用LightGBM不用XGBoost、為什么家族史要單獨(dú)拆列、為什么驗(yàn)證集必須分著抽。都是實(shí)際參賽過程中反復(fù)試出來的經(jīng)驗(yàn)寫下來當(dāng)個(gè)記錄也希望能給你省點(diǎn)時(shí)間。1. 賽題理解糖尿病遺傳風(fēng)險(xiǎn)預(yù)測到底在做什么1.1 賽題數(shù)據(jù)與任務(wù)定義這個(gè)賽題的核心任務(wù)很簡單給定一批用戶的健康檔案和遺傳位點(diǎn)信息讓參賽者構(gòu)建模型預(yù)測該用戶未來患2型糖尿病的風(fēng)險(xiǎn)概率。本質(zhì)上是一個(gè)二分類問題輸出0到1之間的風(fēng)險(xiǎn)分?jǐn)?shù)分?jǐn)?shù)越高代表患病風(fēng)險(xiǎn)越大。很多第一次打這類比賽的人會(huì)犯一個(gè)方向性錯(cuò)誤——把遺傳風(fēng)險(xiǎn)預(yù)測當(dāng)成普通的“體檢指標(biāo)分類”來做。其實(shí)兩者差別很大。普通體檢預(yù)測更依賴BMI、血糖、血壓這些強(qiáng)相關(guān)指標(biāo)模型很容易學(xué)到“血糖高高風(fēng)險(xiǎn)”這種顯性規(guī)律而遺傳風(fēng)險(xiǎn)預(yù)測的數(shù)據(jù)里大量字段是SNP位點(diǎn)單核苷酸多態(tài)性、家族史、生活習(xí)慣等相對“弱”的特征。這些特征單個(gè)看幾乎沒什么預(yù)測能力只有組合成特定模式才有意義。我當(dāng)時(shí)拿到數(shù)據(jù)后先做了一遍字段梳理大致分為四類一是基礎(chǔ)信息包括年齡、性別二是生理指標(biāo)包括BMI、血壓、空腹血糖三是家族史包括父母、兄弟姐妹是否有糖尿病史四是遺傳位點(diǎn)也就是一系列SNP字段。這里有個(gè)細(xì)節(jié)需要注意不是所有SNP位點(diǎn)都是風(fēng)險(xiǎn)位點(diǎn)有些是保護(hù)性位點(diǎn)不能一刀切地“有突變就算風(fēng)險(xiǎn)”。所以我后期做特征時(shí)對每個(gè)位點(diǎn)都單獨(dú)看了分布和與目標(biāo)變量的趨勢關(guān)系而不是直接丟進(jìn)模型。任務(wù)定義清楚了后面所有工作才有抓手。再強(qiáng)調(diào)一次天池官方給的字段命名和具體結(jié)構(gòu)以比賽頁面為準(zhǔn)我這里描述的字段是抽象過后的常見結(jié)構(gòu)目的是把方法講通你換成實(shí)際數(shù)據(jù)一樣能套用。1.2 評估指標(biāo)與排名規(guī)則里的隱藏信息天池這類比賽通常在評估指標(biāo)上很講究Diabetes遺傳風(fēng)險(xiǎn)預(yù)測這道題官方標(biāo)準(zhǔn)以AUC為核心指標(biāo)兼顧準(zhǔn)確率和召回率。為什么用AUC因?yàn)榛疾★L(fēng)險(xiǎn)預(yù)測天然是一個(gè)“正負(fù)樣本不平衡且誤判代價(jià)不對稱”的問題——把高危人群漏掉比把低危人群誤報(bào)成高危要嚴(yán)重得多。AUC不依賴具體閾值能客觀反映模型對正負(fù)樣本的排序能力所以用AUC作為主排名依據(jù)是合理的。這個(gè)信息非常關(guān)鍵。它意味著你在調(diào)參和選模型時(shí)不應(yīng)該追求“準(zhǔn)確率最高”而應(yīng)該追求“排序能力最強(qiáng)”。很多人在初賽階段執(zhí)著于把準(zhǔn)確率從0.86提到0.87結(jié)果AUC反而掉了就是因?yàn)槟繕?biāo)函數(shù)和評估指標(biāo)錯(cuò)位了。我的經(jīng)驗(yàn)是直接以AUC作為模型早停和交叉驗(yàn)證的監(jiān)控指標(biāo)所有超參搜索也圍繞AUC來選這樣最后線上和線下分?jǐn)?shù)基本能對上。還有一點(diǎn)這類比賽的評測集通常會(huì)把家族史和部分SNP位點(diǎn)做掩碼處理模擬的是“缺少部分遺傳信息”的真實(shí)場景。這個(gè)設(shè)定影響很大意味著模型不能過度依賴某一個(gè)強(qiáng)特征否則評測時(shí)特征一缺失預(yù)測結(jié)果直接崩掉。為此我在訓(xùn)練時(shí)專門做了特征穩(wěn)健性測試隨機(jī)遮蔽部分列來看模型效果衰減幅度衰減太嚴(yán)重的模型直接淘汰。后面雖然犧牲了一點(diǎn)線下分?jǐn)?shù)但線上穩(wěn)定性明顯更好。2. 技術(shù)選型為什么是Python和集成學(xué)習(xí)2.1 Python生態(tài)在做表格類任務(wù)時(shí)的優(yōu)勢選Python做這個(gè)賽題基本不需要猶豫原因不是“Python更?!倍撬褦?shù)據(jù)清洗、特征工程、模型訓(xùn)練、結(jié)果可視化整個(gè)鏈條的東西都給你備齊了。pandas做表格處理numpy做矩陣運(yùn)算scikit-learn提供全套基線模型和交叉驗(yàn)證工具LightGBM和XGBoost對這類結(jié)構(gòu)化表格數(shù)據(jù)幾乎是降維打擊。你不需要寫一行C或者Java就能在一個(gè)腳本里完成從原始CSV到提交結(jié)果的全流程。我對幾個(gè)方案做過對比。純SQL做特征工程太痛苦復(fù)雜條件組合和跨行統(tǒng)計(jì)會(huì)寫到崩潰R語言做統(tǒng)計(jì)分析和可視化確實(shí)順手但工程化部署和模型庫的豐富度不如Python深度學(xué)習(xí)框架PyTorch、TensorFlow雖然在圖像和文本上是王者但放到幾百行、幾十列的小表格數(shù)據(jù)上性能和收益反而不如傳統(tǒng)樹模型。所以最終方案就是Python全家桶pandas做數(shù)據(jù)清洗scikit-learn做數(shù)據(jù)劃分和基線模型LightGBM做主力模型XGBoost做輔助模型最后做個(gè)簡單融合。Python還有一個(gè)隱形優(yōu)勢是社區(qū)答案多。比賽期間我遇到過一個(gè)奇怪的報(bào)錯(cuò)LightGBM在驗(yàn)證集上AUC突然變成0.5排查了很久最后在GitHub的issue里找到原因——是類別特征編碼問題。這種問題只有用主流語言和主流庫才會(huì)快速找到答案冷門技術(shù)棧遇到問題只能自己啃源碼。2.2 深度學(xué)習(xí)在這個(gè)場景里為什么不劃算很多人一聽“人工智能輔助”就覺得得上深度學(xué)習(xí)其實(shí)這是一個(gè)典型誤區(qū)。深度學(xué)習(xí)適合處理高維非結(jié)構(gòu)化數(shù)據(jù)比如圖像像素、語音波形、自然語言序列它的優(yōu)勢是能從原始數(shù)據(jù)里自動(dòng)學(xué)習(xí)特征表示。但這個(gè)賽題的數(shù)據(jù)是結(jié)構(gòu)化的表格行數(shù)只有幾千到幾萬列數(shù)幾十列大部分字段都有明確語義這種情況下深度學(xué)習(xí)很難打得過調(diào)好參的梯度提升樹。我做了一組對比實(shí)驗(yàn)用同樣的特征訓(xùn)練一個(gè)兩層的MLP和一份調(diào)參后的LightGBM線上AUC差了大約5個(gè)百分點(diǎn)而且MLP的訓(xùn)練時(shí)間還是LightGBM的好幾倍。原因不復(fù)雜——表格數(shù)據(jù)里特征和目標(biāo)之間的非線性關(guān)系是稀疏的、離散的樹模型通過分裂點(diǎn)天然能捕捉這種關(guān)系而神經(jīng)網(wǎng)絡(luò)需要大量數(shù)據(jù)才能自動(dòng)發(fā)現(xiàn)這些模式。數(shù)據(jù)量不夠的時(shí)候神經(jīng)網(wǎng)絡(luò)更容易欠擬合或者過擬合。不過我也不是完全放棄深度學(xué)習(xí)在模型融合階段我用一個(gè)淺層MLP做stacking的元模型幫LightGBM和XGBoost的輸出做加權(quán)組合。這個(gè)用法比較討巧樹模型負(fù)責(zé)學(xué)習(xí)復(fù)雜非線性關(guān)系神經(jīng)網(wǎng)絡(luò)負(fù)責(zé)學(xué)習(xí)模型之間的“配合方式”各干各的擅長事效果比直接堆模型要好。3. 數(shù)據(jù)預(yù)處理與特征構(gòu)造全流程3.1 缺失值處理遺傳位點(diǎn)數(shù)據(jù)最容易被忽略的問題遺傳位點(diǎn)字段的缺失率通常很高我當(dāng)時(shí)拿到的數(shù)據(jù)里有些SNP列缺失率接近30%。缺失率高不代表這些字段沒用反而可能是基因分型實(shí)驗(yàn)質(zhì)量差異導(dǎo)致的。直接刪掉這些列等于把可能包含預(yù)測信息的特征扔掉直接用均值填充又會(huì)把風(fēng)險(xiǎn)位點(diǎn)的信號“稀釋”掉。我的處理策略是分三檔。第一檔是缺失率超過50%的位點(diǎn)直接刪除這些列即使填充噪聲也遠(yuǎn)大于信號第二檔是缺失率在10%到50%之間的位點(diǎn)根據(jù)不同基因型出現(xiàn)的頻率做眾數(shù)填充同時(shí)保留一個(gè)“是否缺失”的指示列第三檔是缺失率低于10%的位點(diǎn)用中位數(shù)填充。這個(gè)“缺失指示列”很重要因?yàn)槲稽c(diǎn)缺失在某些情況下和人種、樣本批次有關(guān)可能隱含著非隨機(jī)性模型能沿著這個(gè)線索學(xué)到一些有用模式。生理指標(biāo)字段的缺失處理相對常規(guī)BMI、血壓這類用中位數(shù)填充更穩(wěn)健因?yàn)橹形粩?shù)對離群值不敏感。但我特別提醒一句千萬不要在劃分訓(xùn)練集和驗(yàn)證集之前就做全局填充。正確做法是先切分?jǐn)?shù)據(jù)再分別對訓(xùn)練集和驗(yàn)證集做同樣參數(shù)的填充否則驗(yàn)證集的信息會(huì)泄露到訓(xùn)練集里導(dǎo)致線下分?jǐn)?shù)虛高這里特別容易翻車。3.2 特征工程從原始字段到有效特征特征工程是我在這次比賽里花時(shí)間最多、收益也最高的部分。我總結(jié)了幾個(gè)行之有效的構(gòu)造方向每個(gè)方向都做了A/B測試證明確實(shí)有效才敢保留。第一個(gè)方向是家族史的組合特征。原始數(shù)據(jù)里父親糖尿病史、母親糖尿病史、兄弟姐妹糖尿病史是三個(gè)獨(dú)立字段。單獨(dú)看每個(gè)字段的區(qū)分度都不算強(qiáng)但組合起來“父母雙方都有兄弟姐妹之一有”這個(gè)模式的人患病風(fēng)險(xiǎn)遠(yuǎn)高于“只有一個(gè)直系親屬患病”的人。我把三個(gè)字段做了聚合家族史數(shù)量0到3父母雙方是否都有病史一級親屬患病率患病人數(shù)除以已知親屬數(shù)。這幾個(gè)組合特征對模型AUC的提升非常明顯大概貢獻(xiàn)了2個(gè)百分點(diǎn)的提升。第二個(gè)方向是SNP位點(diǎn)的分組特征。單個(gè)位點(diǎn)預(yù)測能力弱但多個(gè)風(fēng)險(xiǎn)位點(diǎn)疊加后風(fēng)險(xiǎn)會(huì)顯著上升。我先對每個(gè)位點(diǎn)做了單變量AUC分析把單變量AUC大于0.52的位點(diǎn)定義為“強(qiáng)風(fēng)險(xiǎn)位點(diǎn)”小于0.48的定義為“潛在保護(hù)位點(diǎn)”然后統(tǒng)計(jì)強(qiáng)風(fēng)險(xiǎn)位點(diǎn)總數(shù)和保護(hù)位點(diǎn)總數(shù)再計(jì)算二者的差值。這個(gè)“風(fēng)險(xiǎn)位點(diǎn)凈得分”成了我最強(qiáng)的一個(gè)特征在特征重要性排行里經(jīng)常排進(jìn)前五。第三個(gè)方向是生理指標(biāo)和遺傳特征的交互項(xiàng)。比如“攜帶風(fēng)險(xiǎn)位點(diǎn)且BMI偏高”和“不攜帶風(fēng)險(xiǎn)位點(diǎn)但BMI偏高”的人群患病風(fēng)險(xiǎn)曲線差異很大。通過樹模型的特征分裂路徑可以間接學(xué)到這種交互但顯式構(gòu)造交互特征能讓模型學(xué)得更快更準(zhǔn)。我用了兩種做法一種是人為構(gòu)造BMI和風(fēng)險(xiǎn)位點(diǎn)數(shù)的乘積項(xiàng)另一種是讓LightGBM直接處理原始特征靠樹的深層分裂來自動(dòng)尋找交互。實(shí)驗(yàn)結(jié)果是顯式構(gòu)造的交互特征讓模型收斂更快效果略好。3.3 特征篩選與數(shù)據(jù)劃分特征不是越多越好這一點(diǎn)在數(shù)據(jù)量不太大的比賽里尤其明顯。我初始構(gòu)造了80多個(gè)特征但最終只選了約40個(gè)進(jìn)入模型。篩選方法不是單純看特征重要性排序因?yàn)闃淠P偷奶卣髦匾匀菀资芟嚓P(guān)性影響兩個(gè)高度相關(guān)的特征可能會(huì)互相“分?jǐn)偂敝匾詫?dǎo)致兩個(gè)看起來都不重要。我用的方案是先從特征重要性排序里去掉明顯無用的底部特征再用“排列重要性”Permutation Importance做二次篩選——通過隨機(jī)打亂某個(gè)特征的值觀察模型效果下降幅度來判斷特征價(jià)值。效果下降越多說明該特征越重要幾乎沒有變化就可以刪除。數(shù)據(jù)劃分這塊要說一下遺傳風(fēng)險(xiǎn)預(yù)測和普通分類比賽不一樣可能存在較強(qiáng)的家庭聚集性。同一個(gè)家庭的成員遺傳位點(diǎn)和生活習(xí)慣都相似如果隨機(jī)劃分可能訓(xùn)練集和驗(yàn)證集里出現(xiàn)同族樣本導(dǎo)致驗(yàn)證分?jǐn)?shù)虛高。穩(wěn)妥做法是按家族ID進(jìn)行分組劃分GroupKFold確保同一家族的數(shù)據(jù)不會(huì)同時(shí)出現(xiàn)在訓(xùn)練集和驗(yàn)證集里。雖然官方數(shù)據(jù)未必顯式提供家族ID但可以通過一些間接特征做近似分組。這是很多人忽略但很重要的一環(huán)。4. 模型訓(xùn)練、調(diào)參與融合4.1 基線模型與訓(xùn)練流程我在正式上復(fù)雜模型之前先用邏輯回歸和隨機(jī)森林各跑了一遍基線。邏輯回歸的優(yōu)勢是結(jié)果可解釋能快速驗(yàn)證特征編碼是否正確隨機(jī)森林的優(yōu)勢是容錯(cuò)率高過擬合風(fēng)險(xiǎn)低。跑基線不是為了拿高分而是為了確認(rèn)整個(gè)訓(xùn)練-預(yù)測流程沒有低級錯(cuò)誤同時(shí)給后面的復(fù)雜模型提供一個(gè)對比基準(zhǔn)。我的流程大概是讀取數(shù)據(jù)做預(yù)處理和特征工程用GroupKFold切5折在每一折上分別訓(xùn)練模型并記錄AUC最后取均值和標(biāo)準(zhǔn)差。這一步非常關(guān)鍵因?yàn)閱未蝿澐值尿?yàn)證集分?jǐn)?shù)波動(dòng)可能很大必須用交叉驗(yàn)證來估計(jì)模型真實(shí)水平。我第一次只用了單一的隨機(jī)劃分驗(yàn)證集AUC是0.87感覺很不錯(cuò)結(jié)果5折交叉驗(yàn)證一做均分只有0.83標(biāo)準(zhǔn)差0.03——真實(shí)水平比想象中低不少。從那以后我只相信交叉驗(yàn)證的結(jié)果。4.2 核心模型參數(shù)詳解主力模型我選了LightGBM主要是因?yàn)樗?xùn)練速度快、內(nèi)存占用小、對類別特征支持友好而且能通過葉節(jié)點(diǎn)分裂自動(dòng)學(xué)到特征交互。下面是我調(diào)參后的一套參數(shù)可以直接作為起始配置import lightgbm as lgb params { objective: binary, learning_rate: 0.05, num_leaves: 31, max_depth: 5, min_child_samples: 20, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 1, lambda_l1: 0.1, lambda_l2: 1.0, metric: auc, seed: 42 } d_train lgb.Dataset(X_train, y_train) d_valid lgb.Dataset(X_valid, y_valid) model lgb.train( params, d_train, num_boost_round2000, valid_sets[d_valid], callbacks[lgb.early_stopping(100), lgb.log_evaluation(100)] )逐個(gè)說下參數(shù)的選擇邏輯。num_leaves控制在31附近太大容易過擬合太小擬合能力不足max_depth5是為了限制單棵樹的深度LightGBM的leaf-wise生長策略如果完全不限深度即使num_leaves不大也可能長出很深的樹feature_fraction0.8表示每棵樹隨機(jī)選取80%特征做分裂候選相當(dāng)于給模型增加了隨機(jī)性能緩解過擬合bagging_fraction0.8是行采樣同樣的作用lambda_l1和lambda_l2正則是防止某些分裂對噪聲過度敏感。調(diào)參順序也很重要。先固定一個(gè)較小的學(xué)習(xí)率和合理的樹結(jié)構(gòu)參數(shù)搜索num_leaves和max_depth然后固定這兩個(gè)值調(diào)feature_fraction和bagging_fraction最后調(diào)正則項(xiàng)。別一上來就用網(wǎng)格搜索把所有參數(shù)一起搜搜索空間太大效率極低。我用的是Optuna做貝葉斯優(yōu)化跑了大概200組試驗(yàn)最終在5折交叉驗(yàn)證上把AUC從0.84左右提升到了0.87。4.3 單模型融合策略單模型到一定程度后會(huì)有瓶頸這個(gè)瓶頸通常來自不同模型對同一批樣本的“盲區(qū)”。LightGBM和XGBoost雖然同屬梯度提升樹但分裂策略和正則方式不同錯(cuò)誤樣本并不是完全重疊的。我的做法是訓(xùn)練三個(gè)模型一個(gè)LightGBM、一個(gè)XGBoost、一個(gè)CatBoost然后在驗(yàn)證集上計(jì)算兩兩之間的預(yù)測相關(guān)性。如果相關(guān)性很高說明兩個(gè)模型學(xué)到的模式幾乎一樣融合意義不大相關(guān)性在0.85以下時(shí)融合收益會(huì)比較明顯。融合方法我用過兩種。一種是簡單加權(quán)平均權(quán)重按照各個(gè)模型驗(yàn)證集AUC的比例來定比如LightGBM的AUC是0.87XGBoost是0.86權(quán)重就按0.51比0.49分配。加權(quán)平均簡單可靠不容易過擬合適合快速驗(yàn)證融合思路。另一種是Stacking把三個(gè)模型的預(yù)測概率作為新的輸入特征再用一個(gè)邏輯回歸訓(xùn)練元模型。Stacking理論上效果更好但如果元模型訓(xùn)練不當(dāng)很容易過擬合。我用了一個(gè)省事且有效的變體——在每一折交叉驗(yàn)證中用訓(xùn)練折直接預(yù)測出驗(yàn)證折模型的輸出把交叉驗(yàn)證的預(yù)測結(jié)果拼起來作為stacking訓(xùn)練集這樣元模型看到的是“模型在陌生數(shù)據(jù)上的表現(xiàn)”而不是訓(xùn)練集上的虛高結(jié)果。最終線上AUC比最好的單模型提升了大約1.5個(gè)百分點(diǎn)融合這件事性價(jià)比很高值得做。5. 源碼結(jié)構(gòu)與復(fù)現(xiàn)指南5.1 項(xiàng)目目錄設(shè)計(jì)與模塊說明比賽結(jié)束后我整理源碼時(shí)刻意把項(xiàng)目組織成下面這個(gè)結(jié)構(gòu)方便自己復(fù)習(xí)也方便別人跑通diabetes_risk/ ├── data/ │ ├── train.csv │ ├── test.csv │ └── sample_submission.csv ├── src/ │ ├── config.py │ ├── preprocess.py │ ├── features.py │ ├── train.py │ ├── predict.py │ └── utils.py ├── models/ │ ├── lgb_model.txt │ ├── xgb_model.json │ └── cat_model.cbm └── README.mdconfig.py里集中管理參數(shù)和路徑避免在每個(gè)文件里硬編碼文件路徑。preprocess.py負(fù)責(zé)缺失值填充、字段類型轉(zhuǎn)換、數(shù)據(jù)劃分。features.py是核心里面實(shí)現(xiàn)了所有特征構(gòu)造函數(shù)每個(gè)函數(shù)都帶輸出說明保證可讀性。train.py負(fù)責(zé)讀入特征、訓(xùn)練模型、保存模型和特征重要性。predict.py讀入測試集生成預(yù)測文件。utils.py放一些輔助函數(shù)比如AUC計(jì)算、特征重要性繪圖。我強(qiáng)烈建議你在做類似項(xiàng)目時(shí)把特征工程單獨(dú)拆成一個(gè)文件。原因很現(xiàn)實(shí)比賽過程中你會(huì)反復(fù)調(diào)整特征如果特征代碼和模型代碼混在一起改一個(gè)特征可能引發(fā)連環(huán)報(bào)錯(cuò)排查起來非常痛苦。單獨(dú)拆開后features.py的輸出就是一份干凈的DataFrame模型文件只關(guān)心這個(gè)DataFrame長什么樣改動(dòng)成本會(huì)小很多。5.2 從訓(xùn)練到預(yù)測的完整流程復(fù)現(xiàn)整體流程大概分六步。第一步是修改config.py里的數(shù)據(jù)路徑和參數(shù)第二步運(yùn)行preprocess.py生成訓(xùn)練集和測試集的中間表第三步運(yùn)行features.py構(gòu)造特征第四步運(yùn)行train.py啟動(dòng)5折交叉驗(yàn)證訓(xùn)練這一步會(huì)打印每一折的AUC和整體均值第五步運(yùn)行predict.py生成測試集的預(yù)測概率第六步將預(yù)測結(jié)果按比賽要求的格式保存成CSV提交。train.py里建議加上斷點(diǎn)續(xù)跑和日志輸出功能。比如模型已經(jīng)訓(xùn)練過再運(yùn)行時(shí)可以直接讀取保存的模型文件不用重新訓(xùn)練。我用logging模塊記錄每一折的AUC、訓(xùn)練耗時(shí)、參數(shù)配置方便回溯哪一次改動(dòng)帶來了提升或下降。這些看起來是“工程化”的東西對提升比賽效率幫助卻很大因?yàn)樘斐乇荣愅ǔS刑峤淮螖?shù)限制每次提交前都值得確認(rèn)一遍“這次提交到底改了什么”。6. 常見問題與排查經(jīng)驗(yàn)6.1 過擬合與驗(yàn)證策略樹模型在這個(gè)賽題里最容易出現(xiàn)的問題就是過擬合癥狀是訓(xùn)練集AUC接近1驗(yàn)證集AUC死活上不去。我遇到過最離譜的一次訓(xùn)練集AUC超過0.99驗(yàn)證集AUC只有0.78差距大得出奇后來定位到原因一個(gè)SNP字段的編碼方式有誤把缺失值誤填成了目標(biāo)風(fēng)險(xiǎn)值模型相當(dāng)于直接“偷看”了答案。排查過擬合我有一個(gè)固定套路先畫訓(xùn)練集和驗(yàn)證集的AUC隨迭代次數(shù)的變化曲線。正常情況是兩條曲線同步上升驗(yàn)證集在某個(gè)點(diǎn)后開始回落如果訓(xùn)練集一路猛漲而驗(yàn)證集幾乎不動(dòng)基本可以斷定模型復(fù)雜度太高需要降低num_leaves或增大min_child_samples。如果驗(yàn)證集AUC在上升過程中出現(xiàn)鋸齒狀劇烈波動(dòng)通常是學(xué)習(xí)率太大或者訓(xùn)練數(shù)據(jù)噪聲多可以把學(xué)習(xí)率從0.1降到0.05甚至0.03。交叉驗(yàn)證分?jǐn)?shù)和線上分?jǐn)?shù)差距過大的另一個(gè)隱蔽原因是樣本分布不一致。賽題訓(xùn)練集和線上評測集可能來自不同的時(shí)間段或不同的采集批次如果只做隨機(jī)劃分模型并沒有機(jī)會(huì)暴露在分布漂移之下。我建議分析特征值的時(shí)序變化趨勢如果發(fā)現(xiàn)某些特征分布隨時(shí)間段有明顯偏移最好采用時(shí)間劃分或分組劃分來模擬評測環(huán)境。6.2 類別不平衡導(dǎo)致AUC虛高糖尿病患病人群在整體人群中比例不高訓(xùn)練數(shù)據(jù)的正負(fù)樣本比可能在1:4到1:9之間。類別不平衡本身并不可怕樹模型對不平衡有不錯(cuò)的容忍度可怕的是評估方式?jīng)]對應(yīng)調(diào)整。如果你用準(zhǔn)確率做早停模型可能會(huì)把所有樣本都預(yù)測成負(fù)類準(zhǔn)確率依然很高但AUC會(huì)很難看。我在訓(xùn)練里直接設(shè)置metricauc同時(shí)監(jiān)控交叉驗(yàn)證每個(gè)折的正負(fù)樣本預(yù)測分布。如果模型輸出的概率普遍偏低比如正樣本均值不到0.2說明模型沒有把正樣本和負(fù)樣本有效區(qū)分開這時(shí)候可以考慮用scale_pos_weight給正樣本加權(quán)因?yàn)槲矣玫氖荓ightGBMscale_pos_weight可以按正負(fù)樣本比例反比設(shè)置。我實(shí)測把scale_pos_weight設(shè)為負(fù)樣本數(shù)除以正樣本數(shù)后AUC有小幅提升但設(shè)得過大反而讓訓(xùn)練集AUC沖到很高、驗(yàn)證集下降。所以這個(gè)參數(shù)要配合交叉驗(yàn)證一起調(diào)不能盲目跟風(fēng)。特征分布和類別不平衡結(jié)合時(shí)還有個(gè)坑如果正樣本里家族史缺失率特別高模型可能把“家族史缺失”本身當(dāng)成風(fēng)險(xiǎn)信號導(dǎo)致線上表現(xiàn)不佳。對這種“缺失率與目標(biāo)相關(guān)”的情況我的處理是單獨(dú)分析每個(gè)字段在不同目標(biāo)類別下的缺失率如果差異過大再做缺失填充時(shí)更要謹(jǐn)慎必要時(shí)加入缺失指示列讓模型自己判斷。6.3 時(shí)間有限時(shí)的取舍建議賽季末很多人會(huì)陷入無休止的調(diào)參和刷榜但時(shí)間有限時(shí)注意力分配比悶頭調(diào)參更重要。我的經(jīng)驗(yàn)是先把精力花在特征工程上因?yàn)樘卣鞲倪M(jìn)帶來的收益上限最高然后做一套靠譜的交叉驗(yàn)證保證線下評估可信最后再搞模型融合。順序反了就會(huì)在最浪費(fèi)時(shí)間的事情上花費(fèi)最多時(shí)間。如果只能用半天時(shí)間沖刺我通常只做三件事第一用LightGBM默認(rèn)參數(shù)跑一個(gè)5折交叉驗(yàn)證確認(rèn)數(shù)據(jù)流程沒問題第二排查前3個(gè)重要特征的構(gòu)造邏輯看是否有明顯bug第三把三個(gè)不同seed的模型預(yù)測結(jié)果做平均這個(gè)操作幾乎零成本能穩(wěn)定提升零點(diǎn)幾個(gè)百分點(diǎn)的AUC。別小看這點(diǎn)提升天池這種比賽前三名和前十名的分?jǐn)?shù)差距有時(shí)候就在千分之幾。寫在最后的小經(jīng)驗(yàn)比賽結(jié)束很久之后回看這份源碼最讓我慶幸的不是用了什么高大上的模型而是堅(jiān)持做了兩件事一是每做一步特征或調(diào)參都會(huì)把驗(yàn)證集AUC變化記錄下來形成了一份完整的實(shí)驗(yàn)日志復(fù)盤時(shí)可以快速定位哪些改動(dòng)真正有效二是每隔一段時(shí)間就停下來說服自己“夠了該提交了”避免陷入無窮無盡的調(diào)參循環(huán)。如果你準(zhǔn)備參加類似的AI競賽找一份入門代碼跑通全流程比停留在“看教程”階段有效一百倍。把這份源碼當(dāng)作起點(diǎn)替換成你拿到的數(shù)據(jù)跑通然后根據(jù)本文提到的思路做特征和調(diào)參很快你就能做出一個(gè)像樣的排名。本文還有配套的精品資源點(diǎn)擊獲取