1. 当Transformer遇上时间序列:为什么需要Informer?
时间序列预测一直是工业界和学术界的热门话题。从早期的ARIMA、LSTM到现在的Transformer,模型架构在不断演进。但传统Transformer在处理长序列时存在明显短板——自注意力机制的计算复杂度随序列长度呈平方级增长(O(L²))。这意味着当我们需要预测电力负荷、股票价格这类超长序列(如1000+时间步)时,普通Transformer会变得极其低效。
这就是Informer的用武之地。作为专门为长序列时间预测设计的Transformer变体,它通过三大创新点解决了这个问题:
- 概率稀疏自注意力(ProbSparse Attention):将计算复杂度从O(L²)降到O(L log L)
- 自注意力蒸馏机制:逐层减少序列长度,降低内存消耗
- 生成式解码器:单次前向传播即可预测所有未来时间点
提示:如果你用过LSTM做时间序列预测,应该记得需要逐步递归预测。Informer的生成式解码就像"开挂"一样直接输出整个预测序列。
2. Informer架构全景解析
2.1 整体架构设计
Informer的架构看似复杂,其实可以拆解为几个关键模块:
输入序列 -> [Embedding] -> [编码器堆栈] -> [解码器堆栈] -> 输出序列编码器部分采用经典的Transformer编码器结构,但有两个重要改进:
- 用ProbSparse Attention替换标准自注意力
- 添加自注意力蒸馏层减少序列长度
解码器部分则完全重新设计,采用生成式预测方式。这是它能一次性输出长预测序列的关键。
2.2 概率稀疏注意力机制详解
这是Informer最核心的创新点。传统自注意力需要计算所有查询-键对的相关性,而ProbSparse Attention通过以下步骤实现高效计算:
- 测量查询稀疏性:对每个查询q_i,计算其与随机采样的一部分键的注意力得分的KL散度
- 选择Top-u稀疏查询:只保留最具区分度的u个查询(u = c·lnL,c为常数)
- 仅计算选定查询的注意力:大幅减少计算量
实测表明,这种采样方法能保留95%以上的注意力质量,同时将计算复杂度降至O(L log L)。
2.3 自注意力蒸馏机制
编码器中的另一个创新是自注意力蒸馏。具体实现方式:
- 在每层编码器后添加一个蒸馏操作
- 对注意力输出进行1D卷积(核大小=3,步长=2)
- 然后通过ELU激活函数
- 序列长度减半,特征维度保持不变
这种设计使得模型可以构建更深层的编码器,而不会因序列过长导致内存爆炸。
3. 手撕Informer源码关键实现
3.1 数据预处理与Embedding
Informer的输入需要特殊处理。以电力负荷预测为例:
class TokenEmbedding(nn.Module): def __init__(self, c_in, d_model): super().__init__() padding = 1 if torch.__version__>='1.5.0' else 2 self.tokenConv = nn.Conv1d( in_channels=c_in, out_channels=d_model, kernel_size=3, padding=padding, padding_mode='circular' ) def forward(self, x): x = self.tokenConv(x.transpose(1,2)).transpose(1,2) return x这里有几个关键点:
- 使用1D卷积而非线性层进行embedding
- 采用circular padding处理时间序列边界
- 输出维度统一为d_model(如512)
3.2 ProbSparse Attention实现
核心代码如下:
def prob_query_selection(query, sample_size): # query: [B, H, L, D] B, H, L, E = query.shape # 随机采样部分键 sample_ids = torch.randint(0, L, (L//sample_size,)) # 计算查询稀疏性得分 sparse_scores = query @ query[sample_ids].transpose(-2,-1) # 选择Top-u查询 top_ids = torch.topk(sparse_scores, k=u, dim=-1) return top_ids.indices def prob_attention(query, key, value): # 仅计算选定查询的注意力 selected_ids = prob_query_selection(query) selected_query = query.gather(2, selected_ids.unsqueeze(-1).expand(-1,-1,-1,E)) # 计算稀疏注意力 attn = (selected_query @ key.transpose(-2,-1)) * (1.0 / math.sqrt(E)) attn = torch.softmax(attn, dim=-1) output = attn @ value return output3.3 生成式解码器实现
解码器的独特之处在于它使用固定长度的"起始token"来生成整个预测序列:
class GenerativeDecoder(nn.Module): def __init__(self, pred_len, d_model): super().__init__() self.pred_len = pred_len self.start_tokens = nn.Parameter(torch.zeros(1, pred_len, d_model)) def forward(self, enc_out): # enc_out: [B, L, D] dec_in = self.start_tokens.expand(enc_out.size(0), -1, -1) # 多层解码器处理 for layer in self.layers: dec_out = layer(dec_in, enc_out) return dec_out这种设计使得模型可以一次性输出所有预测值,而不需要逐步递归。
4. 实战:用Informer预测电力负荷
4.1 数据准备
ETT数据集是常用的电力负荷预测基准数据集。我们需要进行以下预处理:
- 标准化:对每个特征列进行Z-score标准化
- 滑窗处理:构建输入-输出序列对
- 数据集划分:7:2:1的比例分为训练/验证/测试集
class ETDataset(Dataset): def __init__(self, data, seq_len, pred_len): self.data = data self.seq_len = seq_len self.pred_len = pred_len def __getitem__(self, index): s_begin = index s_end = s_begin + self.seq_len r_begin = s_end r_end = r_begin + self.pred_len seq_x = self.data[s_begin:s_end] seq_y = self.data[r_begin:r_end] return seq_x, seq_y4.2 模型训练技巧
训练Informer时需要注意以下几点:
- 学习率调度:使用余弦退火+热重启
- 梯度裁剪:设置max_norm=0.1
- 早停机制:验证损失连续5轮不下降时停止
- 混合精度训练:大幅减少显存占用
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4) scheduler = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts( optimizer, T_0=10, T_mult=2) scaler = torch.cuda.amp.GradScaler() for epoch in range(100): model.train() for x, y in train_loader: with torch.cuda.amp.autocast(): pred = model(x) loss = criterion(pred, y) scaler.scale(loss).backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 0.1) scaler.step(optimizer) scaler.update() scheduler.step()4.3 评估指标解读
时间序列预测常用以下指标:
- MAE(平均绝对误差):对异常值不敏感
- MSE(均方误差):强调大误差惩罚
- RMSE(均方根误差):与原始数据同量纲
- MAPE(平均绝对百分比误差):相对误差度量
在ETTh1数据集上,Informer的典型表现:
| 预测长度 | 24点 | 48点 | 168点 | 336点 |
|---|---|---|---|---|
| MAE | 0.38 | 0.42 | 0.51 | 0.63 |
| RMSE | 0.45 | 0.49 | 0.58 | 0.71 |
5. 常见问题与调优指南
5.1 训练不稳定问题
现象:损失值剧烈波动或突然变为NaN
解决方案:
- 检查输入数据标准化是否正确
- 降低学习率(尝试1e-5到1e-4范围)
- 添加梯度裁剪(norm=0.1)
- 使用更稳定的激活函数(如GELU代替ReLU)
5.2 预测结果滞后问题
现象:预测曲线与真实值形状相似但存在相位差
解决方法:
- 增加位置编码的强度
- 在解码器中添加跳跃连接
- 尝试不同的标准化方法(如实例标准化)
- 调整ProbSparse Attention中的采样率
5.3 显存不足问题
现象:GPU内存溢出,尤其是长序列场景
优化策略:
- 启用注意力蒸馏(减少层间序列长度)
- 使用混合精度训练
- 减小batch size(可配合梯度累积)
- 限制最大序列长度(如截断超过1024的序列)
6. Informer的变体与改进方向
6.1 Autoformer:自相关机制替代注意力
Autoformer提出用自相关(autocorrelation)机制替代传统注意力:
- 基于序列周期性发现重要时间延迟
- 计算复杂度进一步降低到O(L)
- 特别适合具有明显周期性的数据(如电力、交通)
6.2 FEDformer:傅里叶与小波变换结合
FEDformer的创新点:
- 在频域实现注意力计算
- 混合使用傅里叶和小波变换
- 计算复杂度O(L)
- 对突发性变化捕捉更好
6.3 自定义改进建议
根据实际项目需求,可以考虑以下改进:
- 在embedding层添加领域知识(如加入节假日特征)
- 多任务学习:同时预测多个相关序列
- 不确定性估计:输出预测区间而非单点预测
- 在线学习:适应数据分布漂移
我在实际项目中发现,将Informer与简单的业务规则结合往往能取得最佳效果。例如在电力预测中,先使用业务规则处理极端天气日,再用Informer预测常规日负荷,这样既利用了数据规律又结合了领域知识。