荷預(yù)測(cè)實(shí)戰(zhàn):LSTM特征工程與調(diào)參全解析)
簡(jiǎn)介這套基于 Python 深度學(xué)習(xí)的多特征電力負(fù)荷預(yù)測(cè)項(xiàng)目面向課程設(shè)計(jì)與期末大作業(yè)場(chǎng)景適合希望掌握 LSTM 建模與實(shí)際預(yù)測(cè)流程的學(xué)生項(xiàng)目以真實(shí)電力負(fù)荷數(shù)據(jù)為輸入融合溫度、濕度等多維特征利用長(zhǎng)短時(shí)記憶網(wǎng)絡(luò)捕捉時(shí)序規(guī)律適用于教學(xué)演示與實(shí)際場(chǎng)景訓(xùn)練。壓縮包共 8 個(gè)文件包含 3 個(gè) Python 腳本、2 個(gè) CSV 數(shù)據(jù)文件、xlsx 原始數(shù)據(jù)表以及 README 和使用說(shuō)明整體僅 831KB結(jié)構(gòu)簡(jiǎn)潔清晰其中 py 文件分別負(fù)責(zé)數(shù)據(jù)清洗、模型訓(xùn)練與結(jié)果繪圖csv 提供訓(xùn)練集與待預(yù)測(cè)數(shù)據(jù)md 文檔則記錄復(fù)現(xiàn)步驟。已有 479 人學(xué)習(xí)下載作為高分課設(shè)項(xiàng)目提供數(shù)據(jù)預(yù)處理、LSTM 負(fù)荷預(yù)測(cè)、結(jié)果可視化等完整模塊下載后無(wú)需修改即可運(yùn)行可直接用于答辯展示。通過閱讀源碼與注釋可以快速理解多特征電力負(fù)荷預(yù)測(cè)的實(shí)現(xiàn)思路并在此基礎(chǔ)上擴(kuò)展調(diào)參或更換數(shù)據(jù)集。1. 多特征電力負(fù)荷預(yù)測(cè)項(xiàng)目的真實(shí)難點(diǎn)數(shù)據(jù)比模型更花時(shí)間拿到“基于python深度學(xué)習(xí)的多特征電力負(fù)荷預(yù)測(cè)項(xiàng)目源碼數(shù)據(jù)課程設(shè)計(jì).zip”這個(gè)標(biāo)題先別急著解壓跑模型。電力負(fù)荷預(yù)測(cè)是典型的時(shí)序回歸問題但讓它難的不是LSTM本身而是“多特征”三個(gè)字。只看歷史負(fù)荷曲線做預(yù)測(cè)節(jié)假日和極端天氣一來(lái)就崩把溫度、濕度、星期類型、峰谷時(shí)段全部塞進(jìn)模型又容易陷入特征噪聲和維度災(zāi)難。課程設(shè)計(jì)階段最常見的失敗不是模型不收斂而是數(shù)據(jù)處理和特征對(duì)齊根本沒做對(duì)。你缺的不是源碼而是把源碼拆開、改特征、調(diào)參數(shù)、換數(shù)據(jù)后還能跑通的能力。這篇文章不評(píng)價(jià)任何特定項(xiàng)目包只講此類項(xiàng)目最通用、最經(jīng)得起答辯追問的實(shí)現(xiàn)套路特征工程怎么建、LSTM怎么搭、訓(xùn)練參數(shù)怎么調(diào)、結(jié)果怎么對(duì)比。2. 電力負(fù)荷預(yù)測(cè)里的多特征工程先搞清滑動(dòng)窗口與特征對(duì)齊2.1 單序列預(yù)測(cè)的局限與多特征的價(jià)值很多課程設(shè)計(jì)最初版本只用歷史負(fù)荷去預(yù)測(cè)未來(lái)負(fù)荷輸入是[t-24, t-23, ..., t-1]輸出是t時(shí)刻的負(fù)荷值。這個(gè)結(jié)構(gòu)在負(fù)荷規(guī)律穩(wěn)定的工作日表現(xiàn)尚可但遇到以下情況會(huì)明顯失靈周一早高峰負(fù)荷模式與周日完全不同純負(fù)荷序列無(wú)法感知“今天是周幾”夏季高溫日空調(diào)負(fù)荷激增歷史負(fù)荷序列中看不到“今天35度”這個(gè)信號(hào)法定節(jié)假日負(fù)荷腰斬模型只知道昨天和前幾天都高不知道今天放假所以“多特征”的第一層含義是補(bǔ)上負(fù)荷序列之外的外生變量。第二層含義是這些外生變量必須與負(fù)荷序列在時(shí)間軸上嚴(yán)格對(duì)齊并且按相同的步長(zhǎng)滑窗。2.2 輸入特征矩陣的構(gòu)建代碼以小時(shí)級(jí)負(fù)荷數(shù)據(jù)為例我一般會(huì)把原始表設(shè)計(jì)成下面這樣每行代表一個(gè)小時(shí)特征字段示例處理方式load532.6 MW目標(biāo)值做滯后特征temperature28.4 ℃數(shù)值型直接入模humidity76 %數(shù)值型直接入模hour14周期編碼為sin/cosweekday3周期編碼為sin/cosis_holiday00/1二值峰谷時(shí)段峰one-hot或LabelEncoder代碼上核心切成兩步第一步制作滯后特征lag features第二步將原始表切成[樣本數(shù), 時(shí)間步長(zhǎng), 特征數(shù)]的三維張量。import pandas as pd import numpy as np df pd.read_csv(load_data.csv, parse_dates[date]) df df.sort_values(date).reset_index(dropTrue) # 特征列與目標(biāo)列分離 feature_cols [temperature, humidity, hour_sin, hour_cos, weekday_sin, weekday_cos, is_holiday, peak_type] target_col load # 滯后特征過去24小時(shí)負(fù)荷 for lag in [1, 2, 3, 24, 168]: df[fload_lag_{lag}] df[load].shift(lag) feature_cols [load_lag_1, load_lag_2, load_lag_3, load_lag_24, load_lag_168] # 刪除前168行滯后168小時(shí)產(chǎn)生的NaN df df.dropna().reset_index(dropTrue)這里對(duì)滯后特征做三點(diǎn)說(shuō)明load_lag_1捕捉短期慣性load_lag_24捕捉日周期load_lag_168捕捉周周期。滯后階數(shù)的選擇依據(jù)是電力負(fù)荷存在明顯的“日周期性”和“周周期性”不相關(guān)的滯后項(xiàng)只會(huì)增加噪聲。溫度、濕度是否做滯后取決于數(shù)據(jù)表是否包含預(yù)測(cè)時(shí)刻的天氣預(yù)報(bào)值如果沒有常見做法是用預(yù)測(cè)日的氣象預(yù)報(bào)數(shù)據(jù)替代這也是為什么很多課程設(shè)計(jì)項(xiàng)目把溫度特征直接對(duì)齊到預(yù)測(cè)時(shí)刻。2.3 滑動(dòng)窗口切分與歸一化順序有了完整的特征寬表后需要把它切成LSTM輸入需要的三維結(jié)構(gòu)。窗口長(zhǎng)度通常取24或48即用過去24/48小時(shí)的所有特征預(yù)測(cè)下一個(gè)小時(shí)。def create_sequences(data, feature_cols, target_col, window24): X, y [], [] for i in range(len(data) - window): X.append(data[feature_cols].iloc[i:iwindow].values) y.append(data[target_col].iloc[iwindow]) return np.array(X), np.array(y) X, y create_sequences(df, feature_cols, target_col, window24) print(X.shape) # (樣本數(shù), 24, 特征數(shù))歸一化必須先在訓(xùn)練集上fit再transform驗(yàn)證集與測(cè)試集。機(jī)器學(xué)習(xí)中一個(gè)經(jīng)典的錯(cuò)誤是拿全量數(shù)據(jù)做歸一化導(dǎo)致驗(yàn)證集信息泄漏到訓(xùn)練過程里模型表現(xiàn)虛高答辯時(shí)被問幾句就會(huì)露餡。from sklearn.preprocessing import MinMaxScaler # 將三維X還原為二維做歸一化再還原回三維 X_2d X.reshape(-1, X.shape[-1]) scaler_X MinMaxScaler() X_2d_scaled scaler_X.fit_transform(X_2d) X_scaled X_2d_scaled.reshape(X.shape) # 目標(biāo)值單獨(dú)歸一化 scaler_y MinMaxScaler() y_scaled scaler_y.fit_transform(y.reshape(-1, 1)).flatten()3. 基于PyTorch的LSTM多變量負(fù)荷預(yù)測(cè)模型從Dataset到前向傳播3.1 模型選型為什么課程設(shè)計(jì)首選LSTM電力負(fù)荷預(yù)測(cè)的模型選項(xiàng)很多ARIMA、XGBoost、LSTM、GRU、Transformer。課程設(shè)計(jì)場(chǎng)景下LSTM是綜合性價(jià)比最高的選擇。ARIMA對(duì)多特征外生變量的支持較弱XGBoost需要手動(dòng)構(gòu)造時(shí)序特征才能模擬時(shí)間依賴Transformer在小數(shù)據(jù)集上容易過擬合調(diào)參成本高。LSTM的結(jié)構(gòu)天然適合序列建模而且PyTorch封裝完善手寫一個(gè)LSTM模型只需要幾十行代碼。多特征LSTM和單特征LSTM在模型結(jié)構(gòu)上的區(qū)別只在輸入維度input_size。輸入維度等于特征矩陣的列數(shù)每個(gè)時(shí)間步輸入一個(gè)包含溫度、濕度、時(shí)刻、滯后負(fù)荷等所有特征的向量。下面給出一個(gè)可運(yùn)行的多層LSTM回歸模型。import torch import torch.nn as nn class MultiFeatureLSTM(nn.Module): def __init__(self, input_size, hidden_size64, num_layers2, dropout0.2): super().__init__() self.lstm nn.LSTM( input_sizeinput_size, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, dropoutdropout ) self.regressor nn.Sequential( nn.Linear(hidden_size, 32), nn.ReLU(), nn.Dropout(dropout), nn.Linear(32, 1) ) def forward(self, x): # x: (batch, seq_len, input_size) lstm_out, _ self.lstm(x) # 取最后一個(gè)時(shí)間步的輸出 last_out lstm_out[:, -1, :] return self.regressor(last_out).squeeze(-1)模型的關(guān)鍵設(shè)計(jì)點(diǎn)batch_firstTrue讓輸入張量的形狀更符合直覺第一維是batch。num_layers2堆疊兩層LSTM以增強(qiáng)非線性表達(dá)能力但層數(shù)不是越多越好后面會(huì)講為什么課程設(shè)計(jì)里3層以上收益很小。regressor是回歸頭將LSTM最后一個(gè)時(shí)間步的隱含狀態(tài)映射成標(biāo)量負(fù)荷值。取lstm_out[:, -1, :]是時(shí)序預(yù)測(cè)的標(biāo)準(zhǔn)做法它表示用整個(gè)窗口的信息匯總后做預(yù)測(cè)。3.2 Dataset封裝與數(shù)據(jù)加載器光有模型還不夠PyTorch要求數(shù)據(jù)經(jīng)過Dataset和DataLoader封裝才能進(jìn)入訓(xùn)練循環(huán)。封裝的核心價(jià)值在于打亂樣本順序、按batch迭代、支持多線程讀取。from torch.utils.data import Dataset, DataLoader class LoadDataset(Dataset): def __init__(self, X, y): self.X torch.tensor(X, dtypetorch.float32) self.y torch.tensor(y, dtypetorch.float32) def __len__(self): return len(self.X) def __getitem__(self, idx): return self.X[idx], self.y[idx] # 按時(shí)間順序切分前70%訓(xùn)練后30%測(cè)試不打亂時(shí)序 train_size int(len(X_scaled) * 0.7) X_train, X_test X_scaled[:train_size], X_scaled[train_size:] y_train, y_test y_scaled[:train_size], y_scaled[train_size:] train_dataset LoadDataset(X_train, y_train) test_dataset LoadDataset(X_test, y_test) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue) test_loader DataLoader(test_dataset, batch_size64, shuffleFalse)shuffleTrue只作用于訓(xùn)練集打亂的是樣本間的順序不會(huì)破壞單個(gè)樣本內(nèi)的時(shí)間步順序。測(cè)試集必須shuffleFalse否則預(yù)測(cè)結(jié)果和真實(shí)值對(duì)不上時(shí)間軸后續(xù)畫圖會(huì)錯(cuò)位。batch_size和窗口長(zhǎng)度是兩個(gè)容易混淆的概念窗口長(zhǎng)度是單個(gè)樣本包含多少歷史時(shí)間步batch_size是每次迭代同時(shí)處理多少個(gè)樣本。4. 深度學(xué)習(xí)訓(xùn)練參數(shù)解析學(xué)習(xí)率、早停與損失函數(shù)選擇4.1 訓(xùn)練循環(huán)模板與早停機(jī)制數(shù)據(jù)準(zhǔn)備好了模型也定義了接下來(lái)是訓(xùn)練循環(huán)。課程設(shè)計(jì)階段不推薦直接抄開源訓(xùn)練腳本而要理解每個(gè)關(guān)鍵環(huán)節(jié)在做什么。下面這個(gè)訓(xùn)練函數(shù)包含學(xué)習(xí)率衰減和早停是訓(xùn)練LSTM最實(shí)用的組合。import torch.optim as optim model MultiFeatureLSTM( input_sizeX_train.shape[-1], hidden_size64, num_layers2, dropout0.2 ) optimizer optim.Adam(model.parameters(), lr1e-3) scheduler optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemin, factor0.5, patience8 ) criterion nn.MSELoss() epochs 100 best_loss float(inf) patience_counter 0 for epoch in range(epochs): model.train() train_loss 0 for X_batch, y_batch in train_loader: optimizer.zero_grad() y_pred model(X_batch) loss criterion(y_pred, y_batch) loss.backward() # 梯度裁剪防止LSTM梯度爆炸 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() train_loss loss.item() * len(X_batch) # 驗(yàn)證集上評(píng)估決定是否早停 model.eval() val_loss 0 with torch.no_grad(): for X_batch, y_batch in test_loader: y_pred model(X_batch) val_loss criterion(y_pred, y_batch).item() * len(X_batch) val_loss / len(X_test) scheduler.step(val_loss) if val_loss best_loss: best_loss val_loss patience_counter 0 torch.save(model.state_dict(), best_model.pth) else: patience_counter 1 if patience_counter 15: print(fearly stop at epoch {epoch 1}) break損失函數(shù)用MSE是負(fù)荷預(yù)測(cè)任務(wù)中的默認(rèn)選擇因?yàn)樗鼘?duì)大幅偏差懲罰更重。但需要知道MSE的缺點(diǎn)是量綱是平方不能直觀表示誤差百分比所以后面評(píng)估時(shí)還要計(jì)算MAE和MAPE。梯度裁剪clip_grad_norm_對(duì)LSTM很重要梯度范數(shù)超過1.0時(shí)會(huì)被壓縮防止RNN類模型常見的梯度爆炸。ReduceLROnPlateau每8輪驗(yàn)證損失不下降就把學(xué)習(xí)率減半比固定學(xué)習(xí)率600輪更穩(wěn)。4.2 關(guān)鍵超參數(shù)推薦范圍與調(diào)整邏輯課程設(shè)計(jì)里超參數(shù)調(diào)優(yōu)不用做網(wǎng)格搜索按經(jīng)驗(yàn)區(qū)間給幾組對(duì)照實(shí)驗(yàn)就夠了。下表是常見參數(shù)的經(jīng)驗(yàn)范圍參數(shù)名推薦范圍調(diào)整方向window_size24~72負(fù)荷波動(dòng)周期越長(zhǎng)窗口越要拉長(zhǎng)hidden_size32~128特征多、數(shù)據(jù)量大時(shí)上調(diào)num_layers1~3超過3層收益極小且訓(xùn)練更慢dropout0.1~0.3過擬合時(shí)增大欠擬合時(shí)減小或關(guān)閉batch_size32~128數(shù)據(jù)量小時(shí)用小batchlearning_rate5e-4~1e-3loss震蕩時(shí)下調(diào)下降太慢可上調(diào)patience10~20早停輪數(shù)配合ReduceLROnPlateauwindow_size24表示用過去24小時(shí)預(yù)測(cè)下一個(gè)小時(shí)適合以天為周期的負(fù)荷數(shù)據(jù)。如果數(shù)據(jù)是按15分鐘采樣的同樣的日周期需要96個(gè)時(shí)間步窗口取96附近。hidden_size在64是個(gè)安全起點(diǎn)特征維度低時(shí)32足夠加入更多天氣特征后可試128。num_layers2是多數(shù)課程設(shè)計(jì)項(xiàng)目的甜點(diǎn)1層欠擬合3層以上在小數(shù)據(jù)量下表現(xiàn)為驗(yàn)證損失不降反升不是模型能力不夠而是過擬合。4.3 訓(xùn)練過程常見異常排查訓(xùn)練LSTM最常見的異常有四種。第一種是loss曲線完全不動(dòng)此時(shí)優(yōu)先檢查學(xué)習(xí)率是否過小、歸一化是否完成、數(shù)據(jù)里是否有NaN。第二種是loss爆炸到inf大概率是梯度爆炸先調(diào)clip_grad_norm_的max_norm到0.5或1.0。第三種是訓(xùn)練集loss持續(xù)下降而驗(yàn)證集loss升高這是過擬合信號(hào)調(diào)整dropout和早停參數(shù)。第四種是測(cè)試集整體偏移一個(gè)常數(shù)檢查y歸一化后預(yù)測(cè)是否反歸一化時(shí)用了錯(cuò)誤的scaler。反歸一化漏了是課程設(shè)計(jì)項(xiàng)目里最常見的“模型分很高但圖不對(duì)”的原因。5. 多特征負(fù)荷預(yù)測(cè)的評(píng)估與課程設(shè)計(jì)結(jié)果呈現(xiàn)不只跑出數(shù)字5.1 回歸指標(biāo)的計(jì)算與選擇課程設(shè)計(jì)答辯時(shí)老師最常問的問題之一是“你這個(gè)模型效果怎么衡量”。只用loss曲線沒有說(shuō)服力需要用回歸指標(biāo)給出量化結(jié)論。負(fù)荷預(yù)測(cè)的通用指標(biāo)是MAE、RMSE、MAPE三個(gè)一起給才有意義。def evaluate_metrics(y_true, y_pred): mae np.mean(np.abs(y_true - y_pred)) rmse np.sqrt(np.mean((y_true - y_pred) ** 2)) mape np.mean(np.abs((y_true - y_pred) / y_true)) * 100 return {MAE: mae, RMSE: rmse, MAPE: mape} # 反歸一化后再算指標(biāo) model.eval() with torch.no_grad(): X_tensor torch.tensor(X_test, dtypetorch.float32) y_pred_scaled model(X_tensor).numpy() y_pred scaler_y.inverse_transform(y_pred_scaled.reshape(-1, 1)).flatten() y_true scaler_y.inverse_transform(y_test.reshape(-1, 1)).flatten() metrics evaluate_metrics(y_true, y_pred) print(metrics)三個(gè)指標(biāo)各有偏向。RMSE對(duì)大幅偏差敏感負(fù)荷預(yù)測(cè)中高峰時(shí)段的預(yù)測(cè)偏差會(huì)被RMSE放大MAPE是相對(duì)百分比誤差答辯時(shí)最好解釋MAE是最直觀的平均偏差。電力負(fù)荷預(yù)測(cè)領(lǐng)域論文里經(jīng)常同時(shí)報(bào)告這三個(gè)值是因?yàn)閱我恢笜?biāo)無(wú)法描述模型在不同誤差分布下的表現(xiàn)。MAPE有一個(gè)需要警惕的坑當(dāng)真實(shí)負(fù)荷接近0時(shí)MAPE會(huì)異常大。電力負(fù)荷一般不會(huì)為0但如果測(cè)試集中包含深夜低負(fù)荷時(shí)段分母小而分子正常會(huì)導(dǎo)致單點(diǎn)MAPE值偏大。答辯被追問時(shí)可以說(shuō)“我們額外計(jì)算了MAE和RMSE來(lái)規(guī)避MAPE在低負(fù)荷時(shí)段的不穩(wěn)定性”這是非常標(biāo)準(zhǔn)的回答。5.2 預(yù)測(cè)曲線可視化與誤差區(qū)間圖是課程設(shè)計(jì)報(bào)告里最能直接體現(xiàn)工作量的一環(huán)。預(yù)測(cè)曲線和真實(shí)曲線畫在同一張圖上比如連續(xù)畫7天或30天。import matplotlib.pyplot as plt # 取最后7天作為展示區(qū)間 days 7 hours days * 24 plt.figure(figsize(12, 5)) plt.plot(y_true[-hours:], label真實(shí)負(fù)荷, linewidth1.5) plt.plot(y_pred[-hours:], label預(yù)測(cè)負(fù)荷, linewidth1.5, alpha0.8) plt.legend() plt.xlabel(時(shí)間(h)) plt.ylabel(負(fù)荷(MW)) plt.title(LSTM多特征負(fù)荷預(yù)測(cè)結(jié)果最后7天) plt.grid(alpha0.3) plt.savefig(prediction_result.png, dpi150)盯住圖上兩類差異容易在答辯時(shí)形成有效分析一是每天“峰”的位置是否對(duì)齊二是在負(fù)荷劇烈爬升或下降段預(yù)測(cè)是否有明顯滯后。LSTM預(yù)測(cè)常見的滯后效應(yīng)表現(xiàn)為預(yù)測(cè)曲線比真實(shí)曲線晚1到2個(gè)小時(shí)到達(dá)峰值這是因?yàn)槟P瓦^度依賴最近的歷史負(fù)荷對(duì)突變的響應(yīng)天然有延遲。這個(gè)現(xiàn)象不是bug是RNN結(jié)構(gòu)的固有特性能用這個(gè)現(xiàn)象解釋模型行為比假裝模型完美更能體現(xiàn)理解深度。5.3 消融對(duì)比多特征有效性論證課程設(shè)計(jì)報(bào)告里“多特征”必須用對(duì)比實(shí)驗(yàn)證明其價(jià)值。最簡(jiǎn)單的做法是訓(xùn)練兩個(gè)模型一個(gè)只用load_lag特征另一個(gè)加入全部特征在相同測(cè)試集上對(duì)比指標(biāo)。這個(gè)對(duì)比表是報(bào)告里最硬的結(jié)論。模型輸入windowMAERMSEMAPE僅歷史負(fù)荷(7維)2446.263.88.2%歷史負(fù)荷氣象時(shí)間(13維)2438.955.46.7%消融實(shí)驗(yàn)不用做特別多次兩組就夠了。一組是基線只用歷史負(fù)荷一組是完整多特征。如果完整特征組沒有明顯優(yōu)勢(shì)就要檢查特征列是否真正進(jìn)入模型、是否被歸一化、是否有大量缺失值。特征維度增加后指標(biāo)變差的情況也真實(shí)存在說(shuō)明新特征攜帶的是噪聲這時(shí)需要在feature_cols里刪減特征而不是繼續(xù)疊加。6. 提升預(yù)測(cè)穩(wěn)定性的滑動(dòng)驗(yàn)證法把單次預(yù)測(cè)變成滾動(dòng)預(yù)測(cè)6.1 滾動(dòng)預(yù)測(cè)的實(shí)現(xiàn)前面的測(cè)試過程是一步預(yù)測(cè)用歷史窗口預(yù)測(cè)下一個(gè)時(shí)刻然后滑動(dòng)窗口繼續(xù)用真實(shí)值作為輸入。實(shí)際應(yīng)用中要預(yù)測(cè)未來(lái)24小時(shí)初始窗口之后只有預(yù)測(cè)值可用誤差會(huì)隨時(shí)間累積。課程設(shè)計(jì)里把“多步預(yù)測(cè)”做好能明顯拉開與同組同學(xué)的差距。常見的做法是循環(huán)預(yù)測(cè)每預(yù)測(cè)一個(gè)時(shí)刻就把預(yù)測(cè)值拼接到歷史序列末尾同時(shí)丟棄窗口最前面的一個(gè)時(shí)刻保持窗口長(zhǎng)度不變。def recursive_forecast(model, initial_window, predict_steps, feature_scaler): model.eval() current_window initial_window.copy() # shape: (window, feature_dim) results [] for _ in range(predict_steps): with torch.no_grad(): X_input torch.tensor( current_window.reshape(1, current_window.shape[0], -1), dtypetorch.float32 ) y_pred model(X_input).item() results.append(y_pred) # 構(gòu)造下一時(shí)刻的輸入行 next_row current_window[-1].copy() # 將歷史負(fù)荷列整體平移 next_row[:-1] next_row[1:] # 按實(shí)際特征順序調(diào)整 next_row[-1] y_pred current_window np.vstack( [current_window[1:], next_row.reshape(1, -1)] ) results feature_scaler.inverse_transform( np.array(results).reshape(-1, 1) ).flatten() return results這個(gè)實(shí)現(xiàn)的要點(diǎn)是“滾動(dòng)”動(dòng)作本身窗口最舊的時(shí)刻被移除預(yù)測(cè)值進(jìn)入窗口末尾繼續(xù)作為下一次預(yù)測(cè)的輸入。注意實(shí)際項(xiàng)目里特征順序不是固定的需要根據(jù)特征表調(diào)整平移的列。多步預(yù)測(cè)的誤差累積程度可以作為另一個(gè)答辯分析點(diǎn)與單步預(yù)測(cè)的指標(biāo)對(duì)比通常會(huì)讓性能看起來(lái)差一些但更貼近實(shí)際。6.2 針對(duì)節(jié)假日與極端天氣的應(yīng)急策略負(fù)荷預(yù)測(cè)里最難的不是常規(guī)日而是節(jié)假日和極端天氣。多特征模型的優(yōu)勢(shì)在這里體現(xiàn)最明顯如果特征包含is_holiday、temperature_forecast模型至少有機(jī)會(huì)學(xué)習(xí)到這類日期的特殊模式。如果數(shù)據(jù)集中節(jié)假日樣本過少建議單獨(dú)收集最近兩年的節(jié)假日數(shù)據(jù)做二次微調(diào)而不是讓模型在少量樣本上硬學(xué)。另一種實(shí)用技巧是給時(shí)間特征做“距離節(jié)假日天數(shù)”的連續(xù)特征。比如days_to_holiday表示當(dāng)前時(shí)間距離最近節(jié)假日的天數(shù)負(fù)值表示已過去的節(jié)假日。這個(gè)特征在GEFCom2014這類比賽中被反復(fù)證明有效。實(shí)現(xiàn)很簡(jiǎn)單df[days_to_holiday] df[date].apply( lambda d: min(abs((d - h).days) for h in holiday_list) )這樣模型不需要單獨(dú)記住每個(gè)節(jié)假日的日期只需學(xué)習(xí)“距離節(jié)假日越近負(fù)荷模式越偏離常規(guī)”的規(guī)律。6.3 報(bào)告呈現(xiàn)建議課程設(shè)計(jì)報(bào)告里除了訓(xùn)練曲線、預(yù)測(cè)對(duì)比圖、消融表之外建議再加一張“不同時(shí)段的誤差表”比如按峰、平、谷三個(gè)時(shí)段分別計(jì)算MAPE。這會(huì)讓結(jié)果呈現(xiàn)更完整通常谷時(shí)段MAPE偏高但絕對(duì)誤差低峰時(shí)段MAPE偏低但絕對(duì)誤差高。這個(gè)現(xiàn)象反映了負(fù)荷預(yù)測(cè)誤差的典型分布也能證明你不是只跑了別人的代碼而是對(duì)結(jié)果做過系統(tǒng)性分析。表格之外保證源碼里的數(shù)據(jù)加載路徑、特征順序、歸一化邏輯三處一致是讓項(xiàng)目可復(fù)現(xiàn)的底線。本文還有配套的精品資源點(diǎn)擊獲取