Appearance
Layer2B Encoder
覆盖
iTransformer.forecast()中的self.encoder(enc_out, attn_mask=None)调用。Encoder 组件负责将 e_layers=2 个 EncoderLayer 串行堆叠,并在最终做 LayerNorm。注意力在 变量 token 之间计算,token 语义与 PatchTST 不同,但 Encoder 本体结构相同。
§1 在父层中的位置
forecast() 第三步:
python
enc_out, attns = self.encoder(enc_out, attn_mask=None)- 输入:
enc_out (3, 9, 8),即 (B, token_count, d_model) = (batch, N+time_dims, d_model) - 输出:
enc_out (3, 9, 8)(形状不变,token 内容被更新)
§2 I/O 接口定义
| 参数 | shape(toy) | 含义 |
|---|---|---|
输入 enc_out | (3, 9, 8) | B=3,token_count=9(5 变量 + 4 时间),d_model=8 |
输出 enc_out | (3, 9, 8) | 形状不变,token 内容被 Encoder 更新 |
attn_mask | None | iTransformer 不使用掩码 |
为什么形状不变?
iTransformer 没有 ConvLayer distilling(Informer 才有)。每个 EncoderLayer 输入输出 shape 相同,e_layers=2 层堆叠后整体仍是 (3, 9, 8)。
§3 顺序图
§4 语义分组图
§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§5.1 宏观逻辑
用小例子(B=1,token_count=3,d_model=4,e_layers=2)串起来看:
x 初始 (1, 3, 4)
↓ EncoderLayer[0]:3个 token 互相 attend,更新各自表示
x (1, 3, 4)
↓ EncoderLayer[1]:再做一轮 attention + FFN
x (1, 3, 4)
↓ LayerNorm
x (1, 3, 4)
返回 (x, [attn0, attn1])shape 全链不变是 iTransformer 的特点:没有 Informer 的 distilling(每层把 seq_len 减半),因为 token 语义是变量,固定就是 9 个(N+time_dims),不需要压缩。
§5.2 步骤一:分支判断 — conv_layers is None
iTransformer.__init__() 中:
python
self.encoder = Encoder(
[
EncoderLayer(...)
for l in range(configs.e_layers)
],
norm_layer=torch.nn.LayerNorm(configs.d_model)
)Encoder 构造时没有传 conv_layers,因此 self.conv_layers = None,代码走 else 分支。
if self.conv_layers is not None 分支(Informer 的 distilling 逻辑)在 iTransformer 中永远不会执行。
§5.3 步骤二:循环 e_layers=2 个 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_layers共 2 个EncoderLayer实例attn_mask=None:iTransformer 不使用因果掩码(变量 token 之间全局互相关)tau=None, delta=None:iTransformer 不使用这两个参数(为与 Informer 共用接口而保留)- 每轮返回
(x, attn),attn 累积到列表备用(调用方一般不用,但接口保留)
toy 数值(全局 toy 参数):
迭代前:
x[0, 0, :] = [0.12, -0.34, 0.87, -0.21, 0.55, -0.68, 0.33, -0.44]
← var_0 token 的 8 维 embedding(DataEmbedding_inverted 输出)
迭代 0(EncoderLayer[0]):
x 传入后,var_0 通过 self-attention 关注了 var_1~var_4 及 4 个时间 token
并经过 FFN 做非线性变换;内容改变,shape 不变
x[0, 0, :] = [0.03, -0.51, 1.12, 0.08, 0.44, -0.92, 0.17, -0.29]
← 数值已更新,但仍是 8 维,仍代表 var_0 的 token 表示
attn0 = None
迭代 1(EncoderLayer[1]):
在第 0 层已更新的 token 表示上再做一轮 attention + FFN
x[0, 0, :] = [0.07, -0.48, 1.08, 0.11, 0.39, -0.87, 0.14, -0.32]
← 进一步精化,绝对值趋于稳定
attn1 = None
attns = [None, None]EncoderLayer 内部细节见 [[03B1-Layer3-EncoderLayer]]。
§5.4 步骤三:LayerNorm
python
if self.norm is not None:
x = self.norm(x)self.norm = nn.LayerNorm(configs.d_model)=LayerNorm(8)- 对最后一维(d_model=8)做归一化,形状 (3, 9, 8) 不变
- 每个 token 向量独立归一化,跨 token 的统计量不混合
toy 数值:
x[0, 0, :] = [e0, e1, ..., e7] ← var_0 的 8 维表示
norm 后:减去这 8 个数的均值,除以标准差,× weight + bias
输出仍是 8 维向量;其他 8 个 token 各自独立 norm§6 下钻子组件
| 组件 | 输入 shape | 输出 shape | 下层文档 |
|---|---|---|---|
EncoderLayer | (3, 9, 8) | (3, 9, 8) | [[03B1-Layer3-EncoderLayer]] |