Skip to content

Layer 2B — Encoder 精读

父层(Layer 1)forecast() 的第⑤步调用 self.encoder(enc_out)
本文档只覆盖 Encoder.forward 这一层的逻辑(循环调度 + 最终 LayerNorm)。
子层 EncoderLayer 及以下见 04A-Layer3-EncoderLayer


1. 在父层中的位置

forecast()
  ├─ ④ patch_embedding(x_enc) → enc_out (8, 6, 16)
  └─ ⑤ enc_out, attns = self.encoder(enc_out)   ← 本文档
       └─ for attn_layer in attn_layers:
              x, attn = attn_layer(x, ...)       → 详见 Layer3 EncoderLayer
       └─ self.norm(x)

2. I/O 接口定义

python
def forward(self, x, attn_mask=None, tau=None, delta=None):
shape(toy)含义
输入 x(8, 6, 16) = (B*C, patch_num, d_model)PatchEmbedding 输出的 patch token
输出 x(8, 6, 16)Transformer 处理后的 patch 表示,形状不变
输出 attns[None]注意力权重列表;output_attention=False 时每层返回 None

attn_mask / tau / delta 接收但在 PatchTST 中全部为 None,原样透传给子层。


3. 顺序图(具体层)


4. 语义分组图(索引层)

Encoder 只做"管理",不做任何特征计算。三件事:选路径 → ==迭代调度== → 收尾归一化。


5. 逐步解析

5.0 完整原始代码

python
def forward(self, x, attn_mask=None, tau=None, delta=None):
    attns = []
    if self.conv_layers is not None:
        for i, (attn_layer, conv_layer) in enumerate(
            zip(self.attn_layers, self.conv_layers)
        ):
            delta = delta if i == 0 else None
            x, attn = attn_layer(x, attn_mask=attn_mask, tau=tau, delta=delta)
            x = conv_layer(x)
            attns.append(attn)
        x, attn = self.attn_layers[-1](x, tau=tau, delta=None)
        attns.append(attn)
    else:
        for attn_layer in self.attn_layers:
            x, attn = attn_layer(x, attn_mask=attn_mask, tau=tau, delta=delta)
            attns.append(attn)

    if self.norm is not None:
        x = self.norm(x)
    return x, attns

整体结构:分支路由决定走哪条迭代路径,串联完所有 EncoderLayer 后做一次收尾 LayerNorm。


5.1 分支路由

本节的作用

根据 conv_layers 是否存在,选择纯串联(PatchTST)还是交替蒸馏(Informer)两条路径。

步骤一 — if/else 分支判断

python
attns = []
if self.conv_layers is not None:
    ...
else:
    for attn_layer in self.attn_layers:
        x, attn = attn_layer(x, attn_mask=attn_mask, tau=tau, delta=delta)
        attns.append(attn)

if 路径(distilling,Informer)

attn_layers:  [EncoderLayer_0, EncoderLayer_1, EncoderLayer_2, ...]
conv_layers:  [ConvLayer_0,    ConvLayer_1,    ...]

step 0: x → EncoderLayer_0 → ConvLayer_0 → x(seq_len 缩短)
step 1: x → EncoderLayer_1 → ConvLayer_1 → x(再缩短)
...
最后一步: x → attn_layers[-1](无 conv)→ x

每个 ConvLayer 做 MaxPool 压缩序列长度,实现 Informer 的 distilling。

else 路径(纯串联,PatchTST)

attn_layers: [EncoderLayer_0]  (e_layers=1 时只有一个)

step 0: x (8,6,16) → EncoderLayer_0 → x (8,6,16)

只有 attn_layer,无 conv_layer,形状始终不变。PatchTST 不做序列压缩——patch 数量从头到尾保持 6。

分支条件迭代逻辑适用模型
if(distilling)conv_layers is not Noneattn + conv 交替;最后再单跑 attn_layers[-1]Informer
else(简单串联)conv_layers is NonePatchTST只有 attn_layer,纯串联,形状不变PatchTST

toy 追踪(PatchTST,e_layers=1):self.conv_layers = None → 走 else,循环体执行 1 次,attns[] 变为 [None]


5.2 循环调度

本节的作用

将输入 x 依次传过每个 EncoderLayer,前一层输出作为后一层输入,实现 Transformer 的深度堆叠。

步骤二 — 串联调度 EncoderLayer

python
for attn_layer in self.attn_layers:
    x, attn = attn_layer(x, attn_mask=attn_mask, tau=tau, delta=delta)
    attns.append(attn)

self.attn_layersnn.ModuleList([EncoderLayer_0]),toy 里 e_layers=1 只循环一次。每次把当前 x 传给 EncoderLayer,返回的新 x 就地覆盖,作为下一层输入(若有下一层)。

e_layers=2,循环两次,第二层的输入是第一层的输出,形状始终维持 (8, 6, 16) 不变。

toy 追踪:x (8,6,16)EncoderLayer_0x (8,6,16)attn=None,循环后 attns = [None]

attns 是列表而非单个 tensor

每层返回各自的注意力权重并 append,使调用方可以访问任意层的权重。output_attention=False 时每次 append 的是 None,不占内存,但列表结构保持不变,方便切换 output_attention=True 时直接查任意层。


5.3 最终归一化

本节的作用

对所有 EncoderLayer 跑完后的输出做一次整体 LayerNorm,稳定深层堆叠后的 token 分布。

步骤三 — 收尾 LayerNorm + return

python
if self.norm is not None:
    x = self.norm(x)
return x, attns

self.norm = LayerNorm(16),对最后一维逐 token 归一化,形状不变 (8, 6, 16)

Encoder 级归一化 vs EncoderLayer 内部归一化

04A-Layer3-EncoderLayer 内部有 norm1norm2 两个归一化——那是每个 Transformer block 内部的 Post-norm,负责每步残差后的局部稳定。

这里的 self.norm整个 Encoder 的最终归一化,是在所有 block 串联完毕后才执行的收尾操作。两者层级不同,作用互补:block 内归一化控制每步梯度流,Encoder 级归一化在 Transformer 栈顶做最后一次分布对齐,让后续 projection 层接收到分布稳定的输入。

toy 追踪:x (8,6,16)LayerNorm(16)x (8,6,16)attns = [None]return (8,6,16), [None]


6. 下钻子组件

子组件职责下层文档
EncoderLayerattn_layerTransformer block:注意力残差 + FFN 残差 + 2×LayerNorm04A-Layer3-EncoderLayer

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