Appearance
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, attnsAutoformer 的 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两步操作:
x_hat = LayerNorm(x):标准 LayerNorm,对d_model=8维归一化,输出(2,12,8)- 减去时间均值
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. 下钻子组件
| 子组件 | 职责 | 下层文档 |
|---|---|---|
EncoderLayer | AutoCorrelation self-attn + 2×decomp + Conv1d FFN | [[03B1-Layer3-EncoderLayer]] |