top-k Attention(top-k 稀疏注意力):一种免再训练的大模型长度外推泛化的推理方法
January 3, 2025
1. 背景与问题:长序列下,基于SoftMax的注意力泛滥不聚焦
ChatGPT推出至今,基于 Transformer 架构的大语言模型(Large Language Model,LLM)在文本生成、阅读理解、对话交互等众多自然语言处理任务中取得了惊人的进展。然而,这类模型通常在预训练阶段都受到输入序列长度的限制,如 1024、2048、4096 等。当我们在推理时想输入更长的文本(比如上万甚至几十万 token )时,就会面临长度外推泛化 (Length extrapolation)的问题。
在标准的 Transformer 架构中,注意力机制(Attention Mechanism)是核心,尤其是对所有过去的 token 做全局注意力(Global Attention)的自回归 Transformer,通常会使用 softmax(attn_score) 来得到权重分布。随着文本序列长度变得越来越长,softmax 的分母(即指数和)将增大,此时每个token的注意力分数都会被进一步“稀释”,导致注意力分布过于分散,而原本较小但关键的信号可能更容易被噪声淹没,造成模型在超长序列的推理场景下出现性能下降或理解混乱的情况。
简言之:序列一旦超过模型训练时见过的最大长度,它就可能难以有效聚焦到真正重要的上下文信息,而出现注意力泛滥甚至混乱的问题。
softmax is not enough (for sharp out-of-distribution)这篇论文中也有针对注意力分散(泛滥)的问题进行阐述
另外,Differential Transformer(差分注意力)的工作也可以看作是稀疏注意力的一种实现方式,具体是通过“差分”实现的,这样就将注意力聚焦到关键的token上了
而且,差分注意力的作者Tianzhu Ye也针对这点有针对性的补充说明,如下:

2. 一个简单而直接的应对思路:top-k 稀疏注意力
为了应对这一挑战,业界已经出现了许多改进长序列处理的想法和架构,比较有代表性的就是一系列的稀疏(局部)注意力架构,比如滑动窗口attention等,这里不详细介绍了,感兴趣的读者自行查阅。
本文中具体采用的则是top-k Attention
整体的构思如下:
如果仅仅想在免再训练或最少改动的前提下,让现有训练好的大模型具备更强的长文本处理能力,可行的思路之一是 “在推理阶段对注意力做稀疏化”。具体而言,可以尝试以下方法:
-
- 在推理时,当输入长度超过预训练窗口(例如 4096)时,我们对注意力权重
attn_weights做top-k截断,只保留最大注意力打分的前 k 个 token(比如 k=4096)。
- 在推理时,当输入长度超过预训练窗口(例如 4096)时,我们对注意力权重
-
- 对剩下那些注意力权重“靠后的 token”直接置零(mask 掉),并将前 k 个的token的权重和重新归一化为 1。
-
- 用这种截断后的注意力权重来做后续的矩阵乘法,从而让模型推理只聚焦在最重要的少量 token 上。
这样做的动机在于,模型原本在预训练期间就已经学会了在长度 4096 左右的注意力场景下去找出关键的上下文信息;当序列长度超过 4096 时,如果还让它对所有 token 都做 full attention,就容易导致注意力分散。而通过 top-k 稀疏化,把“关注野”人为地限制在 4096 个左右,从而在一定程度上减轻长序列导致的注意力稀释问题。
注意,这里的“关注野”并不等同于模型的“感受野”。感受野依然是所有过去的 token,只是说我们的注意力分布最终只在 top-k token 上保留非零权重。
3. 关键代码示例
下面是一段 PyTorch 代码示例,展示了如何对注意力权重进行 top-k 的稀疏化并重新归一化。该代码可以直接插入到现行的 Transformer 的多头注意力计算流程里(具体位置通常在 attention 部分):
attn_weights = nn.functional.softmax( attn_weights, dim=-1, dtype=torch.float32 ).to(query_states.dtype) # Insert Code Start Here # topk = 4096 if attn_weights.shape[-1] > topk: # 1. 找到最大的前 topk 个值和它们的索引 attn_topk_value, attn_topk_index = attn_weights.topk(topk, dim=-1) # 2. 重新归一化,将 topk 的权重总和调整到 1 # (保证 sum(attn_topk_value, dim=-1) = 1) attn_topk_value = ( (1 / attn_topk_value.sum(dim=-1)).unsqueeze(-1).expand(*attn_topk_value.shape) ) * attn_topk_value # 3. 把除了 topk 以外的权重全部置为 0 attn_weights = attn_weights.zero_().scatter_(-1, attn_topk_index, attn_topk_value) # Insert Code End Here # # 使用稀疏化的 attn_weights 做后续计算 attn_output = torch.matmul(attn_weights, value_states)
实现逻辑十分直接:
-
- 软最大化:先对所有 token 的打分做 softmax;
-
- top-k:如果序列长度超过 topk,保留最大权重的前 topk 个,其他直接置 0;
-
- 归一化:把剩下这 top-k 的注意力权重按行进行归一化(权重总和调整到1);
-
- 矩阵乘法:用这个稀疏注意力权重与 value_states 相乘,完成注意力输出。
笔者在Qwen基座模型的基础上,设置不同的top-k值进行过压力测试,当k值很小的时候(如3、4)会导致非常明显的输出perplexity(乱输出)的问题,当top-k设置为100的时候,几个多轮对话间还能保持正常的输出,没有明显的perplexity的情况,大家可以自行测试评估。
4. 优势与收益
-
- 防止注意力过度分散
- softmax 分母不再随序列长度无界增长,前 k 个最重要的 token 注意力分数更集中,关键信息不易被噪声覆盖。
- 防止注意力过度分散
-
- 对齐训练时的注意力模式
- 由于模型在预训练时通常只见过最多 4096 的有效上下文窗口,如果在推理时把“注意力关注野”也限制在 4096,就与其先前学到的模式更匹配。
- 对齐训练时的注意力模式
-
- 计算/内存开销可能减少
- 虽然需要根据具体实现方式确定是否真正减少矩阵乘法开销,但在很多稀疏注意力的优化方案中,确实能够节省推理时间和显存。这里特别说明:本文展示的代码因为是计算后再mask,矩阵乘法的复杂度仍旧与全局维度相关,所以此处并不涉及降低计算开销。
- 计算/内存开销可能减少
5. 潜在风险与对策
-
- 信息丢失
只有注意力打分最高的前 k 个 token 被保留,如果在模型打分阶段有些罕见但极重要的 token 未进入 top-k,就会被硬性截断,导致信息丢失。
对策:
- 在不确定任务时,可以增大 k 的取值(比如从4096改为32768),或做动态调参:当前的大模型在后训练阶段基本都还要做至少一次的长度泛化微调,比如Qwen系列在预训练的最后阶段将上下文长度从4,096 个token增加到32,768 个token。
- 信息丢失
只有注意力打分最高的前 k 个 token 被保留,如果在模型打分阶段有些罕见但极重要的 token 未进入 top-k,就会被硬性截断,导致信息丢失。
对策:
-
- k 值如何选择
- 过小:容易丢失关键信息;
- 过大:注意力分散度依旧大。
- 对策:可以做不同 k 值下的实验,或设计动态调整策略。
- k 值如何选择
6. 总结与展望
在免再训练或只做最小化改动的前提下,通过推理阶段的top-k 稀疏注意力,可以在一定程度上缓解大模型处理超长文本时的注意力分散问题。这种方法无需改动预训练权重,也不需要重新学习,就能够让模型更好地“对齐”原来的上下文窗口大小,从而获得相对更稳定的生成表现。
不过,需要注意到它并非万能:如果输入文本中分布着很多长距离且细碎的关键信息,top-k 截断就可能导致信息缺失。理想情况下,我们可以结合局部注意力与全局检索机制,或者在预训练/微调阶段就引入稀疏注意力结构,从而兼具稳定性与高效性。
如果你在使用一些大语言模型用于一些长上下文的场景,且主要瓶颈是“序列长度超限后的注意力泛滥”而导致的诸如RAG问答场景下的幻觉等问题,不妨一试这种简单的注意力稀疏化方法,或许能在无需大规模训练的情况下就能取得较好的效果。
具体地,你可以先在小范围内对实际任务做测试,看看 top-k 稀疏注意力对指标(困惑度、准确率、召回率等)的影响,然后灵活调参 k 值或再尝试轻微调优,最终得到满足自己任务需求的超长文本处理性能。