跳转至

12.5   预测与评估

奶茶店准备下周备料。小率想给出一个数字,均哥却让他同时给出一段范围:未来越远,不确定性越大,只报一个点容易让人误会。

图 12.5.1 预测未来时要同时看点和区间

店长问我下周卖多少杯,我直接给预测值不就行了吗?
点预测有用,但区间更诚实。它告诉对方:这不是确定答案,而是带不确定性的判断。

12.5.1   点预测回答“最可能是多少”

点预测 (Point Forecast) 给出一个未来值,例如:

\[ \hat{y}_{T+h} \]

其中 \(h\) 是预测步长。\(h=1\) 表示预测下一期,\(h=7\) 表示预测 7 期后。

图 12.5.2 点预测、预测区间和回测

12.5.2   预测区间回答“可能落在哪”

预测区间 (Prediction Interval) 会把未来误差也考虑进去:

\[ \hat{y}_{T+h} \pm z_{\alpha/2}\operatorname{SE}(\hat{y}_{T+h}) \]

通常 \(h\) 越大,预测区间越宽。因为越远的未来,累积不确定性越多。

点预测和预测区间的区别

点预测像“最可能的一杯数”。
预测区间像“准备原料时要留的安全范围”。
做决策时,两者最好一起看。

12.5.3   回测要像真正预测未来

时间序列不能随机切训练集和测试集。正确做法是用过去训练,预测后面,再不断向前滚动。

常见评估指标:

指标 含义 注意点
MAE 平均绝对误差 单位和原数据相同,容易解释
RMSE 均方根误差 对大误差更敏感
MAPE 平均绝对百分比误差 真实值接近 0 时会不稳定
MASE 相对朴素模型的误差 便于跨序列比较

12.5.4   先打败朴素基线

一个预测模型至少要赢过简单基线:

  • 朴素预测:下一期等于上一期。
  • 季节朴素预测:下一期等于上一个周期同位置。
  • 移动平均:用最近几期平均值预测。
import numpy as np
import pandas as pd
from sklearn.metrics import mean_absolute_error
from statsmodels.tsa.holtwinters import ExponentialSmoothing

rng = np.random.default_rng(12)
dates = pd.date_range("2021-01-01", periods=60, freq="MS")
t = np.arange(len(dates))
sales = pd.Series(
    220 + 2 * t + 20 * np.sin(2 * np.pi * t / 12) + rng.normal(0, 7, len(t)),
    index=dates,
)

train, test = sales.iloc[:-12], sales.iloc[-12:]
fit = ExponentialSmoothing(train, trend="add", seasonal="add", seasonal_periods=12).fit()
forecast = fit.forecast(12)

seasonal_naive = train.iloc[-12:].to_numpy()
print("ETS MAE:", round(mean_absolute_error(test, forecast), 2))
print("季节朴素 MAE:", round(mean_absolute_error(test, seasonal_naive), 2))

完整脚本见:

docs/assets/scripts/ch12_time_series/05_forecasting/main.py

别把未来信息漏进训练集

用全量数据做标准化、调参、特征选择,再切测试集,会让模型提前看见未来。时间序列评估里,这是最常见也最隐蔽的错误。

小率的笔记本

预测要同时给点和区间。
评估要按时间向前滚动,不能随机打散。
好模型必须先打败朴素基线,再谈复杂方法。