Appearance
Layer 1 — forecast 主链
父层(Layer 0)
forward()进入long_forecast()或short_forecast()。
本文档覆盖两条路径,重点展示它们共同的主链结构(embed → encoder → decoder → projection → slice)。
1. 在父层中的位置
Informer.forward()
├─ long_term_forecast → long_forecast(x_enc, x_mark_enc, x_dec, x_mark_dec) ← 本文档
└─ short_term_forecast → short_forecast(x_enc, x_mark_enc, x_dec, x_mark_dec) ← 本文档2. I/O 接口定义
两条路径的 I/O 完全相同:
| shape(toy) | 含义 | |
|---|---|---|
输入 x_enc | (3, 10, 6) = (B, seq_len, enc_in) | encoder 历史序列 |
输入 x_mark_enc | (3, 10, 4) | encoder 时间戳特征 |
输入 x_dec | (3, 12, 6) = (B, label_len+pred_len, enc_in) | decoder 输入(5历史+7零) |
输入 x_mark_dec | (3, 12, 4) | decoder 时间戳特征 |
| 输出 | (3, 12, 6) → 切片后 (3, 7, 6) | 完整 decoder 输出,forward() 再取后 pred_len 步 |
3. 顺序图(具体层)
4. 语义分组图(索引层)
5. 逐步解析
5.0 完整原始代码
python
# Informer.py:116-141
def long_forecast(self, x_enc, x_mark_enc, x_dec, x_mark_dec):
enc_out = self.enc_embedding(x_enc, x_mark_enc)
dec_out = self.dec_embedding(x_dec, x_mark_dec)
enc_out, attns = self.encoder(enc_out, attn_mask=None)
dec_out = self.decoder(dec_out, enc_out, x_mask=None, cross_mask=None)
return dec_out # [B, L, D]
def short_forecast(self, x_enc, x_mark_enc, x_dec, x_mark_dec):
# Normalization
mean_enc = x_enc.mean(1, keepdim=True).detach() # B x 1 x E
x_enc = x_enc - mean_enc
std_enc = torch.sqrt(
torch.var(x_enc, dim=1, keepdim=True, unbiased=False) + 1e-5
).detach() # B x 1 x E
x_enc = x_enc / std_enc
enc_out = self.enc_embedding(x_enc, x_mark_enc)
dec_out = self.dec_embedding(x_dec, x_mark_dec)
enc_out, attns = self.encoder(enc_out, attn_mask=None)
dec_out = self.decoder(dec_out, enc_out, x_mask=None, cross_mask=None)
dec_out = dec_out * std_enc + mean_enc
return dec_out # [B, L, D]5.1 long vs short 路径的唯一区别:归一化
本节的作用
long_forecast直接使用原始值,short_forecast在输入前对x_enc做均值+标准差归一化(.detach()不参与梯度),并在 decoder 输出后反归一化还原。
long_forecast(TFB long_term_forecast 走此路径):
x_enc 原始值直接进 embedding,无任何预处理short_forecast(TFB short_term_forecast 走此路径):
归一化(仅 x_enc,x_dec 不做):
mean_enc = mean(x_enc, dim=1) → (3, 1, 6) # 每条序列的时间维均值
std_enc = std(x_enc, dim=1) → (3, 1, 6) # 标准差,unbiased=False
x_enc = (x_enc - mean_enc) / std_enc # (3, 10, 6),零均值单位方差
.detach() 的原因:归一化统计量不参与梯度回传,只是预处理
反归一化(decoder 输出后):
dec_out = dec_out * std_enc + mean_enc
# std_enc / mean_enc shape 均为 (3, 1, 6),广播到 (3, 12, 6)注解版(toy 数值):
python
mean_enc = x_enc.mean(1, keepdim=True)
# x_enc[0, :, 0] = [1,2,3,4,5,6,7,8,9,10] ← 示意值
# mean_enc[0, 0, 0] = 5.5
std_enc = torch.sqrt(torch.var(x_enc, dim=1, keepdim=True, unbiased=False) + 1e-5)
# var = E[(x-μ)²] (biased),加 1e-5 防除零
# x_enc[0,:,0] 方差 ≈ 8.25,std ≈ 2.872
# std_enc[0, 0, 0] ≈ 2.872
x_enc = x_enc - mean_enc # (3,10,6)
x_enc /= std_enc # (3,10,6),每个变量独立归一化
# x_enc[0, 0, 0] = (1-5.5)/2.872 ≈ -1.5665.2 enc_embedding 和 dec_embedding
本节的作用
DataEmbedding把原始时序(B, L, c_in)+ 时间特征(B, L, 4)变成 token(B, L, d_model=8),内部三路相加:TokenEmbedding + TimeFeatureEmbedding + PositionalEmbedding。
一句话:DataEmbedding(c_in, d_model) 把原始时序 (B, L, c_in) + 时间特征 (B, L, 4) 变成 token (B, L, d_model),内部是三路相加:value + positional + temporal。
- enc_embedding:输入
(3, 10, 6)→ 输出(3, 10, 8) - dec_embedding:输入
(3, 12, 6)→ 输出(3, 12, 8)
→ 详见 03A-Layer2A-DataEmbedding
5.3 encoder — distilling + ProbSparse
本节的作用
Encoder.forward循环调用EncoderLayer×2 + ConvLayer×1,distilling 把序列从 10 压缩到 6;ProbSparse 注意力在每层只为 top-u 个 query 做全量计算,复杂度从降至 。
python
enc_out, attns = self.encoder(enc_out, attn_mask=None)
# 输入: enc_out (3, 10, 8)
# 输出: enc_out (3, 6, 8) ← 序列被 ConvLayer distilling 压缩 10→6
# attns: [None, None] (output_attention=False)一句话:Encoder.forward 循环调用 EncoderLayer×2 + ConvLayer×1,distilling 把序列从 10 压缩到 6(Conv1d→12→MaxPool→6),ProbSparse 注意力在每层只为 top-u 个 query 做全量计算。
→ 详见 03B-Layer2B-Encoder
5.4 decoder — 内嵌 projection 的三段式
本节的作用
Decoder.forward循环调用DecoderLayer×1(masked self-attention + cross-attention + FFN),最后 norm +projection = Linear(8→6)投影到c_out=6;projection 在 Decoder 内部完成,不在父层。
python
dec_out = self.decoder(dec_out, enc_out, x_mask=None, cross_mask=None)
# 输入: dec_out (3, 12, 8) = decoder embedding
# enc_out (3, 6, 8) = encoder 最终输出(cross-attention 的 K/V 来源)
# 输出: dec_out (3, 12, 6) ← decoder 内的 projection Linear(8→6) 已在内部完成一句话:Decoder.forward 循环调用 DecoderLayer×1(masked self-attention + cross-attention + FFN),最后 norm + projection 投影到 c_out=6。
→ 详见 03C-Layer2C-Decoder
5.5 forward() 的切片
本节的作用
Decoder 输出全部
dec_len=12步,只取后pred_len=7步作预测;前label_len=5步是对历史的重建,直接丢弃。
python
return dec_out[:, -self.pred_len :, :]
# dec_out: (3, 12, 6) ← decoder 的全部 12 步输出
# [:, -7:, :]: 取最后 7 步 = pred_len 步
# 返回: (3, 7, 6)图解:
dec_out: [label0 label1 label2 label3 label4 | pred0 pred1 pred2 pred3 pred4 pred5 pred6]
← 5 个历史 token 对应位置 → ← 7 个预测位置 → 取这 7 个
[:, -7:, :] 取最后 7 列5 个历史 token 对应位置的输出是 decoder 对历史的"重建",不是我们关心的预测,直接丢弃。
6. 下钻子组件
| 子组件 | 职责 | 文档 |
|---|---|---|
DataEmbedding | 原始序列 + 时间特征 → d_model token | 03A-Layer2A-DataEmbedding |
Encoder | ProbSparse 注意力 + distilling 压缩 | 03B-Layer2B-Encoder |
Decoder | masked self-attn + cross-attn + FFN + projection | 03C-Layer2C-Decoder |