时间序列预测:任务定义
看一段历史,预测接下来几个时刻的值。

重 点两个窗口
竖虚线左边是输入窗口,图上是 15 个时间步,模型只能看这一段。右边是预测窗口,图上是 5 步,要猜出这 5 个数。两个窗口开多长,都要人事先定好。
打个比方
看了最近半个月每天的气温,猜接下来五天的气温。半个月是输入窗口,五天是预测窗口。
知道更多生活里到处是时间序列
明天的用电量、下周的销量、下个月的客流、未来一小时的车流,都是按时间排好的一串数,过去的规律对未来有参考价值。
看一段历史,预测接下来几个时刻的值。

竖虚线左边是输入窗口,图上是 15 个时间步,模型只能看这一段。右边是预测窗口,图上是 5 步,要猜出这 5 个数。两个窗口开多长,都要人事先定好。
看了最近半个月每天的气温,猜接下来五天的气温。半个月是输入窗口,五天是预测窗口。
明天的用电量、下周的销量、下个月的客流、未来一小时的车流,都是按时间排好的一串数,过去的规律对未来有参考价值。
先读懂数据,再补缺、统一尺度、按时间切分。

油温三十多度,负荷才几点几。不统一尺度,数值大的那一列就会压过别的列,像把用米和用毫米记的数放在一起比。
标准化用的平均值和波动幅度,只能从训练那一份里算,再套到验证和测试上,否则就是第一节讲的全量统计泄露。
六项负荷加上油温,一共七列,油温是预测目标。
| 字段 | 意思 | 第一条记录的值 |
|---|---|---|
| HUFL | 高压有功负荷 | 5.827 |
| HULL | 高压无功负荷 | 2.009 |
| MUFL | 中压有功负荷 | 1.599 |
| MULL | 中压无功负荷 | 0.462 |
| LUFL | 低压有功负荷 | 4.203 |
| LULL | 低压无功负荷 | 1.340 |
| OT | 油温(预测目标) | 30.531 |
| 时刻 | 04:00 | 05:00 | 06:00 |
|---|---|---|---|
| 记录的油温 | 30.46 | 没记下 | 32.78 |
| 线性插值补上 | 30.46 | 31.62 | 32.78 |
数据按时间先后切成 7:1:2,前七成训练,中间一成验证,最后两成测试。时间序列不能随机打乱,打乱了就等于拿未来去预测过去。
一次报出整段未来,误差不会一步步累积。
每个数都直接从真实的历史算出来,误差不会传下去,一次算完,速度也快。
符合一步接一步的直觉,但每一步的误差都会带进下一步,越往后越不准。
传话游戏里,第一个人听错一个字,第二个人在错的基础上再听错一点,传到第十个人,意思已经面目全非。滚动预测每一步都把上一步的猜测当真,越往后越不准。
一次报出 96 个数时,模型不太顾得上这 96 个数彼此之间连不连贯。好在长序列预测里,误差不累积带来的好处通常更大。
滚动预测为什么越往后越不准?
一批数据的形状是三个数:几段、多长、几列。
| 模式 | 用哪些列 | 输入形状 | 输出形状 |
|---|---|---|---|
| 单变量 | 只用油温 | (32, 96, 1) | (32, 96, 1) |
| 多变量 | 七列全用 | (32, 96, 7) | (32, 96, 7) |
多变量给了模型更多线索,也带进了更多噪声和参数。用电负荷对预测油温有没有帮助,要在验证集上实际比一比才知道。
时间序列的程序里常把这三个数写成 B、L、C,分别是批大小(Batch)、序列长度(Length)和通道数(Channel)。读程序时看到这三个字母,就知道说的是哪一个数。
拆成趋势和周期两部分,各用线性模型预测再相加。

历史数据先拆成两部分:总体趋势和周期波动。两部分各交给一个线性模型去预测,最后把两个预测加起来。线性模型给每个输入配一个权重再加起来,和逻辑回归里加权求和的那一步一样。
一年的气温,可以看成一条慢慢升上去又降下来的季节走势,加上每天白天热、夜里凉的起伏。分开来看,两部分都比原来那条线好猜。
DLinear 出自 2023 年发表的一篇论文。这个只做加权求和的小模型,在好几个长序列预测数据集上,效果超过了当时不少复杂得多的模型。
移动平均提出趋势,原序列减去趋势就是周期波动。
股票软件上的“均线”就是移动平均。天天上下跳的股价,取 20 天平均画出来,就变成一条平滑的线,看得出是在涨还是在跌。
线性层的权重一开始都设成九十六分之一,相当于先拿历史的平均值当预测。从这个稳当的起点出发,开头几轮不会报出奇怪的数。
清零、前向、算损失、反向传播、更新,一批一批地转。
做练习时先擦干净草稿纸,再做题、对答案、找错在哪一步、改正。擦草稿纸就是梯度清零,漏了这一步,上一题的草稿会搅乱这一题。
五步里,哪一步改动了模型的参数?
每练一轮就在验证集上考一次,考不好了就停。

竖虚线左边是模型看到的 96 步历史,右边是它预测的 96 步。实线是真实值,虚线是预测值,两条线贴得越紧,预测越准。这张图画的是其中一列负荷 HUFL。
每一轮开始前,把训练样本的先后顺序打乱,再分批送进去。要是总按时间先后喂,连着好几批都是夏天的样本,参数先被拉向夏天,再被拉向冬天,来回摇摆。打乱的是各段样本的先后,每一段内部的时间顺序不动。
验证误差连续几轮不降就停下,留最好的那一版。
训练误差一直在降,说明模型越来越熟悉训练数据。验证误差先降后升,说明从某一轮起它开始死记训练数据里的细节,对新数据反而不准了,这就是过拟合。早停就停在过拟合开始的地方。
实验二里同一份倒立摆程序,只换一个随机种子,练出来的成绩能差好几倍,有的一次涨上去又掉回来。训练结果有起伏,所以要边练边检查,把表现最好的那一版存下来。
五块程序,从读数据一直做到算出成绩。
model = Model(args).to(device) # 这一行可以换成别的模型 model.load_state_dict(best_checkpoint) # 读回最好的一版 mse, mae = compute_metrics(preds, trues)
整条流水线和具体用哪个模型无关。把 DLinear 换成别的时间序列模型,只要改建模型那一行,读数据、训练、早停、测试都不用动。
早停后要把“最好的一版”读回来,原因是?