測實戰(zhàn)指南)
簡介本資源面向計算機(jī)、電子信息工程、數(shù)學(xué)等專業(yè)的大學(xué)生及算法入門者提供一套完整的ARIMA-SSA-LSTM時間序列預(yù)測實現(xiàn)方案可用于課程設(shè)計、期末大作業(yè)或畢業(yè)設(shè)計。代碼基于Python與TensorFlow編寫采用參數(shù)化編程思路參數(shù)可靈活調(diào)整且配有保姆級注釋幾乎一行一注釋便于零基礎(chǔ)讀者理解建模流程。壓縮包共3個文件包含2個CSV數(shù)據(jù)文件與1個Python源碼文件整體約51KB數(shù)據(jù)與代碼配套齊全可直接運行驗證。資源將ARIMA、麻雀搜索算法SSA與LSTM相結(jié)合展示了從數(shù)據(jù)讀取、參數(shù)優(yōu)化到預(yù)測輸出的完整鏈路讀者可據(jù)此掌握組合模型的搭建方法與調(diào)參技巧。目前已有510人學(xué)習(xí)適合希望快速上手時間序列預(yù)測實戰(zhàn)的讀者參考。1. 從一條震蕩的銷量曲線說起ARIMA-SSA-LSTM 到底在解決什么去年幫一個做小家電的朋友看數(shù)據(jù)他手里有 36 個月的出貨記錄想預(yù)測未來 6 個月。我先用 ARIMA 跑了一版趨勢抓得還行但每逢大促月份的尖峰全被抹平換成 LSTM 單模型尖峰能咬住一部分可樣本量太小驗證集上的曲線抖得像心電圖。這不是模型不行是單一模型對「線性趨勢 非線性突變」這種混合結(jié)構(gòu)天然偏科。ARIMA-SSA-LSTM 這套組合就是沖這個痛點來的ARIMA 負(fù)責(zé)把線性成分剝出來SSA奇異譜分析把殘差里的噪聲和周期分量拆開、重構(gòu)出更干凈的序列LSTM 再去學(xué)剩下的非線性映射。它適合手里有幾百到幾千條單變量序列、又不想上重型深度模型的從業(yè)者比如銷量、負(fù)荷、流量、GDP 這類按時間排隊的數(shù)值預(yù)測。下面我按自己落地的順序把數(shù)據(jù)怎么切、三個模塊怎么串、參數(shù)怎么調(diào)、哪里最容易翻車講清楚。2. 數(shù)據(jù)準(zhǔn)備與平穩(wěn)性處理ARIMA 的地基怎么打2.1 時間序列的四個前置檢查拿到一列帶時間戳的數(shù)值別急著往模型里灌。我一般先做四件事確認(rèn)時間間隔是否等距、檢查缺失值和異常點、看量綱是否需要歸一化、畫一遍原始曲線和差分曲線。等距是 ARIMA 的硬要求如果原始數(shù)據(jù)是「有單才記一筆」的事件流得先重采樣成固定頻率比如按天求和或按周取均值。缺失值不要直接dropna銷量類數(shù)據(jù)缺失往往意味著當(dāng)天真沒賣出去填 0 比填均值更合理傳感器類數(shù)據(jù)則適合線性插值。異常點用滾動中位數(shù)加 3 倍 MAD 判定超過閾值的先標(biāo)記別急著刪后面 SSA 重構(gòu)時還能用上。平穩(wěn)性決定了 ARIMA 的差分階數(shù) d。ADF 檢驗是常用手段p 值小于 0.05 認(rèn)為平穩(wěn)。但我要提醒一句ADF 對短序列和強(qiáng)周期序列經(jīng)常誤判最好配合 ACF/PACF 圖一起看。如果 ACF 衰減極慢、PACF 在 1 階后截尾基本就是需要差分的信號。差分不是越多越好過度差分會把序列里的有效信息也差掉一般 d 取 0、1、2 三檔試優(yōu)先 1 階。import pandas as pd import numpy as np from statsmodels.tsa.stattools import adfuller # 讀取數(shù)據(jù)假設(shè)兩列ds(時間), y(數(shù)值) df pd.read_csv(series.csv, parse_dates[ds]).sort_values(ds) df df.set_index(ds).asfreq(D) # 統(tǒng)一成日頻缺失處補(bǔ) NaT df[y] df[y].interpolate(linear) # 線性插值補(bǔ)缺失 # 異常點標(biāo)記滾動中位數(shù) 3倍MAD med df[y].rolling(7, centerTrue).median() mad (df[y] - med).abs().rolling(7, centerTrue).median() df[outlier] (df[y] - med).abs() 3 * 1.4826 * mad # ADF 檢驗 def adf_report(s, name): r adfuller(s.dropna(), autolagAIC) print(f{name}: stat{r[0]:.3f}, p{r[1]:.4f}, lags{r[2]}) adf_report(df[y], 原始序列) adf_report(df[y].diff(), 一階差分)這段代碼里asfreq(D)把不規(guī)則時間軸強(qiáng)制對齊到日頻interpolate(linear)只補(bǔ)數(shù)值不補(bǔ)時間。MAD 判定用 1.4826 這個系數(shù)是為了讓 MAD 在正態(tài)分布下與標(biāo)準(zhǔn)差可比這是穩(wěn)健統(tǒng)計里的常規(guī)做法。ADF 的autolagAIC讓函數(shù)自動選滯后階數(shù)比手動指定穩(wěn)。跑完看兩個 p 值原始不平穩(wěn)、一階差分平穩(wěn)d 就定 1。2.2 ARIMA 定階別迷信 auto_arima定階我踩過的坑最多。pmdarima的auto_arima很方便但它默認(rèn)用 AIC 做準(zhǔn)則短序列上容易選出過大的 p 和 q導(dǎo)致過擬合。我的習(xí)慣是先用auto_arima給一個候選范圍再手動在 (p,d,q) 的鄰域里用 BIC 和滾動驗證比一遍。BIC 對參數(shù)個數(shù)懲罰更重小樣本下更保守。季節(jié)性明顯的序列比如月度數(shù)據(jù)還要加 (P,D,Q,s) 這組季節(jié)項s 取 12。from statsmodels.tsa.arima.model import ARIMA from sklearn.metrics import mean_absolute_error y df[y].values train, test y[:-12], y[-12:] best None for p in range(0, 4): for q in range(0, 4): try: m ARIMA(train, order(p, 1, q)).fit() pred m.forecast(len(test)) mae mean_absolute_error(test, pred) if best is None or mae best[0]: best (mae, p, q, m) except Exception: continue print(最優(yōu)階數(shù):, best[1], best[2], MAE:, round(best[0], 3))這里用滾動外推的方式評估而不是只看訓(xùn)練集擬合優(yōu)度。order(p,1,q)里的 1 就是前面定下的 d。forecast(len(test))做的是樣本外預(yù)測比fittedvalues更能反映真實泛化。如果所有組合的 MAE 都很大說明線性部分解釋力有限這恰恰是后面要上 SSA 和 LSTM 的信號不是失敗。2.3 殘差提取把線性部分「減」出去ARIMA 擬合完真正要交給下游的不是預(yù)測值而是殘差。殘差 真實值 ? ARIMA 擬合值它承載了線性模型沒抓住的非線性結(jié)構(gòu)和周期波動。提取時注意對齊索引訓(xùn)練段和測試段要分開存別把未來信息漏進(jìn)訓(xùn)練。殘差序列通常均值接近 0如果還有明顯趨勢說明 d 選小了回去重定階。resid_train train - best[3].fittedvalues resid_test test - best[3].forecast(len(test)) np.save(resid_train.npy, resid_train) np.save(resid_test.npy, resid_test)fittedvalues是樣本內(nèi)一步預(yù)測forecast是樣本外。兩者相減得到的兩段殘差拼起來才是完整殘差序列。存成 npy 是為了下一步 SSA 直接讀避免反復(fù)跑 ARIMA。到這里ARIMA 這層地基就算打完了接下來處理殘差。3. SSA 分解與重構(gòu)殘差里的噪聲和周期怎么拆3.1 奇異譜分析的軌跡矩陣與分組邏輯SSA 的核心是把一維序列嵌成軌跡矩陣再做奇異值分解按奇異值大小把分量分成「趨勢」「周期」「噪聲」幾組最后對角平均重構(gòu)回一維。它不像小波那樣需要選基函數(shù)也不像 EMD 那樣容易模態(tài)混疊對殘差這種信噪比不高的序列比較友好。關(guān)鍵參數(shù)有兩個窗口長度 L 和分組方式。L 一般取序列長度的 1/3 到 1/2太短拆不出周期太長計算量陡增且分量之間開始混。分組是 SSA 里最像「玄學(xué)」的一步。奇異值從大到小排前幾個通常對應(yīng)趨勢和主周期中間一段對應(yīng)次周期尾部小奇異值基本是噪聲。我一般畫一張奇異值譜找那個明顯拐點拐點之后的重構(gòu)時直接丟掉。如果拐點不明顯就用分組重構(gòu)后的序列做 ADF 和方差比檢驗看保留多少組能讓殘差既平滑又不丟周期。import numpy as np def ssa_decompose(series, L): N len(series) K N - L 1 # 軌跡矩陣 X np.column_stack([series[i:iL] for i in range(K)]) # 奇異值分解 U, s, Vt np.linalg.svd(X, full_matricesFalse) return U, s, Vt, L, K def ssa_reconstruct(U, s, Vt, L, K, groups): groups: 列表的列表每個子列表是一組要保留的分量索引 recon np.zeros(L K - 1) for g in groups: Xg np.zeros((L, K)) for i in g: Xg s[i] * np.outer(U[:, i], Vt[i, :]) # 對角平均 for k in range(L K - 1): idx [j for j in range(K) if 0 k - j L] recon[k] np.mean([Xg[k - j, j] for j in idx]) return recon resid np.load(resid_train.npy) L len(resid) // 3 U, s, Vt, L, K ssa_decompose(resid, L) print(奇異值譜:, np.round(s[:10], 3))ssa_decompose里np.column_stack構(gòu)造的是 Hankel 型軌跡矩陣每一列是原序列的一個滑動窗口。np.linalg.svd返回的 s 是降序奇異值。ssa_reconstruct里的對角平均是 SSA 反變換的標(biāo)準(zhǔn)操作把矩陣元素沿反對角線取平均還原成一維。groups參數(shù)讓你自由指定保留哪幾組比如[[0,1],[2,3,4]]表示趨勢組和周期組分開重構(gòu)。3.2 分組策略用方差貢獻(xiàn)率代替肉眼判斷肉眼找拐點不穩(wěn)定換個人看可能結(jié)論就變了。我后來改成算方差貢獻(xiàn)率每個奇異值的平方除以總和累計到 85% 到 95% 之間的那一段就是主分量。剩下的尾部直接歸入噪聲組丟棄。這樣每次跑結(jié)果可復(fù)現(xiàn)也方便寫進(jìn)腳本自動執(zhí)行。var_ratio s**2 / np.sum(s**2) cum_ratio np.cumsum(var_ratio) keep np.where(cum_ratio 0.90)[0] # 保留累計貢獻(xiàn)90%的分量 print(保留分量數(shù):, len(keep), 累計貢獻(xiàn):, round(cum_ratio[keep[-1]], 3)) recon ssa_reconstruct(U, s, Vt, L, K, [list(keep)]) resid_clean resid - recon # 去掉主分量后的噪聲殘差這里keep取累計貢獻(xiàn)不超過 90% 的索引意味著把 90% 的能量當(dāng)作「有結(jié)構(gòu)的部分」重構(gòu)出來剩下的 10% 當(dāng)噪聲。resid_clean是去噪后的殘差它比原始?xì)埐钇交m合喂給 LSTM。注意recon長度是 LK-1正好等于原序列長度可以直接相減。3.3 重構(gòu)序列的對齊與長度校驗SSA 重構(gòu)最容易翻車的地方是長度對不上。軌跡矩陣的維度是 L × K對角平均后長度是 L K ? 1而 K N ? L 1所以 L K ? 1 N理論上嚴(yán)絲合縫。但如果你在分組時對某些分量做了截斷或補(bǔ)零長度就會漂。我的做法是重構(gòu)完立刻斷言len(recon) len(resid)不等就報錯別讓它悄悄錯位到 LSTM 階段才暴露。assert len(recon) len(resid), f長度不匹配: {len(recon)} vs {len(resid)} resid_clean resid - recon np.save(resid_clean_train.npy, resid_clean)這個斷言看著簡單但幫我省過至少兩次深夜排查。SSA 輸出的resid_clean就是 LSTM 的輸入。到這一步線性部分歸 ARIMA周期和噪聲歸 SSA剩下的非線性映射交給 LSTM。4. LSTM 建模把 SSA 重構(gòu)后的殘差喂進(jìn)網(wǎng)絡(luò)4.1 滑窗構(gòu)造監(jiān)督樣本LSTM 要的是監(jiān)督學(xué)習(xí)格式得把一維序列切成 (樣本, 時間步, 特征) 的三維張量。滑窗長度時間步是個關(guān)鍵參數(shù)太短學(xué)不到長依賴太長梯度容易消失且樣本數(shù)驟減。經(jīng)驗上取序列主周期的 1 到 2 倍比如月度數(shù)據(jù)主周期 12窗口取 12 到 24。步長一般取 1做數(shù)據(jù)增強(qiáng)時可以取 2 或 3 增加樣本多樣性。def make_windows(series, window, horizon1): X, y [], [] for i in range(len(series) - window - horizon 1): X.append(series[i:iwindow]) y.append(series[iwindow:iwindowhorizon]) X np.array(X).reshape(-1, window, 1) y np.array(y).reshape(-1, horizon) return X, y resid_clean np.load(resid_clean_train.npy) WINDOW 12 X, y make_windows(resid_clean, WINDOW, horizon1) print(樣本形狀:, X.shape, y.shape)make_windows里reshape(-1, window, 1)的最后一個 1 是特征維度單變量預(yù)測就是 1。horizon1表示單步預(yù)測要多步就把 horizon 調(diào)大y 會變成二維。注意樣本數(shù) 序列長度 ? window ? horizon 1窗口越大樣本越少小數(shù)據(jù)集上要權(quán)衡。4.2 網(wǎng)絡(luò)結(jié)構(gòu)與訓(xùn)練參數(shù)結(jié)構(gòu)上我用兩層 LSTM 加一層全連接隱藏單元第一層 64、第二層 32dropout 0.2。優(yōu)化器 Adam學(xué)習(xí)率 1e-3損失 MSE早停 patience 設(shè) 10。批大小 32epoch 上限 200。這些不是金科玉律但作為起點很穩(wěn)。歸一化用 MinMax 把殘差縮到 [0,1]因為殘差有正有負(fù)標(biāo)準(zhǔn)化到均值 0 方差 1 也可以我傾向 MinMax反歸一化時不容易出錯。import torch import torch.nn as nn class LSTMForecaster(nn.Module): def __init__(self, input_size1, hidden164, hidden232, dropout0.2): super().__init__() self.lstm1 nn.LSTM(input_size, hidden1, batch_firstTrue) self.lstm2 nn.LSTM(hidden1, hidden2, batch_firstTrue) self.drop nn.Dropout(dropout) self.fc nn.Linear(hidden2, 1) def forward(self, x): out, _ self.lstm1(x) out, _ self.lstm2(out) out self.drop(out[:, -1, :]) # 取最后一個時間步 return self.fc(out) model LSTMForecaster() opt torch.optim.Adam(model.parameters(), lr1e-3) loss_fn nn.MSELoss()batch_firstTrue讓輸入維度是 (batch, seq, feature)符合習(xí)慣。out[:, -1, :]只取最后一個時間步的隱藏狀態(tài)因為預(yù)測目標(biāo)在窗口末尾之后。兩層 LSTM 堆疊能提取更抽象的時序模式但層數(shù)再多小數(shù)據(jù)上就開始過擬合了。dropout 加在 LSTM 輸出和全連接之間別加在 LSTM 內(nèi)部PyTorch 的 LSTM 自帶 dropout 參數(shù)只在多層時生效容易混淆。4.3 訓(xùn)練循環(huán)與早停訓(xùn)練循環(huán)里我固定做三件事每個 epoch 記錄訓(xùn)練和驗證損失、驗證損失連續(xù) 10 輪不降就停、保存驗證損失最低的權(quán)重。這樣即使訓(xùn)練震蕩最后拿到的也是驗證集上最好的那個模型而不是最后一個 epoch 的。from torch.utils.data import TensorDataset, DataLoader X_t torch.tensor(X, dtypetorch.float32) y_t torch.tensor(y, dtypetorch.float32) split int(len(X_t) * 0.8) train_ds TensorDataset(X_t[:split], y_t[:split]) val_ds TensorDataset(X_t[split:], y_t[split:]) train_dl DataLoader(train_ds, batch_size32, shuffleTrue) val_dl DataLoader(val_ds, batch_size32) best_val, patience, wait float(inf), 10, 0 for epoch in range(200): model.train() for xb, yb in train_dl: opt.zero_grad() loss loss_fn(model(xb), yb) loss.backward() opt.step() model.eval() with torch.no_grad(): val_loss np.mean([loss_fn(model(xb), yb).item() for xb, yb in val_dl]) if val_loss best_val: best_val, wait val_loss, 0 torch.save(model.state_dict(), best_lstm.pt) else: wait 1 if wait patience: print(f早停于 epoch {epoch}) breakshuffleTrue只在訓(xùn)練集開驗證集保持順序。torch.save存的是 state_dict加載時先實例化同結(jié)構(gòu)模型再load_state_dict。早停的 patience 設(shè) 10 是經(jīng)驗值數(shù)據(jù)噪聲大可以放寬到 15數(shù)據(jù)干凈可以收緊到 5。訓(xùn)練完把驗證集預(yù)測和真實值畫在一起看是欠擬合還是過擬合再決定要不要調(diào)窗口或隱藏單元。4.4 三模型疊加預(yù)測與反歸一化最終預(yù)測 ARIMA 預(yù)測 SSA 重構(gòu)的 LSTM 預(yù)測。注意 LSTM 學(xué)的是resid_clean而resid_clean resid ? recon所以 LSTM 輸出的是「去噪后殘差」的預(yù)測要加回recon再和 ARIMA 相加。順序別搞反這是最容易出錯的一步。model.load_state_dict(torch.load(best_lstm.pt)) model.eval() with torch.no_grad(): lstm_pred model(torch.tensor(X_test, dtypetorch.float32)).numpy().flatten() final_pred arima_pred recon_test lstm_predarima_pred是 ARIMA 的樣本外預(yù)測recon_test是測試段 SSA 重構(gòu)的主分量lstm_pred是網(wǎng)絡(luò)對去噪殘差的預(yù)測。三者相加才是最終值。如果前面歸一化過這里要先反歸一化再相加或者全程在歸一化空間里加完再反歸一化但三種量的歸一化參數(shù)必須一致否則量綱對不上。5. 避坑與排查這套組合拳最容易翻車的五個地方5.1 現(xiàn)象驗證集 MAE 比測試集還低以為模型很強(qiáng)原因滑窗構(gòu)造時用了未來數(shù)據(jù)或者歸一化參數(shù)在劃分訓(xùn)練測試之前就用全量數(shù)據(jù)算了。這是時間序列里最經(jīng)典的數(shù)據(jù)泄漏。解決所有統(tǒng)計量均值、方差、最大最小值只在訓(xùn)練段計算然后應(yīng)用到驗證和測試段?;暗那蟹忠跉w一化之后、但歸一化參數(shù)必須來自訓(xùn)練段。我習(xí)慣寫一個fit_scaler(train)和transform(scaler, data)分開的函數(shù)從代碼結(jié)構(gòu)上杜絕泄漏。5.2 現(xiàn)象SSA 重構(gòu)后序列和原序列幾乎一樣LSTM 學(xué)不到東西原因分組時保留了太多分量把噪聲也重構(gòu)進(jìn)去了等于沒去噪。或者窗口 L 取太小軌跡矩陣秩不夠分解不出有效結(jié)構(gòu)。解決檢查累計方差貢獻(xiàn)率保留到 85% 到 90% 就停別貪多。L 至少取序列長度的 1/4短序列可以到 1/2。重構(gòu)后算一下和原殘差的相關(guān)系數(shù)如果高于 0.99說明基本沒去掉東西回去調(diào)分組。5.3 現(xiàn)象LSTM 訓(xùn)練損失一直降驗證損失從第 3 輪就開始漲原因模型容量相對數(shù)據(jù)量太大或者窗口太長導(dǎo)致樣本數(shù)太少。小數(shù)據(jù)集上兩層 LSTM 加 64 單元很容易過擬合。解決先減容量隱藏單元降到 32 和 16層數(shù)降到一層。再加大 dropout 到 0.3 到 0.4。還不行就縮短窗口增加樣本數(shù)。數(shù)據(jù)量少于 500 條時我一般直接用單層 LSTM 加 32 單元起步別一上來就堆結(jié)構(gòu)。5.4 現(xiàn)象最終預(yù)測曲線整體偏移所有點都偏高或偏低原因反歸一化時用的參數(shù)不對或者 ARIMA 預(yù)測和 LSTM 預(yù)測的量綱沒對齊。常見于 ARIMA 用了差分、LSTM 用了歸一化兩者還原順序不一致。解決統(tǒng)一在原始量綱上做加法。具體做法是 ARIMA 預(yù)測先還原到原始尺度SSA 重構(gòu)也在原始尺度LSTM 輸出反歸一化到原始尺度三者都還原完再相加。別在歸一化空間里混著加。5.5 現(xiàn)象每次跑結(jié)果都不一樣MAE 波動超過 20%原因PyTorch 默認(rèn)隨機(jī)初始化DataLoader 的 shuffle 也帶隨機(jī)性。深度學(xué)習(xí)部分本身就有方差。解決固定隨機(jī)種子torch.manual_seed(42)、np.random.seed(42)DataLoader 的 generator 也設(shè)種子。然后跑 5 次取平均作為最終報告值。如果 5 次方差仍然很大說明模型不穩(wěn)定回去檢查數(shù)據(jù)質(zhì)量和窗口設(shè)置而不是繼續(xù)調(diào)種子。6. 進(jìn)階技巧用滾動重訓(xùn)和殘差診斷把誤差再壓一檔模型搭完能跑通只是及格線真正拉開差距的是滾動重訓(xùn)和殘差診斷。我現(xiàn)在的習(xí)慣是每預(yù)測一步就把真實值并入訓(xùn)練集重新擬合 ARIMA 和微調(diào) LSTM這叫 walk-forward validation。它比一次性切分訓(xùn)練測試更貼近生產(chǎn)環(huán)境因為生產(chǎn)上你每天都會拿到新數(shù)據(jù)。代價是計算量成倍增加但小數(shù)據(jù)集上完全扛得住。def walk_forward(y, window, horizon, n_splits): preds, truths [], [] for i in range(n_splits): end len(y) - n_splits i train, test y[:end], y[end:endhorizon] if len(test) horizon: break # 這里復(fù)用前面的 ARIMA SSA LSTM 流程 p full_pipeline(train, horizon) preds.append(p) truths.append(test) return np.array(preds), np.array(truths)full_pipeline把前面所有步驟封成一個函數(shù)輸入訓(xùn)練序列和預(yù)測步長輸出預(yù)測值。n_splits控制回測輪數(shù)一般取 5 到 10。跑完算 MAPE 和 RMSE和單次切分的結(jié)果對比如果滾動重訓(xùn)明顯更好說明數(shù)據(jù)分布隨時間漂移生產(chǎn)上必須用滾動策略。殘差診斷是另一個被低估的環(huán)節(jié)。把最終預(yù)測誤差畫出來看有沒有自相關(guān)。如果誤差的 ACF 在滯后幾階還有顯著尖峰說明模型沒抓干凈還有結(jié)構(gòu)留在誤差里。這時候可以回到 SSA 階段多保留一組分量或者給 LSTM 加一層注意力。我一般用 Ljung-Box 檢驗p 值大于 0.05 就認(rèn)為誤差接近白噪聲可以收手了。from statsmodels.stats.diagnostic import acorr_ljungbox err truths.flatten() - preds.flatten() lb acorr_ljungbox(err, lags[5, 10, 20], return_dfTrue) print(lb)acorr_ljungbox返回每個滯后階的統(tǒng)計量和 p 值。如果 10 階 p 值小于 0.05誤差還有自相關(guān)模型沒榨干。這時候別急著換模型先回去看 SSA 分組和 ARIMA 階數(shù)往往問題出在前兩層。參數(shù)搜索上我最后會做一輪粗網(wǎng)格ARIMA 的 (p,q) 在 0 到 3 之間、SSA 的 L 在 N/4 到 N/2 之間、LSTM 窗口在 6 到 24 之間各取三檔組合用滾動驗證的 MAPE 排序。組合數(shù)控制在 50 以內(nèi)不然跑一天都跑不完。排序后取前 5 組人工看預(yù)測曲線和真實曲線的貼合度選那個不僅誤差小、而且拐點也咬得住的。誤差小但曲線平滑得像直線的生產(chǎn)上反而危險因為它把突變?nèi)?。最后說個我自己的習(xí)慣每次上線新數(shù)據(jù)前先拿歷史數(shù)據(jù)做一次「假裝不知道未來」的回測把預(yù)測值和真實值并排打出來逐月看偏差方向。如果連續(xù)三個月同向偏差說明模型有系統(tǒng)性偏移得檢查是不是數(shù)據(jù)采集口徑變了而不是模型參數(shù)問題。這套流程跑順之后ARIMA-SSA-LSTM 在小樣本單變量預(yù)測上確實比單模型穩(wěn)但它不是萬能藥數(shù)據(jù)里的突變?nèi)绻麃碜酝獠渴录魏螘r序模型都抓不住那得靠外部特征。希望幫到你。本文還有配套的精品資源點擊獲取