聊天視窗

掌握時序預測:Python 與統計學的實務指南 - 第 4 章

4. 模型選擇與評估:從統計模型到深度學習

發布於 2026-02-21 11:51

# 4. 模型選擇與評估:從統計模型到深度學習 ## 4.1 先備知識 - **時序資料的核心挑戰**:非平穩性、季節性、長期依賴。 - **選擇模型的三個層面**: 1. **理論基礎**:模型是否能解釋因果關係? 2. **預測表現**:在不同場景下的誤差指標。 3. **實務可行性**:計算成本、部署簡易度、可維護性。 > **小提醒**:即使最先進的模型(例如 LSTM)表現優異,也不一定能在所有領域中取得最好的效果。多元測試與比較是關鍵。 ## 4.2 傳統統計模型 ### 4.2.1 ARIMA / SARIMA - **ARIMA** (AutoRegressive Integrated Moving Average): - *AR* (自回歸) 部分捕捉短期依賴。 - *I* (差分) 使序列平穩。 - *MA* (滑動平均) 調節噪聲。 - **SARIMA** (Seasonal ARIMA):在 ARIMA 基礎上加入季節性參數 (P, D, Q, s)。 python import pandas as pd import numpy as np from statsmodels.tsa.statespace.sarimax import SARIMAX # 假設 df 已經完成前置處理,索引為 DatetimeIndex train, test = df[:-12], df[-12:] model = SARIMAX(train['sales'], order=(1,1,1), seasonal_order=(1,1,1,12)) fit = model.fit(disp=False) forecast = fit.forecast(steps=12) print(forecast) ### 4.2.2 指數平滑 (ETS) - **ETS** (Error‑Trend‑Seasonality) 可自動化選擇加法或乘法模式。 python from statsmodels.tsa.holtwinters import ExponentialSmoothing fit = ExponentialSmoothing(train['sales'], trend='add', seasonal='mul', seasonal_periods=12).fit() forecast = fit.forecast(steps=12) ## 4.3 先進時序工具:Prophet - **Facebook Prophet**:針對多重季節性、假日效應,易於使用。 - **優點**:自動化季節性拆解、缺失值自動處理、可插入自定義假日。 - **缺點**:不支持高階自回歸結構,對於極短期依賴不如 ARIMA 靈活。 python from prophet import Prophet prophet_df = df.reset_index().rename(columns={'index':'ds','sales':'y'}) model = Prophet(yearly_seasonality=True, weekly_seasonality=True, daily_seasonality=False) model.add_country_holidays(country_name='US') # 範例:美國假日 model.fit(prophet_df) future = model.make_future_dataframe(periods=12, freq='M') forecast = model.predict(future) print(forecast[['ds','yhat','yhat_lower','yhat_upper']].tail(12)) ## 4.4 深度學習時序模型 ### 4.4.1 LSTM / GRU - **LSTM**(Long Short‑Term Memory):解決長期依賴與梯度消失問題。 - **GRU**(Gated Recurrent Unit):較輕量化,參數更少。 python import torch import torch.nn as nn class LSTMForecast(nn.Module): def __init__(self, input_size, hidden_size, num_layers, output_size=1): super().__init__() self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True) self.linear = nn.Linear(hidden_size, output_size) def forward(self, x): out, _ = self.lstm(x) out = out[:, -1, :] # 取最後時步 out = self.linear(out) return out # 讀取資料、轉換為 torch.Tensor、訓練迴圈略 ### 4.4.2 Temporal Convolutional Networks (TCN) - 使用因果卷積與膨脹卷積捕捉長序列依賴,計算成本較低。 python # 參考 pytorch‑wavelets 或 tcn‑pytorch 套件 ## 4.5 評估指標 | 指標 | 定義 | 何時適用 | |------|------|----------| | MAE | Mean Absolute Error | 僅考慮絕對誤差,對極端值不敏感 | | RMSE | Root Mean Squared Error | 對極端誤差更敏感,適合重視大幅偏差的場景 | | MAPE | Mean Absolute Percentage Error | 方便比較不同量級時間序列,但對零值敏感 | | SMAPE | Symmetric MAPE | 解決 MAPE 在零值附近不穩定問題 | | MAE_{log} | MAE of log‑transformed series | 當數據呈指數增長時可使用 | python from sklearn.metrics import mean_absolute_error, mean_squared_error def mape(y_true, y_pred): return np.mean(np.abs((y_true - y_pred) / y_true)) * 100 def smape(y_true, y_pred): return np.mean(2 * np.abs(y_true - y_pred) / (np.abs(y_true) + np.abs(y_pred))) * 100 ## 4.6 時序交叉驗證 1. **Rolling Origin (滑動窗口)**:固定訓練窗口向前滑動,每次預測未來 `k` 步。 2. **Expanding Window (擴張窗口)**:每次新增一個新觀測值至訓練集,保持窗口不斷擴大。 3. **Leave‑One‑Out**:僅適用於極短序列。 python from sklearn.model_selection import TimeSeriesSplit tscv = TimeSeriesSplit(n_splits=5, max_train_size=None, test_size=12) for train_idx, test_idx in tscv.split(df): X_train, X_test = df.iloc[train_idx], df.iloc[test_idx] # 模型訓練、預測、評估 ## 4.7 超參數調整 - **網格搜尋 (GridSearchCV)**:穩健、易於實作,但計算成本高。 - **隨機搜尋 (RandomizedSearchCV)**:在大空間中快速定位近似最佳參數。 - **貝葉斯優化 (BayesOpt)**:如 `scikit-optimize`、`optuna`,更高效。 python from sklearn.model_selection import RandomizedSearchCV from scipy.stats import randint param_grid = { 'order': [(1,1,1), (2,1,2)], 'seasonal_order': [(0,0,0,12), (1,1,1,12)] } search = RandomizedSearchCV(estimator=SARIMAX, param_distributions=param_grid, n_iter=10, cv=tscv) search.fit(train['sales']) ## 4.8 實作範例:多模型比較 Pipeline python import numpy as np import pandas as pd from sklearn.base import BaseEstimator, RegressorMixin class ProphetRegressor(BaseEstimator, RegressorMixin): def __init__(self, yearly_seasonality=True, weekly_seasonality=True): self.yearly_seasonality = yearly_seasonality self.weekly_seasonality = weekly_seasonality self.model = None def fit(self, X, y): df = X.copy() df['y'] = y self.model = Prophet(yearly_seasonality=self.yearly_seasonality, weekly_seasonality=self.weekly_seasonality) self.model.fit(df) return self def predict(self, X): future = X.copy() future = future[['ds']] forecast = self.model.predict(future) return forecast['yhat'].values # 建立 Pipeline from sklearn.pipeline import Pipeline pipeline = Pipeline([ ('prophet', ProphetRegressor()), # 可以再加入其他預處理/特徵工程步驟 ]) ## 4.9 小結 - **模型選擇**:先從可解釋性與實務可行性判斷,ARIMA/SARIMA 仍是基礎基準;Prophet 適合多季節性場景;深度學習模型在資料量大、長期依賴強烈時表現優越。 - **評估方法**:指標與交叉驗證必須同時考慮;不同場景下應依需求選擇最合適的指標。 - **實務操作**:建立可重複的 Pipeline、封裝 FeatureEngineer 與 ModelWrapper,確保從資料準備到部署的一體化流程。 > **前進之路**:下一章將深入「模型部署與監控」,協助讀者將預測模型真正落地,並在變動環境中自動調整。