Skip to content

Layer 2B — Encoder 精读

forecast() 主链([[02-Layer1-forecast主链]])调用:self.encoder(enc_out, attn_mask=None)
本层覆盖 Encoder 循环(e_layers=2 个 EncoderLayer)+ my_Layernorm


1. 在父层中的位置

forecast()
  └─ self.encoder(enc_out, attn_mask=None)   ← 本文档
       ├─ EncoderLayer[0](x)                → 详见 03B1-Layer3-EncoderLayer
       ├─ EncoderLayer[1](x)                → 详见 03B1-Layer3-EncoderLayer
       └─ my_Layernorm(x)

与 Informer Encoder 的关键区别:无 ConvLayer distilling,序列长度在 Encoder 中保持不变。


2. I/O 接口定义

shape含义
输入 x(2, 12, 8)enc_embedding 输出
输出(2, 12, 8)编码后的表示(形状不变)

3. 顺序图(具体层)


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


5. 逐步精读

5.0 完整原始代码

python
class Encoder(nn.Module):
    def __init__(self, attn_layers, conv_layers=None, norm_layer=None):
        super(Encoder, self).__init__()
        self.attn_layers = nn.ModuleList(attn_layers)
        self.conv_layers = (
            nn.ModuleList(conv_layers) if conv_layers is not None else None
        )
        self.norm = norm_layer

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

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

        return x, attns

Autoformer 的 Encoder 初始化时不传 conv_layers(为 None),因此走 else 分支——纯循环 EncoderLayer,无 ConvLayer distilling。


5.1 EncoderLayer 循环

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

2 个 EncoderLayer 串联:第 0 层输出作为第 1 层输入,形状均保持 (2, 12, 8)

→ EncoderLayer 内部(AutoCorrelation + 2×decomp + FFN)详见 [[03B1-Layer3-EncoderLayer]]


5.2 my_Layernorm

python
class my_Layernorm(nn.Module):
    def __init__(self, channels):
        super(my_Layernorm, self).__init__()
        self.layernorm = nn.LayerNorm(channels)

    def forward(self, x):
        x_hat = self.layernorm(x)
        bias = torch.mean(x_hat, dim=1).unsqueeze(1).repeat(1, x.shape[1], 1)
        return x_hat - bias

两步操作

  1. x_hat = LayerNorm(x):标准 LayerNorm,对 d_model=8 维归一化,输出 (2,12,8)
  2. 减去时间均值 bias
    • torch.mean(x_hat, dim=1) → 对 L=12 维求均值 → (2, 8)
    • .unsqueeze(1)(2, 1, 8)
    • .repeat(1, 12, 1)(2, 12, 8)
    • x_hat - bias(2, 12, 8)

为什么要减去时间均值?

my_Layernorm 的设计意图

标准 LayerNorm 对每个 token 的特征向量做归一化,但不消除跨时间步的趋势。减去时间均值相当于"去均值"操作:让每个特征维度在时间轴上的均值为零。

这与 Encoder 输出的语义契合:EncoderLayer 每次 decomp 都剥离趋势、只保留 seasonal,最终输出已经是"去趋势"的表示。my_Layernorm 在最后一步强化这个约束,防止残留的 DC 偏置(均值非零分量)干扰 cross-attention。

操作效果
标准 LayerNorm对每个 token 的 d_model 维归一化
my_Layernorm额外让每个特征维度在时间轴上均值=0

toy 数值(batch=0, feature=0):假设 LayerNorm 后 x_hat[0,:,0] = [0.4, -0.2, 0.8, -0.6, 0.3, -0.1, 0.5, -0.3, 0.7, -0.5, 0.6, -0.4]
均值 = 0.1,bias[0,:,0] = 0.1(复制12次),
输出 [0.3, -0.3, 0.7, -0.7, 0.2, -0.2, 0.4, -0.4, 0.6, -0.6, 0.5, -0.5],均值=0 ✓


6. 下钻子组件

子组件职责下层文档
EncoderLayerAutoCorrelation self-attn + 2×decomp + Conv1d FFN[[03B1-Layer3-EncoderLayer]]

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