跳到主要内容

为什么标准 Attention 的 O(n²) 绕不开?softmax 全局归一化才是数学根源

· 阅读需 6 分钟

很多人能脱口而出「Attention 是 O(n²)」,但少有人追问:这个 n² 到底卡在哪一步?为什么优化不掉?

答案不是「矩阵乘法天生就是 n²」这么简单——矩阵乘法有结合律,本来可以调顺序把 n² 甩掉。真正把 n² 焊死的,是 softmax 的全局归一化。这篇把它从数学上拆开。

一、n² 的直接来源:QKᵀ 让每个 token 给每个 token 打分​

标准缩放点积注意力:

attn(Q, K, V) = softmax(QKᵀ / √d) · V

其中 Q、K、V 都是 [n, d](n = 序列长度,d = 每个 head 的维度)。第一步 QKᵀ:

QKᵀ : [n, d] × [d, n] → [n, n]

这个 n×n 矩阵 S 的第 (i, j) 个元素是 S[i][j] = qᵢ · kⱼ,也就是「第 i 个 token 对第 j 个 token 的相关性打分」。

n 个 token 两两打分,就是 n² 个分数。这是 n² 的第一个来源:注意力矩阵本身就装着 n² 个元素。

拆开算复杂度:

步骤形状复杂度
QKᵀ[n,d] × [d,n] → [n,n]O(n²d)
softmax对 [n,n] 逐行归一化O(n²)
×V[n,n] × [n,d] → [n,d]O(n²d)

二、光有 QKᵀ 还焊不死 n²:结合律本来能救你​

这里藏着一个大多数人没意识到的关键点。如果注意力只有 QKᵀV、没有 softmax,那矩阵乘法有结合律:

(QKᵀ) · V = Q · (KᵀV)

左边先算 QKᵀ = [n, n],O(n²d);右边先算 KᵀV = [d, n] × [n, d] = [d, d],O(nd²)。

当 n 远大于 d 时(长序列正是这个场景),右边便宜得多:

左边:O(n²d) 右边:O(nd²) → 省掉了整个 n² 因子

也就是说,矩阵乘法本身并不强制 n²——是结合律给了你「先算哪边」的自由。真按纯线性 QKᵀV 来,完全可以选 d 小的一边,把复杂度压到 O(nd²)。

三、softmax 是怎么把 n² 焊死的:非线性 + 全局归一化​

问题全出在夹在中间的 softmax:

softmax(QKᵀ) · V

softmax 拆开写:

softmax(qᵢ·kⱼ) = exp(qᵢ·kⱼ) / Σⱼ exp(qᵢ·kⱼ)

这一行藏着两个「焊死 n²」的致命点:

1. 非线性,破坏了结合律。

softmax 是逐元素的非线性函数,作用在完整的 [n, n] 矩阵上。它不接受 [d, d] 的输入——你没法「先把 KᵀV 算成 d×d,再套 softmax」。Q(KᵀV) 那套省 n² 的打法在 softmax 面前直接失效,因为 softmax 必须先看到完整的 QKᵀ 才能逐元素归一化。

2. 全局归一化,强迫两两交互。

分母 Σⱼ exp(qᵢ·kⱼ) 是对所有 j(所有 key)求和。这意味着第 i 个 token 的注意力权重,依赖它和每一个 token 的相似度。要让归一化成立,第 i 个 query 必须和全部 n 个 key 都算一遍打分——少一个都不行。

所以结论很硬:

softmax 的「全局」二字,就是 n² 的数学根源。 它要求每个 query 看到所有 key,等价于强制做 n² 次两两打分。除非换掉 softmax,否则这一步在数学上绕不开。

补一个容易混淆的点:计算量和显存是两回事​

有人会拿 FlashAttention 反驳:「它不就绕开了 n² 吗?」——没有。FlashAttention 省的是 n² 的显存(分块计算,不把 n×n 矩阵物化进 HBM),但省不掉 n² 的计算量:softmax 里每一对 (i, j) 的点积 qᵢ·kⱼ 都是必须算的,该做 n² 次还是 n² 次。

想真正把「计算量」从 n² 降下来,只有一条路,往下看。

四、唯一的出路:换掉 softmax​

既然 n² 是 softmax 全局归一化的数学必然,那「绕开 n²」的本质就变成了「绕开 softmax」。所有真正降复杂度的方案,无一例外都在这一步动手:

路线做法复杂度代价
标准 Attentionsoftmax 全局归一化O(n²d)长序列爆炸
Linear Attention核函数 φ(q)ᵀφ(k) 替代 softmaxO(nd²)丢掉概率解释、表达力下降
Sparse Attention只让部分 token 互相打分O(n·k)丢失长程依赖
Mamba / SSM直接换掉注意力,用状态空间O(n)架构重写

Linear Attention 的思路最能说明「换 softmax 才能恢复结合律」。把 softmax 换成可分解的核函数 sim(q,k) = φ(q)ᵀφ(k):

Σⱼ φ(qᵢ)ᵀ φ(kⱼ) · vⱼ = φ(qᵢ)ᵀ · ( Σⱼ φ(kⱼ) vⱼᵀ )

右边括号里 Σⱼ φ(kⱼ)vⱼᵀ 是一个 d×d 矩阵,和 n 无关,可以先一次性算出来,再和每个 query 相乘。复杂度从 O(n²d) 降到 O(nd²)——这正是「换掉 softmax 才绕得开 n²」的具体落地。

def linear_attention(Q, K, V, eps=1e-6):
# φ:特征映射,保证非负即可,常用 elu(x)+1
Q = torch.nn.functional.elu(Q) + 1
K = torch.nn.functional.elu(K) + 1

# 先聚合 key 侧,绕开 [n, n] 注意力矩阵(O(n·d²) 的关键)
KV = K.transpose(-2, -1) @ V # [b, d, d]
K_sum = K.sum(dim=-2, keepdim=True) # [b, 1, d] 归一化分母

num = Q @ KV # [b, n, d]
den = Q @ K_sum.transpose(-2, -1) # [b, n, 1]
return num / (den + eps)

代价也很直白:softmax 换掉后,注意力不再是「全局归一化的概率分布」,表达力下降、训练更不稳定。代表工作:Transformer 原班人马的 Performer(随机特征映射近似 softmax)、Linformer(低秩投影),以及 RWKV / RetNet / Mamba 这条线性注意力与状态空间模型路线。

五、n² 是「全局建模」的代价,不是 bug​

最后拔高一层。softmax 的全局归一化,恰恰是 transformer 表达力的来源——每个 token 能直接看到所有 token、动态加权,这种全局依赖是 transformer 干掉 RNN 的核心武器。

所以 n² 不是实现失误,而是「全局注意力」这项能力的标价。想绕开 n²,本质是想「不付全局建模的价,又想要全局建模的能力」——Linear Attention、Sparse、Mamba 全都在这个 trade-off 上做文章:用精度或表达力,去换复杂度。

总结​

问题答案
n² 的直接来源QKᵀ 两两打分,产生 n×n 注意力矩阵
为什么绕不开softmax 非线性 + 全局归一化(分母 Σⱼ),破坏结合律、强制两两交互
FlashAttention 能绕开吗只能省 n² 的显存,省不掉 n² 的计算量
真正的出路换掉 softmax(Linear / 核函数),或换掉注意力机制(SSM / Mamba)

一句话收尾:矩阵乘法没逼你付 n²,是 softmax 的全局归一化逼的;想不付,就先换掉 softmax。