測(cè)實(shí)戰(zhàn):用Python搭建成績預(yù)測(cè)模型)
簡(jiǎn)介面向希望掌握LSTM多變量預(yù)測(cè)的Python開發(fā)者這份資源以成績預(yù)測(cè)等場(chǎng)景為主線系統(tǒng)覆蓋時(shí)間序列轉(zhuǎn)監(jiān)督數(shù)據(jù)、數(shù)據(jù)預(yù)處理、模型定義與訓(xùn)練、評(píng)估優(yōu)化等關(guān)鍵環(huán)節(jié)同時(shí)按單變量、多變量和多步預(yù)測(cè)拆分為多個(gè)模塊并配置shampoo-sales等數(shù)據(jù)集便于上手實(shí)踐。壓縮包共33個(gè)文件含19個(gè)csv數(shù)據(jù)文件和14個(gè)py腳本整體3.88MB腳本從預(yù)處理、縮放、穩(wěn)定化到模型開發(fā)與預(yù)測(cè)完整串聯(lián)適合初學(xué)者循序漸進(jìn)閱讀。已有3419人瀏覽學(xué)習(xí)資源代碼結(jié)構(gòu)清晰、模塊劃分明確可直接復(fù)用于風(fēng)電、股票、天氣等序列預(yù)測(cè)任務(wù)也可作為入門LSTM的實(shí)操參考資料。1. LSTM 多變量預(yù)測(cè)是什么拿一片成績單能算出下學(xué)期的線「LSTM 多變量預(yù)測(cè)」翻譯成人話你手里有一張按周排開的成績明細(xì)每行不是孤零零一個(gè)分?jǐn)?shù)而是一組互相影響的指標(biāo)——出勤率、作業(yè)提交率、周測(cè)得分、自習(xí)時(shí)長。要做的是拿過去 4 到 6 周這些多列數(shù)據(jù)預(yù)測(cè)下一次考試能考多少。很多人一上來就把歷次分?jǐn)?shù)排成一列丟給 LSTM結(jié)果模型輸出一條接近水平的線真正能跑通的是把它建模成多變量時(shí)間序列讓網(wǎng)絡(luò)自學(xué)“出勤下滑之后兩周成績才開始掉”這類延遲關(guān)聯(lián)。這篇適合手里有歷次測(cè)驗(yàn)明細(xì)、想用 Python 跑通一版預(yù)測(cè)腳本的從業(yè)者按數(shù)據(jù)處理、模型搭建、訓(xùn)練驗(yàn)證和踩坑排查的順序展開照抄能復(fù)現(xiàn)。2. 為什么成績預(yù)測(cè)要選 LSTM從梯度消失到多變量輸入輸出設(shè)計(jì)2.1 RNN 記不住長序列的根源梯度消失與 LSTM 的閘門普通 RNN 在每個(gè)時(shí)間步把上一刻的隱藏狀態(tài)和當(dāng)前輸入一起算得到新的隱藏狀態(tài)。序列一長反向傳播時(shí)梯度沿時(shí)間軸連乘權(quán)重矩陣的特征值只要小于 1梯度就會(huì)指數(shù)級(jí)衰減十幾步之前的信息對(duì)后續(xù)更新幾乎沒貢獻(xiàn)。成績預(yù)測(cè)里“第 3 周開始刷題量加大、第 5 周進(jìn)入疲態(tài)”這類信號(hào)經(jīng)常要隔 6 到 10 周才在大考上兌現(xiàn)普通 RNN 根本傳不到那么遠(yuǎn)。LSTM 的解決辦法是引入細(xì)胞狀態(tài)和三個(gè)門控遺忘門決定上一時(shí)刻的細(xì)胞狀態(tài)保留多少輸入門決定當(dāng)前新信息寫進(jìn)多少輸出門決定細(xì)胞狀態(tài)對(duì)外放出多少。梯度在細(xì)胞狀態(tài)這條“傳送帶”上可以近似無損地流過關(guān)鍵信息能傳幾十步。這也是為什么時(shí)間序列預(yù)測(cè)里L(fēng)STM 比普通 RNN 更常用。LSTM 還有個(gè)更節(jié)儉的親戚 GRU把三個(gè)門簡(jiǎn)化成重置門和更新門參數(shù)更少后面如果遇到小樣本過擬合把兩層 LSTM 換成 GRU 是性價(jià)比最高的第一招。2.2 多變量預(yù)測(cè)的成績建模特征表、時(shí)間步與預(yù)測(cè)目標(biāo)多變量預(yù)測(cè)指的是輸入不是單列分?jǐn)?shù)而是多個(gè)隨時(shí)間變化的特征列預(yù)測(cè)目標(biāo)可以是其中的某一列也可以是一個(gè)單獨(dú)的標(biāo)簽。成績預(yù)測(cè)最常見的建模方式每個(gè)學(xué)生每周生成一條記錄取過去 n_steps 周的所有特征作為輸入 x預(yù)測(cè)下一個(gè)時(shí)間點(diǎn)的考試分?jǐn)?shù)作為 y。我一般先這樣定義特征表以教務(wù)系統(tǒng)能導(dǎo)出的字段為例特征列含義取值范圍attendance本周出勤率0–100homework作業(yè)提交率0–100quiz周測(cè)平均分0–100study_hours本周自習(xí)總時(shí)長小時(shí)rank上周大考班級(jí)排名正整數(shù)預(yù)測(cè)目標(biāo) target 是下一次考試分?jǐn)?shù)。注意 rank 這類“數(shù)值越小越好”的特征歸一化后語義不會(huì)顛倒但解讀反歸一化結(jié)果時(shí)要心里有數(shù)。特征也不是越多越好成績預(yù)測(cè)尤其忌諱把排名和成績同時(shí)塞進(jìn)去——兩者高度共線模型會(huì)把注意力全放在 rank 上一旦排名波動(dòng)預(yù)測(cè)就跟著抖。既然要走序列建模先解釋一個(gè)繞不開的問題為什么不用隨機(jī)森林或 XGBoost因?yàn)樗鼈兡J(rèn)把每行樣本當(dāng)獨(dú)立個(gè)體不利用順序信息。同樣是“上一周出勤低”發(fā)生在連續(xù)三周下滑和偶爾一周波動(dòng)樹模型看到的特征向量完全一樣LSTM 通過時(shí)間步結(jié)構(gòu)能區(qū)分這兩種模式成績預(yù)測(cè)的關(guān)鍵恰恰在趨勢(shì)和延遲效應(yīng)。當(dāng)然這不代表 LSTM 一定更好第 6 章會(huì)給驗(yàn)證方法。預(yù)測(cè)目標(biāo)的頻率也要先想清楚。如果直接預(yù)測(cè)一學(xué)期只有三四次的大考樣本量會(huì)小到?jīng)]法訓(xùn)練常見做法是先用周測(cè)成績當(dāng)近端目標(biāo)把模型訓(xùn)穩(wěn)再在學(xué)期末的大考上做一次遠(yuǎn)端校驗(yàn)。另一種做法是每 4 周一個(gè)窗口預(yù)測(cè)第 5 周周測(cè)最后看期末大考的殘差。這就是“先搞清楚 y 的頻率”的重要性它決定了你的樣本量級(jí)。2.3 動(dòng)手前的 Python 環(huán)境numpy、pandas、tensorflow 怎么配跑 LSTM 之前先確認(rèn)環(huán)境這一步翻車的人最多。我一般建議 python 3.8 以上配合 tensorflow 2.x、numpy、pandas、scikit-learn。Windows 上最容易出問題的是 python 環(huán)境變量配置命令行敲 python 沒反應(yīng)十有八九是安裝時(shí)沒勾選“Add Python to PATH”或者 PATH 里同時(shí)存在多個(gè) python 入口。# 先升級(jí) pip避免裝包時(shí)出現(xiàn)依賴沖突 python -m pip install --upgrade pip # 安裝本次要用到的核心庫numpy 和 pandas 會(huì)一起裝好 python -m pip install numpy pandas scikit-learn tensorflow裝完用一個(gè)最小腳本驗(yàn)證環(huán)境別直接跑模型import numpy as np import pandas as pd import tensorflow as tf print(np.__version__) print(pd.__version__) print(tf.__version__)如果 import tensorflow 報(bào) DLL 加載失敗常見原因是用 32 位 python或者 python 版本太新而 tf 還沒跟上這種情況建議換 64 位 python 3.8–3.11或者直接用 tf 的 CPU 版本。成績預(yù)測(cè)這種小數(shù)據(jù)量任務(wù) CPU 完全夠用不必為 GPU 再折騰 CUDA。VSCode 里跑的話右下角確認(rèn)解釋器選的是同一個(gè) python別讓 Anaconda 的 base 環(huán)境混進(jìn)來。環(huán)境一次配好后面代碼報(bào)錯(cuò)才找得到根因。3. 把多變量成績數(shù)據(jù)切成 LSTM 能吃的形狀窗口化、歸一化與切分3.1 按學(xué)生滑動(dòng)窗口n_steps 取值與切窗口代碼LSTM 的輸入要求是三維數(shù)組樣本數(shù) × 時(shí)間步 × 特征數(shù)。成績表通常是長表每行是一個(gè)學(xué)生某一周的一條記錄。第一步是把每個(gè)學(xué)生的記錄按時(shí)間排序再各自切窗口。手工用 for 循環(huán)切沒問題數(shù)據(jù)量大就用 pandas groupby。關(guān)鍵決策是 n_steps 取多少。預(yù)測(cè)周測(cè)成績我一般取 4 到 6 周預(yù)測(cè)學(xué)期末大考至少取 8 周以上讓模型能看到一個(gè)完整的“狀態(tài)變化周期”。n_steps 太大有兩個(gè)副作用樣本量成倍縮小訓(xùn)練變慢n_steps 太小則學(xué)不到趨勢(shì)。常見做法是先畫出幾個(gè)學(xué)生的成績曲線人工看看成績從下滑到回升大概幾周再定窗口。import numpy as np import pandas as pd def make_sequences(df, n_steps6, feature_colsNone, target_colscore): 按學(xué)生切滑動(dòng)窗口返回 (X, y)。 X 形狀: (樣本數(shù), n_steps, len(feature_cols)) y 形狀: (樣本數(shù),) X, y [], [] for sid, group in df.groupby(student_id): group group.sort_values(week) # 每個(gè)學(xué)生必須按周升序 vals group[feature_cols].values target group[target_col].values for i in range(len(vals) - n_steps): X.append(vals[i:i n_steps, :]) y.append(target[i n_steps]) return np.array(X), np.array(y)這里有兩個(gè)新手常踩的坑。一個(gè)是 groupby 之后忘記 sort_values導(dǎo)致同一學(xué)生的不同周樣本亂序模型等于在看隨機(jī)時(shí)間順序訓(xùn)練 loss 能降但驗(yàn)證一塌糊涂。另一個(gè)是把不同學(xué)生拼成一條長序列直接滑窗窗口會(huì)跨過學(xué)生邊界把學(xué)生 A 的尾巴和學(xué)生 B 的開頭當(dāng)成連續(xù)事件。上面代碼按 student_id 分組就是為了避開這兩個(gè)問題。3.2 切分訓(xùn)練/驗(yàn)證/測(cè)試集按時(shí)間切還是按學(xué)生切時(shí)間序列不能隨機(jī)打亂再切分。成績有明顯學(xué)期趨勢(shì)期初、期中、期末分布不同隨機(jī)切會(huì)讓訓(xùn)練集混進(jìn)“未來”樣本模型等于提前看到考題。最直接的做法是按時(shí)間順序切前 70% 周次訓(xùn)練中間 15% 驗(yàn)證最后 15% 測(cè)試。n_total X.shape[0] train_end int(n_total * 0.7) val_end int(n_total * 0.85) X_train, X_val, X_test X[:train_end], X[train_end:val_end], X[val_end:] y_train, y_val, y_test y[:train_end], y[train_end:val_end], y[val_end:]但這里有個(gè)容易被忽略的問題按時(shí)間切分時(shí)訓(xùn)練集里某個(gè)學(xué)生的最后幾周和測(cè)試集里同一學(xué)生的前幾周是連續(xù)的模型在訓(xùn)練時(shí)見過該學(xué)生的狀態(tài)測(cè)試時(shí)其實(shí)是“繼續(xù)預(yù)測(cè)同一個(gè)人的下一個(gè)點(diǎn)”這和真實(shí)場(chǎng)景里要預(yù)測(cè)一個(gè)新學(xué)生不一樣。如果目標(biāo)是跨學(xué)生泛化我一般改成按學(xué)生 id 切分train_sids df[student_id].unique()[:70] train_mask df[student_id].isin(train_sids)按周切分測(cè)的是“這個(gè)學(xué)生未來怎么樣”按學(xué)生切分測(cè)的是“沒見過的人能不能預(yù)測(cè)”兩者面對(duì)的問題不同腳本里要二選一并寫清楚注釋。3.3 歸一化順序scaler 必須在訓(xùn)練集上 fit成績和自習(xí)時(shí)長的量綱差很多直接喂給 LSTM 會(huì)讓數(shù)值大的特征主導(dǎo)梯度。常見做法是用 MinMaxScaler 把所有特征壓到 0–1。順序有講究先在訓(xùn)練集上 fit再 transform 訓(xùn)練集、驗(yàn)證集和測(cè)試集測(cè)試集絕不能參與 fit。from sklearn.preprocessing import MinMaxScaler n_samples, n_steps, n_features X_train.shape scaler_X MinMaxScaler() X_train_2d X_train.reshape(-1, n_features) X_train_scaled scaler_X.fit_transform(X_train_2d).reshape(n_samples, n_steps, n_features) X_val_scaled scaler_X.transform(X_val.reshape(-1, n_features)).reshape(X_val.shape[0], n_steps, n_features) X_test_scaled scaler_X.transform(X_test.reshape(-1, n_features)).reshape(X_test.shape[0], n_steps, n_features) scaler_y MinMaxScaler() y_train_scaled scaler_y.fit_transform(y_train.reshape(-1, 1)).ravel() y_val_scaled scaler_y.transform(y_val.reshape(-1, 1)).ravel() y_test_scaled scaler_y.transform(y_test.reshape(-1, 1)).ravel()上面的 reshape 是在三維和二維之間切換經(jīng)常有人在這里把形狀搞亂報(bào)錯(cuò)先記下 X_train 的原始三維形狀transform 完再 reshape 回去。fit/transform 順序混亂是成績預(yù)測(cè)里最典型的玄學(xué)問題如果在整份數(shù)據(jù)上 fit scaler等于讓模型在訓(xùn)練時(shí)接觸了測(cè)試集的數(shù)據(jù)分布線下指標(biāo)好看換一批真實(shí)數(shù)據(jù)立刻現(xiàn)原形。測(cè)試集不僅不能進(jìn) fit連 min/max 都不該看見。4. 用 Python 搭建 LSTM 成績預(yù)測(cè)模型結(jié)構(gòu)、訓(xùn)練與畫圖4.1 兩層 LSTM Dropout 的模型結(jié)構(gòu)怎么定數(shù)據(jù)形狀確定后模型結(jié)構(gòu)我一般從兩層 LSTM 起步。第一層用 return_sequencesTrue讓每個(gè)時(shí)間步都輸出隱藏狀態(tài)給第二層第二層 return_sequencesFalse只輸出最后一步的編碼接一個(gè)帶 relu 的中間層再壓到 1 個(gè)輸出節(jié)點(diǎn)。成績預(yù)測(cè)輸出是連續(xù)分?jǐn)?shù)所以最后一層不加激活函數(shù)等價(jià)于線性輸出用 MSE 作為損失。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.optimizers import Adam n_steps X_train_scaled.shape[1] n_features X_train_scaled.shape[2] model Sequential() model.add(LSTM(64, return_sequencesTrue, input_shape(n_steps, n_features))) model.add(Dropout(0.2)) model.add(LSTM(32, return_sequencesFalse)) model.add(Dropout(0.2)) model.add(Dense(16, activationrelu)) model.add(Dense(1))LSTM 單元數(shù)在這個(gè)樣本量級(jí)不需要堆太大。6432 是穩(wěn)妥起點(diǎn)第一層 64 負(fù)責(zé)捕捉每個(gè)特征各自的時(shí)間模式第二層 32 負(fù)責(zé)把多個(gè)時(shí)間步的隱狀態(tài)融合成整體表達(dá)。Dropout 放在 LSTM 層之間而不是 LSTM 內(nèi)部配合后面的早?;灸軌鹤∵^擬合。如果訓(xùn)練集很小把結(jié)構(gòu)縮到 3216 更穩(wěn)參數(shù)少了不容易硬背訓(xùn)練樣本。4.2 編譯與訓(xùn)練參數(shù)adam、mse、early stopping編譯時(shí)優(yōu)化器用 Adam學(xué)習(xí)率默認(rèn) 0.001 對(duì)這種任務(wù)通常夠。損失用 mse同時(shí)監(jiān)控 mae因?yàn)?MSE 對(duì)離群成績比如某次大考整體偏難導(dǎo)致全班低分過于敏感MAE 更能反映平均偏差。EarlyStopping 是 LSTM 訓(xùn)練里最值得設(shè)的參數(shù)成績預(yù)測(cè)數(shù)據(jù)量少訓(xùn)練到過擬合是常態(tài)它的價(jià)值是停在驗(yàn)證集最好的位置并回滾權(quán)重。from tensorflow.keras.callbacks import EarlyStopping model.compile(optimizerAdam(learning_rate0.001), lossmse, metrics[mae]) early_stop EarlyStopping(monitorval_loss, patience15, restore_best_weightsTrue) history model.fit( X_train_scaled, y_train_scaled, validation_data(X_val_scaled, y_val_scaled), epochs100, batch_size16, callbacks[early_stop], verbose1 )這里幾個(gè)參數(shù)按經(jīng)驗(yàn)給參考參數(shù)建議值說明LSTM 第一層 units64時(shí)間模式提取LSTM 第二層 units32多特征融合Dropout0.2防止過擬合batch_size16小樣本下梯度更穩(wěn)learning_rate0.001Adam 默認(rèn)值patience15早停耐心越小越保守batch_size 對(duì)成績預(yù)測(cè)這種小樣本很敏感設(shè) 128 會(huì)讓每個(gè) batch 里學(xué)生多樣性不夠梯度方向不穩(wěn)設(shè) 4 則統(tǒng)計(jì)噪聲太大收斂慢。epochs 給到 100 甚至 200 都行反正早停兜底實(shí)際通常在 30 到 50 個(gè) epoch 內(nèi)就觸發(fā)。訓(xùn)練完看 historyval_loss 先降后升說明早停生效如果從頭到尾都在升先查數(shù)據(jù)順序和歸一化再調(diào)結(jié)構(gòu)。4.3 預(yù)測(cè)結(jié)果還原與曲線繪制橫坐標(biāo)太密集怎么處理模型輸出的 y 是 0–1 之間的歸一化值要還原成真實(shí)分?jǐn)?shù)才能拿去解讀。預(yù)測(cè)與反歸一化如下y_pred_scaled model.predict(X_test_scaled) y_pred scaler_y.inverse_transform(y_pred_scaled).ravel() y_test_real scaler_y.inverse_transform(y_test_scaled.reshape(-1, 1)).ravel()inverse_transform 時(shí)經(jīng)常有人傳了形狀不對(duì)的數(shù)組把 y 先 reshape(-1, 1) 再傳即可這也是最常見的 python 類型轉(zhuǎn)換坑。畫出真實(shí)與預(yù)測(cè)曲線時(shí)樣本一多 x 軸刻度就會(huì)擠成一團(tuán)我把刻度旋轉(zhuǎn)一下再顯示import matplotlib.pyplot as plt plt.figure(figsize(12, 5)) plt.plot(y_test_real, label真實(shí)成績, linewidth2) plt.plot(y_pred, label預(yù)測(cè)成績, linewidth2) plt.title(LSTM 多變量預(yù)測(cè)成績測(cè)試集對(duì)比) plt.legend() plt.xticks(rotation45) # 橫坐標(biāo)太密集時(shí)旋轉(zhuǎn)避免疊成一團(tuán) plt.grid(alpha0.3) plt.show()畫圖只做直觀確認(rèn)數(shù)值指標(biāo)用 MAE 和 RMSE 才有說服力。成績 0–100 分制下MAE 在 5 分以內(nèi)算是能落地8 分以上基本等于白預(yù)測(cè)回去檢查特征和窗口。5. LSTM 預(yù)測(cè)成績的五條踩坑記錄現(xiàn)象、原因、解決5.1 loss 不降或直接變 NaN現(xiàn)象訓(xùn)練一開始 loss 就是 nan或者幾百步都不降。原因多數(shù)是數(shù)據(jù)里帶 NaN 或無窮大MinMaxScaler 處理不了其次是學(xué)習(xí)率太大梯度爆炸。成績表里某個(gè)學(xué)生缺考、周測(cè)缺勤填充方式不對(duì)就會(huì)留下空值。解決清洗時(shí)先 df.isna().sum() 查空值用前向填充或班級(jí)均值填充再把學(xué)習(xí)率從 0.001 降到 0.0001 試。若仍然 NaN檢查是否有一個(gè)特征的量綱異常大比如排名填了 999scaler 之前先做去極值。5.2 訓(xùn)練集表現(xiàn)好、測(cè)試集一塌糊涂現(xiàn)象訓(xùn)練 loss 很低驗(yàn)證 loss 也不差測(cè)試 MAE 突然飆高。原因最常見的是數(shù)據(jù)泄漏——?dú)w一化在整份數(shù)據(jù)上 fit或者切分時(shí)把未來數(shù)據(jù)混進(jìn)了訓(xùn)練集其次是按時(shí)間切分但學(xué)生重疊模型只是在“續(xù)寫”見過的學(xué)生。解決嚴(yán)格先切分再 fit scaler測(cè)試數(shù)據(jù)連 min/max 都不許碰如果要預(yù)測(cè)新學(xué)生按 student_id 切分并驗(yàn)證跨學(xué)生效果。這個(gè)坑最隱蔽也是很多人覺得 LSTM 是黑匣子的主要原因。5.3 預(yù)測(cè)曲線整體滯后一拍現(xiàn)象預(yù)測(cè)曲線和真實(shí)曲線形狀幾乎一樣但整體向右平移了一個(gè)點(diǎn)。原因模型學(xué)到的其實(shí)是“把上一周的分?jǐn)?shù)抄下來”而不是“預(yù)測(cè)下一周的變化”。當(dāng)目標(biāo)序列高度自相關(guān)、輸入特征變化不充分時(shí)LSTM 的最小化損失策略就是輸出接近上一步的值。解決加入更有預(yù)測(cè)力的特征比如作業(yè)完成率的變化量、復(fù)習(xí)時(shí)長增量把目標(biāo)從原始分?jǐn)?shù)改成差分讓模型預(yù)測(cè)變化而不是絕對(duì)值或者把預(yù)測(cè)目標(biāo)改為未來兩周的均值降低單次噪聲。5.4 報(bào)錯(cuò) expected ndim3, found ndim2現(xiàn)象第一次跑 fit 時(shí)直接報(bào)維度錯(cuò)誤說期望三維輸入但拿到了兩維。原因LSTM 輸入要求三維而 X_train 在切分后可能是二維忘了把時(shí)間步維度保留。常見于用 for 循環(huán)組裝特征時(shí)把時(shí)間維壓掉了。解決檢查 X_train.shape期望是 (樣本數(shù), n_steps, n_features)。如果只有兩維說明 n_steps1 或者窗口切片寫錯(cuò)回到 make_sequences 重新切并在模型前面加一句 assert len(X_train_scaled.shape) 3 防呆。5.5 小樣本下每生只有十幾次記錄怎么做都過擬合現(xiàn)象訓(xùn)練 loss 降到接近 0Dropout 加了也沒用。原因LSTM 參數(shù)動(dòng)輒幾千上萬而一個(gè)學(xué)生可能只有 12 周記錄去掉窗口后只剩幾個(gè)樣本模型直接把訓(xùn)練樣本背下來。解決一是把多個(gè)班、多學(xué)期數(shù)據(jù)合并擴(kuò)大樣本二是縮小模型到單層 LSTM 16 個(gè)單元三是用 GRU 替代 LSTM它參數(shù)更少在短序列上往往更抗過擬合。先跑通再談精度小數(shù)據(jù)硬上大模型是成績預(yù)測(cè)最常見的翻車現(xiàn)場(chǎng)。6. 讓成績預(yù)測(cè)真正上考場(chǎng)滾動(dòng)驗(yàn)證與樸素基線對(duì)照6.1 滾動(dòng)多步驗(yàn)證別只測(cè)一步單步預(yù)測(cè)容易給人“模型很準(zhǔn)”的錯(cuò)覺每次都給真實(shí)歷史窗口模型只猜下一個(gè)點(diǎn)誤差自然小。我現(xiàn)在的習(xí)慣是訓(xùn)練好后做滾動(dòng)驗(yàn)證——給定前 n_steps 周數(shù)據(jù)預(yù)測(cè)下一周再把預(yù)測(cè)值當(dāng)真值拼進(jìn)窗口預(yù)測(cè)再往后一周直到把整個(gè)測(cè)試期推完。這樣暴露出來的累積誤差才是真實(shí)使用場(chǎng)景里的誤差。def rolling_predict(model, X_start, n_predict, scaler_X, scaler_y): window X_start[0].copy() # 第一個(gè)窗口 preds [] for _ in range(n_predict): next_pred model.predict(window[np.newaxis, :, :], verbose0) pred scaler_y.inverse_transform(next_pred)[0, 0] preds.append(pred) new_step np.concatenate([window[1:], next_pred], axis0) window new_step return preds滾動(dòng)預(yù)測(cè)里每步都會(huì)把誤差帶進(jìn)去累積幾步之后曲線往往就飄了。如果你的模型滾動(dòng)三步誤差已經(jīng)不可接受說明它只適合做短周期預(yù)警不適合做期末長周期預(yù)測(cè)。6.2 和“用上次成績當(dāng)預(yù)測(cè)”的基線比一比判斷 LSTM 值不值得用最硬的標(biāo)準(zhǔn)是和一個(gè)樸素基線對(duì)照直接用該學(xué)生上一次考試分?jǐn)?shù)作為本次預(yù)測(cè)算 MAE。LSTM 的 MAE 如果壓不過這個(gè)基線說明多變量特征沒有提供增量信息再調(diào)參也是白費(fèi)。我踩過這個(gè)坑之后每次建模第一步都是先跑基線再做 LSTM。成績預(yù)測(cè)方向最終能不能投入教學(xué)管理看的是滾動(dòng)誤差和基線差值而不是訓(xùn)練集那條漂亮的下降曲線。先跑通最小腳本再用滾動(dòng)驗(yàn)證和基線對(duì)照去質(zhì)問結(jié)果只有當(dāng)多變量輸入真的帶來增益時(shí)LSTM 才算在成績預(yù)測(cè)里站住了腳。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取