预测模型的回测应回答:站在过去某个时刻,使用当时可用的数据和当时允许更新的模型,能够给出什么结果?仅把旧日期放进训练集、新日期放进测试集,还没有完整回答这个问题。本文讨论企业负荷、需求与产量等时间序列预测,重点是评估流程的时间语义;所有业务日期、窗口和数值都是构造示例,没有训练或实测模型。
先固定预测时刻、目标范围与更新节奏
设想工厂每天 08:00 发布未来七个完整自然日的用电量预测,即从次日 00:00 起连续七天,用于次日及后续排程。模型每周一 06:00 重训,08:00 开始服务;日内不重新训练。业务时区固定,夏令时等日历差异按现场规则处理。预测发布日期、目标日期和模型版本都必须保存。
回测如果每天重新训练,而线上每周才训练一次,评估的是不同服务;如果只预测明天,却用它证明未来七天排程效果,也改变了任务。应先写清预测起点、步长、发布频率、训练频率、训练数据截止时点,以及特征最迟何时可得。
滚动起点回测是在多个历史起点重复预测。Forecasting: Principles and Practice 说明,应使用测试观察之前的数据构建预测,并可按实际需要评估多步预测。[1] 本文在此基础上进一步要求重放数据到达和模型更新的时间,而不是每走到一个测试点就无条件拟合一次。
每条数据至少有两种时间,标签还有成熟时间
事件时间回答“这项产量属于哪一天”;可见时间回答“预测服务何时实际能读到这个数”。假设 9 月 8 日产量在 9 月 9 日 10:00 才首次入库,那么 9 月 9 日 08:00 的预测不能使用它,即使产量所属日期已经过去。事后修订还应保留版本及各版本的可见时间。
特征构建应同时限制业务时间与可见时间,并选取在预测起点之前可用的合适版本。Feast 文档介绍按时间点向过去查找特征,并特别指出:仅约束事件时间时,后来的补录或更正仍可能被返回。[3] 因而不能看到“point-in-time join”名称就默认它完整还原了当时的数据。
可见时间应尽量取服务实际可读的时间,而非上游声称已经发布的时间。入湖、清洗和特征计算本身可能有延迟;对于无法恢复历史快照的字段,应标为评估限制,保守模拟延迟,或从严格回测中排除。不能将今天的完整修订表伪装成当时的线上视图。
标签成熟时间则决定一条训练样本何时可以进入训练。例如某日用电量两天后完成核对,它可在之后用于评分,却不能在尚未核对的历史训练时点提前成为已知标签。若目标是未来七天总量,一条样本要等整个目标区间结束并达到约定成熟条件后才可训练。
切分间隔要从时间语义推导
scikit-learn 的 TimeSeriesSplit 提供 gap,用于排除训练末端与测试集之间的一定数量样本;文档也指出,等间隔采样是让各折覆盖相同时长、指标可比的前提。[2] gap 的单位是样本数,它不会自动理解一周、标签延迟或设备分组。
先逐条检查训练样本的标签在本次重训截止时点是否成熟,再检查输入在该样本原预测时点是否可见。固定间隔数据可以把这个边界换算成 gap;存在缺测、多个设备或延迟变化时,更适合按时间戳和成熟状态构造掩码。把 gap 简单设成七,不一定适用于七天目标。
过去七天特征窗口与未来七天目标窗口应分别管理。测试样本引用训练期内真实可见的历史测量,通常符合线上使用方式;不应仅因窗口共享历史就一律删除。需要排除的是未来观测、测试目标或跨边界尚未知的标签进入拟合。若目标是新设备泛化,则再增加设备隔离,不能由时间切分替代。
扩大 gap 会减少训练材料,也可能让评估模拟一个现实中不存在的陈旧模型。应说明它阻断哪一种信息路径。随机打乱时间行可作为诊断对照,不能作为本文未来预测任务的正式验收结果。
按线上节奏重放,每一步都留下可追溯记录
第一步,冻结评估起点序列、七天预测范围和评分规则。选取覆盖正常生产、停机、节假日及季节变化的历史期间,同时保留最后一段未参与方案选择的测试期。若现有历史不包含某种工况,应明确尚未验证,而不是补造代表性。
第二步,在每个周一 06:00 的模拟重训时刻,按当时可见与成熟条件选训练样本。只在这些样本上拟合缺失值填充、缩放、特征选择和模型。scikit-learn 的文档强调,预处理参数应仅从训练部分学习,并建议用 Pipeline 降低误用风险。[4] Pipeline 本身不会修复已经混入未来信息的输入表。
第三步,在每天 08:00 重建特征快照,使用最近一次已经完成且允许上线的模型。若训练按假设不能在 08:00 前结束,则应继续旧模型或记录无模型状态;不能免费赋予回测无限算力。保存模型版本、输入快照、各目标日期的预测和失败原因。
第四步,未来气温等外生变量应使用当时发布的预测版本,不能用后来观测到的真实天气替代。事先已知的日历可以使用;生产计划则要用该时刻已批准的版本。若上线依赖另一模型预测某个特征,回测也要包含这个上游预测及其误差。
第五步,等目标达到评分条件后关联真实结果。事后补齐标签用于评价,与事后补齐特征用于生成历史预测,是两回事。重复执行应能从保存的快照产生同样输入;若软件存在随机性,应记录种子、版本与运行条件,但不把固定种子当成全部可复现保证。
构造反例:三种看似合理的回测会高估收益
仍以每日 08:00 的七天预测为例。错误方案 A 从今天的数据仓库取最终产量,再按业务日期回填所有历史特征。它让 9 月 9 日的预测提前看见 10:00 才到达的数据。修正办法是使用可见时间过滤,缺失状态也照实保留。
错误方案 B 在每日测试起点重训,并使用所有业务日期早于当天的标签。它同时忽略每周重训安排和两天核对延迟。修正后,周三预测仍使用周一生成的模型,而周一训练不能纳入当时未成熟的数据;模型差异不能仅归因于算法好坏。
错误方案 C 在七天预测过程中,用已发生的第一天实际用电量继续推第二天,再将七个结果称为一次七天预测。这实际模拟了逐日获得新信息的流程。若上线一次发布七天,递归预测应使用前一步预测值;若上线每天更新预测,则按新的起点另存一组预测。
这些错误可能让复杂模型看起来优于简单基线,也可能改变候选之间的排名。本文没有测量改变幅度。实务上应先修正信息边界,再比较模型;不能从某个回测降幅反推所有历史项目都存在同样问题。
误差要按步长和业务后果报告
对第一个到第七个目标日分别报告 MAE,即对应样本绝对误差的平均值,保留千瓦时等原单位;RMSE 为均方误差的平方根,对大误差更敏感。两者都应配合样本数、日期范围和缺失处理规则。生产停机导致真实值接近零时,百分比误差可能失去解释价值。
加入同星期历史值等简单季节性基线,并在完全相同起点、相同可见数据条件下比较。训练窗口可以是逐步扩大的全部历史,也可以是固定长度滚动窗口:前者利用更多数据,后者减少旧工况影响。窗口长度和超参数只能在开发回测中选择,最终测试期不能反复参与选择。
逐日滚动的七天预测会多次预测同一个目标日。保留起点与步长,先按步长比较;若需要总指标,提前确定权重,避免同一目标被重复计入后掩盖远期表现。这些误差相互相关,不能把每条预测都当作独立样本来声称很窄的置信区间。
若业务更关心超额采购或供给不足,应由业务方规定两类偏差的代价,再计算相应损失,不把 MAE 最低直接等同于业务收益最高。不能生成预测、输入过期或晚于发布期限的日期也应单独报告覆盖率,并按约定计入流程评价。
交付回测协议,而不只是一个分数
最小交付应包含:预测与重训时间表、特征可见性规则、标签成熟规则、各折数据清单、模型及预处理版本、逐起点预测记录、基线结果和分工况误差。接受条件要事先约定,例如是否允许特定工况退化、缺失输入时走何种基线;本文不提供冒充行业标准的统一阈值。
上线前可先做影子预测,按真实时钟记录输入和结果,再与回测假设核对。如果真实数据总是晚到、重训无法按期完成,或计划版本无法追溯,应先修复数据与执行流程。这把企业数据工程和模型评估连接成一条可解释的交付链,也让后续模型更换有共同的比较基础。
参考资料
- [1] Hyndman and Athanasopoulos: Forecasting: Principles and Practice, 3rd ed., Time series cross-validation
- [2] scikit-learn: TimeSeriesSplit (stable documentation, accessed 2026-10-03)
- [3] Feast: Point-in-time joins (accessed 2026-10-03)
- [4] scikit-learn: Common pitfalls and recommended practices (accessed 2026-10-03)
