Informer长序列预测实战:PyTorch工程解析与调参避坑指南

📅 发布时间:2026/10/5 8:24:02
Informer长序列预测实战:PyTorch工程解析与调参避坑指南
简介这份资源是面向深度学习与时间序列预测学习者的Informer模型Python实战案例包适合已具备一定PyTorch基础、希望深入掌握长序列预测技术的开发者与研究人员。案例围绕ProbSparse Self-Attention这一核心创新展开覆盖Encoder-Decoder结构、数据预处理、损失函数与优化器选择、模型训练及MAE、RMSE等指标评估的完整流程可应用于电力消耗、股票走势、气象预报等场景。压缩包共65个文件约115.97MB以py源码、npy数据、pyc缓存、xml配置、csv数据集和pth权重为主另含yml环境文件与txt说明目录按data、models、exp、utils等模块清晰划分便于按功能检索。目前已有330人学习下载。通过研读源码与实验配置读者可理解Informer降低长序列计算复杂度的原理掌握从数据加载到预测输出的工程实现路径并借助预置权重与结果文件快速复现实验提升时间序列建模的实战能力。1. 拿到 Informer 实战包先别急着跑这份代码到底能解决什么如果你正在做电力负荷预测、气象序列建模或者量化因子回测大概率绕不开长序列预测这个坎。传统 LSTM 在预测步长超过 48 之后误差会明显抬头而 Transformer 的注意力矩阵又是 O(L²) 的内存开销序列一长显存直接爆。Informer 就是冲着这两个痛点来的——它用 ProbSparse Self-Attention 把注意力复杂度压到 O(L log L)再配合自注意力蒸馏机制让 Encoder 能吞下超长输入。这份Informer模型实战python案例.zip就是一套可以直接落地的 PyTorch 工程不是那种只给一个 notebook 的玩具。它包含完整的数据加载、模型定义、实验调度和结果输出链路数据集用的是 ETTh1 电力变压器温度序列checkpoints 和 results 目录里已经带了跑过的痕迹。适合两类人一是想快速验证 Informer 在自己数据上效果的算法工程师二是需要一份结构清晰的时间序列项目骨架来二次开发的人。下面我按实际拆包顺序把这份资源怎么用、参数怎么调、哪里容易翻车讲透。2. 工程结构与数据流从 ETTh1.csv 到模型输入张量2.1 目录布局与模块职责解压之后先别打开main_informer.py花两分钟把目录结构过一遍后面调参和排错会省很多时间。这份工程的分层很清晰基本遵循了 Informer 原论文作者的代码组织习惯。根目录下main_informer.py是唯一入口所有实验通过命令行参数驱动。exp/目录放的是实验逻辑exp_basic.py定义基类负责设备选择、模型构建和优化器初始化exp_informer.py继承它实现了训练、验证、测试三个阶段的完整循环。models/下面是模型本体model.py是 Informer 主类attn.py实现了 ProbSparse Attention 和 Full Attention 两套注意力embed.py处理时间特征嵌入encoder.py和decoder.py分别对应编解码器堆叠。data/目录里data_loader.py是数据集类timefeatures.py负责从时间戳里提取小时、星期、月份等特征tools.py提供标准化和滑动窗口工具。utils/下面是评估指标和掩码工具metrics.py里是 MAE、MSE、RMSE 的实现。checkpoints/和results/里已经有一些预训练权重和预测输出文件名编码了超参配置比如informer_custom_ftMS_sl126_ll64_pl24_dm512_nh8_el2_dl1_df2048_atprob_fc5_ebtimeF_dtTrue_mxTrue_test_0这一长串不是乱码每个字段都对应一个命令行参数后面讲参数映射时会拆开说。2.2 数据加载与滑动窗口构造ETTh1.csv 是经典的电力变压器温度数据集包含 date、HUFL、HULL、MUFL、MULL、LUFL、LULL 和 OT 八列其中 OT 是预测目标。数据加载的核心在data_loader.py的Dataset_ETT_hour类里它做的事情是把长序列切成 Encoder 输入和 Decoder 输入两段。# data/data_loader.py 中 Dataset_ETT_hour 的关键逻辑 def __getitem__(self, index): s_begin index s_end s_begin self.seq_len # Encoder 输入长度 r_begin s_end - self.label_len # Decoder 起始位置 r_end r_begin self.label_len self.pred_len # Decoder 目标长度 seq_x self.data_x[s_begin:s_end] # 历史序列 seq_y self.data_y[r_begin:r_end] # 含标签的历史 待预测 seq_x_mark self.data_stamp[s_begin:s_end] # 时间特征 seq_y_mark self.data_stamp[r_begin:r_end] return seq_x, seq_y, seq_x_mark, seq_y_mark这里seq_len是 Encoder 吃进去的历史长度label_len是 Decoder 的起始 token 长度pred_len是真正要预测的未来步数。注意r_begin s_end - label_len这行它让 Decoder 输入和 Encoder 输入在时间轴上有一段重叠这段重叠就是所谓的 label 部分给 Decoder 提供对齐参考。如果你把label_len设成 0Decoder 就完全靠自身起始 token 去猜实测误差会明显变大。__init__里还会根据features参数决定用哪些列。featuresS表示单变量预测只用 OT 列featuresM是多变量预测所有列都进模型但只预测 OTfeaturesMS是多变量输入单变量输出。这份工程默认配置里featuresMS也就是多变量喂进去、只预测 OT 这一列。2.3 时间特征编码与嵌入层timefeatures.py里的time_features函数会把 datetime 列拆成小时、星期、日、月等离散特征再通过embed.py里的TemporalEmbedding或TimeFeatureEmbedding映射成连续向量。这一步容易被忽略但对电力负荷这种强周期序列影响很大。# models/embed.py 中 DataEmbedding 的组装方式 class DataEmbedding(nn.Module): def __init__(self, c_in, d_model, embed_typefixed, freqh, dropout0.1): super(DataEmbedding, self).__init__() self.value_embedding TokenEmbedding(c_inc_in, d_modeld_model) self.position_embedding PositionalEmbedding(d_modeld_model) self.temporal_embedding TemporalEmbedding(d_modeld_model, embed_typeembed_type, freqfreq) self.dropout nn.Dropout(pdropout) def forward(self, x, x_mark): x self.value_embedding(x) self.position_embedding(x) self.temporal_embedding(x_mark) return self.dropout(x)值嵌入、位置嵌入、时间嵌入三者相加这是 Informer 处理时间序列的标准做法。freqh对应小时级数据如果你的数据是 15 分钟粒度要改成freqt否则时间特征提取会错位。embed_typefixed用的是固定频率嵌入learned则是可学习嵌入后者参数更多但小数据集上容易过拟合。提示替换自己的数据时CSV 必须保留 date 列且格式为YYYY-MM-DD HH:MM:SS否则timefeatures.py里的pd.to_datetime会直接抛异常。3. 训练与调参main_informer.py 命令行参数逐个拆3.1 启动命令与核心参数映射工程入口是main_informer.py所有配置通过 argparse 传入。先看一条能跑通的基线命令python -u main_informer.py \ --model informer \ --data ETTh1 \ --root_path ./data/ \ --data_path ETTh1.csv \ --features MS \ --target OT \ --seq_len 96 \ --label_len 48 \ --pred_len 24 \ --e_layers 2 \ --d_layers 1 \ --d_model 512 \ --n_heads 8 \ --d_ff 2048 \ --factor 5 \ --attn prob \ --embed timeF \ --des Exp \ --itr 1 \ --train_epochs 6 \ --batch_size 32 \ --learning_rate 0.0001--model informer指定模型类型工程里还支持informer和informerstack两种后者 Encoder 层数更深。--seq_len 96是历史窗口--pred_len 24是预测未来 24 个时间步这两个参数直接决定任务难度。--factor 5是 ProbSparse Attention 的采样因子控制 query 采样的稀疏程度值越大越接近全注意力计算量也越大。--attn prob启用 ProbSparse改成full就是标准注意力可以用来做消融对比。--e_layers 2和--d_layers 1分别是 Encoder 和 Decoder 的层数。原论文在 ETTh1 上推荐 e_layers2、d_layers1层数再往上加收益递减且容易过拟合。--d_model 512是隐藏维度--n_heads 8是注意力头数两者必须满足 d_model 能被 n_heads 整除否则 forward 时会报维度不匹配。3.2 训练循环与损失函数exp_informer.py里的train方法封装了完整训练流程。损失函数默认用 MSE优化器是 Adam学习率通过--learning_rate控制。训练过程中会动态调整学习率用的是OneCycleLR或者ReduceLROnPlateau具体看exp_basic.py里的_select_optimizer和_select_criterion。# exp/exp_informer.py 训练阶段的核心片段 for epoch in range(self.args.train_epochs): self.model.train() for i, (batch_x, batch_y, batch_x_mark, batch_y_mark) in enumerate(train_loader): batch_x batch_x.float().to(self.device) batch_y batch_y.float().to(self.device) outputs self.model(batch_x, batch_x_mark, dec_inp, batch_y_mark) f_dim -1 if self.args.features MS else 0 outputs outputs[:, -self.args.pred_len:, f_dim:] batch_y batch_y[:, -self.args.pred_len:, f_dim:].to(self.device) loss criterion(outputs, batch_y) loss.backward() self.optimizer.step()注意f_dim -1 if self.args.features MS else 0这行它决定了从模型输出里取哪一列算损失。featuresMS时只取最后一列OTfeaturesM时取所有列。如果你改成单变量预测但忘了改这个逻辑损失会算在所有列上训练出来的模型预测 OT 时反而不准。dec_inp的构造也值得看一眼。它把batch_y的前label_len步和后面pred_len步的零填充拼在一起作为 Decoder 的输入。这个设计让 Decoder 在训练时能看到一部分真实历史值推理时那部分就换成模型自己的预测。3.3 学习率与 batch_size 的实操建议--learning_rate 0.0001是原论文的默认值但在 ETTh1 这种数据量下1e-4 配合 batch_size32 收敛比较稳。如果你把pred_len拉到 96 或 192建议把学习率降到 5e-5否则前期 loss 震荡会很明显。--batch_size在显存允许的前提下尽量往大了设32 是保守值48 或 64 通常能更快收敛。--train_epochs 6是原论文的设置但实测在 ETTh1 上 4 到 6 个 epoch 就基本收敛了再多跑验证集 loss 会抬头。如果你换了自己的数据先用 3 个 epoch 跑一遍看 loss 曲线再决定要不要加。注意--itr 1表示只跑一次实验设成大于 1 会重复跑多次取平均但每次的随机种子不同结果会有波动。做对比实验时建议固定种子在main_informer.py里加--seed 2021之类的参数。4. 避坑与排查这份代码最容易翻车的五个地方4.1 报错 “RuntimeError: The size of tensor a must match tensor b”现象是 forward 过程中维度不匹配通常发生在attn.py的注意力计算或者embed.py的嵌入相加阶段。原因多半是d_model和n_heads不整除或者seq_len和label_len的设置导致 Decoder 输入长度对不上。解决方法是先检查d_model % n_heads 0再确认label_len seq_len且pred_len和label_len之和不超过数据总长度。4.2 训练 loss 不降反升现象是前几个 batch loss 正常下降后面突然飙升到 NaN。原因通常是学习率过大或者数据没做标准化。这份工程在data_loader.py里默认用 StandardScaler 对数据做了归一化但如果你替换了自己的 CSV 且某列方差为 0scaler 会产生除零。解决办法是在data_loader.py的__init__里加一行self.scaler.fit(self.data_x)之前先检查np.std是否为零或者直接删掉常数列。4.3 预测结果全是常数现象是模型输出一条水平线MAE 和 RMSE 都很大。原因可能是--features设成了S但数据里有多列或者--target指定的列名在 CSV 里不存在。检查方法是打印data_loader里self.data_x.shape确认列数和features参数匹配。另一个常见原因是--embed设成了fixed但freq没改时间特征全零模型学不到周期性。4.4 GPU 显存溢出现象是跑几个 batch 后报 CUDA out of memory。原因主要是seq_len或d_model太大或者batch_size没根据显存调整。解决办法是先把batch_size降到 16再逐步降d_model到 256 或 128。如果还不行把--attn从prob改成full反而可能更省显存因为 ProbSparse 的采样操作会额外占内存。4.5 checkpoints 加载失败现象是torch.load报 key 不匹配。原因是保存模型时用了nn.DataParallel包装加载时没有对应处理。这份工程的exp_informer.py里已经做了model.module的判断但如果你自己改了模型结构state_dict 的 key 会变。解决办法是用torch.load(path, map_locationcpu)先加载到 CPU再手动过滤掉不匹配的 key或者直接用strictFalse加载。5. 进阶技巧用自定义数据复现并验证 Informer 效果5.1 替换数据集的完整步骤想用自己的数据跑一遍按下面四步走。第一步把 CSV 放到data/目录下确保有 date 列和目标列。第二步在data_loader.py里仿照Dataset_ETT_hour写一个自己的 Dataset 类或者直接改Dataset_ETT_hour的__init__里的文件路径和列名。第三步在main_informer.py的data_parser里注册新的data选项。第四步调整--seq_len、--label_len、--pred_len三个参数让它们和你的数据粒度匹配。# 自定义 Dataset 的关键改动点 class Dataset_Custom(Dataset): def __init__(self, root_path, flagtrain, sizeNone, featuresS, data_pathcustom.csv, targetOT, scaleTrue, timeenc0, freqh): # size [seq_len, label_len, pred_len] self.seq_len size[0] self.label_len size[1] self.pred_len size[2] # 读取 CSV 并指定时间列和目标列 df_raw pd.read_csv(os.path.join(root_path, data_path)) cols list(df_raw.columns) cols.remove(self.target) cols.remove(date) df_raw df_raw[[date] cols [self.target]] # 后续切分和标准化逻辑与 Dataset_ETT_hour 一致freq参数要根据你的数据粒度改小时级用h天级用d15 分钟用t。timeenc0表示用timefeatures.py里的time_featurestimeenc1表示用TimeFeatureEmbedding的可学习版本。小数据集建议用 0大数据集可以试 1。5.2 验证模型是否真的学到了东西跑完训练后results/目录下会生成pred.npy和true.npy分别是预测值和真实值。用下面这段代码画图对比能直观判断模型是学到了趋势还是只输出了均值。import numpy as np import matplotlib.pyplot as plt pred np.load(results/informer_custom_ftMS_sl96_ll48_pl24_dm512_nh8_el2_dl1_df2048_atprob_fc5_ebtimeF_dtTrue_mxTrue_test_0/pred.npy) true np.load(results/informer_custom_ftMS_sl96_ll48_pl24_dm512_nh8_el2_dl1_df2048_atprob_fc5_ebtimeF_dtTrue_mxTrue_test_0/true.npy) plt.figure(figsize(12, 4)) plt.plot(true[0, :, -1], labelGroundTruth) plt.plot(pred[0, :, -1], labelPrediction) plt.legend() plt.savefig(comparison.png, dpi150)如果预测曲线和真实曲线在波峰波谷处能对上说明模型学到了周期模式如果预测是一条平滑直线大概率是欠拟合或者学习率太小。我一般会先跑 3 个 epoch 看曲线趋势再决定要不要加 epoch 或调学习率。5.3 一个容易被忽略的验证习惯从那以后我每次换数据集都会先跑一遍--pred_len等于--seq_len的极端配置看模型能不能复现历史序列。如果连历史都拟合不了说明数据加载或标准化环节有 bug不用往下调参了。这个习惯帮我省过至少三次通宵排查的时间。另外--attn prob和--attn full的对比实验建议至少跑一次ProbSparse 在长序列上的加速比在seq_len336以上才会明显体现短序列上两者差距不大。希望帮到你。本文还有配套的精品资源点击获取