Skip to content

Layer 0 — 接入界面

本文回答一个问题:TFB 如何把 Informer 当黑盒使用?

覆盖两面:实例化侧(config → Informer.__init__)和调用侧(_processforward 的 I/O 契约)。

1. 在父层中的位置

TFB benchmark 的最外层循环最终走到这里:TransformerAdapter 负责实例化 Informer(config) 并在每个 batch 里调用 Informer.forward()


2. I/O 接口定义

实例化侧

python
model = Informer(config)
config 字段来源Informer 里的用途
seq_len命令行 --seq_lenencoder 输入长度
pred_len命令行 --pred_lenforward() 切片、short_forecast 反归一化
label_len命令行 --label_len_process() 构造 dec_input 的历史部分长度
enc_in框架自动推断enc_embedding 输入维度
dec_in框架自动推断dec_embedding 输入维度
c_out框架自动推断(= enc_in)Decoder 的 projection 输出维度
d_modelMODEL_HYPER_PARAMS["d_model"]=512embedding 和注意力维度
n_headsMODEL_HYPER_PARAMS["n_heads"]=8多头注意力头数
e_layersMODEL_HYPER_PARAMS["e_layers"]=2Encoder 层数(同时控制 ConvLayer 数量)
d_layersMODEL_HYPER_PARAMS["d_layers"]=1Decoder 层数
d_ffMODEL_HYPER_PARAMS["d_ff"]=2048FFN 中间维度
factorMODEL_HYPER_PARAMS["factor"]=1ProbSparse 稀疏因子 c
distilMODEL_HYPER_PARAMS["distil"]=True是否在 Encoder 层间插入 ConvLayer
embedMODEL_HYPER_PARAMS["embed"]="timeF"DataEmbedding 的时间特征类型
freqMODEL_HYPER_PARAMS["freq"]="h"时间特征的频率粒度
task_name命令行 --task_nameforward() 分支判断

注意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_inputlabel_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_forecastlong_forecast()(3, 7, 6)
short_term_forecastshort_forecast()(3, 7, 6)
imputationimputation()✗(encoder only)(3, 10, 6)
anomaly_detectionanomaly_detection()(3, 10, 6)
classificationclassification()(3, num_class)

long vs short 的区别short_forecast 在 encoder 输入前做 RevIN 风格归一化(减均值除标准差),预测后再反归一化。long_forecast 直接跑原始值。TFB 中 task_name 由命令行参数决定。


6. 下钻子组件

子组件职责文档
long_forecast() / short_forecast()主计算链:embed → encoder → decoder → slice02-Layer1-forecast主链

*记录并在线阅读我的笔记*