測實戰(zhàn):從數(shù)據(jù)預(yù)處理到多步外推的完整指南)
簡介基于長短期記憶網(wǎng)絡(luò)的時間序列分析預(yù)測源碼包面向Python開發(fā)者和人工智能入門者提供從數(shù)據(jù)清洗、特征工程、模型構(gòu)建、訓(xùn)練到評估的完整解決方案適用于股票價格、銷售需求、空氣質(zhì)量等典型序列預(yù)測場景。壓縮包共一百二十六個文件其中七十五個py腳本覆蓋數(shù)據(jù)加載、歸一化、滑窗生成、LSTM定義與訓(xùn)練測試二十六個csv文件提供多組實驗數(shù)據(jù)包含空氣污染指標十五個txt說明標注參數(shù)與環(huán)境配置另有h5格式訓(xùn)練好的模型權(quán)重和md項目文檔整包約五點四二兆字節(jié)輕量易上手。已有五千零九十六人學(xué)習(xí)下載社區(qū)驗證度較高。通過該項目可深入理解LSTM輸入門、遺忘門、輸出門的作用掌握時序數(shù)據(jù)歸一化、滑動窗口構(gòu)建、均方誤差與平均絕對誤差評估等方法并能基于附帶數(shù)據(jù)集復(fù)現(xiàn)結(jié)果快速遷移至自己的預(yù)測任務(wù)。1. 先認清LSTM 預(yù)測腳本為什么“跑得通”不等于“測得準”拿到這套基于 LSTM 的時間序列分析預(yù)測代碼包第一件事不是找哪個文件是訓(xùn)練入口而是先想清楚一個問題你要的預(yù)測到底是“把歷史曲線擬合得像”還是“未來一段時間的走勢真正可參考”。這兩個目標在代碼里對應(yīng)完全不同的數(shù)據(jù)處理方式和評估口徑。很多跑通了這個源碼的人對著訓(xùn)練集曲線滿意地點頭然后一換到未來數(shù)據(jù)就翻車——問題幾乎都不在 LSTM 模型本身而是滑窗怎么切、數(shù)據(jù)怎么歸一化、測試集怎么劃分。這套源碼的核心是把“近 N 個點預(yù)測未來 M 個點”的完整流程串起來數(shù)據(jù)預(yù)處理、構(gòu)造樣本、訓(xùn)練 LSTM 神經(jīng)網(wǎng)絡(luò)、輸出預(yù)測值再反歸一化。適合做銷量、氣溫、設(shè)備指標、流量這類帶時間戳的回歸預(yù)測也適合剛?cè)腴T深度學(xué)習(xí)、想把 LSTM 落地而不是停留在教程 demo 上的從業(yè)者。2. 數(shù)據(jù)準備把原始序列切成 LSTM 能吃的樣本兩個參數(shù)決定上限2.1 先別訓(xùn)練單變量還是多變量、缺失值怎么補常見的 LSTM 時間序列項目里數(shù)據(jù)格式形形色色。極簡的是一列數(shù)值比如每天的電價、每個小時的在線人數(shù)復(fù)雜一點的是多列除了目標值還有輔助特征。拿到數(shù)據(jù)后第一個動作不是寫模型而是確認兩件事目標列是哪一列、時間順序是否已經(jīng)被打亂。CSV 讀進來如果是亂序的必須先按時間戳排序這一步漏掉后面所有滑窗樣本的時間含義就全錯了。缺失值處理也要區(qū)分場景。普通表格可以用均值填充時序數(shù)據(jù)我會優(yōu)先用前后合法值插值因為序列里的值是連續(xù)變化的直接取均值會把局部波動抹平。異常值的處理更要慎重一個脈沖尖峰在普通回歸里只是損失一個點但在滑窗構(gòu)造樣本的時候這個壞點會被包含進多個窗口等于把一個錯誤復(fù)制了十幾遍。檢查序列里有沒有超過正常范圍幾個數(shù)量級的跳變先用可視化掃一遍比急著調(diào)模型參數(shù)重要得多。import pandas as pd import numpy as np df pd.read_csv(data.csv, parse_dates[timestamp]) df df.sort_values(timestamp).reset_index(dropTrue) # 缺失值用線性插值補充避免均值填充抹掉趨勢細節(jié) df[value] df[value].interpolate(methodlinear) # 看一眼基本統(tǒng)計量重點檢查有沒有遠超均值量級的異常尖峰 print(df[value].describe())這段代碼里interpolate(methodlinear)是時序缺失值處理的最常用手段比fillna(methodffill)更平滑。檢查統(tǒng)計量時重點看 max 和 mean 之間的差距如果 max 是 mean 的幾十倍就要懷疑是否存在壞點而不是直接拿去做訓(xùn)練。2.2 訓(xùn)練集和測試集必須按時間切這是時序跟普通監(jiān)督學(xué)習(xí)的邊界普通機器學(xué)習(xí)里隨機劃分訓(xùn)練集和測試集沒問題因為樣本獨立。時間序列不行因為相鄰樣本之間存在天然相關(guān)性尤其在滑窗重疊構(gòu)造樣本的場景下如果隨機劃分訓(xùn)練集里很容易出現(xiàn)“測試樣本的上一段窗口”模型等于提前看到了測試段的輸入部分評估結(jié)果當然虛高。這種泄漏不會報錯但會給你一個根本不真實的測試指標。正確做法是嚴格按時間切開比如前 80% 做訓(xùn)練后 20% 做測試驗證集再從訓(xùn)練段尾部切一部分出來不能打亂。切完后做歸一化時順序也很有講究。MinMaxScaler只能 fit 訓(xùn)練段的數(shù)據(jù)然后用這個已經(jīng)學(xué)習(xí)好參數(shù)的標準去 transform 測試段。如果對整個序列統(tǒng)一 fit測試段的最大值和最小值會提前混入縮放邏輯同樣屬于數(shù)據(jù)泄漏。train_size int(len(df) * 0.8) valid_size int(train_size * 0.8) train_df df.iloc[:valid_size] valid_df df.iloc[valid_size:train_size] test_df df.iloc[train_size:] from sklearn.preprocessing import MinMaxScaler scaler MinMaxScaler(feature_range(0, 1)) train_values scaler.fit_transform(train_df[[value]]) valid_values scaler.transform(valid_df[[value]]) test_values scaler.transform(test_df[[value]])這里fit_transform只出現(xiàn)在訓(xùn)練段上驗證段和測試段只用transform。驗證段用來在訓(xùn)練過程中挑超參數(shù)測試段只做最終評估嚴格保持“三次隔離”。我習(xí)慣再用df.iloc[:]的時間切法而不是隨機抽樣強調(diào)的是順序敏感。2.3 滑窗構(gòu)造樣本seq_len 和 step 兩個參數(shù)決定你能預(yù)測什么LSTM 不能直接吃一長串原始序列它的輸入是固定長度的窗口。比如用過去 24 個小時預(yù)測下 1 個小時那就把序列切成每 24 個點一組第 25 個點作為標簽。這個滑動窗口機制里有兩個參數(shù)往往比網(wǎng)絡(luò)結(jié)構(gòu)本身還影響最終效果窗口長度seq_len和滑動步長step。seq_len決定模型能“回頭看”多遠必須結(jié)合數(shù)據(jù)本身的周期來選。如果數(shù)據(jù)是日粒度通常用 7 的倍數(shù)因為要覆蓋一周的周期如果數(shù)據(jù)是小時粒度才優(yōu)先考慮 24。很多人照搬別人項目里的seq_len24結(jié)果換到日粒度數(shù)據(jù)上效果一塌糊涂因為 24 天對日數(shù)據(jù)來說既不是周周期也不是月周期。step1會讓相鄰樣本大量重疊訓(xùn)練集膨脹但有效信息增量不大step設(shè)大一點比如 3 或 5樣本量會明顯下降訓(xùn)練速度更快代價是能學(xué)習(xí)的模式密度變低。我一般先用小步長跑通再用大步長調(diào)優(yōu)。def make_sequences(values, seq_len24, step1): X, y [], [] for i in range(0, len(values) - seq_len, step): X.append(values[i:iseq_len]) y.append(values[iseq_len]) return np.array(X), np.array(y) seq_len 24 train_X, train_y make_sequences(train_values, seq_lenseq_len) test_X, test_y make_sequences(test_values, seq_lenseq_len) # LSTM 期望的輸入形狀: (樣本數(shù), 時間步數(shù), 特征維度) train_X train_X.reshape(-1, seq_len, 1) test_X test_X.reshape(-1, seq_len, 1)最后reshape(-1, seq_len, 1)這一步非常關(guān)鍵1代表單變量特征數(shù)量如果是多變量預(yù)測這個維度等于特征列數(shù)。注意測試集構(gòu)造樣本時只需要從原始測試值中取窗口即可標簽就是下一個點但實際預(yù)測場景里最后一個窗口之后再無標簽?zāi)蔷褪钦嬲耐馔祁A(yù)測——后面第 4 章專門展開。3. 模型搭建與訓(xùn)練把 LSTM 調(diào)到“記得住又不死記”的實用參數(shù)3.1 LSTM 解決什么問題門控結(jié)構(gòu)在時序回歸里的作用為什么不直接用普通全連接網(wǎng)絡(luò)做時間序列回歸時序數(shù)據(jù)的特點是當前值常常依賴于很多個時間步之前的信息。普通網(wǎng)絡(luò)把所有輸入特征平等對待既沒有先后概念也沒有“記憶”機制。LSTM 在循環(huán)結(jié)構(gòu)基礎(chǔ)上加入了三個門遺忘門決定過去的信息保留多少輸入門決定當前時間步的新信息寫入多少輸出門決定當前時間步輸出什么。這套機制理論上能捕捉“昨天這個時候的值對今天有影響”和“一周前同一天的模式也有影響”這類跨步長依賴。對你寫代碼的人來說理解到這一層已經(jīng)足夠。真正要調(diào)的是讓這種“記憶”既不要太長造成過擬合也不要太短記不住周期。這一節(jié)不展開數(shù)學(xué)推導(dǎo)重點放在怎么搭一個能跑起來、能調(diào)參的模型結(jié)構(gòu)。3.2 用 PyTorch 搭一個可直接運行的 LSTM 回歸網(wǎng)絡(luò)常見做法是用 PyTorch 實現(xiàn)一個 LSTM 層接收滑窗序列輸出最后一個時間步的隱藏狀態(tài)再接一個全連接層映射到預(yù)測值。這個結(jié)構(gòu)簡潔、穩(wěn)定適合絕大多數(shù)單變量或多變量預(yù)測任務(wù)。下面這個網(wǎng)絡(luò)結(jié)構(gòu)是每個時間序列預(yù)測任務(wù)的基本起點。import torch import torch.nn as nn class LSTMPredictor(nn.Module): def __init__(self, input_size1, hidden_size32, num_layers2, pred_len1): super().__init__() self.lstm nn.LSTM( input_sizeinput_size, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue ) self.regressor nn.Linear(hidden_size, pred_len) def forward(self, x): out, _ self.lstm(x) # 輸出每個時間步的隱狀態(tài) last out[:, -1, :] # 只取最后一個時間步的隱狀態(tài) return self.regressor(last)batch_firstTrue讓輸入張量形狀符合 (batch, seq_len, features)省去轉(zhuǎn)置的麻煩pred_len是你要預(yù)測的未來步數(shù)默認 1。如果做多步預(yù)測可以直接把pred_len設(shè)為目標步數(shù)全連接層輸出對應(yīng)數(shù)量的值這與后面要講的滾動預(yù)測走的是兩條路各有適用場景。訓(xùn)練環(huán)節(jié)的代碼同樣有講究。損失函數(shù)用均方誤差即可優(yōu)化器選 Adam學(xué)習(xí)率從 0.001 起步。最關(guān)鍵的是訓(xùn)練時數(shù)據(jù)處理別踩亂序的坑。from torch.utils.data import TensorDataset, DataLoader train_dataset TensorDataset( torch.tensor(train_X, dtypetorch.float32), torch.tensor(train_y, dtypetorch.float32) ) train_loader DataLoader(train_dataset, batch_size64, shuffleFalse) val_loader DataLoader( TensorDataset( torch.tensor(valid_X, dtypetorch.float32), torch.tensor(valid_y, dtypetorch.float32) ), batch_size64, shuffleFalse ) model LSTMPredictor(input_size1, hidden_size32, num_layers2, pred_len1) optimizer torch.optim.Adam(model.parameters(), lr1e-3) loss_fn nn.MSELoss() for epoch in range(80): model.train() epoch_loss 0.0 for batch_x, batch_y in train_loader: optimizer.zero_grad() pred model(batch_x).squeeze(-1) loss loss_fn(pred, batch_y) loss.backward() optimizer.step() epoch_loss loss.item() print(fepoch {epoch:2d}, train loss {epoch_loss / len(train_loader):.6f})這里shuffleFalse是刻意設(shè)置的。滑窗構(gòu)造的相鄰樣本高度重疊打亂順序雖然不影響單個樣本內(nèi)部結(jié)構(gòu)但 batch 內(nèi)數(shù)據(jù)的分布會被攪亂訓(xùn)練收斂不穩(wěn)定。驗證集 loss 不參與梯度更新只在每個 epoch 末尾觀察用。訓(xùn)練到后期如果 train loss 持續(xù)下降而驗證 loss 開始抬升那就是過擬合信號應(yīng)該提前停掉而不是繼續(xù)跑滿 80 個 epoch。3.3 訓(xùn)練時看什么loss 曲線、驗證窗口和隨機種子訓(xùn)練過程中的觀察遠比調(diào)參本身重要。第一個觀測點是 loss 下降曲線如果前幾個 epoch loss 紋絲不動多半是學(xué)習(xí)率太大導(dǎo)致震蕩或者歸一化沒做好數(shù)值不穩(wěn)定如果 loss 快速降到極小值、之后驗證集 loss 反彈那就是過擬合。第二個觀測點是定義一個與訓(xùn)練集不重疊的驗證窗口用于在訓(xùn)練過程中評估真實泛化能力這個窗口不能參與任何調(diào)參決策。第三個容易被忽略的是隨機種子LSTM 初始化權(quán)重是隨機的崩潰概率很低但如果你同一個腳本跑兩次結(jié)果差異很大說明沒有固定隨機種子排查問題時會非常痛苦。def set_seed(seed42): np.random.seed(seed) torch.manual_seed(seed) set_seed(42)固定種子之后再調(diào)參每次對比才有意義。否則你改了hidden_size從 32 換成 64效果變好還是變壞無法分辨是參數(shù)起作用還是運氣起作用。這是很多調(diào)參花大量時間卻原地踏步的重要原因。4. 預(yù)測執(zhí)行從預(yù)測下一點到外推未來 30 步4.1 單步預(yù)測的最小調(diào)用注意張量形狀和反歸一化模型訓(xùn)練完真正去預(yù)測的時候最容易出問題的是形狀和量綱。訓(xùn)練時輸入是 (batch, seq_len, features)推理時只有一條輸入所以要構(gòu)造 (1, seq_len, features)。預(yù)測出來的值也別忘了它是在 0~1 區(qū)間經(jīng)過縮放的必須用前面同一個 scaler 反歸一化還原成真實量綱。model.eval() # 用測試集最后 seq_len 個點作為輸入窗口 current_seq test_values[-seq_len:].reshape(1, seq_len, 1) with torch.no_grad(): pred_scaled model(torch.tensor(current_seq, dtypetorch.float32)).item() pred_value scaler.inverse_transform(np.array([[pred_scaled]])) print(f預(yù)測值: {pred_value[0][0]:.2f})這段代碼的關(guān)鍵在最后一步scaler.inverse_transform期望輸入形狀是 (n_samples, n_features)所以這里必須包一層np.array([[pred_scaled]])否則會報維度錯或者得到錯誤結(jié)果。單步預(yù)測本身邏輯簡單但它是一切多步預(yù)測的基石。4.2 多步外推兩種思路滾動預(yù)測和直接多輸出如果目標是預(yù)測未來 7 天、30 天就不能只做一步。兩種常見做法滾動預(yù)測和直接多輸出。滾動預(yù)測的思路是用當前窗口預(yù)測出下一個值把這個預(yù)測值拼進窗口尾部再扔掉最前面一個值保持窗口長度不變繼續(xù)預(yù)測下一步。實現(xiàn)簡單完全復(fù)用單步模型但誤差會累積——第二步的輸入里已經(jīng)包含了第一步的預(yù)測誤差隨著步數(shù)增加預(yù)測序列會逐漸漂移。直接多輸出的思路是把模型最后一層全連接的pred_len設(shè)為目標步數(shù)比如 7訓(xùn)練時直接把標簽數(shù)組當成 7 個值來監(jiān)督。推理時一次輸出未來 7 個點誤差不累積但需要重新準備多步標簽數(shù)據(jù)訓(xùn)練成本稍高。def rolling_forecast(model, init_seq, steps30): model.eval() window init_seq.copy().reshape(-1) preds [] with torch.no_grad(): for _ in range(steps): x torch.tensor( window.reshape(1, seq_len, 1), dtypetorch.float32 ) y_scaled model(x).item() preds.append(y_scaled) # 滾動窗口丟最老的點拼入新預(yù)測值 window np.append(window[1:], y_scaled) return scaler.inverse_transform(np.array(preds).reshape(-1, 1))滾動預(yù)測里window[1:]丟一個點、np.append補一個新點這個操作本身簡單但要注意窗口里的值必須是歸一化狀態(tài)不能混入原始量綱否則模型輸入分布突變預(yù)測直接發(fā)散。我一般把滾動預(yù)測用在短期外推不超過 7 步更長的預(yù)測建議用直接多輸出模型或者兩者結(jié)合用直接多輸出出 7 個點再用這 7 個點作為新的窗口做滾動延伸。4.3 評估必須用 rolling 指標只看第一步的結(jié)果會騙人訓(xùn)練過程中評估用的是單步預(yù)測 loss但真正做多步預(yù)測時評估口徑要跟著變。很多人報告“預(yù)測效果挺好”細問才知道只統(tǒng)計了第一步的誤差——第一步的輸入全部來自真實歷史值難度最低第二步起輸入開始摻入模型自身輸出誤差逐漸積累。所以評估多步預(yù)測時要把 h1、h7、h30 的誤差分開計算。實際操作上用測試集從第seq_len個點開始逐點滾動預(yù)測每一步都記錄當前步的預(yù)測值與真實值之差最終按步數(shù)分桶計算平均絕對誤差或均方根誤差。不同步數(shù)下的誤差曲線能直觀展示模型到底能外推多遠。如果 h1 誤差很小h7 誤差翻倍h30 誤差已經(jīng)接近基線那說明這個模型只適合短期預(yù)測強行做長期預(yù)測沒有意義。5. 避坑清單LSTM 時間序列預(yù)測翻車的 5 個經(jīng)典原因5.1 數(shù)據(jù)集隨機劃分測試集信息泄漏進訓(xùn)練現(xiàn)象訓(xùn)練曲線擬合得很好測試集上的指標也很漂亮但一旦投入真實未來數(shù)據(jù)進行預(yù)測曲線立刻偏離真實走勢偏差比設(shè)想的要大得多。原因滑窗構(gòu)造的樣本之間高度重疊相鄰樣本共享大部分輸入。如果用train_test_split(random_state42)這類隨機劃分方式測試集里的某一個樣本很可能它的整個輸入窗口也出現(xiàn)在訓(xùn)練集里。模型等于直接背過答案測試指標虛高。解決嚴格按時間順序切分先按df.iloc[:train_size]取出訓(xùn)練段再取測試段不做任何打亂。這也是前面第 2 章里強調(diào)的時間切分方式屬于整個流程里最基礎(chǔ)也最要命的一道防線。5.2 MinMaxScaler 先 fit 全量數(shù)據(jù)再做歸一化現(xiàn)象預(yù)測曲線整體看起來和真實曲線像一對平移的平行線邊界處數(shù)值始終差一截。原因MinMaxScaler.fit()在全部數(shù)據(jù)上執(zhí)行等于讓模型在訓(xùn)練階段就看到了測試集的最大值和最小值。模型輸出被壓縮在一個偏向測試分布的空間里反歸一化后自然產(chǎn)生系統(tǒng)性偏移。解決只對訓(xùn)練段數(shù)據(jù)調(diào)用fit_transform驗證段和測試段統(tǒng)一用這個已經(jīng)定型的transform。推理階段拿到的新數(shù)據(jù)也要用同一個 scaler 做transform預(yù)測完成后再用同一個 scalerinverse_transform還原。5.3 多步預(yù)測只報告第一步誤差現(xiàn)象匯報里寫著“預(yù)測誤差 2%”但業(yè)務(wù)方實際使用后說你的預(yù)測根本沒法看尤其是第 7 天之后的預(yù)測偏差大到失去參考價值。原因第一步預(yù)測的輸入完全來自真實歷史觀察值模型沒有機會自我污染第二步開始輸入窗口里有模型上一輪輸出的預(yù)測值誤差開始累積。如果只統(tǒng)計 h1 的誤差測的根本不是模型在真實使用場景下的表現(xiàn)。解決多步預(yù)測必須按步數(shù)分層評估。記錄預(yù)測序列每一個時間步與真實值的誤差按 h1、h5、h10、h30 分別計算 MAE把這條誤差增長曲線暴露出來再決定模型能承諾多少步以內(nèi)的預(yù)測。5.4 缺失值用均值填充、異常尖峰不清理現(xiàn)象訓(xùn)練過程中 loss 下降正常但預(yù)測出的曲線比真實數(shù)據(jù)平滑得多很多正常波動都被吞掉了。原因均值填充會把序列里的局部波動抹平模型學(xué)到的規(guī)律變得過于平滑異常尖峰沒清除的話滑窗會把同一個壞點復(fù)制進幾十個訓(xùn)練樣本導(dǎo)致模型專門學(xué)習(xí)這個錯誤的模式。解決列缺失用interpolate(methodlinear)保留趨勢對于明顯超出正常波動范圍的脈沖點用前后窗口的中位數(shù)替換或者直接剔除再插值??傊茸屝蛄斜旧砀蓛粼僬動?xùn)練效果。5.5 換數(shù)據(jù)集后直接照搬 seq_len24現(xiàn)象上一個項目里seq_len24效果好換到新的業(yè)務(wù)數(shù)據(jù)后怎么調(diào)都上不去模型似乎永遠慢半拍。原因24 這個值通常是為小時粒度數(shù)據(jù)準備的因為一天有 24 小時。如果換到日粒度數(shù)據(jù)24 天既不是一個完整的周周期也不是月周期換到分鐘粒度數(shù)據(jù)24 分鐘又太短。解決拿到新數(shù)據(jù)集先做周期探測。畫出數(shù)據(jù)的自相關(guān)圖或直接按不同seq_len7、14、24、28、48分別訓(xùn)練幾次看驗證集 loss 誰最低。滑窗長度應(yīng)該和數(shù)據(jù)本身的周期對齊而不是沿用別人項目里的習(xí)慣值。6. 讓它能真正投入多種子評估與滾動重訓(xùn)的落地做法前面幾章已經(jīng)覆蓋了從數(shù)據(jù)準備到多步預(yù)測的完整流程但真要把這套代碼用在業(yè)務(wù)決策里還差兩個非常實用的做法。第一個做法是多隨機種子評估。LSTM 初始權(quán)重隨機單次訓(xùn)練的結(jié)果帶有運氣成分。我會用 5 個不同種子分別訓(xùn)練保存每個種子的預(yù)測序列把所有預(yù)測放在同一張圖上。預(yù)測線比較粗、互相分散說明模型對數(shù)據(jù)模式的把握還很不穩(wěn)定如果幾條線幾乎重合說明模型確實學(xué)到了穩(wěn)定的規(guī)律。更進一步的可以從多次預(yù)測中取 10% 和 90% 分位數(shù)畫一條預(yù)測區(qū)間業(yè)務(wù)匯報時給區(qū)間比給單點有用得多。preds [] for seed in [42, 123, 999, 7, 2024]: set_seed(seed) model LSTMPredictor(...) # 重新訓(xùn)練并預(yù)測 preds.append(rolling_forecast(model, init_seq, steps30)) preds np.array(preds) # shape: (5, 30) lower np.percentile(preds, 10, axis0) upper np.percentile(preds, 90, axis0)第二個做法是滾動重訓(xùn)。業(yè)務(wù)數(shù)據(jù)不斷更新模型不可能訓(xùn)一次用一年。我一般以一周為周期把新觀測追加到訓(xùn)練集尾部用上一次訓(xùn)練得到的模型參數(shù)作為初始權(quán)重繼續(xù)訓(xùn)練 10~20 個 epoch。這種方式叫 warm start比每周從頭訓(xùn)練快得多也更容易適應(yīng)緩慢的數(shù)據(jù)漂移。重訓(xùn)后用最新一段真實數(shù)據(jù)做一輪 h7 的驗證誤差如果明顯惡化說明數(shù)據(jù)分布發(fā)生了結(jié)構(gòu)性變化需要重新審視特征和窗口設(shè)置。最后分享一個我自己的習(xí)慣任何新數(shù)據(jù)集跑 LSTM 之前先跑一個最簡單的基線——直接用上一個值當預(yù)測值persistence model。如果 LSTM 連這個傻瓜基線的誤差都壓不下來 10% 以上那不是模型能力問題而是前面的數(shù)據(jù)處理或參數(shù)選擇出了問題。這個習(xí)慣幫我擋掉了大量盲目調(diào)參的時間也讓我更容易分辨模型是真的學(xué)到了規(guī)律還是僅僅在復(fù)讀歷史。希望這些內(nèi)容能幫你在 LSTM 時間序列預(yù)測這條路上少踩幾個坑把代碼包真正變成可上線、可解釋的預(yù)測工具。本文還有配套的精品資源點擊獲取