Appearance
Layer 0 — 接入界面
本文回答一个问题:TFB 如何把 Informer 当黑盒使用?
覆盖两面:实例化侧(config →
Informer.__init__)和调用侧(_process→forward的 I/O 契约)。
1. 在父层中的位置
TFB benchmark 的最外层循环最终走到这里:TransformerAdapter 负责实例化 Informer(config) 并在每个 batch 里调用 Informer.forward()。
2. I/O 接口定义
实例化侧
python
model = Informer(config)| config 字段 | 来源 | Informer 里的用途 |
|---|---|---|
seq_len | 命令行 --seq_len | encoder 输入长度 |
pred_len | 命令行 --pred_len | forward() 切片、short_forecast 反归一化 |
label_len | 命令行 --label_len | _process() 构造 dec_input 的历史部分长度 |
enc_in | 框架自动推断 | enc_embedding 输入维度 |
dec_in | 框架自动推断 | dec_embedding 输入维度 |
c_out | 框架自动推断(= enc_in) | Decoder 的 projection 输出维度 |
d_model | MODEL_HYPER_PARAMS["d_model"]=512 | embedding 和注意力维度 |
n_heads | MODEL_HYPER_PARAMS["n_heads"]=8 | 多头注意力头数 |
e_layers | MODEL_HYPER_PARAMS["e_layers"]=2 | Encoder 层数(同时控制 ConvLayer 数量) |
d_layers | MODEL_HYPER_PARAMS["d_layers"]=1 | Decoder 层数 |
d_ff | MODEL_HYPER_PARAMS["d_ff"]=2048 | FFN 中间维度 |
factor | MODEL_HYPER_PARAMS["factor"]=1 | ProbSparse 稀疏因子 c |
distil | MODEL_HYPER_PARAMS["distil"]=True | 是否在 Encoder 层间插入 ConvLayer |
embed | MODEL_HYPER_PARAMS["embed"]="timeF" | DataEmbedding 的时间特征类型 |
freq | MODEL_HYPER_PARAMS["freq"]="h" | 时间特征的频率粒度 |
task_name | 命令行 --task_name | forward() 分支判断 |
注意:
enc_in / dec_in / c_out不在命令行里,由框架在forecast_fit时根据数据集特征列数自动注入,toy 中均 = 6。
调用侧 — _process 构造的四元组
python
output = self.model(input, input_mark, dec_input, target_mark)| 参数 | shape(toy) | 含义 |
|---|---|---|
input (= x_enc) | (3, 10, 6) = (B, seq_len, enc_in) | encoder 历史输入 |
input_mark (= x_mark_enc) | (3, 10, 4) | encoder 时间特征 |
dec_input (= x_dec) | (3, 12, 6) = (B, label_len+pred_len, enc_in) | decoder 输入:5个历史+7个零 |
target_mark (= x_mark_dec) | (3, 12, 4) | decoder 时间特征 |
| 返回 | (3, 7, 6) = (B, pred_len, c_out) | 预测输出 |
dec_input的label_len=5部分来自target[:, :5, :](最近 5 个真实值),pred_len=7部分是全零占位。
3. 顺序图(具体层)
4. 语义分组图(索引层)
5. 精读
5.0 完整 __init__ 原始代码
python
# Informer.py:21-114
def __init__(self, configs):
super(Informer, self).__init__()
self.task_name = configs.task_name
self.pred_len = configs.pred_len
self.label_len = configs.label_len
# Embedding
self.enc_embedding = DataEmbedding(
configs.enc_in, configs.d_model, configs.embed, configs.freq, configs.dropout,
)
self.dec_embedding = DataEmbedding(
configs.dec_in, configs.d_model, configs.embed, configs.freq, configs.dropout,
)
# Encoder
self.encoder = Encoder(
[EncoderLayer(
AttentionLayer(
ProbAttention(False, configs.factor, attention_dropout=configs.dropout,
output_attention=configs.output_attention),
configs.d_model, configs.n_heads,
),
configs.d_model, configs.d_ff, dropout=configs.dropout, activation=configs.activation,
) for l in range(configs.e_layers)],
([ConvLayer(configs.d_model) for l in range(configs.e_layers - 1)]
if configs.distil and ("forecast" in configs.task_name) else None),
norm_layer=torch.nn.LayerNorm(configs.d_model),
)
# Decoder
self.decoder = Decoder(
[DecoderLayer(
AttentionLayer(ProbAttention(True, ...), configs.d_model, configs.n_heads),
AttentionLayer(ProbAttention(False, ...), configs.d_model, configs.n_heads),
configs.d_model, configs.d_ff, ...
) for l in range(configs.d_layers)],
norm_layer=torch.nn.LayerNorm(configs.d_model),
projection=nn.Linear(configs.d_model, configs.c_out, bias=True),
)
if self.task_name == "imputation":
self.projection = nn.Linear(configs.d_model, configs.c_out, bias=True)
if self.task_name == "anomaly_detection":
self.projection = nn.Linear(configs.d_model, configs.c_out, bias=True)
if self.task_name == "classification":
self.act = F.gelu
self.dropout = nn.Dropout(configs.dropout)
self.projection = nn.Linear(configs.d_model * configs.seq_len, configs.num_class)5.1 参数合并与关键配置
本节的作用
__init__把 TFB config 映射为 Informer 内部结构:encoder 层数决定 ConvLayer 数量(e_layers-1),decoder 的 projection 内嵌在 Decoder 内部而非父模块。
注解版:
python
self.task_name = "long_term_forecast" # TFB 主路径
self.pred_len = 7 # 预测 7 步
self.label_len = 5 # decoder 历史部分 5 个 token
# enc_embedding / dec_embedding 结构相同:
# DataEmbedding(c_in=6, d_model=8, embed="timeF", freq="h")
# 输入 (B, L, 6) → 输出 (B, L, 8)
# encoder:
# e_layers=2 → 2 个 EncoderLayer
# distil=True + "forecast" in task_name → 1 个 ConvLayer(e_layers-1=1)
# ConvLayer 放在 EncoderLayer[0] 和 EncoderLayer[1] 之间
# decoder:
# d_layers=1 → 1 个 DecoderLayer
# DecoderLayer 内有两个 ProbAttention:
# self_attention: mask_flag=True ← masked self-attention
# cross_attention: mask_flag=False ← encoder-decoder cross attention
# projection: Linear(d_model=8, c_out=6) 内嵌在 Decoder 里encoder 内 ConvLayer 数量:e_layers - 1 = 2 - 1 = 1。 distil 开启时,Encoder.forward 走 if self.conv_layers is not None 分支:
for EncoderLayer[0], ConvLayer[0] in zip(attn_layers[:-1], conv_layers):
x = EncoderLayer[0](x) → (3, 10, 8)
x = ConvLayer[0](x) → (3, 6, 8) # 序列缩短 10→6(Conv1d→12→MaxPool→6)
x = EncoderLayer[1](x) → (3, 6, 8) # 最后一层单独调Decoder 里的 projection 位置:不是在 Informer.__init__ 里直接挂,而是作为参数传入 Decoder.__init__,存为 self.decoder.projection,在 Decoder.forward 的最后调用。
5.2 _process() 构造 dec_input
本节的作用
_process()把 label_len 段真实历史与 pred_len 段全零占位拼接成 dec_input,形成 Informer Generative Decoder 的启动 token 序列。
python
# adapters_for_transformers.py
dec_input = torch.zeros_like(target[:, -self.config.horizon:, :]).float()
# zeros shape: (3, pred_len=7, enc_in=6)
dec_input = torch.cat(
[target[:, :self.config.label_len, :], dec_input], dim=1
).float().to(input.device)
# target[:, :5, :]: (3, 5, 6) ← 最近 5 个真实值
# cat: (3, 5+7=12, 6) = (B, label_len+pred_len, enc_in)图解:
target: [ t1 t2 t3 t4 t5 | t6 t7 t8 t9 t10 t11 t12 ] (B, 12, 6)
↓ ↓
取前 5 个: [t1 t2 t3 t4 t5] zeros: [0 0 0 0 0 0 0]
↓ ↓
└────────────── cat ─────────────┘
dec_input: (3, 12, 6)
[历史5个 | 零占位7个]5.3 forward() 任务分支
本节的作用
forward()根据 task_name 路由到不同预测函数,TFB 走long_term_forecast路径;返回时切片[:, -pred_len:, :]取最后 pred_len 步。
原始代码:
python
# Informer.py:176-192
def forward(self, x_enc, x_mark_enc, x_dec, x_mark_dec, mask=None):
if self.task_name == "long_term_forecast":
dec_out = self.long_forecast(x_enc, x_mark_enc, x_dec, x_mark_dec)
return dec_out[:, -self.pred_len :, :] # [B, L, D]
if self.task_name == "short_term_forecast":
dec_out = self.short_forecast(x_enc, x_mark_enc, x_dec, x_mark_dec)
return dec_out[:, -self.pred_len :, :] # [B, L, D]
if self.task_name == "imputation":
dec_out = self.imputation(x_enc, x_mark_enc, x_dec, x_mark_dec, mask)
return dec_out # [B, L, D]
if self.task_name == "anomaly_detection":
dec_out = self.anomaly_detection(x_enc)
return dec_out # [B, L, D]
if self.task_name == "classification":
dec_out = self.classification(x_enc, x_mark_enc)
return dec_out # [B, N]
return None| task_name | 调用函数 | decoder 使用? | 返回 shape |
|---|---|---|---|
long_term_forecast | long_forecast() | ✓ | (3, 7, 6) |
short_term_forecast | short_forecast() | ✓ | (3, 7, 6) |
imputation | imputation() | ✗(encoder only) | (3, 10, 6) |
anomaly_detection | anomaly_detection() | ✗ | (3, 10, 6) |
classification | classification() | ✗ | (3, num_class) |
long vs short 的区别:short_forecast 在 encoder 输入前做 RevIN 风格归一化(减均值除标准差),预测后再反归一化。long_forecast 直接跑原始值。TFB 中 task_name 由命令行参数决定。
6. 下钻子组件
| 子组件 | 职责 | 文档 |
|---|---|---|
long_forecast() / short_forecast() | 主计算链:embed → encoder → decoder → slice | 02-Layer1-forecast主链 |