化LSTM超參數(shù)調(diào)優(yōu)實(shí)踐指南)
去年做電力負(fù)荷預(yù)測(cè)項(xiàng)目時(shí)LSTM網(wǎng)絡(luò)的調(diào)參過(guò)程讓我相當(dāng)崩潰。隱層神經(jīng)元設(shè)多少學(xué)習(xí)率用什么量級(jí)初始學(xué)習(xí)率衰減周期怎么定每換一組超參數(shù)就要重新訓(xùn)練一輪GPU上跑一次動(dòng)輒十幾分鐘網(wǎng)格搜索試了三四十組也沒(méi)找到特別理想的組合驗(yàn)證集誤差一直卡在某個(gè)水平上不去。后來(lái)把貝葉斯優(yōu)化和LSTM結(jié)合超參數(shù)搜索效率完全不一樣了——同樣的訓(xùn)練預(yù)算預(yù)測(cè)精度提升明顯而且整個(gè)過(guò)程能自動(dòng)完成不用我盯著訓(xùn)練曲線手動(dòng)調(diào)。這個(gè)思路放到Matlab里其實(shí)非常順暢因?yàn)镸atlab的深度學(xué)習(xí)工具箱自帶LSTM層支持貝葉斯優(yōu)化又有內(nèi)置的bayesopt函數(shù)兩者組合起來(lái)不需要自己寫(xiě)復(fù)雜框架。這篇就把我當(dāng)時(shí)在Matlab里的完整實(shí)現(xiàn)方案、關(guān)鍵配置和踩過(guò)的坑整理出來(lái)給同樣在做時(shí)間序列預(yù)測(cè)、又比較習(xí)慣用Matlab做數(shù)據(jù)分析和建模的朋友做個(gè)參考。1. 為什么最終選了貝葉斯優(yōu)化而不是網(wǎng)格搜索1.1 LSTM超參數(shù)空間的真實(shí)復(fù)雜度先聊一下LSTM調(diào)參這件事本身的難度。很多入門(mén)教程會(huì)告訴你LSTM有隱層神經(jīng)元數(shù)量、學(xué)習(xí)率、批大小、訓(xùn)練輪數(shù)這幾個(gè)超參數(shù)聽(tīng)起來(lái)好像沒(méi)多少。但真到實(shí)際項(xiàng)目里你會(huì)發(fā)現(xiàn)需要決定的遠(yuǎn)不止這些網(wǎng)絡(luò)結(jié)構(gòu)方面LSTM層數(shù)、每層神經(jīng)元數(shù)量、是否加dropout層、dropout比例、全連接層的寬度訓(xùn)練策略方面初始學(xué)習(xí)率、學(xué)習(xí)率下降因子、學(xué)習(xí)率下降周期、梯度閾值、L2正則化系數(shù)、批大小數(shù)據(jù)預(yù)處理方面滑動(dòng)窗口長(zhǎng)度用過(guò)去多少步預(yù)測(cè)下一步、訓(xùn)練集驗(yàn)證集劃分比例、歸一化方式這些參數(shù)之間不是獨(dú)立作用的。比如窗口長(zhǎng)度和LSTM層數(shù)會(huì)互相影響窗口太長(zhǎng)但層數(shù)太少模型可能學(xué)不到長(zhǎng)期依賴(lài)學(xué)習(xí)率偏大但L2系數(shù)也大收斂過(guò)程就會(huì)非常不穩(wěn)定。這種高維非線性關(guān)系導(dǎo)致你沒(méi)法憑經(jīng)驗(yàn)逐個(gè)參數(shù)去定——因?yàn)檎{(diào)A的時(shí)候最優(yōu)的B值可能完全變了。1.2 傳統(tǒng)搜索策略的致命問(wèn)題網(wǎng)格搜索是大多數(shù)人的第一反應(yīng)但它的計(jì)算成本是隨參數(shù)個(gè)數(shù)指數(shù)增長(zhǎng)的。每個(gè)參數(shù)設(shè)5個(gè)候選值4個(gè)參數(shù)就是5的4次方等于625次訓(xùn)練。就算每次訓(xùn)練只花5分鐘那也是52個(gè)小時(shí)起步這還不算你發(fā)現(xiàn)某些參數(shù)范圍設(shè)錯(cuò)了需要重來(lái)的情況。隨機(jī)搜索比網(wǎng)格搜索聰明一些至少能在同樣的預(yù)算下覆蓋更多參數(shù)組合但它本質(zhì)上是一個(gè)盲人摸象的過(guò)程——每次采樣完全獨(dú)立前面試過(guò)的結(jié)果對(duì)后面的采樣沒(méi)有任何指導(dǎo)意義很可能在一個(gè)已經(jīng)很不錯(cuò)的區(qū)域附近浪費(fèi)大量采樣機(jī)會(huì)。1.3 貝葉斯優(yōu)化的核心邏輯用歷史信息指導(dǎo)下一步采樣貝葉斯優(yōu)化的思路和前面兩者有本質(zhì)區(qū)別它先把已經(jīng)試過(guò)的超參數(shù)組合和目標(biāo)函數(shù)值收集起來(lái)用一個(gè)概率代理模型去擬合超參數(shù)→預(yù)測(cè)誤差這個(gè)未知的函數(shù)關(guān)系然后根據(jù)代理模型的不確定性來(lái)決定下一步最值得試哪組超參數(shù)。這個(gè)決策過(guò)程很講究平衡既要開(kāi)發(fā)——在當(dāng)前表現(xiàn)最好的區(qū)域附近繼續(xù)細(xì)化找局部最優(yōu)又要探索——去那些代理模型還很不確定的區(qū)域試試防止漏掉更好的區(qū)域。這種平衡由采集函數(shù)控制Matlab里常用的expected-improvement函數(shù)就是同時(shí)考慮這兩個(gè)目標(biāo)。用生活化的類(lèi)比來(lái)說(shuō)網(wǎng)格搜索像一個(gè)把所有書(shū)籍都翻一遍的人隨機(jī)搜索像一個(gè)隨機(jī)抽書(shū)看的人而貝葉斯優(yōu)化像一個(gè)會(huì)根據(jù)已經(jīng)看到的內(nèi)容不斷調(diào)整下一本最可能有用的書(shū)在哪里的搜索引擎。我在電力負(fù)荷數(shù)據(jù)上的實(shí)測(cè)對(duì)比同樣30次訓(xùn)練預(yù)算網(wǎng)格搜索最終驗(yàn)證集RMSE在2.1左右隨機(jī)搜索偶然性好一點(diǎn)大概1.9貝葉斯優(yōu)化能穩(wěn)定跑到1.6以下。在多參數(shù)高維搜索場(chǎng)景里這種差距是普遍現(xiàn)象不是個(gè)例。2. Matlab環(huán)境里貝葉斯優(yōu)化LSTM的整體思路與數(shù)據(jù)準(zhǔn)備2.1 整體技術(shù)路線在Matlab里把貝葉斯優(yōu)化和LSTM接起來(lái)核心思路并不復(fù)雜把訓(xùn)練一次LSTM并返回驗(yàn)證集誤差這件事封裝成一個(gè)目標(biāo)函數(shù)這個(gè)函數(shù)接收超參數(shù)結(jié)構(gòu)體作為輸入輸出一個(gè)標(biāo)量誤差作為性能度量。然后把這個(gè)函數(shù)的句柄傳給bayesopt讓優(yōu)化器自動(dòng)搜索最優(yōu)超參數(shù)組合。整個(gè)過(guò)程分四個(gè)階段數(shù)據(jù)準(zhǔn)備加載時(shí)間序列數(shù)據(jù)劃分訓(xùn)練集和驗(yàn)證集完成歸一化構(gòu)造帶時(shí)間步的特征矩陣目標(biāo)函數(shù)封裝編寫(xiě)一個(gè)函數(shù)內(nèi)部根據(jù)輸入的超參數(shù)構(gòu)建LSTM網(wǎng)絡(luò)、設(shè)置訓(xùn)練選項(xiàng)、完成訓(xùn)練、在驗(yàn)證集上評(píng)估誤差貝葉斯優(yōu)化配置定義每個(gè)超參數(shù)的搜索空間范圍、變換方式、采集函數(shù)、最大評(píng)估次數(shù)、并行選項(xiàng)結(jié)果提取與最終訓(xùn)練從貝葉斯優(yōu)化結(jié)果中取出最優(yōu)超參數(shù)用全量數(shù)據(jù)重新訓(xùn)練最終模型評(píng)估測(cè)試集性能這個(gè)結(jié)構(gòu)的好處在于每一塊都能獨(dú)立調(diào)試。比如你可以先不接貝葉斯優(yōu)化手動(dòng)調(diào)用目標(biāo)函數(shù)試一組參數(shù)確認(rèn)訓(xùn)練流程本身沒(méi)問(wèn)題再交給優(yōu)化器去搜索。2.2 數(shù)據(jù)準(zhǔn)備階段的代碼框架數(shù)據(jù)準(zhǔn)備是很容易被低估工作量的一步但它的質(zhì)量直接決定后續(xù)所有環(huán)節(jié)是否順利。我用的步驟是% 加載數(shù)據(jù)假設(shè)data是n行1列的序列 data load(load_series.mat); y data.load_series; % 劃分訓(xùn)練集和驗(yàn)證集比例約為85% : 15% train_ratio 0.85; train_len floor(length(y) * train_ratio); y_train y(1:train_len); y_val y(train_len1:end);有個(gè)關(guān)鍵細(xì)節(jié)我一開(kāi)始沒(méi)注意后來(lái)吃了大虧歸一化參數(shù)只能在訓(xùn)練集上計(jì)算然后應(yīng)用到驗(yàn)證集上。正確的做法是% 對(duì)訓(xùn)練集計(jì)算均值和標(biāo)準(zhǔn)差 mu mean(y_train); sigma std(y_train); % 歸一化 y_train_norm (y_train - mu) / sigma; y_val_norm (y_val - mu) / sigma;如果先對(duì)整個(gè)序列做歸一化再劃分驗(yàn)證集的信息就泄露到訓(xùn)練過(guò)程里去了最后在測(cè)試集上評(píng)估時(shí)指標(biāo)會(huì)虛高這種數(shù)據(jù)泄露在時(shí)間序列預(yù)測(cè)里非常隱蔽因?yàn)樾蛄斜旧砭陀姓w趨勢(shì)你很難通過(guò)肉眼發(fā)現(xiàn)問(wèn)題。2.3 窗口化數(shù)據(jù)矩陣的構(gòu)造LSTM做時(shí)間序列預(yù)測(cè)輸入格式需要組織成樣本×?xí)r間步×特征數(shù)的三維數(shù)組。我最多用過(guò)去10個(gè)時(shí)間點(diǎn)的數(shù)據(jù)來(lái)預(yù)測(cè)當(dāng)前點(diǎn)窗口長(zhǎng)度是一個(gè)需要在貝葉斯優(yōu)化里搜索的超參數(shù)所以這里的實(shí)現(xiàn)要寫(xiě)得靈活一些function [XTrain, YTrain] createWindowData(data, windowSize) N length(data); XTrain zeros(N - windowSize, windowSize, 1); YTrain zeros(N - windowSize, 1); for i 1:N-windowSize XTrain(i, :, 1) data(i:iwindowSize-1); YTrain(i, 1) data(iwindowSize); end end這個(gè)函數(shù)會(huì)被目標(biāo)函數(shù)反復(fù)調(diào)用所以窗口大小要作為參數(shù)傳進(jìn)去。數(shù)據(jù)量大的時(shí)候這種方式比循環(huán)逐條整理要快而且內(nèi)存占用可控。如果你處理的是多維時(shí)間序列比如同時(shí)預(yù)測(cè)多個(gè)變量只需要把第三維改成特征數(shù)量最后一維變成多列即可。我在風(fēng)速預(yù)測(cè)里用過(guò)三維特征風(fēng)速、風(fēng)向、溫度LSTM的輸入層會(huì)自動(dòng)接受多特征輸入。3. 核心實(shí)操變量定義、目標(biāo)函數(shù)與bayesopt配置3.1 搜索空間設(shè)計(jì)的詳細(xì)說(shuō)明這是整個(gè)方案中技術(shù)含量最高也最影響最終效果的部分。用Matlab的optimizableVariable函數(shù)定義每個(gè)待優(yōu)化超參數(shù)的搜索空間% 定義貝葉斯優(yōu)化的變量空間 vars [ optimizableVariable(lstmUnits, [10, 200], Transform, log) % LSTM隱層神經(jīng)元數(shù) optimizableVariable(numLayers, [1, 3], Type, integer) % LSTM層數(shù) optimizableVariable(initialLearnRate, [1e-3, 1e-1], Transform, log) optimizableVariable(dropoutRate, [0, 0.5]) % Dropout比例 optimizableVariable(l2Regularization, [1e-6, 1e-2], Transform, log) optimizableVariable(windowSize, [4, 30], Type, integer) % 滑動(dòng)窗口長(zhǎng)度 optimizableVariable(batchSize, [16, 128], Type, integer) % 批大小 optimizableVariable(learnRateDropPeriod, [5, 30], Type, integer) % 學(xué)習(xí)率下降周期 ];每個(gè)變量的設(shè)定都有背后的考量lstmUnits用log變換是因?yàn)?0和100對(duì)性能的影響差異遠(yuǎn)大于100和190的差異。用對(duì)數(shù)尺度可以讓優(yōu)化器在小數(shù)值區(qū)域有更高采樣密度這和人對(duì)超參數(shù)的敏感度是匹配的numLayers設(shè)為整數(shù)、范圍[1,3]1層最簡(jiǎn)單不容易過(guò)擬合2層通常是最佳平衡點(diǎn)3層以上在這個(gè)數(shù)據(jù)規(guī)模下開(kāi)始顯得冗余訓(xùn)練時(shí)間大幅增加但精度不再提升。把范圍框在[1,3]是合理的約束initialLearnRate的log范圍[1e-3, 1e-1]學(xué)習(xí)率直接決定訓(xùn)練是否發(fā)散以及收斂速度。小于1e-3收斂太慢大于是1e-1在LSTM這種循環(huán)結(jié)構(gòu)上很容易梯度爆炸windowSize范圍[4,30]窗口太短學(xué)不到趨勢(shì)太長(zhǎng)則引入過(guò)多噪聲。這個(gè)參數(shù)在不同數(shù)據(jù)上差異很大所以交給優(yōu)化器去試比較合理如果訓(xùn)練時(shí)間比較緊可以適當(dāng)減少變量數(shù)量。我的經(jīng)驗(yàn)是優(yōu)先保留lstmUnits、initialLearnRate、windowSize這三個(gè)因?yàn)樗鼈儗?duì)結(jié)果的方差貢獻(xiàn)最大其余參數(shù)可以先用經(jīng)驗(yàn)值固定住。3.2 目標(biāo)函數(shù)內(nèi)部如何處理訓(xùn)練貝葉斯優(yōu)化要求目標(biāo)函數(shù)返回一個(gè)標(biāo)量這個(gè)標(biāo)量通常是驗(yàn)證集上的均方根誤差。目標(biāo)函數(shù)內(nèi)部要完成的就是構(gòu)建網(wǎng)絡(luò)→設(shè)置訓(xùn)練選項(xiàng)→訓(xùn)練→驗(yàn)證預(yù)測(cè)→計(jì)算誤差這個(gè)完整鏈路function rmseVal bayesLSTMTrain(x, yTrainNorm, yValNorm, params) % 根據(jù)貝葉斯優(yōu)化傳入的參數(shù)構(gòu)建LSTM網(wǎng)絡(luò) numFeatures 1; numResponses 1; layers [ sequenceInputLayer(numFeatures) lstmLayer(params.lstmUnits, NumHiddenUnits, params.lstmUnits) dropoutLayer(params.dropoutRate) fullyConnectedLayer(numResponses) regressionLayer ]; % 設(shè)置訓(xùn)練選項(xiàng) options trainingOptions(adam, ... MaxEpochs, 100, ... InitialLearnRate, params.initialLearnRate, ... LearnRateSchedule, piecewise, ... LearnRateDropPeriod, params.learnRateDropPeriod, ... LearnRateDropFactor, 0.2, ... L2Regularization, params.l2Regularization, ... MiniBatchSize, params.batchSize, ... Shuffle, never, ... Verbose, 0, ... Plots, none); % 訓(xùn)練網(wǎng)絡(luò) net trainNetwork(XTrain, YTrain, layers, options); % 在驗(yàn)證集上評(píng)估 YPredNorm predict(net, XVal); rmseVal sqrt(mean((YVal - YPredNorm).^2)); end這里有兩個(gè)特別值得提醒的點(diǎn)第一Shuffle必須設(shè)成never。時(shí)間序列樣本之間存在順序依賴(lài)默認(rèn)的every-epoch會(huì)把序列順序打亂導(dǎo)致相鄰時(shí)間點(diǎn)的樣本被分到不同的批次里模型的時(shí)序記憶被破壞。這一點(diǎn)我一開(kāi)始就忽略了結(jié)果驗(yàn)證集誤差曲線一直劇烈震蕩還以為是網(wǎng)絡(luò)結(jié)構(gòu)的問(wèn)題其實(shí)只是shuffle策略錯(cuò)了。第二多隱層LSTM不是直接在lstmLayer里設(shè)置numLayers參數(shù)。Matlab的lstmLayer本身只創(chuàng)建一層多隱層需要自己堆疊多個(gè)lstmLayer。所以上面的代碼里numLayers參數(shù)其實(shí)沒(méi)體現(xiàn)出來(lái)。正確的多層寫(xiě)法是if params.numLayers 1 layers [ sequenceInputLayer(numFeatures) lstmLayer(params.lstmUnits, OutputMode, last) dropoutLayer(params.dropoutRate) fullyConnectedLayer(numResponses) regressionLayer ]; elseif params.numLayers 2 layers [ sequenceInputLayer(numFeatures) lstmLayer(params.lstmUnits, OutputMode, sequence) lstmLayer(params.lstmUnits, OutputMode, last) dropoutLayer(params.dropoutRate) fullyConnectedLayer(numResponses) regressionLayer ]; end注意第一層和第二層的OutputMode設(shè)置中間層要保留完整序列輸出所以是sequence最后一層只需要最后時(shí)間步的輸出所以是last。如果中間層也設(shè)置成last相當(dāng)于把時(shí)序信息截?cái)嗔撕竺娴膶釉僖矊W(xué)不到中間過(guò)程預(yù)測(cè)效果會(huì)大打折扣。3.3 bayesopt函數(shù)的具體配置目標(biāo)函數(shù)封裝好之后就可以調(diào)用bayesopt執(zhí)行搜索了% 貝葉斯優(yōu)化的核心調(diào)用 results bayesopt((params) bayesLSTMTrain(params, yTrainNorm, yValNorm), vars, ... MaxObjectiveEvaluations, 30, ... AcquisitionFunctionName, expected-improvement-plus, ... IsObjectiveDeterministic, false, ... UseParallel, true, ... Verbose, 1, ... PlotFcn, {plotMinObjective, plotObjectiveModel});幾個(gè)配置參數(shù)的選擇理由MaxObjectiveEvaluations設(shè)為30的含義代表優(yōu)化器總共會(huì)評(píng)估30組超參數(shù)組合。這個(gè)數(shù)字是訓(xùn)練預(yù)算、搜索空間復(fù)雜度、性能要求之間的一個(gè)折中。30組夠覆蓋7維參數(shù)空間的主要區(qū)域又不會(huì)讓總訓(xùn)練時(shí)間失控。如果訓(xùn)練時(shí)間緊張可以減到20要求高可以加到40但超過(guò)50之后邊際收益明顯遞減AcquisitionFunctionName用expected-improvement-plusEIEI相比普通的expected-improvement多了一個(gè)防止過(guò)度開(kāi)發(fā)的功能它會(huì)在連續(xù)多次采樣都集中在一個(gè)小區(qū)域時(shí)自動(dòng)增加探索性避免優(yōu)化器陷入局部最優(yōu)IsObjectiveDeterministic設(shè)為false因?yàn)槊看斡?xùn)練用到隨機(jī)初始化相同參數(shù)多次運(yùn)行結(jié)果也有微小差異目標(biāo)函數(shù)本身帶有隨機(jī)噪聲。這個(gè)參數(shù)告訴優(yōu)化器它觀測(cè)到的誤差值不是完全確定的要讓代理模型把隨機(jī)波動(dòng)也納入不確定性估計(jì)并行是另一個(gè)很實(shí)用的功能。如果你的電腦是多核CPU或者有GPU設(shè)置UseParallel為true以后原來(lái)的串行訓(xùn)練變成并行評(píng)估總時(shí)間接近原來(lái)除以核心數(shù)。我用的辦公機(jī)是8核30次評(píng)估從串行的4個(gè)多小時(shí)壓縮到不到1小時(shí)效率提升非常大。4. 訓(xùn)練與驗(yàn)證過(guò)程中必須避開(kāi)的坑4.1 驗(yàn)證集大小和構(gòu)造方式貝葉斯優(yōu)化的目標(biāo)函數(shù)是在驗(yàn)證集上的誤差所以驗(yàn)證集本身的大小、劃分方式直接影響超參數(shù)選擇的偏向性。驗(yàn)證集太小誤差估計(jì)方差大優(yōu)化器可能選到一組碰巧在該驗(yàn)證集上表現(xiàn)好、但實(shí)際泛化差的參數(shù)驗(yàn)證集太大訓(xùn)練集變小模型學(xué)習(xí)不充分選出的參數(shù)又偏向欠擬合。我的經(jīng)驗(yàn)是驗(yàn)證集占總數(shù)據(jù)量的10%~20%比較合理。另外時(shí)間序列劃分驗(yàn)證集不能隨機(jī)抽樣只能按時(shí)間順序切出末尾一段。原因很簡(jiǎn)單隨機(jī)抽樣等于從未來(lái)偷看信息讓驗(yàn)證集里的某些樣本出現(xiàn)在訓(xùn)練樣本的窗口里評(píng)估結(jié)果不可信。% 推薦做法按時(shí)間順序劃分 train_len floor(length(y) * 0.85); val_len floor(length(y) * 0.15); y_train y(1:train_len); y_val y(train_len1:train_lenval_len);4.2 固定隨機(jī)種子保證可重復(fù)性這個(gè)問(wèn)題我在調(diào)試過(guò)程中花了很長(zhǎng)時(shí)間才意識(shí)到。貝葉斯優(yōu)化在評(píng)估不同參數(shù)組時(shí)如果LSTM的初始權(quán)重每次都是隨機(jī)生成的那么即使完全相同的超參數(shù)組合兩次訓(xùn)練出來(lái)的誤差也會(huì)有波動(dòng)。這種波動(dòng)會(huì)讓優(yōu)化器誤以為該區(qū)域的性能不穩(wěn)定導(dǎo)致代理模型低估某些區(qū)域的潛力產(chǎn)生誤導(dǎo)。解決方法很簡(jiǎn)單在目標(biāo)函數(shù)開(kāi)頭設(shè)置隨機(jī)種子。這樣每組參數(shù)評(píng)估的訓(xùn)練過(guò)程都基于相同的初始權(quán)重目標(biāo)函數(shù)的輸出就主要是由超參數(shù)本身決定的function rmseVal bayesLSTMTrain(params, yTrainNorm, yValNorm) % 固定隨機(jī)種子確保每個(gè)超參數(shù)組合的可重復(fù)性 rng(42); % ... 后續(xù)訓(xùn)練過(guò)程 end不過(guò)這里有個(gè)小技巧不要在所有參數(shù)評(píng)估中都使用同一個(gè)種子否則不同參數(shù)組的初始權(quán)重完全一樣相當(dāng)于給優(yōu)化器引入了一個(gè)偏差。我用的方式是讓種子跟著訓(xùn)練次數(shù)變化比如rng(100 iteration)這樣既保證了每組參數(shù)評(píng)估的內(nèi)部可重復(fù)性又讓不同參數(shù)組之間有足夠差異。4.3 召回驗(yàn)證誤差和訓(xùn)練誤差對(duì)比在貝葉斯優(yōu)化的目標(biāo)函數(shù)里我習(xí)慣同時(shí)計(jì)算訓(xùn)練集誤差和驗(yàn)證集誤差并一并返回。這不是必須的但非常有價(jià)值。做法是讓目標(biāo)函數(shù)返回一個(gè)結(jié)構(gòu)體而不是單一標(biāo)量% 返回多個(gè)指標(biāo)的結(jié)構(gòu)體 rmseVal struct(ValidationRMSE, valRMSE, TrainingRMSE, trainRMSE);注意這需要額外做一步數(shù)據(jù)處理bayesopt默認(rèn)最小化第一個(gè)字段或者你可以顯式把第一個(gè)字段設(shè)成你希望優(yōu)化的指標(biāo)。我在實(shí)際項(xiàng)目里用這個(gè)方法很快就發(fā)現(xiàn)了某些超參數(shù)組合是典型的過(guò)擬合模式——訓(xùn)練誤差極低驗(yàn)證誤差很高。這往往意味著lstmUnits太大、dropoutRate太小或L2正則化過(guò)弱。% 指定目標(biāo)函數(shù)為驗(yàn)證集誤差 results bayesopt((params) bayesLSTMTrain(params, yTrainNorm, yValNorm), vars, ... MaxObjectiveEvaluations, 30, ... Goal, minimize, ... OutputFcn, assignOutputFcn, ... AcquisitionFunctionName, expected-improvement-plus, ... IsObjectiveDeterministic, false, ... UseParallel, true, ... Verbose, 1);4.4 早停條件如何防止訓(xùn)練白費(fèi)貝葉斯優(yōu)化過(guò)程中很多參數(shù)組合本來(lái)就沒(méi)有訓(xùn)練價(jià)值。比如learning rate過(guò)大時(shí)訓(xùn)練loss可能在幾個(gè)epoch后就發(fā)散lstmUnits太小時(shí)訓(xùn)練誤差根本降不到有意義的水平。對(duì)這些組合花完整100個(gè)epoch純屬浪費(fèi)計(jì)算資源。我用的策略是在trainingOptions里設(shè)置ValidationData和ValidationFrequency再配合輸出函數(shù)的早停判斷function stop stopOnOverfitting(info) stop false; if ~isempty(info.ValidationLoss) info.ValidationLoss info.TrainingLoss * 10 stop true; % 驗(yàn)證誤差遠(yuǎn)大于訓(xùn)練誤差認(rèn)定過(guò)擬合 end if info.ValidationLoss 100 % 誤差發(fā)散提前終止 stop true; end end然后把early stopping函數(shù)傳給trainingOptionsoptions trainingOptions(adam, ... ValidationData, {XVal, YVal}, ... ValidationFrequency, 10, ... OutputFcn, stopOnOverfitting, ... ... );這樣大部分無(wú)效訓(xùn)練在40-50個(gè)epoch時(shí)就會(huì)停下來(lái)節(jié)省不少時(shí)間。不過(guò)要注意早停的閾值不要設(shè)得太緊否則誤殺一些前期震蕩但后期收斂的組合反而影響搜索質(zhì)量。我當(dāng)時(shí)直接用ValidationLoss大于TrainingLoss乘以10作為判斷閾值跑了幾輪沒(méi)什么誤殺就一直沿用了。5. 最優(yōu)超參數(shù)的提取與最終模型訓(xùn)練5.1 從優(yōu)化結(jié)果中提取最佳參數(shù)貝葉斯優(yōu)化跑完后results里保存了完整的優(yōu)化歷史。提取最優(yōu)超參數(shù)的方式有兩種一種是取最小目標(biāo)函數(shù)值對(duì)應(yīng)的參數(shù)另一種是取優(yōu)化器估計(jì)的最優(yōu)點(diǎn)% 方法一直接取得到最小目標(biāo)函數(shù)值的參數(shù) bestParams results.XAtMinObjective; % 方法二取優(yōu)化器最終估計(jì)的最優(yōu)點(diǎn) estimatedParams results.XAtMinEstimated;兩種方式各有適用場(chǎng)景。XAtMinObjective是實(shí)際評(píng)估歷史中誤差最低的那組參數(shù)缺點(diǎn)是可能只是一次偶然的好結(jié)果。XAtMinEstimated是代理模型綜合考慮不確定性后的預(yù)測(cè)最優(yōu)點(diǎn)更穩(wěn)健但我實(shí)測(cè)中它有時(shí)會(huì)和實(shí)際評(píng)估值有偏差。我的經(jīng)驗(yàn)是優(yōu)先用XAtMinObjective同時(shí)檢查它的驗(yàn)證誤差和訓(xùn)練誤差是否合理。如果二者差異過(guò)大再看看估計(jì)最優(yōu)點(diǎn)是不是更合理。5.2 用全量數(shù)據(jù)重新訓(xùn)練最終模型找到最優(yōu)超參數(shù)后不要直接拿驗(yàn)證集上訓(xùn)練的模型做預(yù)測(cè)。正確做法是用訓(xùn)練集和驗(yàn)證集合并成的全量數(shù)據(jù)重新訓(xùn)練一個(gè)最終模型這樣模型能看到更多數(shù)據(jù)泛化能力更強(qiáng)。% 合并訓(xùn)練集和驗(yàn)證集 y_full y(1:train_lenval_len); [yFullNorm, mu_full, sigma_full] zscore(y_full); % 用最優(yōu)參數(shù)構(gòu)建網(wǎng)絡(luò) layers_best [ sequenceInputLayer(1) lstmLayer(bestParams.lstmUnits, OutputMode, last) dropoutLayer(bestParams.dropoutRate) fullyConnectedLayer(1) regressionLayer ]; options_best trainingOptions(adam, ... MaxEpochs, 100, ... InitialLearnRate, bestParams.initialLearnRate, ... LearnRateSchedule, piecewise, ... LearnRateDropPeriod, bestParams.learnRateDropPeriod, ... LearnRateDropFactor, 0.2, ... L2Regularization, bestParams.l2Regularization, ... MiniBatchSize, bestParams.batchSize, ... Shuffle, never, ... Verbose, 0); net_final trainNetwork(XFull, YFull, layers_best, options_best);這一步要注意歸一化參數(shù)更新用全量數(shù)據(jù)的均值和標(biāo)準(zhǔn)差重新歸一化而不是沿用之前訓(xùn)練集的參數(shù)。5.3 單步與多步預(yù)測(cè)的驗(yàn)證方式時(shí)間序列預(yù)測(cè)的驗(yàn)證方式分單步預(yù)測(cè)和多步預(yù)測(cè)二者難度差異明顯驗(yàn)證標(biāo)準(zhǔn)也不同。單步預(yù)測(cè)是每個(gè)時(shí)間步都用真實(shí)歷史數(shù)據(jù)作為輸入去預(yù)測(cè)下一步誤差不會(huì)累積評(píng)測(cè)的是模型的單步擬合能力YPredFull predict(net_final, XTest); rmse_test sqrt(mean((YTest - YPredFull).^2));多步預(yù)測(cè)難度大得多因?yàn)楹罄m(xù)時(shí)間步的輸入依賴(lài)之前的預(yù)測(cè)值誤差會(huì)逐步累積。真實(shí)業(yè)務(wù)中用得最多的其實(shí)是多步預(yù)測(cè)。我在電力負(fù)荷項(xiàng)目里做的就是連續(xù)預(yù)測(cè)未來(lái)24小時(shí)用的是輸出反饋模式。Matlab里實(shí)現(xiàn)多步預(yù)測(cè)需要用到predictAndUpdateState函數(shù)在每一步把預(yù)測(cè)值反饋到輸入端% 多步預(yù)測(cè)示例預(yù)測(cè)未來(lái)24步 numSteps 24; YTestPred zeros(numSteps, 1); net_pred net_final; % 用最后一段歷史數(shù)據(jù)初始化網(wǎng)絡(luò)狀態(tài) input_last XTest(1, :, 1); [net_pred, YPred_first] predictAndUpdateState(net_pred, input_last); % 從第二步開(kāi)始用上一步預(yù)測(cè)值作為輸入 for i 2:numSteps [net_pred, YTestPred(i)] predictAndUpdateState(net_pred, YTestPred(i-1)); end這里有個(gè)容易踩的坑predictAndUpdateState要求輸入是行向量還是列向量必須保持一致。我在這里因?yàn)榫仃嚲S度不匹配報(bào)過(guò)好多次錯(cuò)建議在使用前用size命令檢查清楚。5.4 誤差指標(biāo)選擇RMSE、MAPE、MAE怎么取舍貝葉斯優(yōu)化目標(biāo)函數(shù)里選什么誤差指標(biāo)會(huì)直接影響最優(yōu)參數(shù)的選擇方向因?yàn)椴煌笜?biāo)對(duì)誤差的關(guān)注點(diǎn)不同RMSE對(duì)大幅誤差懲罰重適合誤差不允許過(guò)大的場(chǎng)景比如電價(jià)預(yù)測(cè)大幅誤差可能帶來(lái)財(cái)務(wù)損失MAE對(duì)所有誤差一視同仁適合希望整體誤差都控制好、不特別在意極端情況的場(chǎng)景MAPE歸一化誤差適合不同量級(jí)的時(shí)間序列之間做橫向?qū)Ρ鹊绻麛?shù)據(jù)里有接近0的數(shù)值MAPE會(huì)被極端放大反而不穩(wěn)定我在電力負(fù)荷預(yù)測(cè)里選擇RMSE作為優(yōu)化目標(biāo)因?yàn)樵摌I(yè)務(wù)對(duì)高峰負(fù)荷的預(yù)測(cè)誤差非常敏感。如果你的數(shù)據(jù)量級(jí)小、數(shù)值平穩(wěn)用MAE或MAPE也能得到不錯(cuò)的結(jié)果。這個(gè)選擇要在寫(xiě)目標(biāo)函數(shù)之前就確定中途更換指標(biāo)意味著整個(gè)貝葉斯優(yōu)化過(guò)程作廢重來(lái)。6. 貝葉斯優(yōu)化參數(shù)的調(diào)優(yōu)技巧與算例擴(kuò)展6.1 MaxObjectiveEvaluations設(shè)置多少合理這個(gè)參數(shù)直接決定優(yōu)化質(zhì)量和耗時(shí)的平衡。設(shè)得越小比如10次搜索速度很快但參數(shù)空間沒(méi)充分探索結(jié)果可能還不如隨機(jī)搜索設(shè)得太大比如100次雖然理論上能找到更好的區(qū)域但計(jì)算時(shí)間翻好幾倍邊際收益遞減。我的經(jīng)驗(yàn)是在LSTM貝葉斯優(yōu)化場(chǎng)景下30次評(píng)估是性?xún)r(jià)比很高的檔位。7個(gè)變量、30次評(píng)估基本能覆蓋主要參數(shù)區(qū)域的組合。如果想追求更高精度可以分兩輪第一輪30次快速定位大致最優(yōu)區(qū)域第二輪把搜索范圍縮到第一輪最優(yōu)值附近的區(qū)間再跑20次精細(xì)化搜索——兩輪合計(jì)50次的效果通常好于直接跑50次。以下是我在幾個(gè)不同數(shù)據(jù)集上的經(jīng)驗(yàn)參考數(shù)據(jù)集規(guī)模建議評(píng)估次數(shù)預(yù)計(jì)耗時(shí)8核并行說(shuō)明小于1000條20-2515-30分鐘數(shù)據(jù)量小快速定位即可1000-10000條301-2小時(shí)性?xún)r(jià)比最高檔位10000條以上30-403-8小時(shí)需要平衡時(shí)間成本適當(dāng)壓縮訓(xùn)練輪數(shù)數(shù)據(jù)量大的時(shí)候更推薦先降采樣跑一輪快速搜索確定大致參數(shù)區(qū)域后再用全量數(shù)據(jù)精細(xì)化訓(xùn)練。6.2 搜索范圍的調(diào)整策略第一輪粗搜第二輪細(xì)搜貝葉斯優(yōu)化不是一定要一次跑完。兩輪式搜索效率更高第一輪把參數(shù)范圍設(shè)得寬一些比如lstmUnits給[10, 300]log空間學(xué)習(xí)率給[1e-4, 1e-1]窗口給[2, 60]。跑25-30次得到的結(jié)果會(huì)指向一個(gè)大致的好區(qū)域。第二輪基于第一輪的最優(yōu)值縮小范圍。比如第一輪最優(yōu)lstmUnits是80第二輪就把范圍設(shè)成[40, 160]最優(yōu)學(xué)習(xí)率是0.005第二輪設(shè)成[1e-3, 1e-2]最優(yōu)窗口是12第二輪設(shè)成[6, 20]。這時(shí)候再跑15-20次模型會(huì)在優(yōu)選的局部區(qū)域里精細(xì)化打磨效率比直接跑50次更高。這種做法有理論依據(jù)貝葉斯優(yōu)化的代理模型在一個(gè)區(qū)域內(nèi)擬合得越準(zhǔn)對(duì)最優(yōu)位置的預(yù)測(cè)就越可靠。第一輪寬范圍搜索建立的全球模型是粗粒度的第二輪窄范圍搜索相當(dāng)于在最重要區(qū)域做細(xì)粒度建模。我實(shí)測(cè)下來(lái)兩輪搜索加起來(lái)的訓(xùn)練時(shí)間往往比一輪50次還要少因?yàn)榈诙喓芏鄥?shù)組合訓(xùn)練時(shí)早停觸發(fā)得很快。6.3 擴(kuò)展到多特征、多步預(yù)測(cè)、序列到序列架構(gòu)上面這套框架的適用范圍比標(biāo)題里體現(xiàn)的要廣我在其他項(xiàng)目里做過(guò)不少擴(kuò)展也說(shuō)一下思路多特征輸入比如除了負(fù)荷還有溫度、濕度、風(fēng)速等多維數(shù)據(jù)。只需要把sequenceInputLayer的輸入維度改成特征數(shù)量數(shù)據(jù)準(zhǔn)備階段變成多列矩陣即可。搜索空間還可以加一個(gè)特征滯后階數(shù)的變量。序列到序列預(yù)測(cè)如果要做的是輸入過(guò)去24小時(shí)輸出未來(lái)24小時(shí)這種整段映射LSTM層需要改成Encoder-Decoder結(jié)構(gòu)。Matlab里沒(méi)有直接的seq2seqLayer但要實(shí)現(xiàn)也不難編碼器LSTM接受輸入序列解碼器LSTM從編碼器最后一層狀態(tài)開(kāi)始逐步生成輸出。這時(shí)的目標(biāo)函數(shù)變成兩個(gè)LSTM網(wǎng)絡(luò)的聯(lián)合訓(xùn)練貝葉斯優(yōu)化除了網(wǎng)絡(luò)參數(shù)還要搜索長(zhǎng)度序列等參數(shù)整體還是一樣的框架只是網(wǎng)絡(luò)結(jié)構(gòu)部分復(fù)雜一些。前提還是那個(gè)——數(shù)據(jù)決定天花板結(jié)構(gòu)決定怎么逼近天花板。多步預(yù)測(cè)反饋模式前面提到的predictAndUpdateState方式是最直觀的多步預(yù)測(cè)。如果要更復(fù)雜的策略比如預(yù)測(cè)每一步時(shí)同時(shí)維護(hù)多個(gè)候選軌跡類(lèi)似beam search的思路Matlab里同樣能實(shí)現(xiàn)但邏輯復(fù)雜度會(huì)上升不少計(jì)算成本也更高。6.4 訓(xùn)練時(shí)間優(yōu)化早停與GPU加速最后聊一下訓(xùn)練時(shí)間的整體優(yōu)化。貝葉斯優(yōu)化最大的開(kāi)銷(xiāo)是反復(fù)訓(xùn)練LSTM任何能加快單次訓(xùn)練的措施都會(huì)成倍地節(jié)省總時(shí)間。首先是早停策略前面提到過(guò)但具體閾值要根據(jù)數(shù)據(jù)量調(diào)整。數(shù)據(jù)量大、訓(xùn)練收斂慢的場(chǎng)景閾值放松一些數(shù)據(jù)量小、收斂快的場(chǎng)景閾值收緊。我是用Info.ValidationLoss和Info.TrainingLoss的倍數(shù)關(guān)系來(lái)判斷的你可以根據(jù)自己的數(shù)據(jù)情況調(diào)。其次是GPU。訓(xùn)練LSTM在GPU上的加速效果非常明顯。Matlab直接在trainingOptions里設(shè)置ExecutionEnvironment為auto如果檢測(cè)到可用GPU會(huì)自動(dòng)調(diào)用。我對(duì)比過(guò)同一組參數(shù)CPU上訓(xùn)練50個(gè)epoch需要約5分鐘GPU入門(mén)級(jí)NVIDIA上只需要約40秒——在30次貝葉斯優(yōu)化評(píng)估中這個(gè)差距會(huì)被放大30倍。還有一個(gè)容易忽略的點(diǎn)MiniBatchSize的選擇對(duì)GPU利用率影響很大。GPU對(duì)大批量數(shù)據(jù)更友好批大小設(shè)成32或64比16更充分。但批大小本身又在搜索空間里不能讓優(yōu)化器隨便試太小的值。我當(dāng)時(shí)在batchSize的搜索范圍里直接設(shè)了[32, 128]這樣既能保證GPU利用率又能讓優(yōu)化器有足夠空間。7. 實(shí)際項(xiàng)目中的完整流程回顧與建議把整個(gè)流程串起來(lái)再說(shuō)說(shuō)關(guān)鍵節(jié)點(diǎn)。第一步是數(shù)據(jù)準(zhǔn)備歸一化參數(shù)只能從訓(xùn)練集算順序切分不能亂這是所有后續(xù)工作的地基。第二步是把訓(xùn)練流程封裝成目標(biāo)函數(shù)Shuffle設(shè)成never驗(yàn)證集誤差作為返回指標(biāo)固定隨機(jī)種子保證可重復(fù)性。第三步是定義搜索空間用log變換的變量給優(yōu)化器更合理的采樣分布。第四步是運(yùn)行bayesopt30次評(píng)估開(kāi)并行配好采集函數(shù)讓它自動(dòng)搜索。第五步是提取最優(yōu)參數(shù)用全量數(shù)據(jù)重訓(xùn)最終模型然后在獨(dú)立的測(cè)試集上驗(yàn)證效果。整個(gè)流程有一個(gè)容易被忽略的整體性原則驗(yàn)證集在貝葉斯優(yōu)化過(guò)程中被反復(fù)用來(lái)評(píng)估參數(shù)好壞所以它本質(zhì)上參與進(jìn)了超參數(shù)選擇最終模型再用驗(yàn)證集數(shù)據(jù)參與訓(xùn)練測(cè)試集必須是從未參與過(guò)任何選擇和訓(xùn)練過(guò)程的獨(dú)立數(shù)據(jù)。我在項(xiàng)目里習(xí)慣留下最后10%的數(shù)據(jù)完全不參與前面任何步驟只用來(lái)做最終評(píng)估這一步能幫你識(shí)別出模型到底是真的泛化好還是只是對(duì)已知數(shù)據(jù)記憶好。寫(xiě)到最后說(shuō)一點(diǎn)個(gè)人體會(huì)。貝葉斯優(yōu)化LSTM這套組合解決的不僅是找出好參數(shù)的問(wèn)題更重要的是它把調(diào)參過(guò)程從拍腦袋碰運(yùn)氣變成了有信息量、有方向感的自動(dòng)搜索。第一次跑完的時(shí)候你可能會(huì)像我一樣有點(diǎn)驚訝——原來(lái)之前手動(dòng)試了幾十組都沒(méi)找到的配置優(yōu)化器自己就能在二三十次評(píng)估內(nèi)找到。這套方案在Matlab里的實(shí)現(xiàn)難度不算高最大的成本其實(shí)是理解布谷鳥(niǎo)優(yōu)化器要什么、LSTM訓(xùn)練時(shí)有哪些關(guān)鍵陷阱這兩點(diǎn)在這篇里都覆蓋到了。有類(lèi)似需求的朋友跟著框架跑一遍應(yīng)該很快能落地到自己的數(shù)據(jù)上。