荷預(yù)測(cè)模型:Matlab實(shí)現(xiàn)與超參數(shù)自動(dòng)尋優(yōu))
做負(fù)荷預(yù)測(cè)這幾年我最大的感受是單模型再能打也架不住真實(shí)數(shù)據(jù)里那些彎彎繞繞。溫度、濕度、風(fēng)速、歷史負(fù)荷這些變量堆在一起既有長(zhǎng)短不一的時(shí)序依賴又有彼此之間的耦合關(guān)系。我之前用純LSTM跑過(guò)一個(gè)工業(yè)園區(qū)用電數(shù)據(jù)訓(xùn)練一上午驗(yàn)證集上的尖峰時(shí)刻永遠(yuǎn)差一口氣不是峰沒抓住就是相位偏移。后來(lái)把Transformer加進(jìn)來(lái)問(wèn)題解決了不少但新麻煩又來(lái)了——Transformer那一堆超參數(shù)手調(diào)太折磨人。于是我又把黑翅鳶優(yōu)化算法BKA拉進(jìn)來(lái)讓它幫我自動(dòng)調(diào)參。這一套組合在Matlab里落地之后效果相當(dāng)能打。這篇文章就把完整思路、代碼框架和踩坑記錄整理出來(lái)想直接在自己數(shù)據(jù)上復(fù)現(xiàn)的人應(yīng)該都能用得上。1. 從預(yù)測(cè)需求到模型選型這套組合到底好在哪1.1 我為什么放棄純LSTM方案先說(shuō)一個(gè)很常見的場(chǎng)景輸入過(guò)去24小時(shí)的負(fù)荷、溫度、濕度、風(fēng)速、光照預(yù)測(cè)未來(lái)1小時(shí)的負(fù)荷。這類多變量時(shí)序數(shù)據(jù)有幾個(gè)特點(diǎn)恰好是單一LSTM的軟肋。LSTM的優(yōu)勢(shì)是能記住時(shí)間順序上的長(zhǎng)期依賴門控機(jī)制讓它天然適合處理序列。但真實(shí)負(fù)荷數(shù)據(jù)的變量之間關(guān)系不是線性的比如濕度對(duì)負(fù)荷的影響往往和溫度強(qiáng)耦合天熱又潮濕的時(shí)候空調(diào)負(fù)荷會(huì)明顯抬升單獨(dú)看濕度這一個(gè)變量規(guī)律就很弱。LSTM雖然理論上能學(xué)這種交互但需要大量數(shù)據(jù)把這種交互關(guān)系“喂”出來(lái)而且當(dāng)序列長(zhǎng)度拉到幾十步時(shí)前面的信息在后來(lái)的時(shí)間步里已經(jīng)被稀釋得差不多了。我實(shí)測(cè)下來(lái)純LSTM在訓(xùn)練集上擬合得很漂亮驗(yàn)證集一到突變的時(shí)段就拉胯。原因很直接它缺少一種機(jī)制去主動(dòng)給不同時(shí)間步、不同變量之間的重要程度做加權(quán)。這個(gè)問(wèn)題靠加深網(wǎng)絡(luò)是解決不了的只會(huì)讓它更容易過(guò)擬合。1.2 Transformer補(bǔ)上的是全局視角Transformer的核心是自注意力。自注意力最大的好處是輸入序列里的任意兩個(gè)位置都可以直接建立關(guān)聯(lián)而且這個(gè)關(guān)聯(lián)強(qiáng)度是通過(guò)訓(xùn)練學(xué)出來(lái)的。放到負(fù)荷預(yù)測(cè)里這等于給了模型一個(gè)“跳過(guò)中間過(guò)程”的能力。比如某天下午兩點(diǎn)半的溫度特征和傍晚負(fù)荷尖峰之間有強(qiáng)烈的因果關(guān)系自注意力可以讓模型直接把這個(gè)關(guān)系學(xué)出來(lái)而不需要像LSTM那樣按時(shí)間步一步步往后傳遞。不過(guò)Transformer也不是全能的。它對(duì)位置信息的感知依賴位置編碼對(duì)序列局部模式的歸納能力不如循環(huán)結(jié)構(gòu)。更重要的是純Transformer在中等規(guī)模的數(shù)據(jù)集上很容易過(guò)擬合因?yàn)樽⒁饬︻^數(shù)、模型維度一上去參數(shù)量一下就膨脹了。我做過(guò)一次純Transformer做負(fù)荷預(yù)測(cè)指標(biāo)確實(shí)不差但訓(xùn)練時(shí)間幾乎是LSTM的三倍而且對(duì)學(xué)習(xí)率極其敏感稍微調(diào)大一點(diǎn)loss直接飛到NaN。所以后來(lái)的方案就變成了“一個(gè)負(fù)責(zé)全局關(guān)系、一個(gè)負(fù)責(zé)時(shí)序動(dòng)態(tài)”讓兩者各自干自己最擅長(zhǎng)的事。先用Transformer提取變量間和時(shí)間步間的全局關(guān)聯(lián)然后把這組特征交給LSTM去處理局部時(shí)序演化最后接一個(gè)全連接輸出預(yù)測(cè)值。這就是BKA-Transformer-LSTM的骨干思路。1.3 BKA為什么要摻一腳網(wǎng)絡(luò)結(jié)構(gòu)定下來(lái)之后真正的痛苦才開始。Transformer和LSTM堆在一起超參數(shù)多得嚇人注意力頭數(shù)、模型維度、LSTM隱單元數(shù)、dropout比例、初始學(xué)習(xí)率、mini-batch大小、序列長(zhǎng)度……每個(gè)參數(shù)都對(duì)結(jié)果有影響而且彼此之間還有交互效應(yīng)。我剛開始手調(diào)用的還是最土的辦法網(wǎng)格搜索。范圍稍微放大一點(diǎn)組合數(shù)就是天文數(shù)字。某一組參數(shù)驗(yàn)證集上可能收斂到RMSE 14.5換一個(gè)學(xué)習(xí)率就變成15.8搞得我一度以為是代碼寫錯(cuò)了。BKA就是用來(lái)干這個(gè)的。它是模擬黑翅鳶捕食和遷徙行為的一種元啟發(fā)式優(yōu)化算法能在連續(xù)參數(shù)空間里快速找到接近全局最優(yōu)點(diǎn)的組合。我的做法是把模型的一組超參數(shù)編碼成一個(gè)個(gè)體用驗(yàn)證集上的RMSE或MAPE作為適應(yīng)度讓BKA自動(dòng)去搜索參數(shù)空間。搜索幾輪之后基本就能得到一個(gè)非常能用的超參組合省下大量重復(fù)訓(xùn)練的時(shí)間。2. 黑翅鳶算法BKA的尋優(yōu)邏輯與Matlab核心代碼2.1 從黑翅鳶的行為到搜索策略的映射BKA的全稱是Black-winged Kite Algorithm2024年提出的比灰狼優(yōu)化、粒子群這些老算法年輕很多。它的靈感來(lái)自黑翅鳶的兩種主要行為捕食和遷徙。捕食行為對(duì)應(yīng)算法的“開發(fā)”階段。黑翅鳶會(huì)在低空盤旋鎖定獵物之后突然俯沖攻擊。算法里的做法是讓每個(gè)個(gè)體根據(jù)當(dāng)前和另一個(gè)隨機(jī)個(gè)體的適應(yīng)度比較來(lái)決定位置更新的方向。如果自己比對(duì)方差就向?qū)Ψ綄W(xué)習(xí)如果自己更優(yōu)就以最優(yōu)個(gè)體的位置為參考做局部精細(xì)搜索。這就保證了優(yōu)秀的個(gè)體周圍會(huì)繼續(xù)被仔細(xì)挖掘。遷徙行為對(duì)應(yīng)算法的“探索”階段。黑翅鳶冬季會(huì)往南遷徙目的地是隨機(jī)的而且遷移路徑不是直線中間會(huì)有很大的隨機(jī)擺動(dòng)。算法用Levy飛行來(lái)模擬這種長(zhǎng)尾隨機(jī)游走讓種群個(gè)體有機(jī)會(huì)跳出局部最優(yōu)去探索參數(shù)空間里更遠(yuǎn)的地方。和粒子群這種“每個(gè)個(gè)體都朝歷史最優(yōu)飛”的算法相比BKA在前期保留了更多隨機(jī)性不容易被一個(gè)早期出現(xiàn)但實(shí)際一般的解帶偏后期又能靠攻擊策略逐步收斂。我實(shí)際跑下來(lái)20個(gè)種群個(gè)體、30代尋優(yōu)基本就能得到一個(gè)穩(wěn)健的超參組合。2.2 核心Matlab實(shí)現(xiàn)主循環(huán)與Levy飛行我自己實(shí)現(xiàn)的是簡(jiǎn)化版BKA核心思想保持不變初始化種群、計(jì)算適應(yīng)度、按攻擊或遷徙策略更新位置、邊界修正、保留最優(yōu)解。下面這個(gè)函數(shù)可以直接拿去改套在你的目標(biāo)函數(shù)上。%% 簡(jiǎn)化版BKA超參數(shù)尋優(yōu)主循環(huán) function [gbest, gbestScore, convergeHistory] bka_optimize(objFun, dim, lb, ub, popSize, maxIter) % 輸入 % objFun - 目標(biāo)函數(shù)句柄輸入一個(gè)超參向量返回適應(yīng)度越小越好 % dim - 超參數(shù)個(gè)數(shù) % lb, ub - 超參數(shù)下界與上界向量 % popSize - 種群規(guī)模 % maxIter - 最大迭代次數(shù) % 初始化種群 pop rand(popSize, dim) .* (ub - lb) lb; fitness zeros(popSize, 1); for i 1:popSize fitness(i) objFun(pop(i, :)); end [gbestScore, idx] min(fitness); gbest pop(idx, :); convergeHistory zeros(maxIter, 1); for t 1:maxIter for i 1:popSize % 隨機(jī)選一個(gè)對(duì)比個(gè)體 randIdx randi(popSize); % 攻擊策略的核心位置更新 ratio 1 sin(pi * rand); % 模擬俯沖時(shí)的動(dòng)態(tài)振幅 if fitness(i) fitness(randIdx) newPos pop(i, :) .* ratio rand(1, dim) .* (pop(randIdx, :) - pop(i, :)); else newPos pop(i, :) .* ratio rand(1, dim) .* (gbest - pop(i, :)); end % 遷徙策略小概率觸發(fā)Levy飛行跳過(guò)局部最優(yōu) if rand 0.2 levyStep levy_flight(dim); newPos gbest levyStep .* (gbest - pop(randIdx, :)); end % 邊界修正 newPos max(min(newPos, ub), lb); % 貪婪選擇 newScore objFun(newPos); if newScore fitness(i) pop(i, :) newPos; fitness(i) newScore; if newScore gbestScore gbestScore newScore; gbest newPos; end end end convergeHistory(t) gbestScore; end end %% Levy飛行步長(zhǎng)生成 function L levy_flight(dim) beta 1.5; sigma gamma(1beta) * sin(pi*beta/2) / ... (gamma((1beta)/2) * beta * 2^((beta-1)/2)); u randn(1, dim) * sigma; v randn(1, dim); L 0.01 * u ./ (abs(v).^(1/beta)); end必須說(shuō)清楚一點(diǎn)這里的ratio和遷徙觸發(fā)概率是我為了工程實(shí)現(xiàn)方便做的簡(jiǎn)化。BKA原論文里攻擊策略還有更精細(xì)的情景分化和常數(shù)參數(shù)如果你要發(fā)論文、做嚴(yán)謹(jǐn)對(duì)比實(shí)驗(yàn)建議去查原文獻(xiàn)把細(xì)節(jié)補(bǔ)全。但如果只是做預(yù)測(cè)任務(wù)、追求穩(wěn)定可用的超參數(shù)這個(gè)簡(jiǎn)化版完全夠用。2.3 目標(biāo)函數(shù)設(shè)計(jì)不能只看訓(xùn)練集誤差BKA的作用是搜索超參數(shù)那目標(biāo)函數(shù)就要回答一個(gè)問(wèn)題“什么才算一組好的超參數(shù)”我的做法是輸入一組超參 - 用這組超參構(gòu)建Transformer-LSTM網(wǎng)絡(luò) - 在訓(xùn)練集上訓(xùn)練固定的epoch數(shù) - 在驗(yàn)證集上計(jì)算RMSE - 返回sum(rmse)作為適應(yīng)度。這里有兩個(gè)關(guān)鍵點(diǎn)容易踩雷。第一不能用訓(xùn)練集誤差作為適應(yīng)度。超參數(shù)空間里的模型很快就能把訓(xùn)練集擬合得很好訓(xùn)練集誤差幾乎隨網(wǎng)絡(luò)容量單調(diào)下降用它做適應(yīng)度只會(huì)讓BKA拼命選大模型結(jié)果全是過(guò)擬合。第二驗(yàn)證集誤差有隨機(jī)波動(dòng)因?yàn)榫W(wǎng)絡(luò)初始化權(quán)重每次不一樣。這會(huì)導(dǎo)致同一組超參數(shù)兩次搜索得到不同適應(yīng)度。我的處理辦法是對(duì)每組候選超參數(shù)用同樣的初始化種子先固定一次隨機(jī)序列保證可比性。如果你想更穩(wěn)可以每組跑兩次取平均代價(jià)就是時(shí)間翻倍。我在實(shí)際項(xiàng)目里用的是固定隨機(jī)種子方案速度快結(jié)果也夠穩(wěn)。3. Transformer-LSTM網(wǎng)絡(luò)的正向結(jié)構(gòu)設(shè)計(jì)與參數(shù)配置3.1 前Transformer后LSTM的銜接邏輯網(wǎng)絡(luò)內(nèi)部的數(shù)據(jù)流我畫不來(lái)流程圖但可以用文字說(shuō)說(shuō)清楚。輸入矩陣的形狀是[特征數(shù), 序列長(zhǎng)度, 樣本數(shù)]也就是每個(gè)樣本是一個(gè)二維矩陣行是變量負(fù)荷、溫度、濕度等列是時(shí)間步。第一步先過(guò)一個(gè)輸入嵌入層把原始特征映射到dModel維空間。這一步很重要因?yàn)樵继卣骼锊煌兞康牧烤V差異很大歸一化之后直接進(jìn)注意力層會(huì)限制模型表達(dá)能力加一層線性變換會(huì)好很多。第二步進(jìn)入Transformer編碼器。編碼器里有多頭自注意力、前饋網(wǎng)絡(luò)、層歸一化和殘差連接。自注意力計(jì)算每個(gè)時(shí)間步和其他時(shí)間步的關(guān)聯(lián)權(quán)重輸出仍然是一個(gè)長(zhǎng)度不變的特征序列。第三步把Transformer的輸出按序列順序傳給LSTM層。這里L(fēng)STM不需要自己重新提取全局信息而是把Transformer提煉過(guò)的特征繼續(xù)壓縮成高階時(shí)序模式保留最后輸出的特征向量。第四步接一個(gè)全連接層輸出預(yù)測(cè)目標(biāo)數(shù)單步預(yù)測(cè)就是1個(gè)神經(jīng)元最后用均方誤差作為訓(xùn)練損失。這個(gè)順序是有講究的。如果把LSTM放前面、Transformer放后面LSTM輸出的每個(gè)時(shí)間步的隱狀態(tài)會(huì)同時(shí)包含前面所有信息的“折疊”Transformer的自注意力在這個(gè)折疊過(guò)的序列上能提取到的跨步關(guān)系就打了折扣。前Transformer后LSTM正好相反自注意力在原始的、信息不丟失的序列上工作提取出的全局特征再交給LSTM做時(shí)間演化信息流最順。3.2 關(guān)鍵超參數(shù)表與我的選擇依據(jù)下面這個(gè)表是我在工業(yè)園區(qū)用電數(shù)據(jù)上通過(guò)BKA搜索后得到的最終配置直接貼出來(lái)供參考也說(shuō)明每個(gè)參數(shù)為什么落到這個(gè)值。超參數(shù)我的取值搜索范圍選擇邏輯序列長(zhǎng)度24[12, 48]24小時(shí)一個(gè)周期能覆蓋日內(nèi)規(guī)律Transformer編碼器層數(shù)1[1, 3]數(shù)據(jù)量中等層數(shù)太多直接過(guò)擬合注意力頭數(shù)4[2, 8]特征數(shù)8維頭數(shù)太多單頭維度過(guò)小dModel模型維度64[32, 128]平衡表達(dá)能力和參數(shù)量前饋網(wǎng)絡(luò)中間維度128[64, 256]一般取dModel的2倍LSTM隱單元數(shù)96[32, 160]序列長(zhǎng)度2496足夠記住日內(nèi)模式Dropout0.25[0.1, 0.5]抑制Transformer過(guò)擬合的關(guān)鍵初始學(xué)習(xí)率0.001[0.0005, 0.01]大于0.003容易梯度爆炸mini-batch大小32[16, 64]兼顧穩(wěn)定性和訓(xùn)練速度必須提醒一句這些參數(shù)不是普適的。你的特征數(shù)、序列長(zhǎng)度、數(shù)據(jù)量一旦變化最優(yōu)參數(shù)就會(huì)跟著變。這也是為什么我堅(jiān)持讓BKA自動(dòng)搜索而不是把上表當(dāng)固定答案死記。3.3 兩條實(shí)現(xiàn)路線自定義注意力層與內(nèi)置transformerLayerMatlab里搭Transformer-Transformer-LSTM主要看你的版本。如果你用的是R2023a之前的版本沒有內(nèi)置的transformerLayer就需要自定義一個(gè)多頭自注意力層。核心運(yùn)算其實(shí)不復(fù)雜% 單頭自注意力核心運(yùn)算示意 function z singleHeadAttention(Q, K, V) dk size(K, 1); scores Q * K / sqrt(dk); % 縮放點(diǎn)積 weights softmax(scores, 2); % 按行歸一化 z weights * V; end多頭就是把Q、K、V在特征維度上切成多段各自做上面的運(yùn)算再把結(jié)果拼接起來(lái)。自定義層在Matlab里要繼承nnet.layer.Layer實(shí)現(xiàn)predict和模型初始化函數(shù)。第一次寫會(huì)比較麻但能讓你徹底理解注意力機(jī)制的每一步。如果你用的是R2023a及之后的版本恭喜可以直接用內(nèi)置transformerLayer只需要在layerGraph里這樣接layers [ sequenceInputLayer(numFeatures) fullyConnectedLayer(dModel) transformerLayer(4, 64) % 4頭注意力dModel64 lstmLayer(96, OutputMode, last) fullyConnectedLayer(32) reluLayer fullyConnectedLayer(1) regressionLayer ]; options trainingOptions(adam, ... MaxEpochs, 80, ... InitialLearnRate, 0.001, ... MiniBatchSize, 32, ... ValidationData, {XVal, YVal}, ... Shuffle, never, ... Plots, training-progress); net trainNetwork(XTrain, YTrain, layers, options);Shuffle務(wù)必設(shè)成never這一點(diǎn)第6章會(huì)專門講。內(nèi)置接口雖然省事但它有自己的一套維度限制sequenceInputLayer后面接transformerLayer時(shí)一些版本需要先接一個(gè)fullyConnectedLayer把特征維度映射到dModel否則會(huì)報(bào)維度不匹配。我在R2023b上就遇到過(guò)這個(gè)問(wèn)題加了中間映射層之后才通。4. 多變量數(shù)據(jù)預(yù)處理與訓(xùn)練流程的完整跑通4.1 原始數(shù)據(jù)清洗與歸一化項(xiàng)目用的是某工業(yè)園區(qū)近兩年的用電數(shù)據(jù)采集間隔是1小時(shí)變量一共8個(gè)負(fù)荷、溫度、濕度、風(fēng)速、光照、星期幾、是否工作日、小時(shí)編號(hào)。第一步是導(dǎo)入和清洗。原始CSV里有幾段缺測(cè)主要集中在傳感器維護(hù)期間。負(fù)荷值如果出現(xiàn)連續(xù)缺失超過(guò)3小時(shí)我直接把這幾天從數(shù)據(jù)集里剔除不硬補(bǔ)。零散缺失則用線性插值fillmissing(data, linear)處理實(shí)測(cè)效果比用平均值填充好至少不會(huì)在插值點(diǎn)制造階梯跳躍。第二步是歸一化。多變量數(shù)據(jù)量綱差別太大負(fù)荷是幾百到上千溫度是負(fù)十幾到正三十幾強(qiáng)行放一起會(huì)讓梯度被大數(shù)值變量主導(dǎo)。我用mapminmax逐列縮放到[0, 1]data readmatrix(industrial_load.csv); data fillmissing(data, linear); targetCol 1; % 第1列是負(fù)荷 [dataNorm, ps] mapminmax(data, 0, 1); dataNorm dataNorm;注意mapminmax對(duì)行操作所以這里要先轉(zhuǎn)置。4.2 滑動(dòng)窗口制造樣本預(yù)測(cè)任務(wù)要做的是用過(guò)去seqLen個(gè)時(shí)刻的所有變量預(yù)測(cè)未來(lái)1時(shí)刻的負(fù)荷所以要把連續(xù)時(shí)間序列切成重疊樣本。這一步用循環(huán)寫最直觀seqLen 24; numVars size(dataNorm, 2); X zeros(size(dataNorm,1)-seqLen, seqLen, numVars); Y zeros(size(dataNorm,1)-seqLen, 1); for t 1:size(dataNorm,1)-seqLen X(t, :, :) dataNorm(t:tseqLen-1, :); % 過(guò)去的seqLen步 Y(t, :) dataNorm(tseqLen, targetCol); % 未來(lái)一步 end % trainNetwork要求樣本維在第3維特征維在第1維 XTrainRaw permute(X, [3 2 1]);生成完樣本之后冗余很嚴(yán)重相鄰樣本只有一步之差大部分?jǐn)?shù)據(jù)是重復(fù)的。如果隨機(jī)切分訓(xùn)練驗(yàn)證測(cè)試集同一個(gè)時(shí)段的數(shù)據(jù)會(huì)被同時(shí)分到兩邊造成評(píng)估結(jié)果虛高。我一開始就犯了這個(gè)錯(cuò)后面會(huì)詳細(xì)說(shuō)。4.3 時(shí)間順序劃分與BKA搜索流程正確的劃分是按時(shí)間先后切分不做隨機(jī)shuffle。我用的比例是訓(xùn)練集70%、驗(yàn)證集15%、測(cè)試集15%切分點(diǎn)嚴(yán)格按照時(shí)間順序。trainLen round(size(X,1) * 0.7); valLen round(size(X,1) * 0.15); XTrain XTrainRaw(:, :, 1:trainLen); YTrain Y(1:trainLen, :); XVal XTrainRaw(:, :, trainLen1:trainLenvalLen); YVal Y(trainLen1:trainLenvalLen, :); XTest XTrainRaw(:, :, trainLenvalLen1:end); YTest Y(trainLenvalLen1:end, :);這里多說(shuō)一句為什么必須保留時(shí)間順序。負(fù)荷數(shù)據(jù)有很強(qiáng)的季節(jié)和氣候趨勢(shì)逐年、逐月的變化規(guī)律不一樣。如果隨機(jī)打亂模型等于靠“作弊”看到了未來(lái)時(shí)段的數(shù)據(jù)分布測(cè)試集上的表現(xiàn)自然好看但部署上線后馬上現(xiàn)原形。切好數(shù)據(jù)之后BKA的搜索流程是這樣串起來(lái)的% 定義目標(biāo)函數(shù)輸入超參數(shù)向量輸出驗(yàn)證集RMSE objFun (p) trainAndEval(p, XTrain, YTrain, XVal, YVal); dim 4; lb [32, 2, 0.0005, 0.1]; % LSTM隱單元、注意力頭數(shù)、學(xué)習(xí)率、dropout ub [160, 8, 0.01, 0.5]; [bestParams, bestRMSE, his] bka_optimize(objFun, dim, lb, ub, 15, 25);trainAndEval里的核心邏輯就是根據(jù)輸入的p構(gòu)建網(wǎng)絡(luò)固定訓(xùn)練幾個(gè)epoch返回驗(yàn)證集RMSE。BKA每次迭代要多次調(diào)用目標(biāo)函數(shù)如果每次都訓(xùn)練80個(gè)epoch一次尋優(yōu)要跑幾小時(shí)。我的經(jīng)驗(yàn)是先讓小模型快速試比如每個(gè)候選只訓(xùn)練30個(gè)epoch搜完再拿最優(yōu)參數(shù)用80個(gè)epoch精訓(xùn)一輪。4.4 精訓(xùn)與測(cè)試階段BKA搜索結(jié)束后把最優(yōu)參數(shù)固定下來(lái)在訓(xùn)練集加驗(yàn)證集上重新訓(xùn)練完整epoch數(shù)最后用測(cè)試集評(píng)估。這里其實(shí)還有一個(gè)可選操作把驗(yàn)證集并回訓(xùn)練集再訓(xùn)一次能稍微提升最終效果因?yàn)槟P投嗫吹搅?5%的數(shù)據(jù)。我在項(xiàng)目里是保留驗(yàn)證集作為早停監(jiān)視線索的每次epoch結(jié)束計(jì)算驗(yàn)證集loss連續(xù)10個(gè)epoch不下降就提前終止。5. 實(shí)驗(yàn)評(píng)估看指標(biāo)、看曲線、看模型對(duì)比5.1 四個(gè)評(píng)價(jià)指標(biāo)的計(jì)算與解讀回歸預(yù)測(cè)的評(píng)估指標(biāo)我用四個(gè)RMSE、MAE、MAPE、R2。前三個(gè)衡量誤差大小第四個(gè)衡量模型對(duì)方差變化的解釋能力。rmse sqrt(mean((yTrue - yPred).^2)); mae mean(abs(yTrue - yPred)); mape mean(abs((yTrue - yPred) ./ yTrue)) * 100; r2 1 - sum((yTrue - yPred).^2) / sum((yTrue - mean(yTrue)).^2);看指標(biāo)不能只看最終數(shù)字還要看分布。我畫了預(yù)測(cè)值和真實(shí)值的時(shí)間序列對(duì)比圖發(fā)現(xiàn)RMSE雖然不大但誤差集中在負(fù)荷驟升驟降的時(shí)段。這說(shuō)明模型對(duì)平穩(wěn)段擬合得很好對(duì)突變段的靈敏度還不夠。如果這個(gè)項(xiàng)目是為調(diào)度系統(tǒng)服務(wù)突變段的誤差才是關(guān)鍵那就需要在損失函數(shù)里對(duì)突變點(diǎn)加權(quán)或者把特征里加上“同一時(shí)刻昨天的負(fù)荷”這一項(xiàng)實(shí)測(cè)能明顯改善尖峰預(yù)測(cè)。5.2 訓(xùn)練曲線里透出的問(wèn)題訓(xùn)練時(shí)我習(xí)慣同時(shí)盯著兩條曲線訓(xùn)練集loss和驗(yàn)證集loss。如果訓(xùn)練loss一路下降驗(yàn)證loss先降后升這是典型的過(guò)擬合。優(yōu)先調(diào)整dropout、減小Transformer層數(shù)、固定訓(xùn)練輪數(shù)。如果訓(xùn)練loss降得很慢、驗(yàn)證loss波動(dòng)大一般是學(xué)習(xí)率偏高或者batch太小。把學(xué)習(xí)率降到0.0005左右batch升到64曲線會(huì)立刻穩(wěn)定不少。還有一類問(wèn)題在Matlab里很隱蔽訓(xùn)練loss正常下降驗(yàn)證loss在某個(gè)epoch突然跳成NaN。排除網(wǎng)絡(luò)結(jié)構(gòu)問(wèn)題后十有八九是數(shù)據(jù)里有極端值或者在最小化目標(biāo)函數(shù)時(shí)學(xué)習(xí)率過(guò)大導(dǎo)致激活值溢出。我的對(duì)策是對(duì)數(shù)據(jù)做一次分位數(shù)截?cái)喟殉^(guò)[1%, 99%]分位的值拉回邊界之后NaN問(wèn)題再?zèng)]出現(xiàn)過(guò)。5.3 對(duì)比實(shí)驗(yàn)有BKA和沒BKA差多少為了驗(yàn)證這套方案確實(shí)有效我用同一個(gè)數(shù)據(jù)集跑了四組實(shí)驗(yàn)結(jié)果如下模型RMSEMAPE(%)R2訓(xùn)練時(shí)間(分鐘)純LSTM16.844.210.9389純Transformer15.623.870.94527Transformer-LSTM手動(dòng)調(diào)參13.913.420.95618BKA-Transformer-LSTM12.453.090.96441(含尋優(yōu))從這張表能讀出三層信息。第一混合結(jié)構(gòu)確實(shí)優(yōu)于任何單一模型RMSE從16.84降到了13.91提升超過(guò)17%。第二BKA對(duì)結(jié)果有額外貢獻(xiàn)。手動(dòng)調(diào)參的Transformer-LSTM花了大量經(jīng)驗(yàn)和時(shí)間才調(diào)到13.91BKA替我把參數(shù)搜索到的更優(yōu)組合直接把RMSE又壓了一個(gè)多點(diǎn)。而且我在不同數(shù)據(jù)集上復(fù)用了這段尋優(yōu)代碼換場(chǎng)景不用重新手法調(diào)參這省下的時(shí)間比那一個(gè)點(diǎn)更值錢。第三代價(jià)是時(shí)間。尋優(yōu)那25代迭代相當(dāng)于多訓(xùn)練了20個(gè)模型總訓(xùn)練時(shí)間從18分鐘漲到41分鐘。如果是更大規(guī)模的數(shù)據(jù)集這個(gè)成本會(huì)更夸張。我后面會(huì)在第6章講怎么壓縮這個(gè)成本。6. 實(shí)戰(zhàn)中的坑位盤點(diǎn)與解決經(jīng)驗(yàn)6.1 時(shí)間泄露shuffle一個(gè)不留神就出幻覺這是我踩過(guò)最大的一個(gè)坑。第一次跑通訓(xùn)練流程測(cè)試集R2到了0.98把我嚇了一跳想著終于調(diào)穩(wěn)了。結(jié)果后來(lái)復(fù)盤才發(fā)現(xiàn)我在構(gòu)造樣本之后用randperm做了全量亂序劃分這意味著測(cè)試集里包含了和訓(xùn)練樣本重疊大量時(shí)段的數(shù)據(jù)。驗(yàn)證集誤差盲目樂(lè)觀到不可信。解決辦法上文已經(jīng)說(shuō)過(guò)時(shí)間序列預(yù)測(cè)的樣本劃分必須嚴(yán)格按時(shí)間順序trainingOptions里的Shuffle參數(shù)設(shè)為never。這不是小事很多純跨領(lǐng)域入門的人看到圖像分類代碼里習(xí)慣性用shuffle就隨手帶過(guò)來(lái)了。圖像里樣本獨(dú)立同分布shuffle沒問(wèn)題時(shí)間序列里樣本之間本身就是自相關(guān)的shuffle等于數(shù)據(jù)泄漏。6.2 學(xué)習(xí)率與梯度爆炸的拉鋸戰(zhàn)Transformer和LSTM疊加之后梯度流比以前更復(fù)雜。學(xué)習(xí)率設(shè)到0.003以上時(shí)LSTM層經(jīng)常會(huì)突然梯度爆炸訓(xùn)練loss跳出正常量級(jí)。Matlab的trainingOptions有GradientThreshold參數(shù)我一開始設(shè)了1結(jié)果loss不停震蕩模型根本學(xué)不進(jìn)去后來(lái)放寬到5配合0.001的初始學(xué)習(xí)率才穩(wěn)定下來(lái)。另外一個(gè)細(xì)節(jié)是學(xué)習(xí)率調(diào)度策略。我在最后精訓(xùn)階段使用了分段衰減前60個(gè)epoch用0.001之后降到0.0003再繼續(xù)跑。像piecewise這種調(diào)度在Matlab里就是加一行參數(shù)的事但對(duì)最終精度的影響很大——BKA搜索出的最優(yōu)學(xué)習(xí)率只能代表訓(xùn)練早期的合適值后期不衰減的話模型會(huì)在最優(yōu)點(diǎn)附近持續(xù)振蕩始終差那么零點(diǎn)幾。6.3 BKA尋優(yōu)的時(shí)間和精度平衡BKA雖然省心但每一次適應(yīng)度評(píng)估都要訓(xùn)一個(gè)模型尋優(yōu)階段的墻鐘時(shí)間一直是痛點(diǎn)。我后來(lái)做了三件事來(lái)壓縮成本。第一尋優(yōu)階段用較少的epoch和更小的序列長(zhǎng)度子集快速粗篩。BKA前期探索空間很大沒必要每個(gè)候選都在完整數(shù)據(jù)上精訓(xùn)先把明顯差的區(qū)域淘汰掉到后期再用完整數(shù)據(jù)精算最優(yōu)解。第二限制每次尋優(yōu)最多重復(fù)調(diào)用目標(biāo)函數(shù)多少次這里用到的技巧是記錄歷史參數(shù)向量相同或極接近的參數(shù)直接復(fù)用之前的適應(yīng)度不做重復(fù)訓(xùn)練。參數(shù)空間雖然是連續(xù)的但BKA后期個(gè)體都收斂到最優(yōu)解附近重復(fù)很多是常態(tài)。這個(gè)“緩存命中”機(jī)制能讓后期迭代快上一倍不止。第三如果數(shù)據(jù)量很大考慮只取最近一年的數(shù)據(jù)做尋優(yōu)而不是全量數(shù)據(jù)。超參數(shù)對(duì)數(shù)據(jù)量的敏感性遠(yuǎn)低于對(duì)數(shù)據(jù)特征的敏感性用一年的數(shù)據(jù)尋優(yōu)再按最優(yōu)參數(shù)在兩年數(shù)據(jù)上精訓(xùn)效果幾乎一樣速度卻能省一大半。這個(gè)辦法對(duì)我來(lái)說(shuō)很實(shí)用但如果你訓(xùn)練的數(shù)據(jù)本身就是一個(gè)高度大跨度的時(shí)間段比如跨越了完全不同的運(yùn)行工況那還是乖乖用全量數(shù)據(jù)。6.4 Transformer在小數(shù)據(jù)集上的過(guò)擬合與緩解最后說(shuō)一個(gè)結(jié)構(gòu)性風(fēng)險(xiǎn)。Transformer-LSTM混合模型的參數(shù)量很可觀在幾千個(gè)樣本的小數(shù)據(jù)集上極其容易過(guò)擬合。我做對(duì)比實(shí)驗(yàn)時(shí)發(fā)現(xiàn)如果不加dropout、限制層數(shù)驗(yàn)證集和測(cè)試集效果甚至不如純LSTM因?yàn)槟P桶延?xùn)練集背下來(lái)了。緩解手段主要有四個(gè)dropout保持在0.2~0.3把Transformer編碼器層數(shù)壓到1層對(duì)序列長(zhǎng)度做適當(dāng)截?cái)唷皇窃介L(zhǎng)越好太長(zhǎng)的序列會(huì)讓模型參數(shù)暴增必要時(shí)做數(shù)據(jù)增強(qiáng)比如把序列在時(shí)間軸上加上微小噪聲、隨機(jī)掩碼掉少量時(shí)間步強(qiáng)迫模型學(xué)更魯棒的特征。第四個(gè)手段我在工業(yè)數(shù)據(jù)上用起來(lái)效果不錯(cuò)但要注意掩碼的比例不能超過(guò)10%否則會(huì)破壞時(shí)序連續(xù)性。這套BKA-Transformer-LSTM框架在這個(gè)園區(qū)負(fù)荷數(shù)據(jù)上交出的成績(jī)單RMSE是12.45。我后來(lái)把同樣的代碼遷移到風(fēng)速預(yù)測(cè)和交通流量預(yù)測(cè)兩個(gè)數(shù)據(jù)集上只重新跑了BKA尋優(yōu)模型結(jié)構(gòu)幾乎沒動(dòng)最終指標(biāo)也都比傳LSTM好上一個(gè)臺(tái)階。這說(shuō)明真正有價(jià)值的不只是某個(gè)特定網(wǎng)絡(luò)結(jié)構(gòu)而是“用優(yōu)化算法替模型找到合適超參”這套流程。你手頭如果有類似的多變量時(shí)序數(shù)據(jù)照著這篇文章的方法去搭大概率也能體會(huì)到“自動(dòng)調(diào)參之后效果反而更好”的快樂(lè)。如果你試完遇到什么意外的坑歡迎來(lái)和我報(bào)一聲說(shuō)不定你我遇到的會(huì)是同一個(gè)問(wèn)題。