分市場銷量預(yù)測:分層建模與特征工程實(shí)戰(zhàn))
1. 賽題背景與整體思路拆解1.1 這個賽題到底在預(yù)測什么乘用車細(xì)分市場銷量預(yù)測說白了就是給定歷史若干個月的車型銷量數(shù)據(jù)去預(yù)測未來若干個月、不同車型在不同細(xì)分市場下的銷量。2019年那場競賽給出的數(shù)據(jù)維度相當(dāng)豐富車型配置、品牌、車身類型、排量、變速箱、上市時間、指導(dǎo)價(jià)區(qū)間以及按月份排列的銷量序列。目標(biāo)是把未來一段時間內(nèi)每個車型-細(xì)分市場組合的銷量預(yù)測準(zhǔn)。這類問題的難點(diǎn)不在于模型有多深而在于數(shù)據(jù)本身極其稀疏且波動劇烈。一款車可能上市三個月月銷過萬然后突然掉到幾百臺也可能長期穩(wěn)定在某個區(qū)間。再加上細(xì)分市場劃分比如按車身形式、按價(jià)格帶、按動力類型每個細(xì)分市場里的樣本量差異巨大有的細(xì)分市場只有幾十條記錄有的上千條。這種長尾分布直接決定了不能用一個統(tǒng)一模型硬套所有細(xì)分市場。適合參考這篇內(nèi)容的人我大致分三類一是打過結(jié)構(gòu)化數(shù)據(jù)預(yù)測類競賽、想看看別人怎么處理稀疏時序的二是做汽車行業(yè)數(shù)據(jù)分析、需要落地銷量預(yù)測的從業(yè)者三是剛?cè)腴T機(jī)器學(xué)習(xí)、想找一個完整項(xiàng)目練手的同學(xué)。不管哪一類核心收獲應(yīng)該是一樣的——如何把臟、亂、稀疏的業(yè)務(wù)數(shù)據(jù)轉(zhuǎn)化成模型能吃的形態(tài)并且在驗(yàn)證環(huán)節(jié)不騙自己。1.2 為什么最終方案是分而治之而不是端到端我們隊(duì)伍在賽后復(fù)盤時最大的共識就是這個賽題如果強(qiáng)行做一個端到端的深度模型大概率會翻車。原因有三個。第一數(shù)據(jù)量不夠。雖然看起來有幾萬條記錄但分?jǐn)偟矫總€細(xì)分市場、每個車型、每個時間點(diǎn)上可用樣本非常有限。深度模型在這種稀疏場景下極易過擬合驗(yàn)證集稍微一變線上成績就崩。第二業(yè)務(wù)邏輯強(qiáng)。汽車銷量有明顯的季節(jié)性金九銀十、春節(jié)前后低谷、政策周期性排放標(biāo)準(zhǔn)切換、購置稅優(yōu)惠、產(chǎn)品生命周期改款、停產(chǎn)。這些因素用樹模型加人工特征工程比讓神經(jīng)網(wǎng)絡(luò)自己學(xué)要靠譜得多。第三評估指標(biāo)敏感。競賽用的評估指標(biāo)對高銷量車型的絕對誤差懲罰很重這意味著大銷量車型的預(yù)測精度直接決定排名而小銷量車型預(yù)測得再準(zhǔn)也拉不開差距。所以策略上必須對大銷量車型單獨(dú)加碼。基于這三點(diǎn)我們最終走的是**分層建模 多模型融合 后處理校準(zhǔn)**的路線。分層是指按細(xì)分市場或銷量量級分組分別訓(xùn)練模型多模型融合是指LightGBM、XGBoost、CatBoost以及一個簡單的時序基線做加權(quán)后處理校準(zhǔn)則是針對預(yù)測結(jié)果的分布做修正避免出現(xiàn)負(fù)銷量或異常尖峰。這里給一個實(shí)操心得不要一上來就追求模型復(fù)雜度。我們前兩周試過LSTM和Seq2Seq驗(yàn)證集RMSE比LightGBM高了將近30%而且訓(xùn)練時間翻了好幾倍。后來果斷放棄深度路線把精力全砸在特征工程和分組策略上成績才穩(wěn)步上升。1.3 整體流程的五個階段把整個項(xiàng)目拆開大概是這么五步數(shù)據(jù)清洗與對齊把車型配置表、銷量表、上市時間表按車型ID和月份對齊處理缺失值和異常值。探索性分析看銷量分布、季節(jié)性、車型生命周期曲線確定分組依據(jù)。特征工程構(gòu)造滯后特征、滑動窗口統(tǒng)計(jì)、同比環(huán)比、生命周期階段標(biāo)記、細(xì)分市場聚合特征等。模型訓(xùn)練與驗(yàn)證按時間切分驗(yàn)證集分組訓(xùn)練多個模型記錄每組最優(yōu)模型。融合與后處理加權(quán)融合、截?cái)嘭?fù)值、對高銷量車型做殘差修正。這五步里第三步和第四步占了我們80%的時間。下面我逐個展開把踩過的坑和最終有效的做法都講清楚。2. 數(shù)據(jù)清洗與探索性分析的關(guān)鍵細(xì)節(jié)2.1 數(shù)據(jù)對齊時最容易忽略的三個坑第一個坑是車型ID不一致。配置表和銷量表里的車型標(biāo)識可能因?yàn)楦目睢⒛晔阶兓霈F(xiàn)一對多或多對一的情況。我們的處理方式是以銷量表的車型ID為主鍵配置表通過品牌車系年式做模糊匹配匹配不上的用同車系最近年式的配置填充。這一步聽起來簡單但實(shí)際做的時候會發(fā)現(xiàn)大概有5%到8%的記錄匹配不上如果直接丟掉會損失不少高銷量車型的樣本。第二個坑是月份缺失。有些車型在某些月份沒有銷量記錄這到底是銷量為零還是數(shù)據(jù)缺失我們的判斷邏輯是如果該車型在缺失月份的前后都有銷量且上市時間早于缺失月份、停產(chǎn)時間晚于缺失月份那就填0否則標(biāo)記為缺失不參與訓(xùn)練。這個邏輯必須寫清楚否則模型會把缺失當(dāng)成零來學(xué)導(dǎo)致預(yù)測偏低。第三個坑是異常值。汽車銷量偶爾會出現(xiàn)極端值比如某個月突然幾萬臺下個月又回到幾百臺。這可能是批發(fā)數(shù)據(jù)集中錄入也可能是特殊促銷。我們的做法是對每個車型的銷量序列做3倍標(biāo)準(zhǔn)差檢測超過閾值的點(diǎn)用前后三個月的移動中位數(shù)替換。注意不要直接刪除異常點(diǎn)因?yàn)閯h除會破壞時間連續(xù)性影響滯后特征的構(gòu)造。2.2 探索性分析三個必須看的圖做時序預(yù)測有三張圖一定要畫。第一張是整體銷量按月匯總的折線圖。這張圖能告訴你季節(jié)性有多強(qiáng)。我們當(dāng)時畫出來發(fā)現(xiàn)每年2月都是低谷9月到11月是高峰這和行業(yè)常識完全吻合。有了這個認(rèn)知后面構(gòu)造月份特征時就有了依據(jù)而不是盲目讓模型去學(xué)。第二張是車型生命周期曲線。隨機(jī)抽20到30個車型把它們的銷量按上市后的月份數(shù)畫出來。你會發(fā)現(xiàn)大部分車型符合上市爬坡-穩(wěn)定-衰退的S型曲線但也有一些車型一上市就是巔峰然后迅速下滑。這個觀察直接啟發(fā)了我們構(gòu)造上市月數(shù)和上市月數(shù)的平方兩個特征用來捕捉生命周期的非線性。第三張是細(xì)分市場銷量分布箱線圖。這張圖決定了你怎么分組。我們當(dāng)時按車身類型和價(jià)格帶交叉分了大概12個細(xì)分市場發(fā)現(xiàn)其中3個細(xì)分市場的銷量中位數(shù)是另外9個的5倍以上。這就意味著如果統(tǒng)一建模大銷量細(xì)分市場會主導(dǎo)損失函數(shù)小銷量細(xì)分市場被忽略。所以必須分組或者至少給樣本加權(quán)。2.3 分組策略的確定過程分組不是拍腦袋我們試了三種方案分組方案依據(jù)優(yōu)點(diǎn)缺點(diǎn)最終是否采用按細(xì)分市場車身類型價(jià)格帶業(yè)務(wù)含義清晰部分組樣本過少作為主分組按銷量量級歷史月均銷量分高/中/低樣本均衡業(yè)務(wù)解釋弱作為輔助加權(quán)按品牌國別自主/合資/進(jìn)口宏觀因素一致組內(nèi)差異仍大放棄最終采用的是主分組按細(xì)分市場同時在每個組內(nèi)按銷量量級給樣本加權(quán)。具體來說月均銷量超過5000臺的樣本權(quán)重設(shè)為31000到5000的設(shè)為2低于1000的設(shè)為1。這樣既保留了業(yè)務(wù)解釋性又緩解了樣本不均衡。注意分組數(shù)量不要太多。我們一開始分了20多個組結(jié)果有些組只有幾十條訓(xùn)練樣本模型根本學(xué)不動。后來合并到12個組每組至少300條以上樣本效果明顯穩(wěn)定。3. 特征工程從原始字段到模型可用的輸入3.1 滯后特征與滑動窗口時序預(yù)測的命根子任何時序預(yù)測滯后特征都是最重要的。我們構(gòu)造了以下幾類滯后1到12個月的銷量直接反映近期趨勢?;瑒哟翱诮y(tǒng)計(jì)過去3個月、6個月、12個月的均值、中位數(shù)、標(biāo)準(zhǔn)差、最大值、最小值。同比特征去年同月的銷量以及今年累計(jì)銷量與去年累計(jì)銷量的比值。環(huán)比特征本月銷量與上月銷量的差值、比值。這里有個細(xì)節(jié)滯后特征在訓(xùn)練集和測試集上的構(gòu)造方式必須一致。比如預(yù)測未來3個月那么測試集里每個樣本的滯后特征只能用預(yù)測起點(diǎn)之前的數(shù)據(jù)。我們當(dāng)時寫了一個滑動窗口生成函數(shù)確保不會出現(xiàn)用未來數(shù)據(jù)預(yù)測未來的泄漏。滑動窗口的窗口長度選擇也有講究。窗口太短噪聲大窗口太長反應(yīng)遲鈍。我們試了3、6、12三種最終發(fā)現(xiàn)6個月窗口在驗(yàn)證集上表現(xiàn)最穩(wěn)。這可能是因?yàn)槠囦N量的中期趨勢比短期波動更有預(yù)測價(jià)值。3.2 生命周期特征讓模型知道車處于什么階段前面探索性分析發(fā)現(xiàn)生命周期很重要所以專門構(gòu)造了一組特征上市月數(shù)當(dāng)前月份減去上市月份。上市月數(shù)的平方捕捉爬坡和衰退的非線性。是否改款如果配置表里有年式變化標(biāo)記為1。是否停產(chǎn)如果后續(xù)月份沒有銷量記錄標(biāo)記為1。這組特征在樹模型里的重要性排名一直很靠前。特別是上市月數(shù)在很多細(xì)分市場組里都是前五重要的特征。3.3 細(xì)分市場聚合特征引入橫向?qū)Ρ瘸藛诬囆蛄刑卣魑覀冞€構(gòu)造了同細(xì)分市場同月的聚合特征該細(xì)分市場當(dāng)月總銷量。該車型銷量占細(xì)分市場總銷量的比例。該細(xì)分市場當(dāng)月車型數(shù)量。該車型銷量在細(xì)分市場內(nèi)的排名。這些特征的作用是給模型提供橫向參照。比如一款車自身銷量在下降但如果整個細(xì)分市場都在下降那它的相對表現(xiàn)其實(shí)沒變。模型有了這些特征就能區(qū)分個體問題和市場問題。3.4 類別特征的處理目標(biāo)編碼 vs 獨(dú)熱編碼品牌、車身類型、變速箱這些類別特征我們試了兩種處理方式獨(dú)熱編碼適合類別數(shù)少的特征比如變速箱手動/自動/雙離合等不到10類。目標(biāo)編碼適合類別數(shù)多的特征比如品牌幾十個、車系上百個。目標(biāo)編碼有個經(jīng)典陷阱必須用交叉驗(yàn)證的方式計(jì)算否則會泄漏標(biāo)簽。我們的做法是把訓(xùn)練集分成5折每折用其他4折的銷量均值來編碼當(dāng)前折的品牌測試集用全量訓(xùn)練集的均值編碼。這樣既利用了品牌信息又避免了過擬合。實(shí)操心得目標(biāo)編碼后一定要加一個平滑項(xiàng)。比如品牌A只有2條記錄均值是10000直接編碼會讓模型對這個品牌過擬合。我們的平滑公式是編碼值 (該品牌銷量和 全局均值 × 平滑系數(shù)) / (該品牌樣本數(shù) 平滑系數(shù))平滑系數(shù)取10到20之間。實(shí)測下來平滑后驗(yàn)證集誤差下降了約5%。4. 模型訓(xùn)練、驗(yàn)證與融合的完整實(shí)操4.1 時間序列驗(yàn)證集的切分方式這是整個項(xiàng)目里最容易犯錯的地方。很多人做時序預(yù)測直接用隨機(jī)切分結(jié)果驗(yàn)證集里混入了未來信息線下成績虛高線上直接崩。正確的做法是按時間切分。比如用前18個月做訓(xùn)練第19到21個月做驗(yàn)證第22到24個月做測試。如果要做多折驗(yàn)證就滾動切分第一折用1到15月訓(xùn)練、16到18月驗(yàn)證第二折用1到18月訓(xùn)練、19到21月驗(yàn)證以此類推。我們當(dāng)時用了3折滾動驗(yàn)證每折的驗(yàn)證集大小和測試集一致都是3個月。這樣得到的驗(yàn)證成績和線上成績相關(guān)性很高基本能反映真實(shí)水平。4.2 三個模型的參數(shù)配置與調(diào)優(yōu)過程我們最終融合了三個模型LightGBM、XGBoost、CatBoost。參數(shù)不是隨便設(shè)的而是用貝葉斯優(yōu)化跑了大概200輪。LightGBM的核心參數(shù)lgb_params { objective: regression, metric: rmse, learning_rate: 0.03, num_leaves: 63, max_depth: 8, min_data_in_leaf: 20, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 5, lambda_l2: 1.0, n_estimators: 2000, early_stopping_rounds: 100 }XGBoost的核心參數(shù)xgb_params { objective: reg:squarederror, eta: 0.03, max_depth: 7, min_child_weight: 5, subsample: 0.8, colsample_bytree: 0.8, alpha: 0.1, lambda: 1.0, n_estimators: 2000, early_stopping_rounds: 100 }CatBoost的核心參數(shù)cat_params { loss_function: RMSE, learning_rate: 0.05, depth: 7, l2_leaf_reg: 3.0, iterations: 2000, early_stopping_rounds: 100, verbose: False }調(diào)參時有個經(jīng)驗(yàn)學(xué)習(xí)率不要設(shè)太大。0.03到0.05之間比較穩(wěn)雖然訓(xùn)練慢一點(diǎn)但驗(yàn)證集波動小。另外min_data_in_leaf和min_child_weight這兩個參數(shù)對稀疏數(shù)據(jù)特別重要設(shè)得太小會過擬合設(shè)得太大又欠擬合。我們最終取20和5是在驗(yàn)證集上試了十幾組才定下來的。4.3 融合策略加權(quán)平均 vs Stacking我們試了兩種融合方式加權(quán)平均三個模型的預(yù)測值按權(quán)重相加權(quán)重用驗(yàn)證集上的RMSE反比確定。Stacking把三個模型的預(yù)測值作為新特征再訓(xùn)練一個線性回歸或小樹模型。結(jié)果發(fā)現(xiàn)加權(quán)平均在驗(yàn)證集上更穩(wěn)Stacking容易過擬合尤其是當(dāng)某個模型在某個細(xì)分市場上表現(xiàn)特別差時Stacking會放大這個錯誤。最終我們用的是加權(quán)平均權(quán)重大概是LightGBM 0.4、XGBoost 0.35、CatBoost 0.25。注意融合前一定要檢查各模型預(yù)測值的量綱是否一致。我們有一次發(fā)現(xiàn)CatBoost因?yàn)橛昧瞬煌哪繕?biāo)函數(shù)預(yù)測值整體偏大融合后反而拉低了成績。后來統(tǒng)一了目標(biāo)函數(shù)才解決。4.4 后處理截?cái)?、平滑與高銷量修正模型輸出后我們做了三步后處理截?cái)嘭?fù)值預(yù)測銷量不能為負(fù)所有負(fù)值直接置0。平滑處理對每個車型的預(yù)測序列做3個月移動平均避免出現(xiàn)鋸齒狀波動。高銷量修正對歷史月均銷量超過5000臺的車型用驗(yàn)證集上的殘差均值做加性修正。第三步是提分的關(guān)鍵。因?yàn)樵u估指標(biāo)對大銷量車型的誤差懲罰重我們專門針對這部分車型做了偏差校正。具體做法是在驗(yàn)證集上計(jì)算每個高銷量車型的預(yù)測殘差真實(shí)值減預(yù)測值取均值然后加到測試集預(yù)測上。這個操作讓我們的線上成績提升了大概2到3個名次。5. 常見問題與排查技巧實(shí)錄5.1 驗(yàn)證集成績很好但線上崩了怎么查這是時序競賽最經(jīng)典的問題。排查順序如下排查項(xiàng)可能原因解決方法特征泄漏用了未來數(shù)據(jù)構(gòu)造特征檢查所有滯后和滑動窗口的生成邏輯驗(yàn)證集切分隨機(jī)切分而非時間切分改為滾動時間切分分布偏移訓(xùn)練集和測試集時間跨度大加入時間衰減權(quán)重過擬合模型太復(fù)雜或特征太多減少特征、增加正則化我們當(dāng)時遇到過一次線上崩盤最后查出來是目標(biāo)編碼時用了全量數(shù)據(jù)導(dǎo)致驗(yàn)證集泄漏。改成交叉驗(yàn)證編碼后線上線下相關(guān)性立刻恢復(fù)正常。5.2 小銷量車型預(yù)測全是零怎么辦小銷量車型樣本少模型容易直接預(yù)測零。我們的處理方式是對月均銷量低于100臺的車型單獨(dú)訓(xùn)練一個分類模型先判斷是否有銷量再回歸具體數(shù)值。在損失函數(shù)里給小銷量樣本更高的權(quán)重但不要太高否則會犧牲大銷量車型的精度。后處理時如果模型預(yù)測為零但歷史上有過銷量用歷史中位數(shù)替換。5.3 訓(xùn)練時間太長怎么加速LightGBM和XGBoost在幾萬條數(shù)據(jù)上其實(shí)很快但如果特征維度上千、樹的數(shù)量上千訓(xùn)練時間會明顯增加。加速手段用feature_fraction和colsample_bytree降低每棵樹的特征采樣比例。提前停止early stopping一定要開通常500到800棵樹就夠了。把數(shù)據(jù)轉(zhuǎn)成LightGBM的Dataset格式能省不少內(nèi)存和時間。如果機(jī)器有多核設(shè)置num_threads為物理核心數(shù)不要設(shè)成邏輯核心數(shù)否則會因超線程反而變慢。5.4 獨(dú)家避坑清單最后整理幾條我們踩過的坑都是文檔里不會寫的不要用未來數(shù)據(jù)做任何統(tǒng)計(jì)包括均值、中位數(shù)、編碼全部要用滾動窗口或交叉驗(yàn)證。驗(yàn)證集一定要和測試集同分布如果測試集是未來3個月驗(yàn)證集也必須是連續(xù)的3個月不能隨機(jī)抽。融合權(quán)重不要用測試集調(diào)只能用驗(yàn)證集否則就是變相泄漏。后處理要謹(jǐn)慎平滑和修正都可能引入偏差每次改動都要在驗(yàn)證集上確認(rèn)。保存每次實(shí)驗(yàn)的配置和結(jié)果我們最后復(fù)盤時發(fā)現(xiàn)最好的方案往往是幾次小改動的組合如果沒有記錄根本復(fù)現(xiàn)不出來。這個賽題做完我最大的體會是結(jié)構(gòu)化數(shù)據(jù)預(yù)測特征工程和驗(yàn)證策略的重要性遠(yuǎn)大于模型本身。把數(shù)據(jù)對齊做干凈、把驗(yàn)證集切對、把滯后特征構(gòu)造好哪怕只用LightGBM單模型也能進(jìn)前10%。反過來如果這些基礎(chǔ)沒做好堆再多深度模型也是白搭。后續(xù)如果還想繼續(xù)優(yōu)化可以考慮引入外部數(shù)據(jù)比如宏觀經(jīng)濟(jì)指標(biāo)、油價(jià)或者對每個細(xì)分市場單獨(dú)做超參搜索但邊際收益可能就沒那么大了。