长上下文建模这两年卷得厉害,核心矛盾就一个:注意力机制给足全局交互,但复杂度 O(L²) 让序列一旦过万就寸步难行。我关注了不少替代方案,从线性注意力到 S4、Mamba,各有各的取舍。最近又看到 SPECTRE,思路很有意思——它干脆绕开注意力矩阵,用 FFT 把“所有 token 和所有 token 交互”搬到频率域去做,得到一个 O(L log L) 的全局混合器。我复现加实验折腾了一段时间,这篇就把它背后的原理、工程细节和踩过的坑一起拆开说说。
1. 长上下文建模的痛点:注意力到底贵在哪
在深度学习里,处理序列的基本操作可以粗暴分成两类:一类是局部的,比如卷积,只看窗口内邻居;另一类是全局的,比如注意力,每个位置都要和所有位置交互。长上下文场景需要的是全局交互,但全局交互恰恰是复杂度爆炸的根源。
以标准自注意力为例,输入长度 L,每个 token 都要计算和其余 L 个 token 的相关性,得到 L×L 的注意力矩阵。这带来两个直接问题。
第一个是时间上的。计算这个矩阵及其与 Value 的乘积,复杂度是 O(L²) 量级。L 是 1024 时还好,L 到 65536 时,矩阵元素数是 40 多亿,即便高端显卡算力很强,单层也要算很久。第二个是空间上的,而且往往更致命。L×L 矩阵训练时要存下来用于反向传播,FP16 下 L=65536 的单层注意力矩阵就要占 8GB 以上,L=131072 时超过 32GB。很多号称支持长上下文的模型,实际训练时根本不敢用全量注意力跑长序列,只能分段、稀疏、近似,本质上都是在绕着 O(L²) 走。
还有个隐患容易被忽略:随着序列变长,注意力分布会越来越“平”。Softmax 在输入维度很高时,输出分布趋向均匀,注意力矩阵里真正有用的信息被稀释。这也是不少长上下文实验里困惑度不降反升的原因——不是模型学不到,是注意力本身的全局建模能力在长序列下被摊薄了。
SPECTRE 的出发点就是同时打这两个痛点。它用一个可学习的全局卷积核替代注意力矩阵,把序列维度上的交互搬到频率域。在频率域里,输入序列的每个频率分量只需要和对应滤波器系数乘一次,不需要和序列里其他位置两两对齐。同样是全局交互,复杂度从 O(L²) 降到 O(L log L),显存占用也随之降到接近线性。
1.1 为什么全局卷积能表达注意力能表达的东西
很多人第一次听到“用全局卷积替代注意力”会疑惑:卷积不是局部的吗?怎么可能替代全局注意力。关键点在于卷积核的长度——传统 CNN 里卷积核大小通常是 3、5、7,感受野有限,确实只能建模局部关系。但卷积核大小如果等于序列长度 L 呢?每个位置的输出就依赖于所有输入位置,感受野是整条序列,这和注意力一样是全局机制。唯一区别是注意力矩阵由 Q、K 动态计算,而全局卷积核对任意输入都一样,静态的,看起来表达能力退化。
没错,固定全局卷积确实缺乏输入自适应性。但 SPECTRE 不是简单地把输入和一个固定卷积核做卷积,它的滤波器是逐头的、可学习的,并且论文证明了它可以模拟 SSM 和线性注意力——后面我单独展开。换句话说,SPECTRE 的目标不是全面超越注意力,而是在保持全局交互能力的同时把复杂度降下来,然后靠训练让滤波器学到对任务最有用的频率模式。
这里有个容易误解的点要说清楚:FFT 本身不产生任何“智能”,它只是加速工具。SPECTRE 之所以有效,是因为卷积核可学习,而且学习发生在频率域。模型可以自由调制每个频率分量的幅度和相位,这等价于学习一个长度为 L 的复杂卷积核。由于滤波器是复数,比实数卷积核多一倍表达能力,可以建模更丰富的序列模式。
2. 三条技术路线的交叉点:线性注意力、SSM、FFT 网络
SPECTRE 不是凭空冒出来的。理解它最好先看它站在哪三条路线的交叉点上。
第一条是线性注意力。标准注意力里 Softmax 是非线性的,很难分解。线性注意力把 Softmax 换成核函数,于是注意力矩阵变成两个低秩矩阵的乘积,(QK^T)V 就可以用结合律先算 K^T V,再乘 Q,时间从 O(L²) 降到 O(L)。代价是低秩限制了表达力,长距离模式被压缩在很小的秩里,信息瓶颈明显。SPECTRE 和它的本质区别在于:线性注意力用低秩近似逼近注意力矩阵,SPECTRE 用频率域滤波器精确表达一个全局卷积,没有低秩瓶颈,代价是多一个 log 因子。
第二条是状态空间模型。S4 的核心是把序列建模看成连续 ODE 的离散化,得到线性递推关系。LTI(线性时不变)的 SSM 在数学上等价于一个卷积,这个事实很多人没注意到。Mamba 加入了输入相关的选择机制,突破了纯 LTI 限制,但代价是失去训练时的完全并行性——它本质还是 RNN,逐步递推。SPECTRE 训练时完全并行,因为 FFT 是全局操作,没有时间依赖,这是它在训练效率上的核心优势。
第三条是 FFT 网络。FNet 直接把输入的 FFT 结果喂给前馈层,几乎不做频率选择性处理,本质上是个简单 token 混合器,表达能力受限。SPECTRE 不一样,它有可学习的复滤波器,逐频逐头调制,滤波本身是模型核心能力,而不是固定预处理步骤。
三线交汇后 SPECTRE 的结构就很自然:嵌入层后面是通道混合(门控线性单元),然后是频率域序列混合,最后输出投影。整体像 Mamba 块,但没有递推;也像 Transformer 块,但注意力换成了更便宜的全局卷积。它长得跟 Mamba 块很像,关键差异就在序列混合这部分。每层的序列混合器对每个头独立做 FFT-滤波-逆 FFT,整个混合过程没有循环依赖,训练时可以在序列维度上完整并行。
3. SPECTRE 的算法拆解:从频率域乘法的本质到因果模式
先把核心算式的符号说清楚。SPECTRE 的序列混合器输入是 X,形状为 (B, L, D),B 是批大小,L 是序列长度,D 是特征维度。按头拆分特征,每个头维度是 D/H。对每个头沿序列维度做 FFT,乘一个可学习的复滤波器 W(形状为 (H, L)),再逆 FFT 回时间域:
y = F^{-1}(W ⊙ F(x))
这个操作妙在哪?根据卷积定理,时间域的卷积等于频率域的乘积,所以这个操作等价于:把 x 和一个由 W 决定的全局卷积核做卷积。W 是复数、逐头的,每个头可以调制各自频率分量的幅度和相位。这意味着每个头可以学到完全不同的卷积模式,有的头专注低频(全局趋势),有的头专注高频(局部细节)。这种分工在注意力里也能体现,但这里实现得更显式。
对初次接触 FFT 的读者,我想强调一点:FFT 不改变信息。正向和逆向 FFT 可逆,只要滤波器不为零,信息就不会丢。整个频率混合器不是有损压缩,而是重新分配信息的表示方式。这和注意力有本质区别——注意力矩阵是数据依赖的路由,SPECTRE 的路由是固定的频率滤波,靠大量训练数据把这组滤波器调到最有用的形态。
3.1 滤波器初始化:恒等映射是训练稳定的关键
把滤波器 W 初始化成什么,决定训练一开始的梯度质量。直接随机初始化 W 往往导致初始输出和输入差别过大,梯度爆炸或震荡。SPECTRE 的初始化策略很优雅:每个频率分量初始化为 1 加小噪声,即 W = 1 + ε·N(0,1),ε 是 0.01 量级。这样初始化时,频率混合器几乎恒等——幅度不变、相位不变,输出就是输入的复现,然后训练慢慢偏离恒等,学习有意义的滤波模式。
为什么恒等初始化这么重要?因为残差连接已经保证梯度直通,如果混合器本身就是恒等,训练就变成在语义良好的起点上微调。我自己复现时试过直接全初始化为 1、完全不加噪声,也稳定,但加入很小噪声让各头初始略有差异,训练初期各头更容易分化出不同频率偏好。ε 控制初始相位差异,太小各头同质化,太大破坏恒等性,0.01 到 0.05 之间比较稳妥。
3.2 共轭对称与实数 FFT:省一半计算量的工程细节
文本和多数序列任务的输入是实数。对实数序列做 FFT,频谱有天然性质:共轭对称。频率 f 处的值等于频率 L-f 处的共轭。正负频率信息冗余,只保存一半频率分量就够了。这正是 torch.fft.rfft 做的事:输出长度只有输入一半加一。
SPECTRE 实现时充分利用了这个性质。滤波器形状不是 (H, L),而是 (H, L/2+1)。训练和推断时对输入做 rfft,乘上半频谱滤波器,再 irfft 回实数域,频率域内存和计算量直接砍半。我第一版用完整 FFT,显存翻倍速度还慢,改成 rfft 后同样模型显存省了接近一半。如果输入不是实数(比如已经是复数特征),才需要另当别论。
3.3 因果模式:从双向 FFT 到分块递推
很多序列建模任务要求因果性——预测位置 t 时只能看到前 t 个位置。双向 FFT 天然全局,会用到未来信息,不能直接用于自回归生成。SPECTRE 的处理策略是“分块+递推”。
把序列切成固定大小的块,块内做 FFT,块与块之间保持一个频率域隐状态。对第一个块,直接做完整因果卷积;对第二个块,先乘上上一个块的频率谱作为前缀,再做逆 FFT。这样每个块的输出由当前块和之前所有块的频率域累积决定。频率域加法满足结合律,前缀累积可以递推维护,不必每个块重算全序列。
这个方案复杂度 O(L·C)(C 是块大小),C 是固定常数时就是线性。它还能支持流式解码:生成第 t 个 token 时,只需维护当前块已生成 token 和频率域前缀状态,不用重算整个历史。
我踩过一个坑:块边界处理不对会导致轻微语义裂缝。解决方案是块之间加一定重叠,或者把块大小设成 2 的幂(FFT 最友好),并确保频率域前缀状态维度和滤波器维度对齐。块大小也要权衡:块越大,块内 FFT 越高效但因果性维护越重;块越小,越接近逐步递推但在短序列上 FFT 效率低。序列长度 4096 时,块大小设 256 到 512 比较均衡。
3.4 为什么说 SPECTRE 能统一三种架构
论文里最有分量的部分,不是刷了多少分,而是可表达性证明:任何线性注意力层、任何卷积层、任何 LTI 状态空间模型,都能被 SPECTRE 层模拟,代价只是换一个 log 因子。这意味着 SPECTRE 不只是新架构,更像一个“架构公共空间”。
从数学上讲,卷积定理保证任意卷积都能在频率域通过逐点乘法实现。线性注意力可以被分解成若干循环矩阵的和,每个循环矩阵又可以被 FFT 对角化,所以也纳入频率域计算框架。SSM 就更直接了——LTI SSM 的核心计算就是卷积,直接用滤波器实现即可。
这个统一性带来实际好处:不必在 SSM、线性注意力、卷积之间反复横跳选择,只要堆 SPECTRE 块,模型理论上可以自己学到这三种计算模式的组合。各头独立滤波相当于给了模型 H 个并行频率通道,有些头表现成卷积模式,有些头表现成注意力模式,这种混合是隐式的、自动学习的。我在实验中观察到,视觉任务某些头学出的滤波器明显偏向低频,语言任务一些头学到了尖锐的谱峰值,暗示不同任务的头在做不同性质的 token 混合。
4. 实验数据深挖:不看单点分数,要看效率和显存
论文实验里,SPECTRE 在 WikiText-103 上以 100M 参数达到 19.2 困惑度,比同规模 Transformer 基线(19.6)略低。LRA 平均分 83.6,超过 S4 的 81.9 和 Mamba 的 82.6。不过单看数字容易误导。LRA 任务集合里,SPECTRE 优势最大的是图像和 Pathfinder 任务,本质是像素序列分类,天然有丰富频率结构,FFT 滤波器能直接捕捉周期性和方向性纹理,优势是结构性的。而纯文本任务(Text)上,优势缩小到 0.4 个百分点。这说明 FFT 在处理带周期/节奏信号的任务上更强,而自然语言的语义交互不一定有清晰频率特征。
所以如果你是做长文本摘要、代码生成、多轮对话,不要期待 FFT 结构带来爆发式精度提升。SPECTRE 价值更核心地体现在推理速度和显存占用。论文实测 400M 参数模型,序列长度 4096 时比 Transformer 快 2 倍,到 16384 时快 3.2 倍。这个趋势说明 FFT 优势随序列增长放大——这正是长上下文最关心的指标。
显存方面,Transformer 注意力矩阵 O(L²),SPECTRE 频率混合器 O(L log L)。序列长度 10 万时,Transformer 注意力矩阵 FP16 下单层就要 32GB,SPECTRE 在同样设定下显存开销几乎等于输入形状常数倍。对真实场景,这可能才是 SPECTRE 最大卖点:不是分数高多少,而是让你能在单卡上处理以前不敢碰的超长序列。
给个使用建议:如果你在跑超长文本分类、长文档检索、DNA 序列建模这类任务,SPECTRE 类结构的性价比是真实存在的。如果任务本身只有几百 token,注意力没那么贵,FFT 带来的复杂度红利会被 FFT 常数开销抵消,未必划算。
5. 实际操作:复现 SPECTRE 时的环境、代码骨架与常见坑
论文没有公开完整代码,根据论文描述和我在类似项目中的经验,整理一份可直接参考的复现骨架。环境上,Python 3.9+、PyTorch 2.0+。FFT 直接用 torch.fft,底层是 cuFFT,性能很好。建议混合精度训练,FFT 在 FP16 下有轻微精度损失,AMP 自动缩放能兜住。
核心模块的频率混合器代码如下:
import torch import torch.nn as nn class FrequencyMixer(nn.Module): def __init__(self, heads, length, eps=0.01): super().__init__() self.heads = heads half_length = length // 2 + 1 # 实部虚部各自初始化,保证初始接近恒等 real = torch.ones(heads, half_length) imag = torch.zeros(heads, half_length) self.filter_real = nn.Parameter(real) self.filter_imag = nn.Parameter(imag) self.eps = eps self.reset_parameters() def reset_parameters(self): nn.init.normal_(self.filter_real, mean=1.0, std=self.eps) nn.init.normal_(self.filter_imag, mean=0.0, std=self.eps) def forward(self, x): # x: (B, H, L), 每个头独立处理 B, H, L = x.shape x_fft = torch.fft.rfft(x, dim=-1) # (B, H, L//2+1) W = torch.complex(self.filter_real, self.filter_imag) # (H, L//2+1) y_fft = x_fft * W.unsqueeze(0) y = torch.fft.irfft(y_fft, n=L, dim=-1) return y几个工程细节值得展开。
第一,滤波器参数必须定义成实部和虚部分开,而不是一个复数 Parameter。PyTorch 复数 Parameter 在部分优化器里有兼容性问题,分拆成两个实数参数最稳妥,forward 里再合成复数。
第二,初始化为恒等。filter_real 全 1、filter_imag 全 0 时,频率响应正好是 1,irfft 之后还原输入。如果用了分块因果模式,初始化逻辑要对应微调:块内卷积的零频分量必须为 1,否则稳态输入会被缩放错。
第三,训练时残差连接建议放外面,即 block 输出 = 输入 + 经过整个块后的结果。我在实验中发现,如果把频率混合器当作唯一模块并去掉残差,会出现低频漂移,loss 震荡明显。残差连接必须有,这和论文结构保持一致。
第四,因果分块模式的扩展代码:
def forward_chunked_causal(self, x_chunk, F_state): # x_chunk: (B, H, C) x_fft = torch.fft.rfft(x_chunk, dim=-1) F_state = F_state + x_fft * W.unsqueeze(0) # 累积频率前缀 y_fft = F_state y = torch.fft.irfft(y_fft, n=C, dim=-1) return y, F_state容易被忽视的问题:F_state 表示整个历史前缀频谱,对它做逆 FFT 得到当前块输出,但输出长度只能等于块长 C,相当于周期延拓,会有环绕误差。缓解办法是让块长 C 等于 FFT 窗口长度,并确保每个块边界做一次状态重置。实际操作中,块间加 10% 左右重叠能明显减少边界伪影。
第五,归一化层。SPECTRE 建议用 RMSNorm 而不是 LayerNorm。RMSNorm 计算量更小,对 FFT 数值稳定性也更友好。FP16 下 RMSNorm 动态范围更宽,不容易上溢。
第六,我自己复现时踩过的坑:不要对序列维度做 padding 到 2048 这种非 2 幂长度。FFT 对任意长度都能算,但初始化逻辑依赖“滤波器覆盖全部频率”。序列被 padding 后频谱含义变了,全 1 初始化就不再是精确恒等。正确做法是让块长、序列长度和滤波器长度严格对齐。
6. 和其他方案对比:SPECTRE 不是万能解,但补上了关键一环
现在高效序列模型的核心思路几乎穷尽了四种范式:注意力近似、状态空间递推、线性注意力/核方法、FFT。SPECTRE 属于第四类,但它通过与 SSM 和线性注意力的等价性,把第四类和其他三类联通起来,这是它作为研究的核心价值。
对比 Mamba:Mamba 优势是推理恒定内存和流式生成友好,缺点是训练必须逐步递推,GPU 利用率受限。SPECTRE 训练完全并行,代价是自回归推理需要额外因果处理方案(论文用分块)。如果部署明确要求流式解码且长上下文不多,选 Mamba 类;如果训练预算有限、需要最快在大规模长序列上跑实验,SPECTRE 类更省心。
对比线性注意力:SPECTRE 滤波器没有低秩瓶颈。线性注意力本质把注意力矩阵近似成两个低秩矩阵的积,表达力受限于秩。SPECTRE 的全局卷积理论上能表达任何线性时不变映射,不受秩约束,代价是复杂度高一个 log 因子。
对比 S4:SPECTRE 不吃那道复杂的矩阵参数化。S4 需要对状态矩阵 A 做 HiPPO 初始化并处理离散化,数学门槛高,工程实现数值上也容易出问题。SPECTRE 的滤波器就是复数矩阵,初始化就是一,直观直接好调参。在工程复现友好性上,SPECTRE 赢得很轻松。
7. 频率域混合值得关注,但别神话它
SPECTRE 这类 FFT 混合器给我的最大启发是:效率不一定要靠牺牲表达能力换。注意力贵,是因为在时间域里做全连接交互;但同样全局交互,完全可以在频率域里用逐点乘法表达。卷积定理存在了几百年,深度学习到近两年才系统性地把它用在序列模型全局混合上,不算颠覆,但确实把很多架构统一到了同一频率域框架下。
如果一句话说清 SPECTRE 适用范围:最适合序列极长、需要全局交互、显存吃紧、且推理吞吐要求高的任务。这些场景下,FFT+可学习滤波器的组合,几乎是用最少的工程复杂度拿到 O(L log L) 全局建模能力。
后续值得改进的方向无非两个:一是把因果分块模式做得更无缝,最好能自适应选块大小;二是加入选择性机制——Mamba 证明了输入相关选择能大幅提升精度,SPECTRE 目前滤波器输入无关,这可能就是它和 Mamba 在复杂任务上有差距的最大原因。如果能将选择性引入频率域,同时保持 FFT 并行性,这个方向会更有意思。
最后分享一个实操体会:复现 SPECTRE 时最深的感受是,FFT 类模型的调参节奏和 Transformer 完全不同。Transformer 主要调学习率和注意力头数,FFT 类模型还要密切观察滤波器谱结构——如果训练完后所有头的滤波器谱都长得一模一样,说明模型没有分化出多频率模式,需要增大初始化噪声或增加通道数。这个问题在 loss 曲线上不一定立刻显现,但会影响最终精度上限。希望这篇拆解能帮你避开这些坑,更快上手频率域序列建模。