Appearance
Informer 调试形参
Abstract
这篇只做一件事:
保存当前用于学习 Informer 代码运行流程的 PyCharm 参数,并保证关键模型分支至少执行一次。
1. PyCharm 配置
Script path
text
D:\1sudyta\1ai-self\aistyle\TFB\scripts\run_benchmark.pyWorking directory
text
D:\1sudyta\1ai-self\aistyle\TFBEnvironment variables
text
KMP_DUPLICATE_LIB_OK=TRUE2. Parameters
直接复制下面这一整行到 PyCharm 的 Parameters:
text
--config-path rolling_forecast_config.json --data-name-list cif_2016_dataset_1.csv --model-name time_series_library.Informer --adapter transformer_adapter --model-hyper-params "{\"batch_size\":4,\"d_model\":32,\"d_ff\":128,\"e_layers\":2,\"d_layers\":1,\"factor\":3,\"horizon\":6,\"n_heads\":2,\"norm\":true,\"seq_len\":24,\"dropout\":0.0,\"lr\":0.0001,\"num_epochs\":1,\"num_workers\":0,\"output_attention\":0}" --strategy-args "{\"horizon\":6,\"tv_ratio\":0.8,\"train_ratio_in_tv\":0.75,\"stride\":6,\"num_rollings\":2}" --num-workers 1 --timeout 600 --save-path debug\cif1_Informer_rolling_cover_distil来源
scripts/multivariate_forecast/*_script/Informer.sh里的正式 benchmark 命令一般是rolling_forecast_config.json + time_series_library.Informer + transformer_adapter。这里把正式参数缩小成学习版:小数据集、小
seq_len/horizon、小d_model/d_ff、e_layers=2、d_layers=1、num_epochs=1、num_rollings=2。
e_layers=2是刻意设置:Informer 的 ConvLayer distilling 数量是e_layers - 1,如果写成e_layers=1,ConvLayer 实际不会跑。
2.1 VSCode 调试配置
VSCode 不把参数写成一整行,而是写进 .vscode/launch.json 的 args 数组。
先在 VSCode 里执行:
text
Ctrl+Shift+P
-> Python: Select Interpreter
-> 选择 D:\Anaconda\envs\tfb\python.exe然后在仓库根目录创建或修改:
text
D:\1sudyta\1ai-self\aistyle\TFB\.vscode\launch.json加入下面配置:
json
{
"version": "0.2.0",
"configurations": [
{
"name": "TFB Informer rolling debug",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}\\scripts\\run_benchmark.py",
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"env": {
"KMP_DUPLICATE_LIB_OK": "TRUE"
},
"args": [
"--config-path", "rolling_forecast_config.json",
"--data-name-list", "cif_2016_dataset_1.csv",
"--model-name", "time_series_library.Informer",
"--adapter", "transformer_adapter",
"--model-hyper-params", "{\"batch_size\":4,\"d_model\":32,\"d_ff\":128,\"e_layers\":2,\"d_layers\":1,\"factor\":3,\"horizon\":6,\"n_heads\":2,\"norm\":true,\"seq_len\":24,\"dropout\":0.0,\"lr\":0.0001,\"num_epochs\":1,\"num_workers\":0,\"output_attention\":0}",
"--strategy-args", "{\"horizon\":6,\"tv_ratio\":0.8,\"train_ratio_in_tv\":0.75,\"stride\":6,\"num_rollings\":2}",
"--num-workers", "1",
"--timeout", "600",
"--save-path", "debug\\cif1_Informer_rolling_cover_distil"
]
}
]
}关键区别:
text
PyCharm Parameters:
一整行字符串,JSON 需要用 \" 转义。
VSCode args:
一个参数一个数组元素,JSON 参数整体作为一个字符串元素。2.2 为什么这版不是 e_layers=1
Informer 构造 encoder 时:
python
self.encoder = Encoder(
[
EncoderLayer(...)
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),
)当前 adapter 默认:
text
distil = True
task_name = short_term_forecast所以:
text
conv_layers 数量 = e_layers - 1如果旧参数是:
text
e_layers = 1那么:
text
EncoderLayer 数量 = 1
ConvLayer 数量 = 0Encoder.forward(...) 虽然进入 if self.conv_layers is not None 分支,但这里不会执行任何 distilling:
python
for i, (attn_layer, conv_layer) in enumerate(zip(self.attn_layers, self.conv_layers)):
...因为:
text
zip([EncoderLayer_0], []) = []所以旧版参数没有真正跑到 ConvLayer.forward(...)。
修正版:
text
e_layers = 2会得到:
text
EncoderLayer 数量 = 2
ConvLayer 数量 = 1执行顺序变成:
text
EncoderLayer_0
-> ConvLayer_0 ← distilling 至少跑 1 次
-> EncoderLayer_1
-> final LayerNorm3. 当前参数含义
数据与任务
--data-name-list cif_2016_dataset_1.csv- 使用很小的单变量数据,方便看清 Informer 的代码流。
--config-path rolling_forecast_config.json- 使用 rolling forecast 评测协议。
--strategy-argshorizon = 6- 每次预测未来 6 步。
tv_ratio = 0.8- 前 80% 作为 train/valid 区域。
train_ratio_in_tv = 0.75- train/valid 区域内部再切训练段。
stride = 6- rolling forecast 每次测试窗口往后移动 6 步。
num_rollings = 2- 只滚 2 次,减少调试负担。
模型
--model-name time_series_library.Informer- 当前调试模型是 Informer。
--adapter transformer_adapter- 走 TFB 的 transformer 统一 adapter。
seq_len = 24- encoder 输入历史窗口长度。
horizon = 6- 会被
Config映射成pred_len = 6。
- 会被
d_model = 32- embedding 后每个 token 的隐藏维度。
n_heads = 2- 多头注意力 head 数。
- 每个 head 的维度是
d_model / n_heads = 16。
factor = 3- ProbAttention 采样 query/key 时使用的稀疏采样系数。
e_layers = 2- encoder 放 2 层,保证
ConvLayerdistilling 至少执行 1 次。
- encoder 放 2 层,保证
d_layers = 1- decoder 只放 1 层,便于调试。
d_ff = 128- EncoderLayer / DecoderLayer 内部 FFN 隐藏维度。
dropout = 0.0- 调试时关闭 dropout,减少随机性。
output_attention = 0- 不额外返回 attention 矩阵,先看主输出。
label_len 不建议在这里手动作为主参数理解
label_len 不建议在这里手动作为主参数理解Informer 的 decoder 输入长度是:
textlabel_len + pred_len但在这个 TFB 流程里,
forecast_fit(...)会根据数据形态自动补齐label_len:
- 单变量数据:
label_len = horizon- 多变量数据:
label_len = seq_len // 2当前
cif_2016_dataset_1.csv是单变量,所以调试时重点看:textlabel_len = horizon = 6 dec_input.length = label_len + horizon = 12
4. 当前小例子的关键 shape
当前数据是单变量,所以模型初始化前后会得到:
text
C = enc_in = dec_in = c_out = 1当前 batch 里大概率会看到:
text
input / x_enc: (4, 24, 1)
input_mark / x_mark: (4, 24, time_feature_dim)
target: (4, 12, 1)
target_mark: (4, 12, time_feature_dim)
dec_input: (4, 12, 1)其中:
text
seq_len = 24
horizon = pred_len = 6
label_len = 6
decoder 输入长度 = label_len + pred_len = 12进入 Informer 后的主 shape:
text
x_enc: (4, 24, 1)
x_mark_enc: (4, 24, time_feature_dim)
x_dec: (4, 12, 1)
x_mark_dec: (4, 12, time_feature_dim)
enc_embedding: (4, 24, 32)
dec_embedding: (4, 12, 32)
encoder out: (4, 13, 32)
decoder out: (4, 12, 1)
final output: (4, 6, 1)Encoder 长度从 24 变成 13 的原因是 ConvLayer.forward(...):
python
x = self.downConv(x.permute(0, 2, 1)) # Conv1d 后长度 24 -> 26
x = self.norm(x)
x = self.activation(x)
x = self.maxPool(x) # MaxPool1d 后长度 26 -> 13
x = x.transpose(1, 2)所以当前 encoder 主链是:
text
enc_embedding: (4, 24, 32)
EncoderLayer_0: (4, 24, 32)
ConvLayer_0: (4, 24, 32) -> (4, 13, 32)
EncoderLayer_1: (4, 13, 32)
LayerNorm: (4, 13, 32)time_feature_dim
time_feature_dim
DataEmbedding使用embed="timeF",时间特征维度由freq决定。这个小数据集的
date是数字序列,TFB 加载后会转成 datetime,再由forecast_fit(...)推断freq。调试时不需要先记死time_feature_dim,在断点里直接看input_mark.shape最准确。
5. Informer 和 PatchTST 的调试差异
PatchTST 的主链是:
text
x_enc
-> PatchEmbedding
-> Encoder
-> FlattenHead
-> outputInformer 的主链是:
text
x_enc + x_mark_enc
-> enc_embedding
-> encoder
x_dec + x_mark_dec
-> dec_embedding
-> decoder(self-attn + cross-attn)
-> projection
-> output所以 Informer 的断点重点不是 patch,而是:
text
1. dec_input 怎样构造
2. x_mark_enc / x_mark_dec 怎样进入 DataEmbedding
3. Encoder 里的 ProbAttention
4. Encoder 里的 ConvLayer distilling
5. Decoder 里的 self-attention 和 cross-attention5.1 当前参数覆盖的关键路径
这组参数保证下面这些模型内部结构都会实际执行:
| 结构 | 是否执行 | 原因 |
|---|---|---|
Encoder.forward 的 distilling 分支 | 会 | distil=True 且 task_name 包含 forecast |
EncoderLayer_0 | 会 | e_layers=2 |
ConvLayer_0 | 会 | e_layers - 1 = 1 |
EncoderLayer_1 | 会 | distilling 后最后单独执行 attn_layers[-1] |
Decoder.forward 循环 | 会 | d_layers=1 |
DecoderLayer.self_attention | 会 | decoder layer 固定执行 |
DecoderLayer.cross_attention | 会 | decoder layer 固定执行 |
ProbAttention._prob_QK | 会 | encoder/decoder attention 都使用 ProbAttention |
6. 断点顺序
第一轮只看代码流,先不要看论文。
ts_benchmark/baselines/time_series_library/adapters_for_transformers.pyTransformerAdapter._process(...)- 看
dec_input = cat(label_len 历史尾部, horizon 零占位)。
ts_benchmark/baselines/time_series_library/models/Informer.pyforward(...)- 看进入
short_term_forecast(...)。
ts_benchmark/baselines/time_series_library/models/Informer.pyshort_forecast(...)- 看标准化、embedding、encoder、decoder、反标准化。
ts_benchmark/baselines/time_series_library/layers/Embed.pyDataEmbedding.forward(...)- 看
value_embedding + temporal_embedding + position_embedding。
ts_benchmark/baselines/time_series_library/layers/Transformer_EncDec.pyEncoder.forward(...)- 当前
e_layers=2,重点看EncoderLayer_0 -> ConvLayer_0 -> EncoderLayer_1。
ts_benchmark/baselines/time_series_library/layers/SelfAttention_Family.pyAttentionLayer.forward(...)- 看
Q/K/V projection -> view(B,L,H,-1)。
ts_benchmark/baselines/time_series_library/layers/SelfAttention_Family.pyProbAttention.forward(...)- 看
_prob_QK -> _get_initial_context -> _update_context。
ts_benchmark/baselines/time_series_library/layers/Transformer_EncDec.pyConvLayer.forward(...)- 看
permute -> Conv1d -> BatchNorm1d -> ELU -> MaxPool1d -> transpose,长度24 -> 13。
ts_benchmark/baselines/time_series_library/layers/Transformer_EncDec.pyDecoder.forward(...)- 看 decoder layer 循环。
ts_benchmark/baselines/time_series_library/layers/Transformer_EncDec.py
DecoderLayer.forward(...)- 看 self-attention、cross-attention、FFN。
7. 当前学习主线
text
run_benchmark
-> pipeline
-> eval_model
-> RollingForecast._eval_batch
-> forecast_fit
-> _process
-> Informer.forward
-> Informer.short_forecast
-> DataEmbedding encoder侧
-> Encoder
-> EncoderLayer_0
-> ProbAttention
-> ConvLayer distilling
-> EncoderLayer_1
-> ProbAttention
-> DataEmbedding decoder侧
-> Decoder
-> self-attention + cross-attention
-> projection
-> output[:, -pred_len:, :]这一轮的第一性:
先看清 Informer 怎样把 encoder 历史窗口和 decoder 半真实半占位窗口送进 encoder-decoder 结构,再用 ProbSparse attention 和 decoder cross-attention 生成未来窗口。
8. 最小数学例子的对应关系
当前命令的最小理解参数:
text
B = 4
seq_len = 24
label_len = 6
pred_len = 6
C = 1
d_model = 32
n_heads = 2
head_dim = 16
e_layers = 2
d_layers = 1decoder 输入构造:
text
target: (4, 12, 1)
历史部分: target[:, :6, :] -> (4, 6, 1)
未来占位: zeros_like(last 6步) -> (4, 6, 1)
拼接: cat(dim=1) -> (4, 12, 1)attention 拆头:
text
enc_embedding: (4, 24, 32)
Q/K/V Linear: (4, 24, 32)
view: (4, 24, 2, 16)encoder distilling:
text
EncoderLayer_0 output: (4, 24, 32)
ConvLayer_0:
permute: (4, 24, 32) -> (4, 32, 24)
Conv1d: (4, 32, 24) -> (4, 32, 26)
MaxPool1d: (4, 32, 26) -> (4, 32, 13)
transpose: (4, 32, 13) -> (4, 13, 32)
EncoderLayer_1 output: (4, 13, 32)decoder cross-attention:
text
query 来自 decoder: (4, 12, 2, 16)
key/value 来自 encoder: (4, 13, 2, 16)
score: (4, 2, 12, 13)最终输出:
text
decoder projection: (4, 12, 1)
取最后 pred_len: (4, 6, 1)