間序列預(yù)測:灰狼算法自動(dòng)搜索深度學(xué)習(xí)模型最優(yōu)超參數(shù))
預(yù)測不好做尤其是那種一眼看去有趨勢(shì)、有周期、還夾雜著噪聲的時(shí)間序列。我做過不少類似的項(xiàng)目從電力負(fù)荷到設(shè)備振動(dòng)信號(hào)踩過很多坑之后最后發(fā)現(xiàn)一條相對(duì)省心的路子用灰狼優(yōu)化算法GWO去自動(dòng)搜索雙向長短期記憶網(wǎng)絡(luò)BiLSTM的超參數(shù)。這個(gè)方向很適合那些想給時(shí)間序列預(yù)測模型提質(zhì)增效、但又不想在調(diào)參上耗費(fèi)太多精力的開發(fā)者。這篇文章會(huì)從前因后果、數(shù)據(jù)處理、算法實(shí)現(xiàn)、模型訓(xùn)練到常見坑位把整套方案講透希望能幫你少走幾周彎路。時(shí)間序列預(yù)測本身不新鮮但用對(duì)方法很重要。BiLSTM在序列建模上的優(yōu)勢(shì)GWO在參數(shù)尋優(yōu)上的優(yōu)勢(shì)組合起來就是一種很務(wù)實(shí)的技術(shù)方案。我接下來會(huì)先聊清楚為什么選這個(gè)組合再逐步拆開每部分的實(shí)現(xiàn)細(xì)節(jié)和實(shí)操經(jīng)驗(yàn)。1. 方案選型為什么是GWO-BiLSTM1.1 BiLSTM的價(jià)值不只多了一個(gè)反向通道很多人覺得BiLSTM就是把普通的LSTM倒過來再跑一遍這種理解不能算錯(cuò)但會(huì)忽略它的真正意義。普通的LSTM是單向的當(dāng)前時(shí)刻的輸出只依賴過去的輸入這在很多場景下確實(shí)夠用比如逐點(diǎn)預(yù)測溫度、預(yù)測下一幀語音。但你一旦面對(duì)完整的時(shí)間序列窗口比如用過去48小時(shí)的數(shù)據(jù)預(yù)測未來12小時(shí)窗口中間某個(gè)時(shí)刻的上下文其實(shí)是既包含過去、也包含未來的。舉一個(gè)我常跟人舉的例子讀一句“小明拿著傘出門因?yàn)橥饷嫦轮辍蹦憧吹健皞恪钡臅r(shí)候能猜到后面有“下雨”但真正確認(rèn)這個(gè)關(guān)系要等到句子末尾。BiLSTM就是在正向讀取的同時(shí)再從末尾反向讀取一遍把兩個(gè)方向的信息拼在一起這樣每個(gè)時(shí)間步都能同時(shí)看到前后文。在時(shí)間序列場景里很多規(guī)律不是嚴(yán)格單方向的比如天氣數(shù)據(jù)、空氣質(zhì)量數(shù)據(jù)、金融價(jià)格數(shù)據(jù)往往局部模式與前后都有相關(guān)性雙向結(jié)構(gòu)能把這種關(guān)聯(lián)抓得更完整。需要說明的是BiLSTM并不是所有場景都優(yōu)于單層LSTM。如果你的任務(wù)是典型的流式預(yù)測也就是只有過去沒有未來的時(shí)刻那就必須小心用BiLSTM時(shí)輸入窗口本身不能包含真實(shí)未來信息否則會(huì)直接導(dǎo)致數(shù)據(jù)泄漏。這也是后面我專門強(qiáng)調(diào)窗口切分的原因。在實(shí)際項(xiàng)目里BiLSTM更多用于“拿一段完整序列做編碼然后預(yù)測后半段”的場景而不是逐點(diǎn)滾動(dòng)式預(yù)測。從我個(gè)人的經(jīng)驗(yàn)看BiLSTM光結(jié)構(gòu)本身就比普通LSTM多出一倍參數(shù)量訓(xùn)練時(shí)間也接近翻倍。如果你數(shù)據(jù)量很小比如只有幾千條BiLSTM可能反而不穩(wěn)定。這個(gè)組合適合數(shù)據(jù)量中等偏上、窗口信息相關(guān)性較強(qiáng)的預(yù)測問題。1.2 灰狼優(yōu)化到底解決了什么問題BiLSTM需要調(diào)的超參數(shù)不算少隱藏層神經(jīng)元數(shù)、層數(shù)、學(xué)習(xí)率、批次大小、Dropout比例、序列窗口長度。這些參數(shù)互相影響網(wǎng)格搜索的代價(jià)高得離譜。我曾經(jīng)試過對(duì)一批參數(shù)做網(wǎng)格搜索每個(gè)組合訓(xùn)練一次要五分鐘幾百個(gè)組合下來幾天就沒影了。隨機(jī)搜索快一點(diǎn)但結(jié)果看臉經(jīng)常搜出一堆不穩(wěn)定的結(jié)果。GWO的優(yōu)勢(shì)在于它是一個(gè)結(jié)構(gòu)簡單但收斂性能很好的無梯度優(yōu)化算法。它是受灰狼群體捕獵行為啟發(fā)提出來的假設(shè)一匹狼的解代表一組候選超參數(shù)整個(gè)狼群按適應(yīng)度分成頭狼、第二和第三剩下的狼跟著這三匹頭狼不斷調(diào)整位置。真正寫代碼時(shí)會(huì)發(fā)現(xiàn)GWO的核心就是一個(gè)位置更新公式比基因算法實(shí)現(xiàn)起來還要簡單調(diào)節(jié)參數(shù)少不太容易出現(xiàn)那種“一換數(shù)據(jù)效果就崩了”的情況。為什么在時(shí)間序列預(yù)測里GWO比一些復(fù)雜算法更合適因?yàn)槌瑓?shù)搜索空間往往是連續(xù)的、低維的而且目標(biāo)函數(shù)的評(píng)估比較昂貴——每次評(píng)估都要真實(shí)訓(xùn)練一次BiLSTM。GWO用很少的評(píng)估次數(shù)就能逼近較優(yōu)區(qū)域在性能和時(shí)間之間平衡得較好。相比之下像貝葉斯優(yōu)化雖然更“聰明”但它需要擬合代理模型參數(shù)一旦多起來收斂并不一定快實(shí)現(xiàn)復(fù)雜度也高。我在實(shí)踐中發(fā)現(xiàn)GWO最常見的坑是把優(yōu)化目標(biāo)設(shè)成了訓(xùn)練集誤差。訓(xùn)練集誤差小不代表驗(yàn)證集誤差小超參數(shù)很容易被帶偏成過擬合狀態(tài)。正確的做法是用驗(yàn)證集上的損失或者評(píng)價(jià)指標(biāo)作為適應(yīng)度值這一點(diǎn)我下一節(jié)會(huì)詳細(xì)展開。2. 數(shù)據(jù)準(zhǔn)備與預(yù)處理比算法本身更決定成敗2.1 清洗、窗口切分與樣本量推算做時(shí)間序列預(yù)測第一步不是建模而是搞懂手頭的數(shù)據(jù)。先把數(shù)據(jù)里的缺失值處理掉如果是長時(shí)間連續(xù)缺失不要用線性插值硬填那會(huì)引入人造趨勢(shì)最好用前后同周期均值替代比如同一時(shí)刻前幾天的平均值如果是零星缺失線性插值或者前向填充問題不大。接著是異常值檢測。我發(fā)現(xiàn)一個(gè)很實(shí)用的辦法對(duì)原始序列做一階差分然后用3倍標(biāo)準(zhǔn)差篩掉差分值異常跳變的點(diǎn)。比起直接看原始值的箱線圖這種做法對(duì)趨勢(shì)型數(shù)據(jù)更友好不至于把正常的上升階段誤判為異常。處理完以后做一次平滑比如移動(dòng)平均能明顯減少模型對(duì)高頻抖動(dòng)的過擬合。接下來是最容易被新手忽略的窗口切分。時(shí)間序列預(yù)測不能像圖像分類那樣隨機(jī)抽樣必須按時(shí)間順序構(gòu)造樣本。比如窗口長度為24預(yù)測步長為6那前24個(gè)點(diǎn)作為輸入第25到30個(gè)點(diǎn)作為輸出然后窗口往后滑動(dòng)一個(gè)步長再取下一組?;暗拇笮≈苯佑绊懣捎脴颖玖?0000條數(shù)據(jù)窗口24預(yù)測步長6滑一步大約能生成9900多個(gè)樣本但如果窗口長96預(yù)測步長12滑一步就只有9800多個(gè)。窗口太長會(huì)導(dǎo)致樣本變少窗口太短又捕捉不到長周期規(guī)律這個(gè)平衡點(diǎn)需要多次試。我自己常用的估算方法是樣本數(shù)量大約等于總長 - 窗口 - 預(yù)測長度/ 滑步?;皆O(shè)成1最好但如果數(shù)據(jù)量特別大可以適當(dāng)設(shè)為預(yù)測步長減少冗余樣本。注意滑步變大后樣本相關(guān)性下降模型方差可能變大所以也別太激進(jìn)。構(gòu)造樣本時(shí)務(wù)必要防止窗口內(nèi)混入未來信息。你的輸入特征可以包含歷史同期的外部變量比如星期幾、節(jié)假日標(biāo)記這些是允許的因?yàn)樗鼈儾灰蕾囄磥?。但如果你引入了某個(gè)未來才知道的變量整條訓(xùn)練管線就廢了。2.2 歸一化與時(shí)間順序劃分歸一化在每個(gè)項(xiàng)目中都會(huì)遇到但很多人沒想清楚一個(gè)重要問題統(tǒng)計(jì)量到底從哪里算。最忌諱的做法是先對(duì)全部數(shù)據(jù)做歸一化再劃分訓(xùn)練集和測試集因?yàn)闅w一化的時(shí)候已經(jīng)“偷看”了測試集的均值和方差。正確做法是只對(duì)訓(xùn)練數(shù)據(jù)擬合歸一化器然后用同一組參數(shù)去變換驗(yàn)證集和測試集。代碼上很簡單以sklearn的MinMaxScaler為例訓(xùn)練集上用fit_transform驗(yàn)證集和測試集上只用transform。這樣測試集的分布信息不會(huì)提前泄露到訓(xùn)練階段。對(duì)時(shí)間序列來說這種區(qū)分不只是理論潔癖它是實(shí)打?qū)嵱绊戭A(yù)測精度的如果未來一段時(shí)間序列的整體均值發(fā)生了變化而你已經(jīng)用包含它的統(tǒng)計(jì)量做了歸一化那模型在真實(shí)部署時(shí)會(huì)對(duì)新數(shù)據(jù)分布措手不及。樣本劃分也必須按時(shí)間順序。常見比例是訓(xùn)練集:驗(yàn)證集:測試集 8:1:1或7:1.5:1.5具體看數(shù)據(jù)總量。關(guān)鍵是絕對(duì)不能采用隨機(jī)劃分或KFold交叉驗(yàn)證因?yàn)闀r(shí)間序列有時(shí)間先后邏輯隨機(jī)打亂等于是把未來搬到了過去模型會(huì)學(xué)到根本不可能的規(guī)律。我在項(xiàng)目里常被問“為什么不能在時(shí)間序列上用隨機(jī)劃分”答案很簡單一旦模型在訓(xùn)練時(shí)見過未來樣本它的驗(yàn)證表現(xiàn)就毫無意義上線后必然翻車。如果你的數(shù)據(jù)呈現(xiàn)明顯的年度周期比如用電負(fù)荷、氣溫那劃分邊界最好落在季節(jié)邊界附近確保測試集包含至少一個(gè)完整周期。不要簡單截?cái)喾駝t測試集可能只包含某一季度的數(shù)據(jù)評(píng)估結(jié)果會(huì)有偏差。3. GWO優(yōu)化BiLSTM的核心實(shí)現(xiàn)細(xì)節(jié)3.1 灰狼算法的數(shù)學(xué)邏輯和參數(shù)設(shè)定先把灰狼算法的核心邏輯捋一遍后面代碼就好寫了。GWO把狼群位置理解為解空間中的一個(gè)點(diǎn)也就是一組超參數(shù)。每匹狼的位置向量代表一組候選參數(shù)位置向量的每個(gè)維度就是一種超參數(shù)。狼群中適應(yīng)度最好的三匹狼分別記為頭狼、次頭狼和三頭狼其他狼根據(jù)這三匹狼的位置來更新自己的位置。更新公式大致是先算當(dāng)前狼與頭狼、次頭狼、三頭狼的距離再分別向三個(gè)方向移動(dòng)一步最后取平均。移動(dòng)步長由一個(gè)系數(shù)控制前期讓系數(shù)大于1狼群大步探索后期讓系數(shù)逐漸小于1狼群收縮到最優(yōu)解附近。還有一個(gè)隨機(jī)分量控制探索能力避免狼群太早抱團(tuán)。這些數(shù)學(xué)描述對(duì)應(yīng)到代碼里其實(shí)很克制核心函數(shù)不超過20行。關(guān)鍵參數(shù)就兩個(gè)狼群規(guī)模和迭代次數(shù)。狼群規(guī)模建議在20到40之間太少探索不充分太多訓(xùn)練次數(shù)成倍增加。迭代次數(shù)看目標(biāo)函數(shù)的耗時(shí)如果訓(xùn)練一個(gè)BiLSTM要一分鐘20次迭代配20匹狼就要6個(gè)多小時(shí)所以需要在時(shí)間預(yù)算內(nèi)設(shè)定。我在項(xiàng)目里通常先讓GWO跑15到20次迭代把候選狼群規(guī)??刂圃?5到20左右然后觀察適應(yīng)度曲線的收斂速度。如果曲線還明顯在下降說明迭代太早結(jié)束如果已經(jīng)平了說明再搜也不會(huì)更好可以提前收工。至于狼群位置向量的上下界就是每種超參數(shù)的搜索范圍這個(gè)范圍必須符合實(shí)際比如學(xué)習(xí)率設(shè)成1e-4到1e-2隱藏層神經(jīng)元設(shè)成32到256Dropout設(shè)成0到0.5。這里有一個(gè)很容易踩的坑GWO的坐標(biāo)更新天然會(huì)產(chǎn)生小數(shù)但隱藏層神經(jīng)元數(shù)和批次大小必須是正整數(shù)。解決辦法是編碼時(shí)保存連續(xù)值在傳給模型前做四舍五入取整。學(xué)習(xí)率這類參數(shù)則最好取對(duì)數(shù)尺度因?yàn)閷W(xué)習(xí)率在0.001和0.002之間的差別遠(yuǎn)大于0.01和0.011之間用原始尺度會(huì)讓小數(shù)值幾乎搜不到。實(shí)際編碼時(shí)可以對(duì)學(xué)習(xí)率做log10變換讓搜索空間從 -4 到 -2取回來再算出真實(shí)值。3.2 目標(biāo)函數(shù)設(shè)計(jì)用驗(yàn)證集誤差做適應(yīng)度GWO的適應(yīng)度函數(shù)是整套流程的地基。我踩過最大的坑就是最開始用訓(xùn)練集的均方誤差當(dāng)適應(yīng)度跑出來的超參數(shù)在測試集上表現(xiàn)很差后來才明白是過擬合問題。正確目標(biāo)函數(shù)應(yīng)當(dāng)評(píng)估一組超參數(shù)在驗(yàn)證集上的表現(xiàn)具體步驟是對(duì)每個(gè)候選超參數(shù)組合實(shí)例化一個(gè)BiLSTM模型在訓(xùn)練集上訓(xùn)練一定輪數(shù)然后計(jì)算驗(yàn)證集上的誤差將其作為適應(yīng)度值返回給GWO。但這里有個(gè)現(xiàn)實(shí)問題每匹狼都要訓(xùn)練一輪模型如果每次都訓(xùn)練到完全收斂時(shí)間開銷太大。我自己的做法是限制訓(xùn)練輪數(shù)比如固定最多訓(xùn)練30輪并用早停機(jī)制在驗(yàn)證損失連續(xù)5輪不下降時(shí)提前終止。這樣既保證了適應(yīng)度值的可比性又節(jié)約了時(shí)間。你也不用擔(dān)心訓(xùn)練輪數(shù)太少導(dǎo)致結(jié)果不公平因?yàn)樗泻蜻x解都接受同樣的預(yù)算GWO要比較的是“在同等計(jì)算資源下誰表現(xiàn)更好”這個(gè)前提是公平的。適應(yīng)度指標(biāo)我推薦用驗(yàn)證集上的平均絕對(duì)百分比誤差或均方根誤差。MAPE在數(shù)據(jù)沒有接近0的值時(shí)比較直觀但一旦真實(shí)值接近0百分比誤差會(huì)被放大得很離譜。RMSE對(duì)大的偏差更敏感適合誤差集中在少數(shù)點(diǎn)的場景。你也可以兩個(gè)都算出來存一個(gè)字典形式的適應(yīng)度值方便后續(xù)分析。目標(biāo)函數(shù)里還應(yīng)該把異常場景兜住。比如某組超參數(shù)下模型發(fā)散NaN遍地那這匹狼的適應(yīng)度就要設(shè)成極大值讓它被淘汰。我一般這樣判斷驗(yàn)證集上第一次出現(xiàn)NaN或Inf直接把損失賦值為一個(gè)特別大的常數(shù)比如1e6GWO會(huì)自然遠(yuǎn)離這個(gè)區(qū)域。給個(gè)簡單框架方便參考import numpy as np def fitness_function(params, train_loader, val_loader): lr 10 ** params[0] # 對(duì)數(shù)尺度還原 hidden int(round(params[1])) # 取整 dropout float(params[2]) model build_bilstm(hidden_sizehidden, dropoutdropout) optimizer torch.optim.Adam(model.parameters(), lrlr) early_stop_cnt 0 best_val_loss float(inf) for epoch in range(max_epochs): train_one_epoch(model, train_loader, optimizer) val_loss evaluate(model, val_loader) if np.isnan(val_loss) or np.isinf(val_loss): return 1e6 if val_loss best_val_loss: best_val_loss val_loss early_stop_cnt 0 else: early_stop_cnt 1 if early_stop_cnt 5: return best_val_loss return best_val_loss整體流程就是初始化狼群位置解碼超參訓(xùn)練模型返回誤差更新狼群位置重復(fù)迭代。這個(gè)閉環(huán)搭建完成之后剩下的事情就是等結(jié)果。4. BiLSTM模型的搭建與訓(xùn)練細(xì)節(jié)4.1 網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計(jì)和參數(shù)選擇BiLSTM的網(wǎng)絡(luò)結(jié)構(gòu)其實(shí)不復(fù)雜核心是利用雙向循環(huán)層替代單向循環(huán)層。在PyTorch里L(fēng)STM層加上bidirectionalTrue輸出維度會(huì)自動(dòng)變成隱藏層神經(jīng)元數(shù)的兩倍。后續(xù)接全連接層時(shí)要注意維度的匹配。我一般設(shè)計(jì)成三到四層輸入層接BiLSTM后面接全連接層輸出層。BiLSTM層數(shù)在數(shù)據(jù)量不大時(shí)設(shè)1層就可以設(shè)2層以上參數(shù)量容易爆炸。每一層的輸出如果想保留全部時(shí)間步信息可以取最后一個(gè)時(shí)間步的輸出也可以對(duì)不同時(shí)間步做加權(quán)匯總。在時(shí)間序列預(yù)測里面最常用的是取最后一個(gè)時(shí)間步的隱狀態(tài)然后經(jīng)過幾個(gè)非線性變換層層壓縮到預(yù)測維度。有一個(gè)容易忽略的細(xì)節(jié)BiLSTM的隱藏層輸出不是直接拼接有些框架默認(rèn)會(huì)按時(shí)間步累加兩個(gè)方向的輸出有些是拼接。拼接保留的信息更完整但維度更高后續(xù)參數(shù)也更多。如果你不是為了刻意減少參數(shù)量建議用拼接而不是相加。相加在信息量上是有損失的你等于假設(shè)正反向信息可以線性疊加到同一個(gè)語義空間這個(gè)假設(shè)并不總是成立。Dropout放在循環(huán)層之間比放在輸出層效果更明顯。PyTorch的LSTM自帶dropout參數(shù)但它只對(duì)多層循環(huán)層的非最后一層生效單層LSTM即使設(shè)置了Dropout也等于沒設(shè)。要真正做正則化得在BiLSTM層和全連接層之間手動(dòng)插入Dropout層。這個(gè)細(xì)節(jié)很多人會(huì)忽視導(dǎo)致設(shè)了參數(shù)卻沒效果。網(wǎng)絡(luò)搭建示例import torch import torch.nn as nn class BiLSTMPredictor(nn.Module): def __init__(self, input_size, hidden_size, num_layers, dropout, output_size): super().__init__() self.lstm nn.LSTM( input_size, hidden_size, num_layers, batch_firstTrue, bidirectionalTrue, dropoutdropout if num_layers 1 else 0.0 ) self.dropout nn.Dropout(dropout) self.fc1 nn.Linear(hidden_size * 2, hidden_size) self.fc2 nn.Linear(hidden_size, output_size) def forward(self, x): out, _ self.lstm(x) # (batch, seq_len, hidden*2) out out[:, -1, :] # 取最后一個(gè)時(shí)間步 out self.dropout(torch.relu(out)) out self.fc2(self.fc1(out)) return out這種結(jié)構(gòu)適合多步預(yù)測時(shí)把輸出維度設(shè)成預(yù)測步長一次性輸出多步。需要提醒的是多步預(yù)測有兩種思路一種是一次性輸出未來多個(gè)步長另一種是滾動(dòng)預(yù)測——每次只預(yù)測一步再把它拼回輸入繼續(xù)預(yù)測。滾動(dòng)預(yù)測會(huì)累積誤差訓(xùn)練時(shí)可以用教師強(qiáng)制緩解但推理階段誤差仍會(huì)逐漸放大所以一次性輸出往往更穩(wěn)。4.2 訓(xùn)練過程管理學(xué)習(xí)率調(diào)度與早停BiLSTM訓(xùn)練過程中學(xué)習(xí)率怎么調(diào)度很關(guān)鍵。我的默認(rèn)設(shè)置是Adam優(yōu)化器加初始學(xué)習(xí)率0.001配合ReduceLROnPlateau調(diào)度器驗(yàn)證集損失連續(xù)5個(gè)epoch不下降就把學(xué)習(xí)率減半。這種做法的好處是前期大步走、后期小步磨能有效避免在損失曲面陡峭區(qū)域來回震蕩。早停幾乎是必須的。我在GWO目標(biāo)函數(shù)里已經(jīng)用了早停其實(shí)正式訓(xùn)練階段同樣需要用。注意早停要保存驗(yàn)證集損失最低時(shí)候的模型參數(shù)而不是訓(xùn)練過程最后一個(gè)epoch的參數(shù)。很多框架自帶checkpoint機(jī)制這里只需要比較驗(yàn)證損失碰到更低就保存一次。批次大小對(duì)訓(xùn)練穩(wěn)定性和目標(biāo)函數(shù)評(píng)估都有影響。64到128在大多數(shù)數(shù)據(jù)集上表現(xiàn)均衡太小比如16或8會(huì)讓訓(xùn)練不穩(wěn)定且耗時(shí)太大又可能讓模型收斂不徹底。在GWO搜索里批次大小如果也作為超參搜索建議用離散值集合比如[32, 64, 128]而不是連續(xù)搜索。因?yàn)檫B續(xù)搜索很難對(duì)離散參數(shù)保持穩(wěn)定還容易搜出非整數(shù)。還需要留意損失函數(shù)的選擇。時(shí)間序列回歸預(yù)測一般是MSE損失。但MSE對(duì)大誤差非常敏感如果序列里有尖峰模型會(huì)花很大精力擬合尖峰反而忽視整體趨勢(shì)。我習(xí)慣在MSE基礎(chǔ)上加一點(diǎn)MAE加權(quán)也就是Huber損失閾值設(shè)為1.0兼顧魯棒性和梯度穩(wěn)定性。如果你在意峰值預(yù)測精度那MSE可能更合適如果更在意整體走勢(shì)Huber損失會(huì)更穩(wěn)。訓(xùn)練日志也應(yīng)該認(rèn)真記。我每次跑實(shí)驗(yàn)都會(huì)把模型結(jié)構(gòu)參數(shù)、超參數(shù)、隨機(jī)種子、每個(gè)epoch的訓(xùn)練損失和驗(yàn)證損失記錄下來。這不是形式主義而是等結(jié)果不好的時(shí)候能快速定位到底是數(shù)據(jù)問題、超參問題還是模型結(jié)構(gòu)問題。我見過太多人跑完一組實(shí)驗(yàn)連用的什么批次大小都忘了。5. 實(shí)驗(yàn)對(duì)比、結(jié)果分析與踩坑記錄5.1 評(píng)價(jià)指標(biāo)與對(duì)比方案設(shè)計(jì)評(píng)估時(shí)間序列預(yù)測模型不能只靠一張預(yù)測曲線“看著像”。三個(gè)指標(biāo)基本夠用RMSE、MAE和MAPE。RMSE懲罰大誤差MAE反映平均誤差水平MAPE則給出百分比的直觀感受。前兩者有量綱具體數(shù)值受數(shù)據(jù)尺度影響很大所以同比比較才有意義MAPE相對(duì)直觀但真實(shí)值接近0的時(shí)候會(huì)失真要不要用需根據(jù)數(shù)據(jù)分布判斷。對(duì)比方案建議這樣安排固定同一份數(shù)據(jù)、同樣的滑窗大小和預(yù)測步長依次跑普通LSTM、BiLSTM、以及GWO優(yōu)化后的GWO-BiLSTM。普通LSTM和BiLSTM也要留出調(diào)參空間不能讓它們裸奔就得出“GWO更強(qiáng)”的結(jié)論因?yàn)楣叫詫?duì)結(jié)論的可信度影響很大。我用固定超參數(shù)訓(xùn)練普通LSTM再用GWO搜索同一組參數(shù)空間最后對(duì)比。從經(jīng)驗(yàn)來看在對(duì)周期和趨勢(shì)混合的數(shù)據(jù)上GWO-BiLSTM的性能通常優(yōu)于普通LSTM提升幅度大概在5%到15%之間但這個(gè)數(shù)字僅供參考如果你的數(shù)據(jù)本身就非常平穩(wěn)、線性深度模型的提升空間可能很小。除了數(shù)值指標(biāo)最好還看兩件事一是訓(xùn)練好的模型在多步預(yù)測時(shí)的誤差累積曲線看它預(yù)測到第幾步開始發(fā)散二是對(duì)異常區(qū)間的局部預(yù)測效果比如高峰時(shí)段、突變時(shí)段。只算平均指標(biāo)容易掩蓋局部失效尤其是時(shí)間序列里常常有重要的異常事件。另外GWO搜索結(jié)果不只是拿來用最好的那組還可以做敏感性分析看看最終狼群中適應(yīng)度排名前幾的解都落在哪些區(qū)域。如果前幾名的學(xué)習(xí)率都偏小說明學(xué)習(xí)率這個(gè)維度對(duì)結(jié)果影響大如果它們的Dropout分布很散說明Dropout不是當(dāng)前的主要瓶頸。這種分析能幫你判斷下一步優(yōu)化的重點(diǎn)。5.2 常見問題排查表與避坑經(jīng)驗(yàn)下面是我在實(shí)際項(xiàng)目中反復(fù)遇到并總結(jié)下來的一些問題和對(duì)應(yīng)處理方式直接看表就能省不少時(shí)間現(xiàn)象可能的根因處理辦法GWO迭代很久但適應(yīng)度下降緩慢搜索范圍設(shè)得太大根據(jù)經(jīng)驗(yàn)縮小范圍比如學(xué)習(xí)率限定在0.0001到0.01驗(yàn)證損失在某個(gè)解上為NaN學(xué)習(xí)率過大或模型發(fā)散在目標(biāo)函數(shù)里返回極大值讓GWO直接淘汰該解GWO-BiLSTM比普通LSTM還差數(shù)據(jù)量太小或序列太長導(dǎo)致有效樣本太少檢查樣本量考慮縮小窗口或改用單向LSTM預(yù)測曲線整體滯后一拍數(shù)據(jù)有強(qiáng)自相關(guān)且模型欠擬合增加隱藏層神經(jīng)元或嘗試多步輸出而非滾動(dòng)預(yù)測訓(xùn)練和驗(yàn)證差距極大過擬合增大Dropout減小模型容量增加早停測試集表現(xiàn)和驗(yàn)證集差距大數(shù)據(jù)分布漂移或劃分邊界不合理重新檢查劃分保證測試集覆蓋完整周期數(shù)據(jù)泄漏是時(shí)間序列預(yù)測里最隱蔽的坑。我單獨(dú)強(qiáng)調(diào)一次歸一化必須先對(duì)訓(xùn)練集fit再對(duì)驗(yàn)證集和測試集transform構(gòu)造特征時(shí)禁止使用窗口之外的未來信息劃分?jǐn)?shù)據(jù)集時(shí)禁止隨機(jī)打亂這些都是在代碼里很容易無意識(shí)犯下的錯(cuò)。一次項(xiàng)目中我就因?yàn)閳D省事先歸一化整份數(shù)據(jù)導(dǎo)致驗(yàn)證集分?jǐn)?shù)好看了換數(shù)據(jù)上線就露餡白白浪費(fèi)了一周時(shí)間。還有一個(gè)常見問題是GWO的隨機(jī)性導(dǎo)致結(jié)果不穩(wěn)定。同樣配置跑兩次一次效果很好一次很差。解決方案是設(shè)置固定隨機(jī)種子從NumPy到PyTorch都固定住跑多個(gè)隨機(jī)種子取平均也可以把GWO初始種群固定下來確保實(shí)驗(yàn)可復(fù)現(xiàn)。關(guān)于樣本量不足時(shí)的取舍我也給一點(diǎn)個(gè)人經(jīng)驗(yàn)如果數(shù)據(jù)量低于一萬條BiLSTM的優(yōu)勢(shì)很難發(fā)揮出來GWO調(diào)參反而是浪費(fèi)計(jì)算資源建議先跑普通LSTM或結(jié)構(gòu)化模型數(shù)據(jù)量在兩萬條以上、序列帶明顯周期依賴時(shí)GWO-BiLSTM的性價(jià)比才真正體現(xiàn)出來。如果窗口長度超過數(shù)據(jù)量的十分之一就要警惕樣本數(shù)過少可以先適當(dāng)縮小窗口再考慮增加數(shù)據(jù)采集頻率。最后再說一個(gè)問題多步預(yù)測的誤差累積。很多優(yōu)化目標(biāo)只看一步預(yù)測誤差結(jié)果模型在滾動(dòng)預(yù)測中后期誤差大得嚇人。GWO的適應(yīng)度函數(shù)如果能包含多步預(yù)測誤差搜索出來的超參數(shù)會(huì)更貼合實(shí)際使用場景。我通常用三步或六步預(yù)測的累積誤差作為適應(yīng)度雖然訓(xùn)練更耗時(shí)但換來的是更好的在線表現(xiàn)。結(jié)尾這套方案我從初版摸索到現(xiàn)在最大的體會(huì)是GWO-BiLSTM不是一個(gè)萬能模板而是時(shí)間和數(shù)據(jù)規(guī)模到了一定程度之后自然值得嘗試的一條技術(shù)路線。實(shí)際操作里我通常會(huì)先用一個(gè)小樣本量把整套流程跑通確認(rèn)沒有泄漏、沒有NaN、日志輸出正常再放全量數(shù)據(jù)讓GWO慢慢搜。如果時(shí)間緊張也可以把狼群規(guī)模和迭代次數(shù)調(diào)小先粗搜一輪摸出好參數(shù)的大致范圍再在這個(gè)范圍里細(xì)搜第二輪效果會(huì)比一次大范圍搜索更好。這套流程你搭起來之后會(huì)發(fā)現(xiàn)真正花時(shí)間的不是GWO那兩行更新公式而是前面說的數(shù)據(jù)預(yù)處理和實(shí)驗(yàn)評(píng)估——但這些工夫都是值得的。