Appearance
Layer 2C — Decoder(调度层)
父层(Layer 1)步骤⑤:
decoder(dec_out, enc_out, x_mask=None, cross_mask=None)。
本文档只覆盖Decoder.forward这一层(循环调度 + LayerNorm + projection)。
子层 DecoderLayer 及以下见 03C1-Layer3-DecoderLayer。
1. 在父层中的位置
long_forecast()
├─ ④ encoder(enc_out) → (3,6,8)
└─ ⑤ dec_out = self.decoder(dec_out, enc_out) ← 本文档
├─ DecoderLayer × d_layers=1 → 详见 Layer3
├─ LayerNorm(8)
└─ projection Linear(8→6)
→ (3, 12, 6)2. I/O 接口定义
python
def forward(self, x, cross, x_mask=None, cross_mask=None, tau=None, delta=None):| shape(toy) | 含义 | |
|---|---|---|
输入 x(dec_out) | (3, 12, 8) = (B, dec_len, d_model) | decoder embedding 输出,含 label_len+pred_len |
输入 cross(enc_out) | (3, 6, 8) = (B, enc_seq, d_model) | encoder 最终输出(distilling 后 seq=6) |
| 输出 | (3, 12, 6) | projection 后的完整 decoder 输出;long_forecast() 再取后 pred_len=7 步 |
x_mask / cross_mask / tau / delta全为None,原样透传给 DecoderLayer。
3. 顺序图(具体层)
4. 语义分组图(索引层)
Decoder 只做"管理":迭代调度 DecoderLayer → 收尾 LayerNorm → 投影到输出维度。
5. 逐步解析
5.0 完整原始代码
python
def forward(self, x, cross, x_mask=None, cross_mask=None, tau=None, delta=None):
for layer in self.layers:
x = layer(x, cross, x_mask=x_mask, cross_mask=cross_mask,
tau=tau, delta=delta)
if self.norm is not None:
x = self.norm(x)
if self.projection is not None:
x = self.projection(x)
return x5.1 循环调度
本节的作用
循环调用
d_layers=1个 DecoderLayer,每次把 decoder tokenx和固定的 encoder 输出cross一起传入,返回的新x携带历史信息。
python
for layer in self.layers:
x = layer(x, cross, x_mask=x_mask, cross_mask=cross_mask,
tau=tau, delta=delta)self.layers 是 nn.ModuleList([DecoderLayer_0]),toy 里 d_layers=1 只循环一次。每次把 x(decoder token)和 cross(encoder 输出)同时传给 DecoderLayer,返回的新 x 覆盖旧值。
cross 在整个循环中保持不变——所有 DecoderLayer 都读同一份 encoder 输出。
x: (3,12,8) + cross: (3,6,8) → DecoderLayer 0 → x: (3,12,8)Generative Decoder 并行生成原理:decoder 输入的后 pred_len=7 步是全零占位,每步在 cross-attention 中同时向 encoder 检索历史信息,整个 pred_len 一次性并行输出,而非逐步 autoregressive 生成。
DecoderLayer 的内部三段式见 03C1-Layer3-DecoderLayer。
5.2 最终归一化 + projection
本节的作用
所有 DecoderLayer 跑完后,
self.norm对输出序列做最终归一化,self.projection将隐层维度映射回原始变量数。两者均在Informer.py构造 Decoder 时注入。
初始化来源(Informer.py:102-103):
python
norm_layer=torch.nn.LayerNorm(configs.d_model),
projection=nn.Linear(configs.d_model, configs.c_out, bias=True),toy:self.norm = LayerNorm(8);self.projection = Linear(8, 6, bias=True),参数名是 c_out=6(输出变量数)而非 enc_in——两者数值相同但语义不同:enc_in 是 encoder 输入通道数,c_out 是 decoder 输出通道数。
原始代码:
python
if self.norm is not None:
x = self.norm(x)
if self.projection is not None:
x = self.projection(x)
return xself.norm — 序列级最终 LayerNorm:
LayerNorm(d_model=8) 对每个 token 的 8 维特征向量独立归一化,形状 (3,12,8) 不变。作用于最后一维,前两维 (B, dec_len) 视为独立的归一化单位。
self.projection — 维度投影:
nn.Linear(in_features=8, out_features=6, bias=True)。公式:
nn.Linear 仅作用于最后一维,(3, 12) 共 36 个 token 各自独立做 (3, 12, 6)。
toy 数值:设 x[0,0,:] = [0.3, -0.1, 0.5, -0.2, 0.4, -0.3, 0.2, -0.4](LayerNorm 后),W[0,:] = [0.1, 0.2, -0.1, 0.3, -0.2, 0.1, 0.4, -0.3],b[0] = 0.05:
最终 Decoder.forward 返回 (3, 12, 6),父层 long_forecast() 取 [:, -pred_len:, :] = (3, 7, 6) 作预测输出。
DecoderLayer 已有三次 LayerNorm,外层 Decoder 为什么还要再加一个?
这不是重复,而是 Post-LN Transformer 的两级归一化设计:
第一级:子块级归一化(DecoderLayer 内部)
norm1(self-attn 之后)、norm2(cross-attn 之后)、norm3(FFN 之后)——每个残差子块做完立刻归一化,控制残差叠加不爆炸。它们保证的是"当前子块的输出在一个合理范围",而不是整个 DecoderLayer 的输出。第二级:层序列级归一化(Decoder 外层)
d_layers个 DecoderLayer 串联后,残差累积效果与d_layers=1时不同——层数越多,输出的 DC 偏置越大。外层self.norm在所有层跑完后统一标准化,让projection接收到稳定的输入,权重学习不受层数变化的影响。Encoder 侧完全对称:EncoderLayer 内部有 norm1/norm2(子块级),Encoder 外层有 norm(序列级)——这份标准化后的 encoder 输出是 cross-attention 的 K/V,稳定的 K/V 对 cross-attention 的梯度流动至关重要。
归一化位置 作用对象 作用 norm1/norm2(EncoderLayer 内部) 每个残差子块 控制残差累加,防梯度爆炸 norm(Encoder 外层) 全部 EncoderLayer 跑完后的序列 稳定 cross-attn 的 K/V 来源 norm1/norm2/norm3(DecoderLayer 内部) 每个残差子块 同 EncoderLayer norm(Decoder 外层) 全部 DecoderLayer 跑完后的序列 稳定 projection 的输入
6. 下钻子组件
| 子组件 | 职责 | 下层文档 |
|---|---|---|
DecoderLayer(layer) | 三段式:masked self-attn + cross-attn + FFN,各带残差 | 03C1-Layer3-DecoderLayer |