Skip to content

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.566

5.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 做全量计算,复杂度从 O(L2) 降至 O(LlogL)

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 token03A-Layer2A-DataEmbedding
EncoderProbSparse 注意力 + distilling 压缩03B-Layer2B-Encoder
Decodermasked self-attn + cross-attn + FFN + projection03C-Layer2C-Decoder

*记录并在线阅读我的笔记*