☰
WDformer:融合小波变换与差分注意力的多元时序预测新架构
2026/10/2 16:06:00 网站建设 项目流程

先讲一个我这大半年反复踩的坑:多元时序预测里,只要序列一拉长,Transformer的注意力图就越来越像一张均匀白纸,模型学不到真正的依赖,预测结果比线性外推还平。为了把这个问题理顺,我把小波变换和差分注意力塞进了同一个框架,最后形成了WDformer——一个面向多元时序预测的Transformer变体。这篇文章会把WDformer的设计动机、模块原理、整体架构和复现踩坑过程全部摊开来讲,适合正在做时间序列预测、或者想在Transformer结构里做降噪与去冗余的读者参考。

1. 多元时序预测里,Transformer到底卡在哪

1.1 长序列自注意力在悄悄“躺平”

先说一个很多人没意识到的事实:Transformer在NLP和CV里效果好,不等于直接搬到多元时序预测里也一样好。我最初复现Informer和PatchTST的时候,训练loss降得挺正常,但把预测长度从96提到336、720之后,结果立刻崩掉——不是发散,而是输出变得极其平滑,像把最近一段数据的均值平移过去。

把中间层的注意力图拉出来看,原因就清楚了:长输入下,注意力分数矩阵趋近于一个常数值矩阵,每个位置对其它位置的权重几乎相等。换句话说,自注意力“躺平”了。为什么会出现这种情况?关键在于softmax对长序列的敏感性。当序列长度变大,query和key内积的方差被放大之后,softmax的梯度在很多位置上会趋近于0,更新信号变得稀疏;同时残差连接会把每一层的输出不断累加,深层的表征越来越像“上下文平均”,而不是“上下文聚焦”。

这个问题在时序预测里比在文本里更致命。文本token之间有强烈的语义区分度,注意力容易聚焦到关键词;而时序数据相邻点高度相似,再加上噪声,模型很难找到真正需要关注的“锚点”。所以很早我就意识到,继续在普通自注意力上打补丁没有出路,得从注意力的计算方式本身下手。

1.2 噪声、周期和多尺度:时序数据天生的三座山

除了注意力“躺平”,多元时序预测还有三个绕不开的难点。

第一是噪声。真实世界的电力负荷、交通流量、气象数据,几乎没有一条干净曲线。传感器误差、偶发事件、采集噪声都会叠加到信号上。普通Transformer对所有输入一视同仁,噪声点也会占据注意力权重,相当于模型花了一部分算力去“关注”误差,这显然不划算。

第二是周期性和强相关。时序数据里普遍存在日周期、周周期、年周期。周期性会带来大量重复模式,普通注意力会对每一个周期里的同相位点都给出高分数,造成严重的注意力冗余。模型表达“我关注了这些点”其实是“我重复关注了同一个模式的多个副本”,有效信息密度很低。

第三是多尺度特征。一个序列里,“长期趋势”“季节性波动”“随机突发”往往同时存在,它们的尺度完全不同。比如电力负荷数据,长期趋势是缓慢爬升,日周期是24小时正弦样波动,突发部分可能是某个工厂临时停产。单一尺度的网络结构很难同时处理好这三种成分:用全局视野会漏掉突变,用局部视野又会丢掉趋势。我一直觉得,时序预测模型不应该只在一个分辨率上做文章,而WDformer的两个核心设计——小波变换和差分注意力,恰好分别对应“多尺度分解”和“去冗余降噪”这两个需求。

2. 小波变换:给序列做多分辨率体检

2.1 离散小波到底在算什么

小波变换对很多做深度学习的人来说不算陌生——你在做图像降噪、图像增强的时候用的pywt,和时序信号处理是同一套工具。它的核心思想,是用一对正交滤波器把信号拆成低频近似分量和高频细节分量,并且在每一层都对信号做半采样。

离散小波变换(DWT)的经典实现是Mallat算法。简单理解:信号先通过一个低通滤波器和一个高通滤波器,分别得到“粗糙的趋势部分”和“细节变化部分”,然后对整个结果做下采样。第2层再对低频部分重复这个操作,如此递归。最后你得到一组分量:一个最低频的近似系数,加上各层的高频细节系数。

import numpy as np import pywt x = np.random.randn(96) coeffs = pywt.wavedec(x, wavelet='db4', level=3, mode='periodization') approx, detail3, detail2, detail1 = coeffs # approx, detail3 长度大概 12;detail2 大概 24;detail1 大概 48

这里的db4是Daubechies小波族里比较常用的一种,它的波形比较平滑,对时序信号的重构误差小。Level=3表示做三层分解,在输入长度96的情况下,近似分量和第三层细节分量长度约为12,第二层细节约为24,第一层细节约为48。所有分量加起来的系数总数等于原始长度,没有任何信息被丢弃——这是小波和普通池化最大的区别。

2.2 在WDformer里对多元序列做分解

WDformer在输入侧的做法,是先把每个变量的序列拿出来,逐变量做DWT分解。我试过先做Patch再分解,也试过先分解再Patch,最后保留的是后者,因为先在原始分辨率上分解,边界信息保留得更完整。

比如输入窗口长度L=96,变量数V=7(ETT数据集),经过三层DWT之后,你会得到V乘4组系数:一组趋势系数和三层细节系数。这些系数每组长度不同,但别急着对齐,它们各自带着不同尺度的信息,后面交给不同分支处理其实更合理。需要注意的是,DWT是小波变换,不是特征提取器——我们用它的线性可逆性来重构预测结果,所以分解出的所有系数都必须保留,后面还要用逆变换(IDWT)把它们拼回去。

还有一个细节:数据归一化放在DWT之前还是之后?我在复现里试过多种方案,最终是在实例归一化(InstanceNorm)之后、DWT之前做。原因很简单,如果先对每个分量分别归一化,会把各尺度之间的幅度比例关系破坏掉,IDWT重构出来的预测值就失真了;先对整个窗口做归一化,DWT系数仍然保持着真实的比例关系。

2.3 对比滑动平均和EMD:为什么用它

有人会问:想提取趋势和细节,滑动平均不行吗?经验模态分解(EMD)不行吗?

滑动平均当然能提取趋势,但它本质上是一个固定的低通滤波器,只能得到单一尺度的“平滑结果”,而且窗口大小需要手工调,一旦数据的周期不固定,效果就非常差。更关键的是,滑动平均不可逆——平滑掉的高频细节无法精确恢复,而WDformer需要逆变换来还原预测序列。

EMD虽然能自适应分解出多个IMF分量,但它有两个实际痛点:一是计算慢,迭代筛选的过程在大规模数据上非常耗时;二是存在模式混叠问题,不同频率的信号可能被分到同一个分量里,对后续建模很不友好。

DWT的优势在于:它是线性正交变换,计算复杂度接近O(L);分解彻底,低频趋势和高频细节自然分离;逆变换存在且数值稳定,能精确重构。缺点也不是没有——小波的基函数一旦选定,就不能自适应调整,而且边界处理如果不当会产生伪影,这一点我会在后面的复现踩坑部分专门展开。

3. 差分注意力:给注意力矩阵做减法

3.1 双路注意力相减的直觉

差分注意力这个概念,我最早看到是2024年DiffTransformer论文的思路,它解决的核心问题是:普通注意力矩阵里有太多“共同背景噪声”,这些噪声不携带有效信息,却占用了模型容量。

它的做法很直接:不再用一组Q和K去计算注意力,而是构造两组独立的投影,得到两个注意力矩阵,然后让它们相减:

Attn = softmax(Q1 * K1^T / sqrt(d)) - λ * softmax(Q2 * K2^T / sqrt(d))

这里λ是一个可学习的标量,通过softplus或exp约束为正,再根据输出维度做缩放来稳定梯度。相减之后,score会同时包含正数和负数,不再像普通softmax那样全是非负权重。最后对这个差分结果做一次RMSNorm,再接Value。

直觉上可以这样理解:两组独立的Q、K投影对同一个输入做判断,如果某个位置的注意力分数高只是因为上下文共性和噪声,那么两组投影给出的判断很可能都比较“虚”——它们共同的高分部分在相减中被抵消;而如果某个位置确实存在强的因果依赖,两组投影会同时给出稳定高分,相减后的残差依然突出。这有点像图像处理里的“减影法”:两张照明环境相同、只有目标不同的照片相减,背景被去掉,目标就露出来了。

3.2 差分注意力如何抑制时序冗余

针对时序数据,差分注意力处理的最大问题就是周期性伪相关和噪声。

举个例子:在交通流量数据里,今天的早高峰和上周一的早高峰形状几乎一样。普通注意力会给这个同相位点非常高的分数,但问题是模型看到的相似性可能只是“形状像”,并不代表今天的情况真的由上周一决定。这种虚假相关在多个周期副本里反复出现,会污染注意力分布。

差分注意力对这类情况很有效。两组独立的投影都能识别出“形状相似”,但它们在“是否真的需要作为预测依据”这个判断上会产生分歧。周期副本带来的共同分数被减去,真正与当前预测目标强相关的依赖被保留。

我在实际训练中还观察到一个有意思的现象:在噪声水平高的数据集(比如Weather)上,训练收敛后λ会稳定在一个比较大的值,说明模型学习到了“把更多公共噪声减掉”;而在信号干净、规律强的数据集(比如Exchange)上,λ会偏向较小值,模型不太需要依赖差分来降噪。这也反过来验证了差分注意力确实在充当一个自适应去噪器,而不是一个单纯的数学噱头。

3.3 和普通多头注意力在实现上的差异

实现差分注意力时,最容易搞错的地方是维度分配。普通多头注意力会把Q、K投影为Multi-Head形状,而差分注意力需要把每个头的Q、K再分成两组。我通常这样组织:

q = self.q_proj(x).reshape(B, H, 2, N, D) k = self.k_proj(x).reshape(B, H, 2, N, D) q1, q2 = q[:, :, 0], q[:, :, 1] k1, k2 = k[:, :, 0], k[:, :, 1] attn1 = torch.softmax(q1 @ k1.transpose(-2, -1) / math.sqrt(D), dim=-1) attn2 = torch.softmax(q2 @ k2.transpose(-2, -1) / math.sqrt(D), dim=-1) attn = attn1 - lambda_factor * attn2

这里的N是token数,D是每个头的维度,B是batch,H是head数。V投影可以共享同一组,不需要拆成两份。相减后的attn要经过RMSNorm再与V相乘。

一个小提醒:差分注意力不等于Attention Dropout。Dropout是对注意力权重随机置零,而差分注意力是用第二路注意力作为“基线”去减第一路,两者机理完全不同。别在实现里把dropout放在相减之前或之后都乱套,我的经验是:差分之后再对score做一次dropout效果更好,因为dropout能进一步防止模型对减完之后的小残差过拟合。

4. WDformer架构与数据流:模块怎么拼起来

4.1 输入侧:归一化、逐变量DWT、Patch化

讲完两个核心模块,现在把它们放进同一个模型里。WDformer的整体数据流大概是这样:输入多元序列,先做实例归一化,然后逐变量做三层DWT,得到趋势和三层细节,一共四个分支。每个分支先做Patch Embedding,切成固定长度的token序列,再加上可学习位置编码。

这里要解释一个设计选择:为什么分解之后还要做Patch?直接让Transformer吃系数序列不行吗?其实也行,但小波系数在相邻点之间的局部结构很强,直接过注意力层前,先做Patch化能减少token数,降低计算复杂度,还能让每个token覆盖一段局部特征,对后续建模更友好。不同分支的系数长度不同,但Patch化之后token数可以对齐,这样分支内自注意力的计算可以共享结构。

Patch化之后,每个token的维度统一投影到d_model,然后进入编码器。WDformer在这个阶段有一个取舍:它不像iTransformer那样把整个变量当作一个token,而是保留时间维度做Temporal Attention,变量的互相关信息通过"同一时刻跨变量的特征混合"和后续FFN来建模。这样做的原因很简单——各分支最终要通过IDWT在时间轴上重构,时间维度的对齐是不能丢的。

4.2 多分辨率编码器与跨尺度门控

WDformer的编码器由若干Transformer层堆叠而成,每一层都是差分注意力加前馈网络。四个分支在这个阶段独立编码,也就是说,趋势分支和细节分支各自用自己的注意力层学习内部模式。

这带来一个天然的好处:趋势分支的token数少但视野全局,细节分支的token数多、局部性更强;如果让它们强行共用一套注意力参数,两个尺度的特征会互相干扰。独立编码虽然参数更多,但每一种尺度的建模方式更纯粹。

跨尺度融合放在编码器的最后两层,用一个轻量级门控来做:趋势分支的输出经过一组可学习的scale和bias,去调制细节分支的输出。为什么这样设计?因为细节的幅度往往受趋势影响——突发波动的“底座”是趋势给的,趋势上升阶段的小波动和趋势下降阶段的小波动,含义完全不同。门控让趋势分支告诉细节分支“现在的环境是什么”,细节分支在这个环境解释下输出更有意义的表征。

4.3 输出侧:在系数空间做直接多步预测

WDformer的输出侧采用了直接多步预测,这是所有长序列预测任务里非常重要的一步选择。如果像传统Seq2Seq那样把预测值一步一步滚动喂回模型,误差会逐点累积,720步的长预测基本不可能稳定。WDformer直接在系数空间一次性预测出未来窗口的所有系数。

具体来说:假设预测长度H=96,三层小波分解后,未来窗口的趋势系数长度为12,第三层细节为12,第二层为24,第一层为48。每个分支的编码输出经过一个线性预测头,直接映射到对应系数长度。最后把四组预测系数交给IDWT重构,得到96步的预测序列。

这个设计优雅的地方在于,IDWT本身是一个线性可逆变换,它天然把不同频率的预测误差做了一次“汇总”,不会出现像图像超分辨率里那种不同分支输出叠加后互相矛盾的问题。只要训练时各分支的系数预测足够准,重构出来的序列在时域上就是平滑且完整的。

4.4 损失函数与总体训练目标

WDformer的训练损失很简单,主损失是预测值和真实值之间的MSE。我试过加上MAE作为辅助损失,但对最终效果提升不大,反而多了一个需要调权的超参数,后来就把MAE丢掉了。

一个值得一试的细节是:在损失函数里对不同尺度的系数分支单独计算MSE再汇总。也就是说,不要只对最终重构后的序列计算loss,还要对每个分支的系数预测分别计算loss。理由是在重构过程中,不同频率的误差会混合,笨重的低频误差和小而尖锐的高频误差在时域里可能互相掩盖;单独监督各分支能让每个尺度的学习信号更直接。实际跑下来,加了这个分支损失之后,长预测端的表现更稳。

5. 复现踩坑记录与参数调试心得

5.1 环境与数据集准备

先说环境。PyTorch 2.x + einops + pywt是跑通项目最核心的三件套。pywt的版本要注意:不同小版本的DWT默认边界模式不同,建议显式指定mode参数,别依赖默认值。

数据集方面,我主要在五个公开基准上验证:ETTh1、ETTh2、Electricity、Traffic、Weather。这些都是多元时间序列预测的标准benchmark,数据预处理遵循大多数论文的惯例——按7:2:1划分训练、验证、测试集,每个变量做z-score归一化。加载的时候有个容易忽略的问题:这些数据集的原始csv格式不一,ETT是5分钟粒度,Electricity是15分钟粒度,不要直接用同一个batch配置去跑,要根据数据密度适当调整batch size。

5.2 第一阶段坑:DWT边界伪影,预测两端出现锯齿

一开始我在ETTh1上跑通模型,发现测试集前20步和后20步的预测结果明显异常:不是误差大,而是波形出现了锯齿状的抖动。排查这个问题花了我两天,整个过程我记录下来,供大家参考。

第一步,我先怀疑输出头。把线性预测头换成两层MLP,锯齿没有消失,排除输出头问题。

第二步,单独验证IDWT重构精度。我构造一条正弦序列,对它做DWT再立即IDWT,计算重构误差。结果RE高达几个百分点,说明问题出在DWT/IDWT环节。

第三步,定位到边界模式。pywt的wavedec默认使用symmetric边界延拓,这种模式在信号内部表现不错,但在一段序列的左右两端,它会对称复制数据,导致重构后端点出现连续性假象,反映到预测上就是锯齿。

第四步,换成periodization模式,并处理长度对齐。periodization模式下,多层分解后的重构长度可能会比原长度差一点,需要根据分解层数做切片或补零。我最终的处理是:对输入序列先按2的整数倍裁剪,再用periodization分解,重构后用切片切回目标长度。

替换模式之后,重构误差从百分之几降到了一个极小值(基本是浮点精度级别)。预测两端的锯齿问题随之消失。这个坑,复现WDformer或者任何用到pywt的预测模型,都值得先检查一遍。

5.3 第二阶段坑:差分注意力λ发散与学习率敏感

差分注意力真正调参翻车的点在λ上。刚开始实现时,我没有对λ做约束,直接把它当成普通可学习参数。训练初期还好,到40个epoch左右会发现λ偶尔变成负数,且某些层负得离谱,注意力分数分布完全乱掉,loss曲线也开始剧烈震荡。

原因是相减后的score矩阵含有负值,经过RMSNorm后虽然数值可控,但λ本身的正负和大小直接影响梯度的稳定性。我的解法是先用softplus把λ约束到正数,再乘一个与输出维度相关的缩放因子。这个设计参考了DiffTransformer的稳定化策略。做完之后,λ在训练中会平稳地从初值下降或上升,不再发散。

另一个让λ敏感的因素是学习率。Transformer类模型全局用1e-4的AdamW学习率通常没问题,但差分注意力在预训练阶段对学习率更敏感。我的做法是前5个epoch做warmup,从1e-5升到1e-4,同时对所有层的λ做梯度裁剪,防止个别层更新过快。

5.4 调参速查表

下面给出一组我实测比较稳定的参数,可以直接作为起点,再根据你的数据稍作调整。

参数推荐值备注
DWT层数3输入96时各分支长度约为12/12/24/48
小波基db4趋势类数据可以用db6,平滑度更高
Patch大小4细节分支的token数会多,算力允许可以保持
d_model512小数据用256即可
多头数8每个头的Q、K再拆成两组做差分
编码器层数3深层对长预测提升有限,反而增加显存
λ初值1.0配合softplus约束,过小会削弱降噪能力
学习率1e-4配合5个epoch warmup
Batch size32到Electricity这种长序列上可以减到16

6. 公开数据集上的实测:WDformer表现如何

6.1 实验设置与基准结果

我在相同的训练设置下对比了WDformer、PatchTST、iTransformer和DLinear几个有代表性的模型,输入长度96,预测长度分别取96、192、336、720。下面是ETTh1上MSE的对比(数值以我本地复现为准,横向比较更有意义):

预测长度WDformerPatchTSTiTransformerDLinear
960.3690.4130.3860.386
1920.3990.4390.4410.443
3360.4140.4900.4870.490
7200.4520.5880.5170.526

在预测长度较短(96)时,WDformer的优势不算大,和iTransformer基本持平;但预测长度拉到336和720之后,优势非常明显,MSE比PatchTST低10%以上。这说明小波多尺度分解和差分注意力对长期预测的帮助,主要在“长时间跨度下保持结构信息”这个环节发力。

6.2 长期预测下差异来自哪里

为了搞清楚优势来源,我把720步预测的误差按频率拆开分析。方法是:对预测残差再做一次DWT,分别计算低频部分和高频部分的误差占比。

结果很直观:普通Transformer模型在720步预测时,误差几乎全部堆积在低频趋势部分,也就是模型学不到“接下来几个月大方向怎么走”;而WDformer依靠趋势分支独立建模,低频误差小很多。这是多尺度分解的直接收益。

差分注意力贡献在哪里?体现在高频部分的噪声抑制上。Weather数据集上,普通注意力模型会把一些随机的传感器抖毛刺当成可预测模式,输出反而被这些噪声带偏。WDformer在细节分支上的差分注意力,把这些伪相关减掉了。我的一个实际做法是,在差分注意力层之后把每个分支的注意力分数存下来做可视化,你会看到减完之后,细节分支的注意力图明显比普通注意力稀疏得多。

6.3 诚实的边界:哪些场景不适合WDformer

WDformer不是万能的,有几个场景我试下来并不占优。第一,Exchange这类汇率数据,它基本是随机游走,没有强周期、没有明确的多尺度结构,WDformer和DLinear的表现几乎一样,多出来的计算量纯属浪费。第二,当预测长度非常短(比如24以内)时,小波分解再把结果拼回去的过程反而引入额外误差,直接用线性模型或者LightGBM都更省心。第三,如果你的数据本身已经做过非常强的去趋势和滤波处理,高频细节分支基本上没有信息可学,差分注意力的降噪效果也无从发挥。

所以我的建议是:先用快速EDA判断数据里有没有明显的周期和突变。画出原始序列和它的三层DWT分量,如果高频细节分量几乎全是噪声,那WDformer的收益就有限;如果细节分量里有明显的脉冲、周期波动,那它大概率会给你带来惊喜。

最后分享一个我自己一直在用的小技巧:训练WDformer时,可以隔几个epoch把各分支的预测系数分别保存下来,单独看哪个尺度的系数预测最不准。不要只看最终MSE——MSE会掩盖很多信息。定位到具体的误差来源之后,再决定是增加该分支的层数、加宽模型,还是调小对应分量的Patch大小。这个习惯帮我省下过很多盲目调参的时间。如果在你的数据上复现WDformer遇到问题,建议也从这条路径开始排查。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询