简介:面向长时间序列预测与分类任务的研究者和工程师,资源包整合了15种基于Transformer的代表性算法代码,包括Autoformer、PEDformer、Informer、Crossformer、ETSformer、Pyraformer、TimesNet、Reformer、DLinear等,覆盖电力负荷、ETT油温、外汇、病情、交通车流量及天气等典型数据集,可直接迁移到自己的实验场景。压缩包共含1378个文件,以PDF说明文档、Shell运行脚本、Python源码及pyc编译文件为主,同时附有npy预处理数据和CSV原始数据集,整体大小182.2MB,目录结构便于快速检索。目前已有3467人学习下载。通过对比各模型在不同数据上的预测效果,可系统掌握Transformer系模型的架构差异、训练配置与调参技巧,适合算法对比、论文复现及长期序列预测入门进阶。
1. Transformer长时间序列代码包:从Autoformer到PatchTST,这份汇总能帮你省下什么
做长时间序列预测的人,如今绕不开Transformer——Autoformer每次发布都要把SOTA往前推一截,Informer把注意力复杂度从O(L²)压到O(LlogL),PatchTST又用patch把序列建模重新定义了一遍。但真到动手跑实验,第一关往往不是原理,而是代码:论文主页链接失效、GitHub仓库里缺数据、版本对不上。
这份汇总代码的核心价值,是把Autoformer、FEDformer、Informer、PatchTST、DLinear、iTransformer等15个长序列预测算法的源码、数据装载器和训练脚本收在一个工程里。目录层次统一,命令行参数风格一致,下载解压之后不用再挨个去不同仓库里对齐环境,直接按一个模子跑就行。
它适合三类人:补论文实验基线的研究生、做算法选型评估的工程师、刚接触序列预测想尽快跑通一条完整pipeline的新手。它的目标不是把Transformer原理再讲一遍,而是让你在一个工作日内把至少三五个模型跑出可比的结果,并且知道每个模型的边界在哪。
2. 15个算法怎么选:稀疏注意力、频率分解与线性基线的定位
拿到一个包含15个模型的工程,最忌讳的是上来就逐个跑一遍。先花半小时把算法分类看清楚,后面所有实验才有意义。这一章按注意力机制和建模思路把这批模型拆成几个阵营,每个阵营告诉你它解决了什么、适合哪些数据。
2.1 稀疏注意力阵营:Informer、LogTrans与Pyraformer
Informer是这个汇总里最常被第一个跑通的模型。它提出的ProbSparse Attention不做全量点积,而是先算每个query的稀疏度,只保留信息量最大的那批query参与attention计算,把复杂度从O(L²)降到O(LlogL)。LogTrans走的是另一条路,用LogSparse让每个query只看指数步长范围内的key,Pyraformer则用金字塔结构在不同尺度上做注意力聚合。这三个模型共同的特点是“拿Transformer骨架换效率”,适合序列长度几千以上、显存吃紧的场景。
实际跑实验时,这一类往往表现最稳。Informer的代码结构后来被很多仓库继承,run.py、exp_main.py、models目录的划分方式几乎成了时间序列Transformer的默认工程范式。如果只挑一个模型作为全仓库的sanity check,我一般选Informer,因为它训练快、资源占用可控、出问题容易排查。
2.2 分解与频率阵营:Autoformer、FEDformer与ETSformer
Autoformer的核心是序列分解加自相关机制。它先把序列拆成趋势项和季节项,再用自相关取代原来的点对点注意力,按周期相似度聚合信息。FEDformer把输入变换到频域,在傅里叶域和小波域做稀疏表示,对强周期信号特别敏感。ETSformer则把指数平滑思想融进Transformer结构,趋势建模更直接。
这里要特别提醒一个命名坑:标题和很多博客里写的“PEDformer”,在开源代码里几乎全部对应FEDformer,也就是Frequency Enhanced Decomposed Transformer。你下载的汇总包里最有可能出现的是FEDformer文件夹,运行命令也是--model FEDformer。如果实在找不到PEDformer这个名字,别怀疑自己下错了包,先把models目录列出来看一眼。
这三个模型适合气温、电力负荷、流感这种带明显季节周期的数据。很多人喜欢造一个正弦波CSV来验证Transformer预测正弦数据的效果,这类单一强频率的玩具数据恰好是Autoformer和FEDformer最擅长的场景,跑出来的曲线会非常漂亮。
2.3 线性与补丁阵营:DLinear、PatchTST与iTransformer
DLinear严格说不是Transformer,但长序列预测领域你几乎不可能绕开它。Autoformer的论文里就专门跟DLinear做过对比,结论是DLinear在某些数据上甚至反超Transformer。这类汇总包把它收进来,就是为了给所有Transformer模型提供一个强基线。先跑DLinear再跑Transformer,你才知道Transformer到底值不值得上。
PatchTST把时间序列切成patch,每个patch当成一个token输入Transformer,相当于把注意力粒度从单点提升到局部片段。iTransformer则更激进,直接把“变量维度”当作token,时间步的信息靠内部FFT和卷积处理。这两个模型打破了一个默认假设——“时间步必须等于token”,是理解Transformer架构扩展方向的好样本。
2.4 选型速查表
| 模型 | 核心机制 | 复杂度特征 | 适合场景 |
|---|---|---|---|
| Informer | ProbSparse Attention | O(LlogL) | 长序列、显存敏感 |
| LogTrans | LogSparse Attention | O(L(logL)²) | 超长序列、日志数据 |
| Pyraformer | 金字塔注意力 | 近似O(L) | 极长序列、多层次周期 |
| Autoformer | 序列分解+自相关 | 接近O(LlogL) | 强周期、趋势明显 |
| FEDformer | 频域稀疏增强 | O(L)级别 | 强季节性、多周期叠加 |
| ETSformer | 指数平滑+Transformer | O(LlogL) | 趋势+季节混合 |
| DLinear | 线性分解 | 线性 | 全场景基线 |
| PatchTST | Patch嵌入+Transformer | O(LlogL) | 多变量预测 |
| iTransformer | 变量维度token化 | O(L²) | 变量数多、序列中等 |
| Crossformer | 跨维度注意力 | O(L1.5) | 多变量交叉依赖 |
| TimesNet | 2D时序变化 | 与周期相关 | 多周期信号 |
| SCINet | 多分辨率采样 | 线性 | 长序列、降采样场景 |
| StemGNN | 图+谱方法 | 与图结构相关 | 多变量强耦合 |
剩下一两个模型通常是同仓库里附带的小改进版本,选型思路逃不出上面这些阵营。我的建议是:上Transformer之前,先跑DLinear;数据周期特征强,重点试Autoformer和FEDformer;序列太长显存不够,Informer优先。
3. 跑通第一个预测实验:数据放哪、命令怎么敲、参数怎么改
这一章解决的是从下载到出指标的全过程。照着做,半小时内你屏幕上应该能看到test mse和test mae。
3.1 数据放对位置:CSV约定与日期解析
这类汇总工程的数据目录一般是 ./dataset,下面按数据集分子目录,比如ETT-small、weather、exchange_rate。ETT是电力变压器温度数据集,ETTh1是小时级,ETTm1是15分钟级,WTH是气象站数据,ILI是流感比例。先打开一个CSV看看结构:
$ head -5 dataset/ETT-small/ETTh1.csv date,HUFL,HULL,MUFL,LLUFL,MUFL_1,OT 2016-07-01 00:00:00,5.327,5.512,0.447,0.439,0.784,5.412 2016-07-01 01:00:00,5.301,5.465,0.441,0.434,0.776,5.390 2016-07-01 02:00:00,5.287,5.438,0.438,0.432,0.772,5.365注意两个硬性约定:第一列必须叫date,时间格式必须是“yyyy-MM-dd HH:mm:ss”;第二列以后全是数值列,最后一列通常是target列。data_loader用正则解析日期,如果你自己的数据只有日期没有小时,或者列名写作DateTime,轻则报KeyError,重则loss全程NaN。
如果只是想快速验证流程,不想去找公开数据,可以自己造一个正弦波CSV放进去。很多入门者验证Transformer预测正弦数据都用这招:生成两列date和value,value=sin(2πt/24),直接按训练脚本跑,模型收敛快、可视化效果直观。这个包对自定义CSV没有额外限制,只要列名和日期格式对它就行。
3.2 单模型训练:命令行参数在讲什么
确认数据集就位后,先用Autoformer跑一次完整训练加测试。这个系列的命令行参数跨模型通用,学会一组就等于会了所有。
python -u run.py \ --model Autoformer \ --data ETTh1 \ --root_path ./dataset/ETT-small/ \ --data_path ETTh1.csv \ --features M \ --target OT \ --seq_len 96 \ --label_len 48 \ --pred_len 96 \ --e_layers 2 \ --d_layers 1 \ --factor 3 \ --batch_size 32 \ --use_gpu 1--features三个取值要分清:M表示多变量预测多变量,输入所有列、预测所有列;S表示单变量预测单变量;MS表示输入多变量但只预测--target指定的单变量。--target对应CSV里的预测目标列名。--seq_len是回看窗口,--label_len是decoder里“已知尾部”的长度,--pred_len是要预测的步数。
Informer、Autoformer这套架构里,label_len不建议设成0,它负责给decoder提供已知真值尾部,设太小会影响训练稳定性。--e_layers和--d_layers分别控制编码器和解码器层数,显存不足时先砍e_layers。--factor是稀疏注意力或自相关的采样因子,调小直接降低计算量,但也可能牺牲精度。
日志会打印每一轮的train loss和val loss,训练结束后自动跑测试集,输出best val loss、test mse和test mae。看到这三项说明整条链路已经通了。
3.3 用循环把多个模型串起来
单模型跑通之后,下一步是把多个模型放到同一数据集上对比。这时候不建议开多个终端并行跑,显存会互相挤兑,日志也会混在一起。我一般写成串行循环:
for model in Autoformer FEDformer Informer DLinear PatchTST do echo "=== $model on ETTh1 ===" | tee -a exp_logs.txt python -u run.py \ --model $model \ --data ETTh1 \ --features M \ --target OT \ --seq_len 96 --label_len 48 --pred_len 96 \ --batch_size 32 --use_gpu 1 \ >> exp_logs.txt 2>&1 done这里用>>追加而不是>覆盖,防止后面的模型把前面的日志冲掉。tee -a是为了让屏幕上也实时看到当前跑的是哪个模型。跑完直接grep日志文件就能拿到所有模型的指标对比。如果只有CPU,把--use_gpu改成0,ETTh1这种小数据集每个模型大概多花几分钟,不至于等不起。
提示:训练日志里如果同时出现best val loss和test mse,以test mse为准做模型对比。best val loss只是选checkpoint用的,不代表最终测试表现。
4. 拆开黑匣子:Embedding、稀疏注意力与序列分解的实现细节
网上讲transformer模型详解的文章很多,但对着这份汇总里的源码读一遍,你会发现三件事:embedding不是简单线性映射,注意力不一定非要全量计算,长期预测离不开序列分解。这一章挑三个最关键的实现细节讲透。
4.1 Embedding与位置编码:时间特征怎么变成向量
这类代码包的Embedding模块通常由三部分相加组成:数值embedding、时间特征embedding、位置编码。TokenEmbedding最常用的实现是1D卷积,kernel_size为3,padding_mode设为circular:
class TokenEmbedding(nn.Module): def __init__(self, c_in, d_model): super().__init__() self.tokenConv = nn.Conv1d( in_channels=c_in, out_channels=d_model, kernel_size=3, padding=1, padding_mode='circular' ) def forward(self, x): # x: [batch, seq_len, c_in] x = self.tokenConv(x.permute(0, 2, 1)).transpose(1, 2) return x用卷积而不是全连接做embedding,好处是能保留相邻时间步的局部平滑信息。circular padding的意义在于序列两端不丢周期信息——对时间序列来说,开头和结尾很可能是同一个周期的不同相位。时间特征embedding接收的是x_mark,也就是hour、weekday、month这些扩展列,通过线性层压到d_model维。三者相加后过dropout,得到最终输入。
这个模块在代码里通常叫DataEmbedding,不管是Autoformer还是Informer、PatchTST,换汤不换药。如果你要改输入变量的数量,改c_in就行;要改时间特征粒度,改freq参数,比如freq='h'表示小时级特征,freq='t'会细化到分钟。
4.2 稀疏注意力与自相关:效率到底从哪来
Informer的ProbSparse Attention在源码里看起来不复杂,但理解它需要抓住一个点:不是所有query都值得计算。常见实现里先算出每个query的稀疏性得分,再取top-u个query参与后续attention:
L_K = K.shape[-2] u = int(self.factor * np.ceil(np.log(L_K))) # 采样query数量 # 对每个query计算近似稀疏度 score = self.measure_scores(Q, K) # 内部逻辑:点积减去logsumexp top_u_idx = score.topk(u, dim=1).indices # 只对top-u的query做完整attention Q_reduced = Q.gather(1, top_u_idx.unsqueeze(-1).expand(-1, -1, Q.shape[-1])) attn = torch.softmax(Q_reduced @ K.transpose(-2, -1) / math.sqrt(d_k), dim=-1) @ Vfactor取3时,每个query只对数个key做计算,序列长度越长,节省的算力越明显。这就是Informer能处理长序列的底气。Autoformer的自相关则完全换了一套思路,它用FFT把query序列变换到频域,找出能量最强的周期,再按周期偏移对不同子序列做聚合:
q_fft = torch.fft.rfft(query, dim=-1) freqs = torch.argmax(q_fft.abs(), dim=-1, keepdim=True) periods = (query.shape[-1] // freqs).clamp(min=1)这只是找主周期的直观示意,实际代码还会取topk周期做时延聚合。核心思想是:时间序列的相似性不一定体现在相邻点上,更可能体现在相同相位上。自相关正是从频域视角捕捉这种远程依赖,这也是Autoformer在长周期数据上比传统Transformer稳的原因。
4.3 序列分解:移动平均的前后padding
Autoformer、ETSformer、DLinear都依赖序列分解模块,把原始序列拆成趋势项加季节项。最常见的实现是移动平均:
class moving_avg(nn.Module): def __init__(self, kernel_size, stride): super().__init__() self.avg = nn.AvgPool1d(kernel_size=kernel_size, stride=stride, padding=0) def forward(self, x): # x: [batch, seq_len, features] front = x[:, 0:1, :].repeat(1, (self.avg.kernel_size[0] - 1) // 2, 1) end = x[:, -1:, :].repeat(1, (self.avg.kernel_size[0] - 1) // 2, 1) x = torch.cat([front, x, end], dim=1) return self.avg(x.permute(0, 2, 1)).permute(0, 2, 1)这段代码的玄机在padding。AvgPool1d会缩短序列长度,所以要在序列两端各补(kernel_size-1)//2个点。前端复制第一个值,后端复制最后一个值,这样趋势项的端点不会出现突变。kernel_size必须是奇数,否则两端补的点数不对称,趋势项在边界会歪。这个细节直接决定预测序列前几个点的质量,很多跑出来的曲线开头明显偏离,十有八九是这里出了问题。
为什么用AvgPool1d而不是直接卷积做平滑?因为池化层没有可学习参数,训练更稳定,反向传播简单,序列分解模块不需要引入额外参数量。ETSformer里还加了可学习的分解层,但基础版都是这个结构。
5. 常见问题与避坑:五个最容易翻车的现场
这一章写的都是真实跑代码时高频出现的坑,每一条我都按现象、原因、解决三步拆开。遇到了直接照做。
5.1 命名不一致与数据格式的坑
现象:解压代码包后,在models目录和脚本参数里都找不到PEDformer,直接执行--model PEDformer报ModuleNotFoundError。原因:社区里这个算法几乎统一叫FEDformer,即Frequency Enhanced Decomposed Transformer,标题里的PEDformer大概率是文章笔误或习惯性简写。解决:先执行ls models看一下真实目录名,用FEDformer作为--model参数启动。如果一定要保留旧的拼写习惯,可以在models里加一个别名文件:
# models/fedformer.py 里实际定义 FEDformer # 在 models/pedformer.py 里加一行 from models.fedformer import FEDformer现象:自己准备的数据集一训练,loss全程是NaN,或者加载时报KeyError: 'date'。原因:data_loader对日期列的解析依赖“date”这个列名和“yyyy-MM-dd HH:mm:ss”格式,列名不同或时间格式缺了秒段都会解析失败。解决:统一列名和时间格式,用pandas先洗一遍再放进去:
import pandas as pd df = pd.read_csv('your_data.csv') df.columns = ['date'] + list(df.columns[1:]) df['date'] = pd.to_datetime(df.date).dt.strftime('%Y-%m-%d %H:%M:%S') df.to_csv('dataset/custom/clean.csv', index=False)如果只是做冒烟测试,先拿仓库自带的ETTh1.csv跑通,再换自己的数据,能少踩很多无谓的雷。
5.2 算力与选型的坑
现象:设置seq_len=336、pred_len=720后,batch_size=32直接CUDA out of memory,换到batch_size=16还是爆。原因:注意力计算量和序列长度强相关,即便Informer做了稀疏化,336的历史窗口加720的预测长度,中间特征图尺寸依然不小。解决:按下面顺序依次调整,直到能跑起来:
python -u run.py --model Informer \ --batch_size 8 \ --e_layers 1 \ --factor 1 \ --use_amp先砍batch_size,再砍encoder层数,最后降factor。如果这三步都做完还在OOM,说明这个长度的实验应该先上DLinear这种线性模型,Transformer等资源充足再试。
现象:同样的ETTh1数据,Autoformer跑出来的MSE比DLinear还高。原因:Autoformer论文里明确承认过,DLinear这类线性分解模型在某些数据上的表现能反超Transformer。Transformer不是默认上分工具,它的优势集中在长序列、多周期、大样本场景。解决:每次做实验前先跑一轮DLinear当基线。如果DLinear已经达到业务指标,就不要硬上Transformer;如果Transformer明显更好,再继续往下调参。这个习惯能帮你省掉大量无效训练时间。
现象:同一套参数,把batch_size从16改成64后,最终test mse波动很大。原因:这个系列代码默认随机种子不固定,数据装载顺序、dropout、attention采样都会变,小batch下梯度噪声本来就大。解决:训练命令里固定seed:
python -u run.py --model Autoformer --seed 42 ...固定seed之后,多次跑的结果应该基本一致。调batch_size时同步把learning_rate按比例调整,经验值是batch翻倍、lr也翻倍,但在这套代码里我习惯反向操作——先降batch,再降lr,训练更稳。
6. 进阶用法:把15个模型整理成你自己的对比基线
跑通三五个模型之后,下一步是把整个仓库变成你自己的实验工具,而不是每次手动记录指标。我的做法是统一日志命名,写好收集脚本,让结果自动汇总成表。
先给每个实验建独立日志:
for model in Autoformer FEDformer Informer DLinear PatchTST iTransformer do python -u run.py \ --model $model --data ETTh1 \ --seq_len 96 --label_len 48 --pred_len 96 \ --batch_size 32 --seed 42 --use_gpu 1 \ 2>&1 | tee logs/${model}_96.log done跑完之后用grep把关键指标抽出来,再把结果统一解析成json:
import re, glob, json rows = [] for f in glob.glob('logs/*.log'): text = open(f).read() model = f.split('/')[-1].split('_')[0] mse = re.search(r'test_mse:([\d.]+)', text) mae = re.search(r'test_mae:([\d.]+)', text) if mse and mae: rows.append({ 'model': model, 'mse': float(mse.group(1)), 'mae': float(mae.group(1)) }) rows.sort(key=lambda x: x['mse']) print(json.dumps(rows, indent=2))这样每一个pred_len对应一份结果表,96、336、720三个预测长度各跑一轮,你就能看清每个模型对预测长度的敏感度——有些模型短预测很准、长预测崩盘,有些模型恰恰相反。这个视角对选型比单看一组指标有用得多。
如果想加自己的模型,套路也很固定:在models目录里复制一个现有模型文件,改成你的类名,把forward逻辑改掉,run.py大多是基于--model参数动态加载models目录下的文件,不需要改入口代码。改完先用短预测长度跑一轮,确认loss能降下来,再拉长seq_len和pred_len做正式对比。我自己第一次拿这个包做实验时就栽在PEDformer命名上,排查了两天才意识到是FEDformer。从那以后,每次拿到新的汇总包都强制自己先ls一遍models目录、先跑一轮DLinear基线、再固定seed和batch,这一套流程走下来,翻车率降了一大半。希望帮到你。
本文还有配套的精品资源,点击获取