測(cè)系統(tǒng)源碼:從數(shù)據(jù)清洗到Web可視化全鏈路實(shí)戰(zhàn))
簡(jiǎn)介這份資源是面向高校計(jì)算機(jī)相關(guān)專業(yè)學(xué)生的軌道交通客流預(yù)測(cè)系統(tǒng)完整源碼包適用于畢業(yè)設(shè)計(jì)、期末大作業(yè)與課程設(shè)計(jì)等場(chǎng)景難度適中評(píng)審得分達(dá)到98分且源碼均經(jīng)過(guò)本地編譯驗(yàn)證可運(yùn)行。壓縮包共包含1707個(gè)文件整體約37.25MB以TypeScript與JavaScript文件為主構(gòu)成前端交互與可視化界面另有Python腳本承擔(dān)客流預(yù)測(cè)核心算法并輔以JSON配置、XML與HTML頁(yè)面、Vue組件及少量模型文件目錄結(jié)構(gòu)清晰便于按模塊閱讀與二次開(kāi)發(fā)。目前已有360人學(xué)習(xí)下載可作為同類選題的參考方案。讀者可從中獲取一套完整的客流預(yù)測(cè)實(shí)現(xiàn)思路涵蓋數(shù)據(jù)預(yù)處理、模型訓(xùn)練與結(jié)果展示等環(huán)節(jié)適合需要快速搭建項(xiàng)目框架、理解預(yù)測(cè)流程或進(jìn)行功能擴(kuò)展的學(xué)習(xí)者使用。1. 從一份畢設(shè)源碼說(shuō)起軌道交通客流預(yù)測(cè)到底在算什么每年畢業(yè)季計(jì)算機(jī)和交通相關(guān)專業(yè)的選題里軌道交通客流預(yù)測(cè)是出現(xiàn)頻率很高的一類。原因很直接它同時(shí)踩中了「數(shù)據(jù)可視化」「機(jī)器學(xué)習(xí)建?!埂竁eb 系統(tǒng)開(kāi)發(fā)」三個(gè)答辯加分項(xiàng)而且數(shù)據(jù)來(lái)源相對(duì)規(guī)范不像某些選題那樣找不到公開(kāi)數(shù)據(jù)。但真正動(dòng)手做過(guò)的人都知道這類項(xiàng)目最容易翻車的地方不是模型精度而是從原始客流數(shù)據(jù)到系統(tǒng)能跑起來(lái)之間的那一大段工程活。這份基于 Python 的軌道交通客流預(yù)測(cè)系統(tǒng)源碼核心解決的就是這段工程活。它把數(shù)據(jù)清洗、特征構(gòu)造、預(yù)測(cè)模型、可視化展示和 Web 交互串成了一條完整鏈路適合兩類人一類是正在做畢設(shè)、需要一個(gè)能跑通、能講清楚、能改得動(dòng)的完整項(xiàng)目骨架的同學(xué)另一類是想快速了解客流預(yù)測(cè)系統(tǒng)實(shí)際長(zhǎng)什么樣、各模塊怎么銜接的從業(yè)者。你拿到的不只是一段預(yù)測(cè)代碼而是一個(gè)從數(shù)據(jù)到界面的閉環(huán)。需要提前說(shuō)清楚的是客流預(yù)測(cè)系統(tǒng)的價(jià)值不在于預(yù)測(cè)得多準(zhǔn)而在于整條鏈路是否可解釋、可復(fù)現(xiàn)、可擴(kuò)展。一個(gè) MAE 只有 3% 但代碼亂成一團(tuán)的系統(tǒng)在答辯和實(shí)際使用中都不如一個(gè) MAE 8% 但結(jié)構(gòu)清晰、參數(shù)可調(diào)、日志完整的系統(tǒng)。這份源碼的定位就是后者。2. 系統(tǒng)架構(gòu)拆解數(shù)據(jù)層、模型層、展示層怎么分工2.1 三層結(jié)構(gòu)的設(shè)計(jì)邏輯拿到一份源碼第一件事不是急著跑而是先看清楚它怎么分層。這份項(xiàng)目的結(jié)構(gòu)是典型的「數(shù)據(jù)層 → 模型層 → 展示層」三段式各層之間通過(guò)約定好的數(shù)據(jù)格式解耦。數(shù)據(jù)層負(fù)責(zé)原始客流記錄的讀取、清洗和特征構(gòu)造。軌道交通客流數(shù)據(jù)通常包含刷卡記錄、進(jìn)出站時(shí)間、線路站點(diǎn)編號(hào)、日期類型等字段。原始數(shù)據(jù)最大的問(wèn)題是時(shí)間粒度和空間粒度不統(tǒng)一——有的按小時(shí)統(tǒng)計(jì)有的按 15 分鐘統(tǒng)計(jì)有的按線路匯總有的按站點(diǎn)明細(xì)。數(shù)據(jù)層的任務(wù)就是把這些統(tǒng)一成模型能吃的格式。模型層接收數(shù)據(jù)層輸出的特征矩陣完成訓(xùn)練和預(yù)測(cè)。這里常見(jiàn)做法是用時(shí)間序列模型如 ARIMA、LSTM或樹(shù)模型如 XGBoost、LightGBM做基線再用滑動(dòng)窗口構(gòu)造監(jiān)督學(xué)習(xí)樣本。模型層的輸出是預(yù)測(cè)值加評(píng)估指標(biāo)不直接對(duì)接界面。展示層是 Web 部分負(fù)責(zé)把預(yù)測(cè)結(jié)果、歷史對(duì)比、誤差分析用圖表呈現(xiàn)出來(lái)并提供參數(shù)調(diào)整入口。三層之間通過(guò)文件或數(shù)據(jù)庫(kù)表傳遞數(shù)據(jù)而不是函數(shù)直接調(diào)用這樣任何一層出問(wèn)題都不會(huì)把整個(gè)系統(tǒng)拖死。2.2 目錄結(jié)構(gòu)與關(guān)鍵文件在動(dòng)手之前先確認(rèn)目錄結(jié)構(gòu)。常見(jiàn)做法是下面這種布局project/ ├── data/ │ ├── raw/ # 原始客流數(shù)據(jù) │ └── processed/ # 清洗后的特征數(shù)據(jù) ├── models/ │ ├── train.py # 模型訓(xùn)練腳本 │ ├── predict.py # 預(yù)測(cè)腳本 │ └── saved/ # 訓(xùn)練好的模型文件 ├── web/ │ ├── app.py # Web 入口 │ ├── templates/ # 頁(yè)面模板 │ └── static/ # 靜態(tài)資源 ├── utils/ │ ├── preprocess.py # 數(shù)據(jù)清洗與特征構(gòu)造 │ └── metrics.py # 評(píng)估指標(biāo) ├── config.yaml # 全局參數(shù)配置 └── requirements.txt # 依賴清單這個(gè)結(jié)構(gòu)的好處是職責(zé)清晰。utils/preprocess.py只做數(shù)據(jù)轉(zhuǎn)換models/train.py只做訓(xùn)練web/app.py只做展示。改模型不影響界面改界面不影響數(shù)據(jù)。2.3 環(huán)境準(zhǔn)備與依賴安裝環(huán)境配置是第一個(gè)容易卡住的地方。這份源碼基于 Python建議用 3.8 到 3.10 之間的版本太新的版本某些庫(kù)可能還沒(méi)適配。依賴安裝步驟如下# 創(chuàng)建虛擬環(huán)境避免污染全局 python -m venv venv # 激活虛擬環(huán)境Windows venv\Scripts\activate # 激活虛擬環(huán)境macOS/Linux source venv/bin/activate # 安裝依賴 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple這里用清華源是因?yàn)椴糠稚疃葘W(xué)習(xí)庫(kù)體積大默認(rèn)源下載容易超時(shí)。requirements.txt里通常包含 pandas、numpy、scikit-learn、flask 或 streamlit、matplotlib 等。如果安裝過(guò)程中某個(gè)庫(kù)報(bào)編譯錯(cuò)誤優(yōu)先檢查是否缺少系統(tǒng)級(jí)依賴而不是反復(fù)重裝。提示虛擬環(huán)境激活后命令行前面會(huì)出現(xiàn)(venv)標(biāo)識(shí)。如果沒(méi)出現(xiàn)說(shuō)明激活失敗后續(xù)所有操作都會(huì)裝到全局環(huán)境里后面排查依賴沖突會(huì)很痛苦。3. 數(shù)據(jù)預(yù)處理與特征工程把原始刷卡記錄變成模型能吃的矩陣3.1 原始數(shù)據(jù)的典型問(wèn)題軌道交通客流原始數(shù)據(jù)一般長(zhǎng)這樣一行代表某個(gè)站點(diǎn)在某個(gè)時(shí)間段的進(jìn)出站人數(shù)字段包括日期、時(shí)間、線路編號(hào)、站點(diǎn)編號(hào)、進(jìn)站量、出站量??雌饋?lái)干凈實(shí)際用起來(lái)問(wèn)題不少。第一是缺失值。某些站點(diǎn)在某些時(shí)段沒(méi)有記錄不是真的沒(méi)人而是設(shè)備沒(méi)上報(bào)。直接填 0 會(huì)把「無(wú)數(shù)據(jù)」和「無(wú)人流」混為一談模型會(huì)學(xué)到錯(cuò)誤規(guī)律。第二是異常值。節(jié)假日、突發(fā)事件會(huì)導(dǎo)致某些時(shí)段客流暴漲這些點(diǎn)如果不處理會(huì)拉偏整體分布。第三是時(shí)間粒度不統(tǒng)一。有的數(shù)據(jù)按小時(shí)有的按 15 分鐘建模前必須對(duì)齊。3.2 清洗與對(duì)齊的代碼實(shí)現(xiàn)下面這段是數(shù)據(jù)清洗的核心邏輯常見(jiàn)做法是先對(duì)齊時(shí)間粒度再處理缺失和異常import pandas as pd import numpy as np def clean_flow_data(raw_df, freq1H): 清洗客流數(shù)據(jù)時(shí)間對(duì)齊、缺失填充、異常截?cái)?raw_df: 原始 DataFrame含 timestamp, station_id, flow 列 freq: 目標(biāo)時(shí)間粒度默認(rèn) 1 小時(shí) df raw_df.copy() # 統(tǒng)一時(shí)間格式 df[timestamp] pd.to_datetime(df[timestamp]) # 按站點(diǎn)分組后重采樣保證每個(gè)站點(diǎn)時(shí)間軸完整 df df.set_index(timestamp).groupby(station_id).resample(freq).sum().reset_index() # 缺失值用前后均值填充而不是直接填 0 df[flow] df.groupby(station_id)[flow].transform( lambda x: x.fillna(x.rolling(3, min_periods1).mean()) ) # 異常值截?cái)喑^(guò) 3 倍標(biāo)準(zhǔn)差的記為邊界值 mean, std df[flow].mean(), df[flow].std() df[flow] df[flow].clip(mean - 3 * std, mean 3 * std) return df邏輯說(shuō)明resample(freq)把不規(guī)則時(shí)間戳對(duì)齊到固定粒度groupby(station_id)保證不同站點(diǎn)獨(dú)立處理。缺失填充用滾動(dòng)均值而不是全局均值是因?yàn)榭土饔忻黠@的時(shí)間連續(xù)性用鄰近時(shí)段的值更合理。異常截?cái)嘤?3 倍標(biāo)準(zhǔn)差是通用做法如果數(shù)據(jù)本身波動(dòng)大可以放寬到 4 倍。參數(shù)方面freq決定了建模的時(shí)間分辨率。改成15T就是 15 分鐘粒度數(shù)據(jù)量會(huì)翻四倍訓(xùn)練時(shí)間相應(yīng)增加。如果顯存或內(nèi)存吃緊優(yōu)先用小時(shí)粒度。3.3 特征構(gòu)造讓模型看到「時(shí)間」和「歷史」原始流量值本身信息有限模型需要額外的特征才能學(xué)到規(guī)律。常見(jiàn)做法是構(gòu)造三類特征時(shí)間特征、滯后特征、滑動(dòng)窗口特征。def build_features(df): 構(gòu)造時(shí)間特征、滯后特征和滑動(dòng)窗口統(tǒng)計(jì)量 df df.sort_values([station_id, timestamp]) # 時(shí)間特征小時(shí)、星期、是否周末 df[hour] df[timestamp].dt.hour df[weekday] df[timestamp].dt.weekday df[is_weekend] (df[weekday] 5).astype(int) # 滯后特征前 1、2、24 個(gè)時(shí)間步的流量 for lag in [1, 2, 24]: df[flag_{lag}] df.groupby(station_id)[flow].shift(lag) # 滑動(dòng)窗口過(guò)去 3 步和 24 步的均值 df[roll_mean_3] df.groupby(station_id)[flow].transform( lambda x: x.rolling(3, min_periods1).mean() ) df[roll_mean_24] df.groupby(station_id)[flow].transform( lambda x: x.rolling(24, min_periods1).mean() ) return df.dropna()滯后特征lag_1和lag_2捕捉短期趨勢(shì)lag_24捕捉日周期。滑動(dòng)均值平滑噪聲。dropna()會(huì)丟掉前幾行沒(méi)有滯后值的數(shù)據(jù)這是正常代價(jià)。如果數(shù)據(jù)量本來(lái)就少可以把min_periods調(diào)小但會(huì)引入偏差。注意特征構(gòu)造必須在劃分訓(xùn)練集和測(cè)試集之前完成但滯后和滑動(dòng)窗口的計(jì)算不能跨集。正確做法是先按時(shí)間切分再在各自集合內(nèi)做窗口計(jì)算否則會(huì)數(shù)據(jù)泄露測(cè)試指標(biāo)虛高。4. 預(yù)測(cè)模型選型與訓(xùn)練LSTM、XGBoost 還是 ARIMA4.1 三種模型的適用邊界客流預(yù)測(cè)常見(jiàn)的模型有三類各有各的適用場(chǎng)景不存在絕對(duì)最優(yōu)。ARIMA 適合線性趨勢(shì)明顯、周期穩(wěn)定的單站點(diǎn)序列優(yōu)點(diǎn)是訓(xùn)練快、可解釋性強(qiáng)缺點(diǎn)是處理多站點(diǎn)、多特征時(shí)很吃力。XGBoost 適合特征工程做得好、樣本量中等的場(chǎng)景能自動(dòng)處理特征交互訓(xùn)練速度比深度學(xué)習(xí)快缺點(diǎn)是預(yù)測(cè)的是點(diǎn)值不擅長(zhǎng)捕捉長(zhǎng)序列依賴。LSTM 適合序列長(zhǎng)、周期復(fù)雜、多站點(diǎn)聯(lián)合建模的場(chǎng)景能學(xué)到長(zhǎng)期依賴缺點(diǎn)是需要更多數(shù)據(jù)、訓(xùn)練慢、調(diào)參玄學(xué)。這份源碼通常以 LSTM 為主模型XGBoost 作為對(duì)比基線。選型理由很實(shí)際畢設(shè)答辯時(shí)深度學(xué)習(xí)模型更容易講出技術(shù)含量而且 LSTM 對(duì)輸入序列長(zhǎng)度的靈活性比 ARIMA 好。4.2 LSTM 模型搭建與訓(xùn)練下面是一個(gè)可復(fù)現(xiàn)的 LSTM 訓(xùn)練骨架import torch import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset class FlowLSTM(nn.Module): def __init__(self, input_size, hidden_size64, num_layers2, output_size1): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue) self.fc nn.Linear(hidden_size, output_size) def forward(self, x): # x shape: (batch, seq_len, input_size) out, _ self.lstm(x) # 取最后一個(gè)時(shí)間步的輸出 return self.fc(out[:, -1, :]) def train_model(train_loader, input_size, epochs50, lr1e-3): model FlowLSTM(input_size) optimizer torch.optim.Adam(model.parameters(), lrlr) criterion nn.MSELoss() for epoch in range(epochs): model.train() total_loss 0 for xb, yb in train_loader: optimizer.zero_grad() pred model(xb) loss criterion(pred, yb) loss.backward() optimizer.step() total_loss loss.item() print(fEpoch {epoch1}, Loss: {total_loss/len(train_loader):.4f}) return model邏輯說(shuō)明input_size是特征數(shù)量等于特征工程輸出的列數(shù)。hidden_size控制 LSTM 記憶容量64 是常見(jiàn)起點(diǎn)數(shù)據(jù)量大可以加到 128。num_layers2表示兩層堆疊層數(shù)越多表達(dá)能力越強(qiáng)但越容易過(guò)擬合。batch_firstTrue讓輸入維度是(batch, seq_len, features)符合直覺(jué)。訓(xùn)練時(shí)seq_len一般取 24對(duì)應(yīng)一天的小時(shí)數(shù)或 48。學(xué)習(xí)率1e-3是 Adam 的常用值如果 loss 震蕩明顯降到1e-4。epochs50不是固定值要看 loss 是否收斂通常加早停機(jī)制更穩(wěn)妥。4.3 訓(xùn)練集、驗(yàn)證集、測(cè)試集的劃分時(shí)間序列數(shù)據(jù)不能隨機(jī)劃分必須按時(shí)間順序切。常見(jiàn)比例是 7:1:2 或 8:1:1。下面是一個(gè)按時(shí)間切分的例子def split_by_time(df, train_ratio0.7, val_ratio0.1): 按時(shí)間順序劃分?jǐn)?shù)據(jù)集避免未來(lái)數(shù)據(jù)泄露 n len(df) train_end int(n * train_ratio) val_end int(n * (train_ratio val_ratio)) train df.iloc[:train_end] val df.iloc[train_end:val_end] test df.iloc[val_end:] return train, val, test這個(gè)函數(shù)看起來(lái)簡(jiǎn)單但很多翻車案例就出在這里。如果用train_test_split隨機(jī)劃分模型會(huì)「看到」未來(lái)的數(shù)據(jù)測(cè)試指標(biāo)會(huì)好得離譜實(shí)際部署時(shí)完全不能用。血淚經(jīng)驗(yàn)是任何時(shí)間序列項(xiàng)目劃分函數(shù)必須按時(shí)間切且要在特征構(gòu)造之前就確定切分點(diǎn)。4.4 評(píng)估指標(biāo)與結(jié)果解讀客流預(yù)測(cè)常用的指標(biāo)是 MAE、RMSE 和 MAPE。MAE 反映平均絕對(duì)誤差單位和人流一樣直觀RMSE 對(duì)大誤差更敏感MAPE 是百分比誤差方便跨站點(diǎn)比較。from sklearn.metrics import mean_absolute_error, mean_squared_error import numpy as np def evaluate(y_true, y_pred): mae mean_absolute_error(y_true, y_pred) rmse np.sqrt(mean_squared_error(y_true, y_pred)) mape np.mean(np.abs((y_true - y_pred) / (y_true 1e-6))) * 100 return {MAE: mae, RMSE: rmse, MAPE: mape}1e-6是防止除零。MAPE 在真實(shí)客流為 0 的時(shí)段會(huì)失真所以看結(jié)果時(shí)要結(jié)合 MAE 一起判斷。如果 MAPE 很低但 MAE 很高說(shuō)明模型在低流量時(shí)段誤差大高流量時(shí)段反而還行。5. 避坑與排查那些讓系統(tǒng)跑不起來(lái)的常見(jiàn)問(wèn)題5.1 依賴版本沖突導(dǎo)致 import 失敗現(xiàn)象pip install -r requirements.txt裝完后運(yùn)行python web/app.py報(bào)ImportError或AttributeError提示某個(gè)庫(kù)沒(méi)有某個(gè)函數(shù)。原因requirements.txt 里沒(méi)有鎖死版本號(hào)pip 裝了最新版而最新版改了 API。比如 pandas 2.x 和 1.x 在resample行為上有差異numpy 2.x 對(duì)部分舊庫(kù)不兼容。解決在 requirements.txt 里給關(guān)鍵庫(kù)加版本上限例如pandas1.3,2.0、numpy1.21,2.0。已經(jīng)裝亂的先pip uninstall再按鎖定版本重裝。最穩(wěn)妥的做法是用pip freeze requirements_lock.txt生成一份鎖定版本清單。5.2 數(shù)據(jù)路徑寫(xiě)死導(dǎo)致?lián)Q機(jī)器就報(bào)錯(cuò)現(xiàn)象在自己電腦上跑得好好的換到同學(xué)電腦或答辯現(xiàn)場(chǎng)報(bào)FileNotFoundError。原因代碼里用了絕對(duì)路徑比如pd.read_csv(C:/Users/xxx/data/raw/flow.csv)。解決統(tǒng)一用相對(duì)路徑基于項(xiàng)目根目錄拼接。常見(jiàn)做法是在 config 里定義BASE_DIR Path(__file__).resolve().parent.parent所有路徑基于它拼接。這樣無(wú)論項(xiàng)目放在哪個(gè)盤、哪個(gè)目錄都能找到文件。5.3 模型預(yù)測(cè)結(jié)果全是同一個(gè)值現(xiàn)象訓(xùn)練 loss 降不下去預(yù)測(cè)輸出幾乎不變或者所有站點(diǎn)的預(yù)測(cè)值一樣。原因常見(jiàn)有三種。一是特征沒(méi)有歸一化LSTM 對(duì)輸入尺度敏感流量值幾百上千梯度爆炸或消失。二是學(xué)習(xí)率太大模型在最優(yōu)解附近震蕩。三是標(biāo)簽泄露或特征構(gòu)造錯(cuò)誤模型學(xué)不到有效信息。解決先加歸一化用MinMaxScaler或StandardScaler把流量縮放到 0 到 1 之間預(yù)測(cè)后再反歸一化。學(xué)習(xí)率從1e-3降到1e-4試。最后檢查特征矩陣確認(rèn)滯后特征和滑動(dòng)窗口沒(méi)有全為 0 或全相同。5.4 Web 頁(yè)面能打開(kāi)但圖表空白現(xiàn)象Flask 或 Streamlit 啟動(dòng)正常瀏覽器能訪問(wèn)但圖表區(qū)域一片空白控制臺(tái)報(bào) 404 或數(shù)據(jù)格式錯(cuò)誤。原因前端請(qǐng)求的數(shù)據(jù)接口返回了空數(shù)組或者返回的 JSON 格式和前端解析邏輯不匹配。常見(jiàn)的是后端返回了 numpy 的float32類型JSON 序列化失敗。解決在返回前把 numpy 類型轉(zhuǎn)成 Python 原生類型用float()或int()包一層。檢查接口返回內(nèi)容可以在瀏覽器開(kāi)發(fā)者工具的 Network 面板看實(shí)際響應(yīng)。如果是靜態(tài)資源 404檢查static目錄路徑和 Flask 的static_folder配置是否一致。5.5 訓(xùn)練時(shí)間過(guò)長(zhǎng)導(dǎo)致答辯演示卡住現(xiàn)象模型訓(xùn)練要跑幾十分鐘甚至幾小時(shí)現(xiàn)場(chǎng)演示等不起。原因數(shù)據(jù)量大、模型層數(shù)多、epoch 設(shè)置過(guò)高或者沒(méi)有用 GPU。解決提前訓(xùn)練好模型并保存權(quán)重演示時(shí)直接加載預(yù)測(cè)不現(xiàn)場(chǎng)訓(xùn)練。保存和加載用torch.save(model.state_dict(), model.pth)和model.load_state_dict(torch.load(model.pth))。如果必須現(xiàn)場(chǎng)訓(xùn)練把數(shù)據(jù)量縮小到演示夠用的程度epoch 降到 10 以內(nèi)并提前確認(rèn)機(jī)器有 GPU 且 PyTorch 能調(diào)用。6. 進(jìn)階技巧讓預(yù)測(cè)結(jié)果更可信的三個(gè)實(shí)操方法6.1 用殘差分析定位模型盲區(qū)模型跑通之后別只看 MAE 一個(gè)數(shù)。把預(yù)測(cè)值和真實(shí)值的殘差按小時(shí)、按站點(diǎn)畫(huà)出來(lái)能看出模型在哪些時(shí)段、哪些站點(diǎn)系統(tǒng)性偏高或偏低。常見(jiàn)做法是import matplotlib.pyplot as plt def plot_residuals(df, y_true_colflow, y_pred_colpred): 按小時(shí)統(tǒng)計(jì)殘差均值定位系統(tǒng)性偏差 df[residual] df[y_true_col] - df[y_pred_col] hourly df.groupby(hour)[residual].mean() hourly.plot(kindbar, title各小時(shí)平均殘差) plt.axhline(0, colorred, linestyle--) plt.show()如果早高峰殘差持續(xù)為正說(shuō)明模型低估了早高峰客流可以在特征里加強(qiáng)早高峰標(biāo)識(shí)或者對(duì)早高峰樣本加權(quán)。這個(gè)分析比單純調(diào)參更有方向感。6.2 滑動(dòng)窗口回測(cè)代替單次劃分單次劃分的測(cè)試結(jié)果有偶然性。更穩(wěn)的做法是滑動(dòng)窗口回測(cè)用前 N 天訓(xùn)練、后 1 天測(cè)試然后窗口向前滑動(dòng)重復(fù)多次取平均指標(biāo)。def walk_forward_validation(df, window_size7, horizon1): 滑動(dòng)窗口回測(cè)返回多輪評(píng)估指標(biāo) results [] for start in range(0, len(df) - window_size - horizon, horizon): train df.iloc[start:start window_size] test df.iloc[start window_size:start window_size horizon] # 這里調(diào)用訓(xùn)練和預(yù)測(cè)函數(shù) # metrics evaluate(test[flow], pred) # results.append(metrics) return resultswindow_size是訓(xùn)練窗口天數(shù)horizon是預(yù)測(cè)步長(zhǎng)。這個(gè)方法計(jì)算量大但能反映模型在不同時(shí)間段的穩(wěn)定性。答辯時(shí)如果被問(wèn)「你怎么保證模型不是碰巧準(zhǔn)」滑動(dòng)回測(cè)的結(jié)果就是最好的回答。6.3 把預(yù)測(cè)區(qū)間一起輸出點(diǎn)預(yù)測(cè)容易給人「很準(zhǔn)」的錯(cuò)覺(jué)實(shí)際使用中更需要知道預(yù)測(cè)的不確定性。簡(jiǎn)單做法是用分位數(shù)回歸或?qū)Χ噍喕販y(cè)的預(yù)測(cè)值取分位數(shù)輸出一個(gè)區(qū)間。方法輸出適用場(chǎng)景點(diǎn)預(yù)測(cè)單個(gè)值快速展示、對(duì)比基線分位數(shù)回歸10%、50%、90% 分位需要風(fēng)險(xiǎn)提示的場(chǎng)景多輪回測(cè)取分位預(yù)測(cè)區(qū)間已有回測(cè)框架時(shí)最省事我一般會(huì)在 Web 界面上把預(yù)測(cè)區(qū)間用陰影畫(huà)出來(lái)中間實(shí)線是預(yù)測(cè)值。這樣即使預(yù)測(cè)偏了區(qū)間覆蓋住了真實(shí)值說(shuō)服力也強(qiáng)很多。從那以后我每次做預(yù)測(cè)類項(xiàng)目都強(qiáng)制走一遍殘差分析和滑動(dòng)回測(cè)不再只盯著一個(gè) MAE 數(shù)字。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取