返回目錄
A
掌握時序預測: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,確保從資料準備到部署的一體化流程。
> **前進之路**:下一章將深入「模型部署與監控」,協助讀者將預測模型真正落地,並在變動環境中自動調整。