← 回到花园
数据实验 · 长文

从最后一个窗口,到一组滚动回测

把 TimesFM 的单次预测扩展为八个窗口,并保留基线、逐窗口指标与逐小时输出。

第一次跑通预测脚本时,看见一条曲线已经很有成就感。但当结果只来自最后一个窗口,它回答的是一次具体预测表现怎样,还没有展示同一方法在不同时间段的变化。

2026 年 5 月,我把 TimesFM 的 ETTh1 快速实验从单窗口扩展为 rolling backtest。本文记下这次改造的设计和当时的结果。

把评估单位从一次预测改成一组窗口

原脚本评估最后一个 96 小时窗口。修改后选择了 8 个非重叠窗口,每个窗口输出 96 个小时的预测。

这样一来,除了一个汇总分数,还可以逐窗口检查:哪些时间段误差较大,某个模型的优势是否只来自其中一段,以及示例图是否碰巧挑中了最好看的窗口。

历史上下文 → 窗口 1:预测 96h → 计算指标
历史上下文 → 窗口 2:预测 96h → 计算指标
                         …
历史上下文 → 窗口 8:预测 96h → 计算指标
                              ↓
                 保留逐窗口结果与汇总结果

窗口设计只是评估的一部分。复现实验时,还需要一并核对数据划分、输入上下文、预处理和模型版本;这些信息决定了数字能否相互比较。

给模型放上简单的对照

实验保留了 SeasonalNaive_24h,又加入 SeasonalNaive_168h,分别用一天和一周的季节性作为简单对照。

简单方法有一个好处:它给复杂模型的结果提供了可解释的参照。如果只报告基础模型自己的误差,很难判断这个数值在当前数据上意味着什么。

当时记录的结果

方法 MAE RMSE
SeasonalNaive_24h 1.906902 2.492032
SeasonalNaive_168h 1.852737 2.473503
TimesFM_2.5_200M 1.252056 1.757624

TimesFM 的 P10–P90 区间覆盖率记录为 0.805990。在这组窗口和当时的设置下,TimesFM 的汇总误差低于两个基线;这组结果的范围就是该次 ETTh1 实验。

让结果可以被重新检查

脚本保留了几种互补的输出:

文件 用途
metrics.csv 三个模型的汇总指标
per_window_metrics.csv 8 个窗口 × 3 个模型的明细
forecast_values.csv 8 × 96 = 768 行逐小时预测
timesfm_forecast.png 最后一个窗口的可视化

汇总表适合快速比较,明细表适合解释波动,逐小时值允许重算指标。图片只是其中一种查看方式。

跑通过程中的两个小问题

当时环境里的 NumPy 没有可用的 np.trapz,代码改为使用 np.trapezoid 或梯形积分 fallback;Matplotlib 缓存目录则通过 MPLCONFIGDIR 显式指定,避免环境权限干扰绘图。

这类问题也应该写进运行说明。它们不会改变预测任务本身,却会影响另一个人能否顺利复跑。

下一步更值得看什么

继续实验时,我更想看逐窗口差异,而不仅是再获得一个汇总数字:误差集中在哪段时间,预测区间是否稳定,不同季节性对照在哪些窗口有竞争力。

相关代码保存在 我的 TimesFM 实验仓库。模型来自 Google Research 的 TimesFM;这里记录的是使用与评估实验。

✳

记于 2026-11-05 · 2026-11-05 更新

看看关联项目 ↗