1. 为什么是Informer:先看看长序列预测到底难在哪
很多人第一次接触时间序列预测,都是从LSTM或者普通Transformer入手的。我也不例外,最早用Transformer做长序列预测的时候,被计算复杂度折磨得够呛。标准的自注意力机制需要对序列中的每一个位置两两计算相关性,也就是说注意力得分矩阵的规模是序列长度的平方,即O(L²)。如果输入是1000个时间步,那就要计算100万个两两关系,显存占用和推理延迟都很难接受。更麻烦的是,在能源、气象、金融这类场景里,我们往往需要基于过去几百甚至上千个时间步,去预测未来同样长度的窗口。训练一次模型,GPU占用高得离谱,迭代一次要等很久,根本谈不上"轻松上手"。
后来我接触到Informer,才意识到这个方向是可以被重新设计的。Informer的出发点就是解决Transformer在长序列预测上的两个核心瓶颈:一个是自注意力机制的计算复杂度太高,另一个是传统Transformer的解码方式不适合一次生成足够长的预测序列。它通过ProbSparse自注意力机制把复杂度从O(L²)降到了O(L·logL),通过自注意力蒸馏进一步压缩特征,再通过生成式解码器实现一步到位的长序列输出。简单说,别人还在用笨办法两两比较所有位置的时候,Informer已经在用一套更聪明的筛选逻辑,只让真正信息量大的位置参与注意力计算,结果又快又准。
如果你手头有类似的任务——比如用电负荷预测、交通流量预测、设备温度趋势预测——并且输入输出窗口都偏长,那Informer基本就是为这个场景量身定做的。这篇博文里,我会从原理讲到实战,完整走一遍数据准备、模型搭建、训练和调优的流程,也会把我在实际项目里踩过的坑一并列出来。不管你是刚接触时间序列预测的新手,还是已经在用传统Transformer但想提升效率的开发者,都可以照着这篇内容上手试试。
2. 站在Transformer肩膀上:Informer到底改了什么
2.1 ProbSparse自注意力:不是所有时间步都值得被关注
在动手写代码之前,我建议先把Informer的核心机制吃透。因为后面很多调试问题,追根溯源都会回到这几个设计选择上。
标准自注意力先计算Query和Key的点积,再做缩放和Softmax,得到每个位置对所有位置的注意力权重。这套机制在自然语言处理里表现很好,因为句子里的词与词之间确实存在丰富且多样的依赖关系。但时间序列不一样——尤其是周期性很强的序列,比如每天的用电曲线、一周内的交通流量曲线,大量时间步之间的相关性是高度重复的。一个中午12点的数据点和昨天中午12点的数据点有强相关性,这很正常,但和前天凌晨3点的数据点也有强相关性吗?大概率没有。换句话说,标准的注意力矩阵里有一大批计算是"陪跑"的,值不值得算,其实一开始就可以判断。
Informer提出了一个叫ProbSparse自注意力的替代方案。它的做法是:先用一个近似公式快速评估每个Query的注意力分布是否足够有区分度,然后只保留最有区分度的若干个Query参与完整注意力计算。信息量低的Query对应的行,直接从注意力矩阵中剔除。这样计算量大幅下降,而且论文里还证明了这种稀疏化在近似误差上是可控的。实际用下来,效果不仅没变差,反而在长序列场景下比完整注意力更稳,因为它天然具备一定的平滑和去噪作用。
2.2 自注意力蒸馏:把序列一层一层浓缩
就算注意力计算已经稀疏化了,对于特别长的输入序列,网络层数一深,中间特征图的尺寸依然很大。Informer在这里加了一个蒸馏模块,类似CNN里的池化操作:每经过一层Encoder,就对特征序列做一次下采样,把序列长度压到原来的一半。蒸馏使用的是带MaxPool的一维卷积加激活函数,实验参数通常把distil开关打开,每层Encoder输出的长度就会按倍数递减。
这个设计对显存非常友好。当输入长度是1000时,第一层Encoder可能还在处理1000个位置的特征,第二层就只剩500,第三层250。越往后的层,计算量越小,但学到的都是高层语义信息。这也意味着Encoder层数不宜过多,一般2到3层就足够,堆太多反而会把序列压得过短,丢失细节。
2.3 生成式解码器:一次前向直接输出预测窗口
传统Transformer在做预测时,解码阶段是逐字(或逐步)生成的——预测出t+1步,再把它拼到输入里预测t+2步。这种方式在训练和推理时都存在串行依赖,速度慢且误差会累积。
Informer换了一个思路。它把解码器的输入设计成两部分拼接:一部分是从输入序列尾部截取的一段已知序列(称为"开始标记",start token),另一部分是待预测的未知序列,用0填充占位。解码时一次性前向计算,整个预测窗口同时输出。因为解码器内部还是有自注意力,那些0占位的位置会在注意力机制的帮助下逐步和真实历史信息交互,最终生成合理的预测值。这样做的好处是推理速度极快,而且避免了逐步生成带来的误差累积。
不过这也带来了一个要求:训练时和预测时,解码器输入的结构必须保持一致。很多人在复现时踩坑,就是因为预测阶段直接只给了一个零张量,没有在开头拼上历史片段,导致预测结果明显偏离训练分布。
3. 环境准备与最小复现:从拉代码到跑通第一个模型
3.1 环境依赖和数据集选择
我是在一台没有独立显卡的服务器上先把流程跑通的,后来又换到单卡GPU上训练。坦白讲,Informer在CPU上也能训练小数据集,就是速度慢一些,但不妨碍你理解整个流程。建议的环境版本大致如下:
- Python 3.8以上,实测3.9和3.10都没问题
- PyTorch 1.10以上,2.0版本也兼容
- NumPy 1.21及以上
- Pandas做数据读取和预处理
- Matplotlib用于可视化预测结果
数据集方面,最常用的是ETT(Electricity Transformer Temperature)系列,包含电力变压器的油温、负载等记录,按时间粒度分为15分钟、1小时等不同版本。此外还有Exchange-Rate汇率数据集和ILI流感病例数据集,都能从公开渠道下载。我自己的项目里还用过某跨平台系统的服务器CPU温度监控数据,格式上其实大同小异——都是一个时间列加若干特征列。
3.2 数据格式与标签划分
Informer的官方实现里,数据格式要求是CSV,第一列是日期时间,后面的列是数值特征。以ETTh1为例,它有7个特征列,分别是油温、负载以及6个外部载荷特征。这里要注意,时间列本身不会直接进入模型,它只是用来对齐和画图的。
分割比例上,默认常见的是0.7训练、0.1验证、0.2测试。对于长序列数据,这种比例基本够用。你也可以根据自己的数据量调整,如果数据量大可以适当增加验证集比例,如果数据量小则要保证测试集尽可能覆盖完整周期。我的经验是,测试集至少要包含两个以上完整业务周期(比如一周数据做预测,测试集至少两周),否则评估结果会有很大随机性。
3.3 跑通官方Demo
先找到官方的main_informer.py入口脚本,把--data参数指向你下载好的ETTh1.csv,然后设置一组最小参数,比如:
python -u main_informer.py \ --model informer \ --data ETTh1 \ --features M \ --seq_len 96 \ --label_len 48 \ --pred_len 24 \ --e_layers 2 \ --d_layers 1 \ --attn prob \ --d_model 512 \ --n_heads 8 \ --d_ff 2048 \ --dropout 0.05 \ --batch_size 32 \ --learning_rate 0.0001 \ --loss mse \ --train_epochs 10 \ --gpu 0这里几个关键参数我先解释一下。seq_len是输入窗口长度,label_len是解码器开始标记的长度,pred_len是输出窗口长度。也就是说,模型会看到最近96步的历史数据,然后把最后48步作为开始的提示信息,一次性预测出未来24步的数值。attn设置为prob表示启用ProbSparse注意力,如果设为full就是普通的完整注意力,方便做对比实验。
我第一次跑的时候,模型loss下降得很快,前三轮训练就能看到验证集loss在同步下降。一个epoch在单卡V100上跑ETTh1大概需要几十秒,10个epoch也就是几分钟的事情。跑完之后,程序会在results/目录下生成预测结果和真实值对比的图表,同时把MSE和MAE打印出来。看到那张预测曲线和真实曲线几乎重合的图,整个流程就算跑通了。
4. 训练过程中的常见问题:我踩过的坑和排查思路
流程跑通只是第一步,真正让人头疼的往往是训练过程中出现的各种"不对劲"。下面这几个问题,是复现Informer时最容易遇到的。
4.1 学习率设置不当导致loss震荡
如果你一上来就把学习率设成0.001甚至更高,很容易看到loss在前几轮下降之后开始剧烈震荡,验证集loss忽高忽低。Informer虽然整体训练比较稳定,但它对学习率依然敏感。
我个人的经验是先设成0.0001,如果loss下降太平缓,再适当调高到0.0005,同时配合StepLR或者CosineAnnealing做周期衰减。相比之下,直接用Adam默认参数也算稳定,但想要省时间,可以先跑3个epoch看loss曲线趋势再决定。
4.2 蒸馏模块带来的序列长度对齐问题
自注意力蒸馏虽然效果好,但也带来了一个隐性约束:Encoder每一层输出序列长度都会减半,那么最终传给解码器的特征序列长度必须和解码器输入长度能对齐,否则在Concat之后做全连接投影时纬度会不匹配。
我遇到过的典型报错是size mismatch,排查方式很简单:不用去看代码内部细节,直接把distil设为False对比一下能否跑通。如果关闭蒸馏后能正常训练,说明问题出在蒸馏导致的长度变化上。这时候要么修改蒸馏层数,要么调整seq_len让它能被2整除多次。比如seq_len=96,经过两层蒸馏后变成24,再经过一层就直接变成12了。所以你要确保Encoder最终输出长度,能够和支持的预测长度匹配起来。
提示:如果你用的是官方仓库原版代码,一般这些长度匹配问题在参数合理的情况下不会出现。但一旦你改了数据集的序列长度,或者增删了Encoder层数,就要格外注意这个对齐问题。
4.3 概率稀疏注意力在短序列上的"退化"
我一直强调Informer是为长序列设计的,如果你输入特别短,比如seq_len=24,ProbSparse机制的效果就会打折扣。因为序列长度太短时,本来就是几十个位置,再怎么稀疏化,筛选出来的Query数量也有限,优势发挥不出来,可能还不如直接用完整注意力。
从算法原理上也好理解:稀疏化是从"冗余"中提炼信息。序列短,本身就不冗余,强行筛选反而会丢掉有用信息。所以我会给一个建议:用Informer做短序列预测不是不行,但只有在序列长度明显偏长时才推荐使用ProbSparse。如果做短序列,用标准的Transformer或者LSTM可能更省事。
5. 数据预处理与工程化的几个细节心得
很多人训练效果不如预期,其实问题不在模型,而在数据喂进模型之前的那几步。
5.1 归一化:必须只用训练集统计量
时间序列的特征值往往跨度极大。拿用电负荷来说,冬夏两季的峰值可能相差一倍以上;拿油温来说,昼夜温差会让数值波动明显。如果不对数据做归一化,模型训练会非常不稳定,梯度更新也会被大数值特征带偏。
Informer官方代码默认使用Z-Score归一化,即减均值再除以标准差。这里有一个非常关键的工程细节:均值和标准差只能从训练集上统计,不能拿全量数据去算。如果用了全量数据的统计量,等于把测试集的信息偷偷漏进了训练过程,测试指标的"含金量"就没了。我之前在做一个模拟项目X的时候,因为图省事直接对整个CSV做了scaler.fit_transform,结果测试MSE漂亮得不对劲,后来重做数据分割才意识到问题在哪。
5.2 滑动窗口构建样本:注意步长不要太大
Informer训练时会用滑动窗口从原始时间序列上截取输入输出对。seq_len决定了每次截取多长历史,pred_len决定了截取多长未来。步长如果设得过大,样本数量会减少;如果设得过小,相邻样本高度重叠,模型的泛化能力会受到一定影响,还可能让单次epoch的时间变长。
官方代码里通过train_epochs和batch_size来间接控制数据吞吐,内部实现会在数据加载时根据设置自动切分。我自己在自定义数据集上会额外控制滑动窗口的步长,确保训练集样本数在一万到十万之间,太少容易欠拟合,太多训练太慢。
5.3 缺失值和异常值的处理:不要随便插值
工业场景里的传感器数据经常有掉线情况,漏采、重复采都很常见。处理缺失值的时候,我一般会先看缺失比例。如果缺失比例小于5%,线性插值一般是够用的;如果超过10%,就要认真想想是不是传感器本身有问题了。异常值方面,我比较推荐基于分位数+业务阈值来筛,比如超过历史99分位数的值直接标记为异常并做削峰处理,而不是用3σ法则——因为时间序列的分布往往不是高斯分布,3σ容易误杀。
5.4 周期对齐:预测之前先看业务周期
很多人在数据预处理阶段就忽略了一个问题:时间序列预测到底在预测什么?如果一条序列有明显的日周期和周周期,但你的seq_len不足以覆盖一个完整周期,模型就缺少参考信息。比如周周期是7天,如果你的数据是小时粒度,那一个完整周期是168个时间步。seq_len设置到168以上,模型才能"看到"上周同一时刻的趋势。
这也是为什么官方Demo里seq_len经常用96或168而不是随便取一个数。在做自己的数据集时,建议先画出原始序列的自相关图,观察几个显著的滞后点,再确定seq_len。如果自相关图里没有明显周期,说明序列更接近随机游走或者强趋势性序列,这时候Informer的优势也发挥不出来,可能改用其他模型更合适。
6. 从Demo到业务落地:模型选型对比与推理部署
6.1 和LSTM、普通Transformer相比,Informer赢在哪
我做过一个比较实验:同样的ETTh1数据集,同样的输入输出窗口,分别用两层LSTM、标准Transformer、Informer训练相同轮次。结果很有意思。
LSTM训练速度最快,参数少,在小样本上甚至能超过Transformer,但一旦预测长度加大到48步以上,它的长期记忆能力就跟不上了,误差累积非常明显。标准Transformer在预测精度上不比Informer差太多,但训练和推理时的显存占用和耗时大约是Informer的3到5倍,因为它的注意力复杂度是平方级的。Informer在预测长度96步这个设定下,MSE大约是标准Transformer的0.8倍左右,但训练速度却明显更快。用一张非旗舰级GPU就能把长时间序列跑起来,这是它最实用的价值。
6.2 推理阶段的缓存与批处理
模型训练完成后,真正上线时往往需要考虑推理性能。Informer的生成式解码器是"一步到位"的,所以推理时不需要循环生成,这一点非常友好。你只需要保证输入格式和训练时一致,即做一个长度为seq_len的窗口,然后模型直接输出pred_len长度的预测。
如果你要部署到CPU上,建议在导出模型时用torch.jit.trace做一次静态化,这样能省掉不少动态图分支的开销。不过要注意,Informer里存在一些动态控制流(比如稀疏注意力中对Query的采样排序),做trace的时候要确保输入张量的形状完全固定,否则可能trace失败或者结果不对。
6.3 多步递推预测与窗口滚动更新
业务中还有一种常见需求:模型一次只预测未来一小时,但业务需要未来一天的曲线。这时候不需要重新训练模型,只需要做一个滚动预测的循环:把模型输出拼接到输入窗口末尾,去掉最前面的旧数据,形成一个新窗口,再喂给模型,得到下一个时间段的结果。这样做虽然会引入部分误差累积,但因为Informer的输入窗口足够长,短期滚动误差通常可控。我实际测试过,96步输入、24步输出,滚动4次预测未来96步,精度比直接训练一个96步输出的模型要差一些,但比LSTM强很多。
7. 可视化与结果解读:别只盯着MSE看
训练完成后,仅仅看一个MSE数字是不够的。MSE是一个全局指标,它会把峰值预测误差和平常时段的误差混在一起。我通常会做下面几种可视化来辅助评估。
第一,画完整时间段的预测曲线vs真实曲线。注意观察预测曲线是否有滞后现象,即预测峰和真实峰错开了一两个时间步。如果滞后明显,说明窗口长度或模型容量可能需要调整。
第二,画残差分布直方图和残差自相关图。如果残差还有明显的自相关,说明模型没有学到序列中的某些动态规律,通常的表现是残差在预测误差较大的时段呈现正相关聚集。
第三,分时段统计误差,比如把一天分成24个小时,分别看每个小时的MAE。这能帮你发现模型在哪些时段表现差。我自己的经验是,换季和极端天气时段往往是误差最大的地方,因为这些时段的序列模式和训练集里的模式差异太大,模型很难外推。这也是时间序列预测的固有难点,不要指望模型在分布外场景依然保持精度。
8. 轻量化尝试:我对配置参数的调优心得
Informer的参数空间看起来不大,实际上调一调差别很明显。我试着把几个重要参数的个人经验整理出来。
d_model和d_ff决定了模型容量。d_model从512降到256,显存会明显下降,训练速度更快,但在复杂序列上精度会下降几个百分点。如果数据量不大,建议先用d_model=128或d_model=256快速验证流程,确认有效后再往上加。没必要一上来就开512,那是刷论文榜单的配置,不是把模型投入业务的首选。n_heads我一般保持8,改小到4会损失一些表达能力,改大到16在小数据集上容易过拟合。
dropout的值要跟着数据量走。数据只有几千条时,dropout设置到0.1到0.2之间可以防止过拟合。数据量大到十万条以上,dropout可以适当降回0.05,因为这时候模型不容易过拟合,反而需要更强的拟合能力来捕捉细节。
factor是ProbSparse注意力里控制稀疏度的一个参数,官方默认是5。调小到3,注意力计算更少,速度更快,但信息丢失可能增多。调大到7,计算稍慢,但保留的信息更多。这个参数可以在验证集上小范围搜索,一般1到2个点就能确定最优值。
我自己在某个制造业数据项目的实践中发现,factor=5配合dropout=0.05在大多数数据集上是稳的。如果时间充裕,可以做一个简单的网格搜索,参数范围不必太大,每组参数跑两三遍取平均,选出验证集上表现最稳的组合。
9. 复现Informer时我建议你准备的一些排查工具
在实际复现过程中,除了模型代码本身,我还建议你准备几个辅助工具。
第一,TensorBoard或者简单的loss曲线记录脚本。不用做得很复杂,只需要记录每个epoch的训练loss和验证loss,画在同一个图里。判断模型是否过拟合,看这两条曲线的走势比看任何数字都直观。
第二,一个可以快速绘制预测对比图的脚本。每次调参后,都应当抽几张图出来看,而不是只停留在终端打印的数值上。
第三,保存最优模型和对应参数的文件。建议在训练脚本里加上EarlyStopping,如果验证集loss连续多少轮不降,就停止训练并保存当前最优权重。这在长时间训练时非常有用。
官方仓库里本身带有utils模块,里面有不少常用的工具函数,包括时间序列数据的归一化、滑动窗口采样、数据加载器和绘图函数,直接拿来改比自己造轮子省力得多。把官方工具读一遍,能省下一天以上的前期工作量。
10. 关于Informer的局限性和替代方案,我的一些实话
Informer在长序列预测上确实是一个好选择,但它也不是万能的。诚实地说,它更适合周期性较强、规律较明显的序列。如果你的数据几乎没有周期,或者噪音占比非常高,任何基于历史模式的模型都很难做好,这时候与其执着于换模型,不如先花时间梳理业务特征,看看是不是缺了外部变量。
还有一点是,Informer对训练数据量有一定要求。数据太少(比如只有几百条),训练起来容易过拟合,效果往往不如简单模型。作为一个经验原则,样本量至少在seq_len的十倍以上,模型才有可能学到有意义的模式。当然这也不是绝对,如果你的预测窗口很短、任务本身比较简单,小样本也能有不错的结果。
另外,近几年时间序列领域也不断出现新思路,比如基于路径依赖的分解结构、基于随机过程的深度混合模型等等。但是Informer在"中长序列预测+中等训练资源"这个区间里的性价比,依然是很高的。作为入门长序列预测的第一个模型,Informer能帮你把数据流程、评估体系、调参逻辑完整跑一遍,其价值远超过刷一个漂亮分数。我最后再分享一条个人经验:不要一直盯着模型架构调来调去,先把数据管好、评估做扎实,模型的潜力才能真正发挥出来。