據(jù)實(shí)戰(zhàn)回歸預(yù)測(cè):從滯后特征到數(shù)據(jù)泄漏避坑指南)
1. 為什么拿疫情數(shù)據(jù)練回歸模型1.1 這不是蹭熱點(diǎn)是一個(gè)回歸問(wèn)題最好的入門(mén)樣本在機(jī)器學(xué)習(xí)的各類任務(wù)里回歸是最基礎(chǔ)、也最容易被低估的一種。很多人習(xí)慣用房?jī)r(jià)預(yù)測(cè)、波士頓房?jī)r(jià)、加利福尼亞房?jī)r(jià)做演示但這些數(shù)據(jù)集已經(jīng)被寫(xiě)爛了缺少真實(shí)場(chǎng)景的臟亂差和波動(dòng)性。相比之下疫情人數(shù)預(yù)測(cè)這個(gè)選題天然具備回歸問(wèn)題的所有要素?cái)?shù)值型目標(biāo)、時(shí)序相關(guān)特征、外部擾動(dòng)、周期性變化以及一個(gè)很重要但常被忽視的殺手——數(shù)據(jù)不干凈。當(dāng)時(shí)我的想法很簡(jiǎn)單與其空談理論不如拿真實(shí)世界里大家都有感知的數(shù)據(jù)建一個(gè)回歸模型預(yù)測(cè)未來(lái)幾天的新增感染人數(shù)。利益相關(guān)不大但數(shù)據(jù)公開(kāi)、可復(fù)現(xiàn)而且這個(gè)場(chǎng)景里的坑特別多踩坑的過(guò)程比結(jié)果有價(jià)值得多。這篇文章適合什么人如果你剛剛學(xué)完線性回歸想找一個(gè)比教材案例更“真實(shí)”的練習(xí)如果你已經(jīng)會(huì)用 sklearn 跑回歸但對(duì)時(shí)間序列數(shù)據(jù)做回歸時(shí)容易掉進(jìn)數(shù)據(jù)泄漏、自相關(guān)陷阱那么這篇文章正好對(duì)路。你不需要有深厚的數(shù)學(xué)功底但最好會(huì)一點(diǎn) Python 和 pandas。1.2 建模目標(biāo)預(yù)測(cè)新增人數(shù)而不是累計(jì)人數(shù)很多初學(xué)者第一次拿到疫情數(shù)據(jù)第一反應(yīng)是預(yù)測(cè)累計(jì)確診人數(shù)。這個(gè)選擇在回歸問(wèn)題上是一個(gè)典型的坑累計(jì)序列是一條單調(diào)遞增的曲線無(wú)論用什么模型只要把昨天的累計(jì)值搬過(guò)來(lái)就能得到非常漂亮的 R2 0.99 以上看起來(lái)厲害得不行實(shí)際上毫無(wú)意義。我在實(shí)際建模時(shí)把目標(biāo)定為“每日新增確診人數(shù)”也就是當(dāng)日新增值。這個(gè)指標(biāo)波動(dòng)更大、更貼近“回歸預(yù)測(cè)”的本質(zhì)——模型需要從歷史特征里學(xué)到變化規(guī)律而不是靠慣性復(fù)制前一天的累計(jì)值。預(yù)測(cè)新增人數(shù)的問(wèn)題也更現(xiàn)實(shí)因?yàn)樗苯臃从沉藗鞑ニ俣鹊淖兓皇谴媪恳?guī)模。所以先立個(gè)規(guī)矩永遠(yuǎn)不要預(yù)測(cè)累計(jì)值。累計(jì)值天然是上一期的函數(shù)模型很容易作弊。預(yù)測(cè)增量、預(yù)測(cè)變化率、預(yù)測(cè)差分值這類目標(biāo)才有挑戰(zhàn)性。2. 數(shù)據(jù)準(zhǔn)備從拿到原始序列到能喂給模型2.1 數(shù)據(jù)源選擇與字段說(shuō)明疫情數(shù)據(jù)有很多公開(kāi)渠道GitHub 上也有很多歷史存檔。我用的是一份按天記錄的確診數(shù)據(jù)字段非常干凈主要包括日期、地區(qū)、當(dāng)日新增、當(dāng)日治愈、當(dāng)日死亡、累計(jì)確診。為了演示我只保留日期和當(dāng)日新增兩個(gè)字段先做一個(gè)單變量回歸。有兩點(diǎn)要提醒新手。第一從原始網(wǎng)頁(yè)或 CSV 讀入數(shù)據(jù)后一定要把日期列解析成 datetime 類型并且用pd.to_datetime強(qiáng)制轉(zhuǎn)換否則后續(xù)排序、做滯后特征時(shí)到處報(bào)錯(cuò)。第二觀察數(shù)據(jù)是否存在缺失日期。部分?jǐn)?shù)據(jù)源在早期會(huì)跳過(guò)某些天或者某幾天集中在同一天上報(bào)這些需要顯式處理。我當(dāng)時(shí)拿到數(shù)據(jù)后先做了三件事檢查日期是否連續(xù)缺的天數(shù)用前一天的值填充或者直接刪掉視缺失比例而定。把新增值中的負(fù)數(shù)和異常大值標(biāo)記出來(lái)比如某天突然幾萬(wàn)需要核對(duì)是不是重復(fù)統(tǒng)計(jì)。做一個(gè)初步的可視化折線圖看趨勢(shì)、是否有明顯的尖峰和周期性。這一步看著瑣碎但后續(xù)模型效果好不好一半取決于這里的數(shù)據(jù)清洗。2.2 基礎(chǔ)特征滯后項(xiàng)、滑動(dòng)平均、星期指數(shù)拿到每天的“當(dāng)日新增”序列之后接下來(lái)要把一維序列變成回歸模型可以吃的 tabular 數(shù)據(jù)。最核心的思路是用過(guò)去若干天的值作為特征預(yù)測(cè)當(dāng)天的值。舉例來(lái)說(shuō)如果你用前 7 天的新增人數(shù)來(lái)預(yù)測(cè)第 8 天那么對(duì)于某條樣本特征就是第 1 到第 7 天的值標(biāo)簽就是第 8 天的值。這種特征叫滯后特征lag features在時(shí)間序列回歸里是最常用的做法。我實(shí)際用的特征組合包括滯后 1 天到滯后 21 天的當(dāng)日新增值覆蓋 3 周的傳播周期。滯后 3 天和 7 天的滑動(dòng)平均用來(lái)平滑突發(fā)波動(dòng)。星期因子疫情數(shù)據(jù)經(jīng)常有“星期日低、星期二高”的人工報(bào)告周期引入星期幾作為類別特征可以讓模型學(xué)到這種偏置。滯后 7 天與滯后 14 天的比值用來(lái)表達(dá)增長(zhǎng)趨勢(shì)的方向。不要全部堆上去特征太多在小樣本下會(huì)過(guò)擬合。我當(dāng)時(shí)的數(shù)據(jù)一共只有 500 多天樣本量并不大所以保留 7 到 15 個(gè)特征是比較安全的區(qū)間。2.3 數(shù)據(jù)切分與時(shí)間序列陷阱這里的坑比模型本身更值得講。普通回歸做訓(xùn)練測(cè)試切分是隨機(jī)打亂數(shù)據(jù)但時(shí)間序列數(shù)據(jù)絕對(duì)不能隨機(jī)切。你今天的數(shù)據(jù)和三個(gè)月前的數(shù)據(jù)之間存在時(shí)間依賴隨機(jī)切會(huì)把未來(lái)的信息放進(jìn)訓(xùn)練集導(dǎo)致模型在測(cè)試集上得分虛高上線后發(fā)現(xiàn)模型“吃未來(lái)”。正確的做法是按時(shí)間順序切分。比如前 80% 作為訓(xùn)練集后 20% 作為測(cè)試集。更嚴(yán)格一點(diǎn)的做法是使用時(shí)間序列交叉驗(yàn)證比如擴(kuò)大窗口方式先用前 100 天訓(xùn)練預(yù)測(cè)第 101-110 天再用前 110 天訓(xùn)練預(yù)測(cè)第 111-120 天以此類推。我在第一次實(shí)驗(yàn)時(shí)因?yàn)樨澐奖阌昧藅rain_test_split默認(rèn)的隨機(jī)切分結(jié)果在測(cè)試集上 R2 高達(dá) 0.95當(dāng)時(shí)還很高興。后來(lái)發(fā)現(xiàn)訓(xùn)練集中混入了測(cè)試集后面的未來(lái)數(shù)據(jù)等于提前告訴模型答案。換成按時(shí)間順序切分后R2 立刻掉到 0.6 以下這才是正常水平。3. 回歸模型怎么選從線性回歸到隨機(jī)森林、XGBoost3.1 線性回歸先跑通看系數(shù)和殘差我一直主張任何回歸項(xiàng)目先跑一個(gè)最簡(jiǎn)單的線性回歸不是因?yàn)樗Ч詈枚且驗(yàn)樗芙o你一個(gè)基線并且讓你看到系數(shù)和殘差。很多新手一上來(lái)就是 XGBoost結(jié)果模型調(diào)了一堆參數(shù)卻說(shuō)不清楚數(shù)據(jù)里到底有什么信號(hào)。線性回歸的假設(shè)是特征和目標(biāo)之間存在線性關(guān)系。放在疫情數(shù)據(jù)上滯后項(xiàng)其實(shí)就是過(guò)去的觀測(cè)值相當(dāng)于一個(gè)自回歸模型。你會(huì)發(fā)現(xiàn)線性回歸在這個(gè)問(wèn)題上并不差因?yàn)橄噜弾滋斓男略鋈藬?shù)本身高度相關(guān)僅靠滯后 1 天和滯后 7 天的特征就能解釋一部分方差。跑完線性回歸重點(diǎn)看兩個(gè)東西系數(shù)的符號(hào)和大小滯后 1 天的系數(shù)通常最大接近 1。這說(shuō)明當(dāng)日新增和前一天高度相關(guān)屬于正常現(xiàn)象。但如果所有滯后項(xiàng)的系數(shù)都亂糟糟說(shuō)明特征之間存在多重共線性可以考慮用嶺回歸。殘差是否隨機(jī)把預(yù)測(cè)值和真實(shí)值畫(huà)出來(lái)再畫(huà)殘差圖。如果殘差呈現(xiàn)明顯的波浪形或趨勢(shì)說(shuō)明模型沒(méi)有捕捉到周期性和突變信息需要加特征或換模型。線性回歸的另一個(gè)作用是檢查是否有明顯的“滯后復(fù)制”現(xiàn)象。如果預(yù)測(cè)序列比真實(shí)序列晚一天說(shuō)明模型幾乎是在搬運(yùn)前一天的數(shù)值這是自回歸模型常見(jiàn)的病態(tài)。3.2 正則化與嶺回歸處理多重共線性滯后特征之間天然高度相關(guān)比如第 5 天和第 6 天的新增人數(shù)相關(guān)性可以達(dá)到 0.9 以上。這種情況下普通最小二乘回歸的系數(shù)估計(jì)方差會(huì)變得很大訓(xùn)練集擬合好測(cè)試集一塌糊涂。解決多重共線性有兩個(gè)常用思路一是做特征篩選只留少量滯后項(xiàng)二是用正則化方法比如嶺回歸L2 正則和 LassoL1 正則。我當(dāng)時(shí)把線性回歸替換成嶺回歸參數(shù)alpha取 1.0效果比普通線性回歸穩(wěn)很多。嶺回歸會(huì)讓系數(shù)的絕對(duì)值變小不會(huì)像普通回歸那樣出現(xiàn)正負(fù)交替的大系數(shù)。如果你用的是 Pythonsklearn.linear_model.Ridge里有一個(gè)RidgeCV可以交叉驗(yàn)證自動(dòng)選 alpha非常方便。還有人會(huì)想到邏輯回歸。這里提醒一下邏輯回歸本身是做分類的輸出的是概率不適用于連續(xù)數(shù)值的預(yù)測(cè)。除非你把新增人數(shù)分成高、中、低三檔否則邏輯回歸在這個(gè)項(xiàng)目里用不上。3.3 樹(shù)模型隨機(jī)森林回歸與 XGBoost 回歸的對(duì)比樹(shù)模型能擬合非線性關(guān)系也能捕捉特征之間的交互比如“如果前一天新增很高但滯后 7 天在下降則可能出現(xiàn)拐點(diǎn)”這種規(guī)則。所以我會(huì)在基線之后跑隨機(jī)森林回歸和 XGBoost 回歸。隨機(jī)森林的優(yōu)點(diǎn)是參數(shù)少不容易過(guò)擬合開(kāi)箱即用。它對(duì)特征縮放不敏感也不需要像線性回歸那樣嚴(yán)格處理共線性。缺點(diǎn)是外推能力弱如果訓(xùn)練集中的最大值是 10 萬(wàn)測(cè)試集突然出現(xiàn) 20 萬(wàn)隨機(jī)森林最多預(yù)測(cè)到接近訓(xùn)練集的最大值無(wú)法外推得更高。疫情數(shù)據(jù)經(jīng)常會(huì)出現(xiàn)新高峰這會(huì)導(dǎo)致隨機(jī)森林在突增階段預(yù)測(cè)嚴(yán)重偏低。XGBoost 回歸在帶正則的梯度提升框架下表現(xiàn)更靈活而且可以通過(guò)樹(shù)的深度和葉子節(jié)點(diǎn)權(quán)重控制復(fù)雜度。它的缺點(diǎn)是超參數(shù)多調(diào)參需要時(shí)間。我常用的參數(shù)組合是learning_rate0.05max_depth4n_estimators300subsample0.8colsample_bytree0.8。如果數(shù)據(jù)量不大一定要開(kāi) early stopping不然會(huì)過(guò)擬合訓(xùn)練集。樹(shù)模型和線性模型在這個(gè)項(xiàng)目上的表現(xiàn)差異很有意思。在平穩(wěn)期線性回歸和樹(shù)模型差距不大因?yàn)橄噜徧鞌?shù)的高度相關(guān)性已經(jīng)提供了主要信號(hào)。但在疫情快速上升或下降階段樹(shù)模型對(duì)突變點(diǎn)的反應(yīng)往往更遲鈍線性模型對(duì)幅度的外推反而稍好。這也是為什么一個(gè)成熟的建模流程里應(yīng)該同時(shí)跑多類模型而不是把寶押在某個(gè)熱門(mén)模型上。3.4 為什么不做時(shí)序?qū)S媚P捅疚囊曰貧w視角做對(duì)照提到時(shí)序預(yù)測(cè)很多人會(huì)想到 LSTM、Prophet、ARIMA。這些確實(shí)是時(shí)間序列領(lǐng)域常用的方法但回歸模型的優(yōu)勢(shì)在于可解釋性、門(mén)檻低、調(diào)試方便以及可以靈活加入外部特征。本文選擇用回歸模型來(lái)做不是否定時(shí)序模型而是希望提供一個(gè)更容易理解的基線。ARIMA 適合平穩(wěn)序列疫情數(shù)據(jù)在爆發(fā)期的非平穩(wěn)性非常強(qiáng)直接用 ARIMA 需要反復(fù)差分和調(diào)參。Prophet 對(duì)假期、趨勢(shì)和季節(jié)性的處理很優(yōu)雅但它的擬合過(guò)程像一個(gè)黑盒出了問(wèn)題不好排查。LSTM 需要的數(shù)據(jù)量更大在幾百個(gè)樣本上的表現(xiàn)可能不如樹(shù)模型穩(wěn)定。所以我建議把回歸模型作為基準(zhǔn)如果回歸效果已經(jīng)夠用就不必急著上復(fù)雜模型。4. 特征工程與實(shí)操代碼把回歸跑起來(lái)4.1 生成滯后特征的代碼下面是我實(shí)際用來(lái)生成滯后特征和訓(xùn)練集的代碼基于 pandas 和 sklearn。這段代碼會(huì)讀取一份包含 date 和 new_case 兩列的 CSV 文件生成滯后特征。import pandas as pd import numpy as np df pd.read_csv(covid_data.csv) df[date] pd.to_datetime(df[date]) df df.sort_values(date).reset_index(dropTrue) # 生成滯后1-21天的特征 for lag in range(1, 22): df[flag_{lag}] df[new_case].shift(lag) # 生成滑動(dòng)平均特征 df[ma_3] df[new_case].rolling(3).mean().shift(1) df[ma_7] df[new_case].rolling(7).mean().shift(1) # 星期因子 df[weekday] df[date].dt.weekday # 刪除前21行因?yàn)槟切┬械臏筇卣鳛榭?df df.iloc[21:].reset_index(dropTrue) # 定義特征和標(biāo)簽 features [flag_{i} for i in range(1, 22)] [ma_3, ma_7, weekday] X df[features] y df[new_case] # 按時(shí)間順序切分 train_size int(len(df) * 0.8) X_train, X_test X.iloc[:train_size], X.iloc[train_size:] y_train, y_test y.iloc[:train_size], y.iloc[train_size:]這段代碼里有幾個(gè)細(xì)節(jié)值得說(shuō)明。shift(lag)會(huì)把當(dāng)天之前第 lag 天的值放到當(dāng)前行如果數(shù)據(jù)已經(jīng)按日期排序這個(gè)操作就是正確的。滑動(dòng)平均必須用.shift(1)否則未來(lái)數(shù)據(jù)會(huì)泄漏進(jìn)來(lái)也就是用當(dāng)天及當(dāng)天的前 3 天均值去預(yù)測(cè)當(dāng)天這等于把標(biāo)簽信息帶進(jìn)了特征。4.2 訓(xùn)練集/驗(yàn)證集切分注意不要 shuffle上面代碼里用的是順序切分沒(méi)有打亂數(shù)據(jù)。這里再?gòu)?qiáng)調(diào)一次如果有人改了參數(shù)train_test_split里的shuffleFalse那是可以的但千萬(wàn)不要設(shè)置shuffleTrue。如果使用TimeSeriesSplit做交叉驗(yàn)證會(huì)更嚴(yán)謹(jǐn)因?yàn)橐咔閿?shù)據(jù)的趨勢(shì)是不斷變化的單一時(shí)間點(diǎn)切分只能驗(yàn)證某一階段的預(yù)測(cè)能力。我第一次測(cè)試時(shí)用train_test_split(test_size0.2, shuffleTrue)模型得分虛高到可以騙過(guò)自己。后來(lái)學(xué)乖了所有時(shí)間序列任務(wù)一律用順序切分。4.3 模型訓(xùn)練與超參數(shù)設(shè)定線性模型直接調(diào)用即可from sklearn.linear_model import Ridge from sklearn.ensemble import RandomForestRegressor from xgboost import XGBRegressor models { ridge: Ridge(alpha1.0), rf: RandomForestRegressor(n_estimators300, max_depth5, random_state42), xgb: XGBRegressor( learning_rate0.05, max_depth4, n_estimators300, subsample0.8, colsample_bytree0.8, random_state42 ) } for name, model in models.items(): model.fit(X_train, y_train) score model.score(X_test, y_test) # R2 print(f{name} R2: {score:.4f})Ridge(alpha1.0)是一個(gè)比較保守的正則強(qiáng)度如果訓(xùn)練集很大alpha 可以調(diào)小一點(diǎn)。RandomForestRegressor的max_depth不要設(shè)置得太深疫情數(shù)據(jù)本身只有幾百個(gè)樣本如果讓樹(shù)無(wú)限生長(zhǎng)很容易把所有訓(xùn)練樣本都記住。XGBoost 的核心是learning_rate和n_estimators的配合學(xué)習(xí)率越小需要的樹(shù)越多但不代表越多越好。4.4 評(píng)估指標(biāo)回歸任務(wù)最常用的三個(gè)指標(biāo)是均方誤差MSE、均方根誤差RMSE和平均絕對(duì)誤差MAE。我一般會(huì)同時(shí)看 RMSE 和 MAE因?yàn)檫@兩個(gè)指標(biāo)的含義不同。RMSE 對(duì)大誤差特別敏感如果某一天模型預(yù)測(cè)值和真實(shí)值相差 5000這個(gè)誤差會(huì)在 RMSE 里貢獻(xiàn)很大。MAE 反映的是平均誤差水平不容易被極端值帶偏。比如兩個(gè)模型模型 A每天都在真實(shí)值附近波動(dòng)偶爾某一天誤差巨大。模型 B每天誤差都在 1000 左右但沒(méi)有極大值。模型 A 的 RMSE 可能很高M(jìn)AE 可能略低模型 B 的 RMSE 適中MAE 較高。放在疫情預(yù)測(cè)場(chǎng)景里我更關(guān)注 MAE因?yàn)檎鎸?shí)使用者不會(huì)只關(guān)心某一兩天的極端誤差而是整體偏差水平。多步預(yù)測(cè)時(shí)還可以計(jì)算 MAPE平均絕對(duì)百分比誤差但要注意新增人數(shù)在低位時(shí)MAPE會(huì)劇烈膨脹比如真實(shí)值 10預(yù)測(cè)值 20誤差是 100%看著嚇人但實(shí)際偏差只有 10 人。所以百分比誤差在數(shù)值較小的場(chǎng)景里要慎用。5. 常見(jiàn)問(wèn)題排查與避坑實(shí)錄5.1 預(yù)測(cè)值變成前一天的水平——“偽預(yù)測(cè)”這是我調(diào)試階段遇到的問(wèn)題中最常見(jiàn)的一個(gè)。模型跑完畫(huà)圖預(yù)測(cè)曲線和真實(shí)曲線高度重合但仔細(xì)看預(yù)測(cè)曲線整體向右平移了一天。換言之模型并沒(méi)有預(yù)測(cè)明天它只是把今天的值復(fù)制到明天而因?yàn)榈诙煺鎸?shí)值和第一天往往相差不大所以誤差看起來(lái)也很小。解決思路有兩步。第一步是使用多步滯后特征而不僅是滯后 1 天。第二步是計(jì)算“一步延遲相關(guān)”對(duì)比預(yù)測(cè)序列與真實(shí)序列錯(cuò)位一天后的相關(guān)系數(shù)如果相關(guān)性明顯高于對(duì)應(yīng)當(dāng)天的相關(guān)系數(shù)就說(shuō)明模型在偷懶。增加滯后 7 天的特征可以緩解一部分這類問(wèn)題但根本上模型確實(shí)缺少對(duì)趨勢(shì)的捕捉。如果你想讓模型準(zhǔn)確預(yù)測(cè)拐點(diǎn)還需要加入外部變量比如政策變化、出行指數(shù)等。5.2 數(shù)據(jù)泄漏用未來(lái)信息預(yù)測(cè)過(guò)去這個(gè)坑在特征工程里特別隱蔽。我見(jiàn)過(guò)有人把當(dāng)天的“治愈人數(shù)”也作為特征這看起來(lái)合理因?yàn)獒t(yī)院忙不忙可能和感染人數(shù)有關(guān)。但問(wèn)題是治愈人數(shù)在當(dāng)天公布之前是拿不到的它本身就是一個(gè)未來(lái)值。類似地滑動(dòng)平均如果沒(méi)有 shift也屬于泄漏。還有一個(gè)容易被忽略的場(chǎng)景在數(shù)據(jù)清洗階段用整段數(shù)據(jù)的均值或中位數(shù)填充缺失值。如果你在訓(xùn)練前用全量的均值填充了測(cè)試集中的缺失值那么這個(gè)均值包含了未來(lái)信息。正確的做法是只用訓(xùn)練部分的統(tǒng)計(jì)量來(lái)填充或者用前向填充。我建議每次生成特征后都從訓(xùn)練集和測(cè)試集中隨機(jī)抽樣幾行手動(dòng)檢查這些特征的值在樣本當(dāng)天是否真的“已知”。比如預(yù)測(cè) 3 月 10 日時(shí)特征里是否出現(xiàn)了 3 月 10 日當(dāng)天的數(shù)據(jù)如果有就是泄漏。5.3 疫情數(shù)據(jù)的特殊波動(dòng)節(jié)假日、變異株爆發(fā)真實(shí)疫情數(shù)據(jù)不像教科書(shū)數(shù)據(jù)集那么干凈。我遇到的第一個(gè)問(wèn)題是明顯的星期周期性——周一的新增數(shù)通常比周中低原因是周末檢測(cè)量減少、報(bào)告延遲。這種周期性不是疫情本身的傳播規(guī)律而是報(bào)告流程造成的模型如果看到了星期特征能學(xué)到一個(gè)固定的星期偏移但無(wú)法理解為什么某個(gè)假期后數(shù)據(jù)突然翻倍。更麻煩的是突發(fā)事件。比如某段時(shí)間出現(xiàn)了新的傳播毒株感染人數(shù)快速上升模型訓(xùn)練集中從來(lái)沒(méi)有見(jiàn)過(guò)這種形態(tài)。這種情況下基于回歸的模型普遍會(huì)低估新增數(shù)因?yàn)樗奶煨允窍驓v史均值回歸而不是猜測(cè)“這是一個(gè)新世界”。面對(duì)這種數(shù)據(jù)漂移一個(gè)有效的手段是給訓(xùn)練樣本加上時(shí)間衰減權(quán)重距離當(dāng)前時(shí)間越近的樣本權(quán)重越高讓模型更關(guān)注最近的變化。XGBoost 里可以直接用sample_weight傳遞權(quán)重我試過(guò)之后對(duì)近期突變的擬合確實(shí)更敏感。5.4 評(píng)估指標(biāo)選擇的坑RMSE 容易被大值綁架有一段時(shí)間我覺(jué)得 RMSE 變小了很多心里還挺高興。后來(lái)我看了殘差分布發(fā)現(xiàn)原因是模型在大多數(shù)平穩(wěn)日子的預(yù)測(cè)變得保守把誤差控制在幾百以內(nèi)但在某幾個(gè)爆發(fā)日誤差超過(guò)一萬(wàn)導(dǎo)致 RMSE 下降不明顯甚至不降反升。如果只看 RMSE會(huì)以為模型沒(méi)有進(jìn)步其實(shí) MAE 已經(jīng)改善了很多。后來(lái)我改用“分位數(shù)評(píng)估法”把真實(shí)值和預(yù)測(cè)值分別按天排序看模型在高值區(qū)間前 10% 的天的誤差也看低值區(qū)間的誤差。對(duì)于傳染病預(yù)測(cè)高值區(qū)間才是需要警惕的因?yàn)楦咧狄馕吨焖僭鲩L(zhǎng)政策響應(yīng)往往依賴這些數(shù)值。如果你只關(guān)注全局平均誤差模型可能為了“平均”犧牲掉對(duì)高峰的敏銳度。6. 值得再深挖的方向6.1 多步預(yù)測(cè)遞歸預(yù)測(cè)與直接預(yù)測(cè)上面的模型本質(zhì)上是一個(gè)“單步預(yù)測(cè)器”預(yù)測(cè)明天需要用到今天的真實(shí)值。但在實(shí)際使用中我們往往需要預(yù)測(cè)未來(lái) 7 天甚至 14 天這時(shí)候兩個(gè)選擇遞歸預(yù)測(cè)和直接預(yù)測(cè)。遞歸預(yù)測(cè)的思路是先用模型預(yù)測(cè)明天然后把預(yù)測(cè)結(jié)果作為特征輸入到模型里繼續(xù)預(yù)測(cè)后天如此滾動(dòng)。這種方式的優(yōu)點(diǎn)是只需訓(xùn)練一個(gè)模型缺點(diǎn)是一旦前面預(yù)測(cè)偏了誤差會(huì)不斷累積預(yù)測(cè)到第 7 天時(shí)可能已經(jīng)完全偏離。直接預(yù)測(cè)的思路是為每一天訓(xùn)練一個(gè)獨(dú)立的模型比如模型 1 預(yù)測(cè)明天模型 2 預(yù)測(cè)明天加后天依此類推。每個(gè)模型根據(jù)自己的滯后特征預(yù)測(cè)固定天數(shù)后的值不會(huì)把中間預(yù)測(cè)誤差帶進(jìn)來(lái)。缺點(diǎn)是訓(xùn)練成本高而且多個(gè)模型之間可能不一致。從穩(wěn)定性的角度在樣本量有限時(shí)我傾向于遞歸預(yù)測(cè)但要對(duì)誤差累積有心理準(zhǔn)備。如果你想做得更細(xì)可以結(jié)合兩種方式用直接預(yù)測(cè)模型的結(jié)果對(duì)遞歸預(yù)測(cè)做校正。6.2 加入外部變量天氣、出行、疫苗數(shù)據(jù)只靠歷史新增人數(shù)做回歸模型能學(xué)到的基本就是一個(gè)平滑器。真正想在預(yù)測(cè)上提升必須加入外部變量。比如可以把每日新增特征換成 7 天移動(dòng)平均再加入一個(gè)“出行強(qiáng)度指數(shù)”因?yàn)槿肆髁肯陆禃?huì)直接影響傳播速度。還有疫苗接種率、重點(diǎn)區(qū)域新增占比、檢測(cè)數(shù)量等都能提供額外信息。不過(guò)外部變量也有風(fēng)險(xiǎn)。一方面很多數(shù)據(jù)的統(tǒng)計(jì)口徑經(jīng)常調(diào)整特征前后不一致模型會(huì)學(xué)到假規(guī)律。另一方面外部變量本身也有發(fā)布時(shí)間延遲比如今天的出行指數(shù)可能要第二天才能拿到如果用它預(yù)測(cè)今天等于用了未來(lái)數(shù)據(jù)。所以加外部變量前一定要確認(rèn)這些特征在預(yù)測(cè)時(shí)點(diǎn)已經(jīng)可知。6.3 從不完美預(yù)測(cè)中得到的教訓(xùn)我做這件事最大的收獲不是某個(gè)模型擬合得多好而是接受一個(gè)事實(shí)傳染病傳播無(wú)法用簡(jiǎn)單回歸模型準(zhǔn)確預(yù)測(cè)。真實(shí)世界中人的行為變化、政策調(diào)整、病毒變異這些都是模型看不到的變量?;貧w模型的價(jià)值不在于它給出了一個(gè)“準(zhǔn)確答案”而在于它提供了一個(gè)可量化的基線。如果你把這個(gè)項(xiàng)目當(dāng)作學(xué)習(xí)素材建議不要只盯著 R2 和 RMSE。多畫(huà)幾條曲線多看看模型在哪些階段失效多想想失效的原因這些比調(diào)參更有意義。當(dāng)我看到預(yù)測(cè)曲線在高峰期遠(yuǎn)遠(yuǎn)低于真實(shí)值時(shí)我理解了為什么機(jī)器學(xué)習(xí)里的“外推”困難當(dāng)我看到平穩(wěn)期的預(yù)測(cè)幾乎完美時(shí)我理解了自相關(guān)性強(qiáng)大的威力。這些體會(huì)比任何教材都能更深刻地帶你進(jìn)入數(shù)據(jù)分析的實(shí)戰(zhàn)狀態(tài)。最后再分享一個(gè)小技巧做這類預(yù)測(cè)項(xiàng)目時(shí)盡量保留每一次實(shí)驗(yàn)的參數(shù)和結(jié)果寫(xiě)成一個(gè)實(shí)驗(yàn)日志。我當(dāng)時(shí)沒(méi)有記錄后來(lái)想復(fù)現(xiàn)一個(gè)幾周前效果不錯(cuò)的模型卻怎么也想不起來(lái)當(dāng)時(shí)用了哪些特征、 alpha 是多少。從那天起我老老實(shí)實(shí)用 CSV 記錄每次實(shí)驗(yàn)的配置和結(jié)果這個(gè)習(xí)慣幫我省了大量時(shí)間。