Appearance
Layer0 接入界面
本文档描述 TFB 框架如何把原始数据切片、配置超参数,最终构造出传入
iTransformer.forward()的四元组。对应 [[00-总览]] BFS 树的第一层。
§1 在父层中的位置
RollingForecast 策略按滑动窗口切出 (x_enc, x_mark_enc, x_dec, x_mark_dec),交由 TransformerAdapter._process() 拼装 dec_input 并直接调用 model.forward()——本文档覆盖的就是这条从"数据切片"到"模型入口"的完整准备路径。
§2 I/O 接口定义
四元组 shape(toy 参数)
| 张量 | shape(toy) | 具体含义 | 使用情况 |
|---|---|---|---|
x_enc | (3, 12, 5) | B=3 批次,seq_len=12 时间步,N=5 变量 | ✅ 实际使用 |
x_mark_enc | (3, 12, 4) | encoder 时间特征,time_dims=4(hour/weekday/monthday/month) | ✅ 实际使用 |
x_dec | (3, 12, 5) | label_len=6 历史 + pred_len=6 零占位,共 12 步 | ⚠️ 传入但完全未使用 |
x_mark_dec | (3, 12, 4) | decoder 时间特征,同 x_enc 的 time_dims | ⚠️ 传入但完全未使用 |
forward() 返回:dec_out[:, -pred_len:, :] → (3, 6, 5),即 (B, pred_len, N)
x_dec / x_mark_dec 是"哑参数"
_process()按通用框架逻辑构造了dec_input,但iTransformer.forecast()函数体中一行都没有引用这两个参数。iTransformer 是 encoder-only 模型,Decoder 分支根本不存在。详见 [[#§5.1 encoder-only 与 哑参数]]。
label_len 推导(toy)
| 参数 | 值 | 来源 |
|---|---|---|
seq_len | 12 | rolling_forecast 切片窗口 |
label_len | 6 | multi_forecasting_hyper_param_tune() 设置:seq_len // 2 |
pred_len | 6 | 评测配置指定 |
dec_len(实际构造) | 12 | label_len + pred_len = 6 + 6(但模型不使用) |
§3 顺序图(具体层)
§4 语义分组图(索引层)
§5 逐步精读
§5.1 encoder-only 与"哑参数"
iTransformer 是 encoder-only 模型,完全没有 Decoder 模块。但 TFB 框架的通用适配器 TransformerAdapter 并不区分模型架构——它对所有 Transformer 类模型统一构造四元组并调用相同签名的 forward()。
结果是:forecast() 函数签名接收 x_dec 和 x_mark_dec,但函数体第一行到最后一行都没有引用它们。
与有 Decoder 的模型对比:
| 模型 | Decoder | x_dec / x_mark_dec 实际用途 |
|---|---|---|
| Informer | ✅ 有(ProbSparse Attention) | 传入 Decoder,做自回归预测 |
| Autoformer | ✅ 有(Auto-Correlation Decoder) | 传入 Decoder,序列分解 |
| iTransformer | ❌ 无 | ⚠️ 收到但函数体完全不用(哑参数) |
这是一个框架通用性 vs 模型实现之间的"接口冗余"——对 iTransformer 而言,_process() 中构造 dec_input 的代码是运行但无效的死代码路径。
§5.2 MODEL_HYPER_PARAMS 与 iTransformer 的关系
iTransformer 没有专属超参数。Autoformer 有 moving_avg,PatchTST 有 patch_len 和 stride,而 iTransformer 直接复用通用字段,无需额外注册。
与 iTransformer 直接相关的通用参数:
| 参数 | 默认值 | iTransformer 用途 |
|---|---|---|
d_model | 512(toy: 8) | DataEmbedding_inverted Linear 输出维度 |
n_heads | 8(toy: 4) | FullAttention 多头数 |
e_layers | 2 | Encoder 堆叠层数 |
d_ff | 2048(toy: 16) | EncoderLayer FFN 隐层维度 |
dropout | 0.1 | 各层 Dropout |
embed | "timeF" | 时间特征编码方式(影响 time_dims=4) |
freq | "h" | 时间频率(hour → 4 维时间特征) |
activation | "gelu" | FFN 激活函数 |
output_attention | 0 | 不输出注意力权重矩阵 |
task_name | "short_term_forecast" | 默认值;multi_forecasting_hyper_param_tune 会覆写为 "long_term_forecast" |
factor | 1 | FullAttention 中 top_k = factor × ceil(ln(L))(稀疏注意力比例,Full 模式不使用) |
enc_in / dec_in / c_out 不在 MODEL_HYPER_PARAMS 中
这三个参数由
multi_forecasting_hyper_param_tune()在运行时根据数据集动态推断,不在静态默认值字典里。
§5.3 _process() 构造 dec_input
原始源码(adapters_for_transformers.py):
python
def _process(self, input, target, input_mark, target_mark):
dec_input = torch.zeros_like(target[:, -self.config.horizon:, :]).float()
dec_input = torch.cat([target[:, :self.config.label_len, :], dec_input], dim=1).float().to(input.device)
output = self.model(input, input_mark, dec_input, target_mark)
return {"output": output}逐行注解:
第 1 行:target[:, -self.config.horizon:, :]
target是完整的标签窗口,shape =(B, label_len + pred_len, N)=(3, 12, 5)[-horizon:]=[-pred_len:]= 取最后 6 步 → shape(3, 6, 5)torch.zeros_like(...)创建等形全零张量,作为"未来未知"的占位符
第 2 行:torch.cat([target[:, :label_len, :], dec_input], dim=1)
target[:, :label_len, :]= 取前 6 步历史(label 段),shape(3, 6, 5)- 与全零占位拼接,dim=1(时间维)→
dec_input最终 shape(3, 12, 5)
第 3 行:直接调用 self.model(input, input_mark, dec_input, target_mark)
- 参数名映射:
input=x_enc,input_mark=x_mark_enc,dec_input=x_dec,target_mark=x_mark_dec
toy 数值追踪:
target shape: (3, 12, 5) # label_len=6 历史 + pred_len=6 未来
target[:, -6:, :] → (3, 6, 5) # pred_len=6 的未来段
zeros_like(...) → (3, 6, 5) # 全零占位
target[:, :6, :] → (3, 6, 5) # label_len=6 的历史段
cat([历史, 零占位], dim=1) → (3, 12, 5) # dec_input 最终 shape(⚠️ iTransformer 不会使用)⚠️ 重要:上述构造对 iTransformer 是无效的——代码会运行,dec_input 会被创建并传入 forward(),但 forecast() 函数体中没有任何代码访问 x_dec 参数。这是框架通用性导致的"合法但多余"的计算。
§5.4 forward() 分支路由
原始源码(iTransformer.py):
python
def forward(self, x_enc, x_mark_enc, x_dec, x_mark_dec, mask=None):
if self.task_name == "long_term_forecast" or self.task_name == "short_term_forecast":
dec_out = self.forecast(x_enc, x_mark_enc, x_dec, x_mark_dec)
return dec_out[:, -self.pred_len:, :] # [B, L, D]注解:
task_name由multi_forecasting_hyper_param_tune()覆写为"long_term_forecast",因此命中第一个条件forecast()接收四元组签名,但函数体只使用x_enc和x_mark_enc;x_dec、x_mark_dec进入函数后立即被忽略- 返回
dec_out[:, -self.pred_len:, :],对forecast()的输出做末端裁剪(详见 §5.5)
§5.5 返回值裁剪:dec_out[:, -pred_len:, :]
裁剪机制:
forecast() 内部 Projection 层的输出 shape 为 (B, pred_len, N),即已经是目标维度。forward() 再用 [:, -pred_len:, :] 裁剪是防御性写法——确保无论 forecast() 返回多长的序列,都只取最后 pred_len 步。
toy 数值追踪:
forecast() 输出 dec_out: (3, 6, 5) # B=3, pred_len=6, N=5
[:, -pred_len:, :]
= [:, -6:, :] (3, 6, 5) # ← 索引 [0..5],无裁剪(dec_len = pred_len)在本 toy 参数下,forecast() 输出恰好是 (3, 6, 5),裁剪前后 shape 不变。
⚠️ 为什么在这里 pred_len = dec_len?
forecast()的 Projection 层是nn.Linear(d_model, pred_len),permute 后直接输出(B, pred_len, N)- 不像 Informer/Autoformer 会输出
label_len + pred_len长度的序列再裁剪 - 因此
[:, -pred_len:, :]对 iTransformer 而言是幂等操作,但保留它是为了与其他 Transformer 模型保持接口一致性