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