详细解释
注意力汇(Attention Sinks) 是 2023 年由 MIT 等团队在 StreamingLLM 工作中发现并命名的一个普遍现象:在 Transformer 的注意力分布里,序列最开头的少数几个 Token 会持续吸收大量注意力权重,哪怕这些 Token 在语义上几乎没有信息量,它们分到的权重占比依然很高。这个发现之所以重要,是因为它直接决定了长流式场景能不能「丢历史」。在多轮对话、长文档流式处理、永不重启的服务里,上下文会不断增长,如果把早期 Token 全部从 KV 缓存 里删掉来节省显存,模型往往会突然崩坏、输出变得毫无条理;而只保留开头那几个「锚点」,再叠加 滑动窗口注意力,模型就能长时间稳定运行。它把「无限长流式输入」从不可能变成了工程上可落地的方案,参考:StreamingLLM 论文。业内在讨论时也常把开头这几个 Token 称为锚点 Token,这个名字强调的正是它们「钉住开头、稳住注意力」的作用:它们不一定携带语义,却承担着让注意力分布保持稳定的结构性职责,删除它们等于抽掉了地基。它也让业界第一次意识到,长上下文的瓶颈不只是算力,还有注意力分配本身的规律。
要从机制上理解注意力汇,关键在 Softmax 归一化这一步:注意力权重经过 Softmax 之后必须被分配到序列中的某些位置上,所有权重加起来恒定,不会凭空消失。当一个 Token 与当前上下文里的历史内容都不相关时,它并不需要把权重投给任何一个具体的语义相关位置,于是这些无处安放的权重就会大量流向序列最开头那几个几乎无信息的 Token,形成一个稳定的「汇点」。这解释了一个反直觉的现象——为什么直接丢弃早期 Token 会让模型输出崩坏:那些被丢弃的开头 Token 恰恰承担着「收纳多余注意力」的功能,一旦删掉,Softmax 的权重被迫重新分配到别处,注意力分布被彻底打乱,模型行为随之失控。而只要保留这几个锚点、再叠加一个只覆盖最近内容的滑动窗口,权重就有了稳定的去处,长序列推理就能保持平稳。换句话说,注意力汇并不是模型「记住」了开头的内容,而是注意力机制在缺乏明确目标时的一种默认归宿,理解这一点就不会再误以为删掉它们无关紧要。
工程上,注意力汇是把「无限流式」变成可行的关键拼图。典型应用包括永不停机的流式对话(会话可能持续数天甚至数周)、无限长的日志与监控流分析、超长文档的流式摘要等——这些场景都要求模型在处理当前内容时不间断地接收新输入,同时又不能无限期地保留全部历史。常见做法是保留开头若干锚点 Token,再搭配一个固定大小的 上下文窗口 只保留最近的若干 Token,中间的历史按策略淘汰,这样显存占用稳定、延迟可预期。唯元智创 在长流式网关侧的实践是:把锚点保留与 KV 缓存淘汰 策略、前缀缓存组合起来,对上层应用完全透明——客户端只管持续推送新内容,网关负责在固定显存预算下维持稳定的长会话,长时间运行的 流式输出 任务不会因上下文增长而中途崩坏,配合 长上下文扩展 技巧,超长会话的单位成本也可预期。这样即便是连续运行数周的长会话,显存水位与单次响应延迟也能保持在稳定区间,不会随上下文持续增长而逐步劣化;对业务方而言,这意味着可以放心地把长会话能力做成常驻服务,而不必担心中途崩溃带来的体验断裂。
长上下文 KV 保留策略对比表
| 策略 | 保留内容 | 显存占用 | 最大可处理长度 | 是否需重训练 | 实现复杂度 | 适用场景 |
|---|---|---|---|---|---|---|
| 全量保留 | 全部历史 Token 的 KV | 最高,随序列长度线性增长 | 受显存限制,难以做到无限 | 否 | 低 | 短中等长度、需要精确回忆全部历史 |
| 滑动窗口 | 仅最近 W 个 Token | 固定,只与窗口大小相关 | 理论无限,但会丢失早期内容 | 否,但需与预训练窗口匹配 | 低 | 局部依赖强的流式任务 |
| 注意力汇(锚点加窗口) | 开头若干锚点 Token 加最近窗口 | 固定且较小 | 理论无限 | 否,锚点机制最好在训练期验证 | 中 | 永不停机流式对话、长日志分析 |
| 基于注意力的重击者保留(H2O 思路) | 累计注意力得分高的少数 Token | 固定,可按配额调整 | 理论无限 | 否 | 高 | 需要保留关键历史片段的问答 |
| StreamingLLM 完整方案 | 锚点 Token 加滑动窗口,配合缓存位置重映射 | 固定且较小 | 理论无限 | 否 | 中 | 长流式生成、超长上下文推理 |
| 唯元智创 长流式网关组合 | 锚点加窗口,叠加分页与前缀缓存 | 固定,按显存预算统一分配 | 理论无限 | 否 | 中,网关侧统一封装 | 长会话与长日志流式处理 |
锚点 Token 数量与效果对照表
| 锚点数 | 显存开销 | 输出稳定性 | 精度影响 | 建议场景 |
|---|---|---|---|---|
| 0 个 | 最低 | 差,长序列极易崩坏 | 严重退化,注意力分布失稳 | 仅适用于极短上下文,不推荐长流式 |
| 1 个 | 很低 | 不稳定,勉强可用 | 明显退化 | 实验探索,不建议生产使用 |
| 4 个(业界默认) | 很低 | 稳定 | 几乎无影响 | 通用长流式场景的默认选择 |
| 8 个 | 略高 | 稳定 | 与 4 个差别很小 | 对开头指令或设定敏感的任务 |
| 16 个 | 略高 | 稳定 | 收益趋于饱和 | 开头含关键指令与角色设定的长任务 |
实战经验与避坑
-
锚点数量 4 个是长期验证的默认值,不要为了省显存删到 0,删到 0 会直接崩。 锚点的作用是为多余的注意力权重提供一个固定去处,数量太少权重无处安放、输出会飘,直接归零则长序列几乎必然崩坏。相反,锚点数也不是越多越好,加到十几个之后收益迅速饱和,却要多占一点显存。工程上建议从 4 个起步,跑一轮真实长流式回归,再根据业务表现微调;如果发现长会话后段输出开始发散,优先考虑的是检查锚点是否被淘汰策略误删,而不是先怀疑模型本身。另一个细节是,锚点必须在会话一开始就进入缓存,中途才补上通常会失效,因为注意力汇的形成依赖于序列起始位置,事后插入的 Token 并不占据那个结构性位置,实际效果很有限。不同模型的最佳锚点数可能略有差异,仍然建议用实测来定。遇到长会话异常时,先按「锚点在不在、窗口配不配」两个问题自查一遍。
-
锚点必须是「原始序列最开头」的那几个 Token,不能是任意挑选的 Token。 注意力汇现象绑定的是序列的绝对起始位置,那几条 Token 之所以能吸收权重,正是因为它们位于最开头这个固定的结构性位置。如果换成中间某几个 Token、或者每轮换一批,注意力就无法形成稳定汇点,效果甚至会不如不加锚点。因此实现时要明确锚点的定义是「序列索引从 0 开始的连续若干 Token」,任何按语义、按重要性动态挑选的做法都不能替代它。换句话说,锚点的定义是「位置决定」而不是「内容决定」,这也是实现时最容易写错的地方。
-
滑动窗口大小要和模型的预训练窗口匹配,窗口设得远大于训练窗口反而会退化。 很多模型在预训练时使用的窗口或位置编码外推范围是有限的,推理时若把滑动窗口盲目放大到远超训练规模,位置信息会进入模型没见过的区间,长距离表现不升反降。合理做法是让窗口落在训练覆盖的范围内,需要更长有效上下文时,配合 长上下文扩展 类的位置编码处理技巧,而不是直接改大窗口数字。判断窗口是否设得过大,可以观察长序列后段是否出现明显的重复、失焦或跑题,这些往往是位置外推失效的信号,此时应回到与训练匹配的窗口并辅以位置编码处理。
-
注意力汇方案不能替代检索,需要精确回忆早期内容时应走 RAG 而不是依赖窗口。 锚点加窗口保证的是稳定性,被窗口淘汰掉的中段历史确实不再被记住,最多只保留开头锚点的存在感。所以一旦业务需要精确引用早期对话原句或文档细节,就必须把内容沉淀到外部存储、用检索按需取回再注入上下文。把注意力汇当「稳定性底座」,把检索当「记忆能力」,两者组合才能在超长会话里既稳又不丢关键信息。更稳妥的做法是把这份历史以结构化形式落库,检索时带回原文片段,让模型的引用有据可查。这条边界要写进方案文档,避免后续维护者误以为窗口能当记忆用。
-
流式场景要把锚点与 KV 分页、前缀缓存结合起来,否则长会话仍会吃满显存。 只加锚点和窗口,在大并发长会话下显存依然会被多路会话的固定开销叠加占满。工程上应把锚点保留与 KV 缓存 分页管理、前缀复用的能力整合到同一套调度里,让相同前缀的请求可以共享缓存,把每路会话的固定成本压到最低,这样长流式的并发容量才真正上得去。同时要给每路会话设置显存上限与超时回收机制,避免个别超长会话把整张卡的缓存占满,进而拖垮同实例上其他请求的延迟。
-
上线前要测「长会话第 1 万轮」这类极端场景,常规短测试发现不了崩坏。 注意力汇相关的失效往往是渐进式的,短测试里完全看不出问题,只有在上下文持续累积到很长之后才会暴露。因此压测用例必须包含超长会话、超长日志流这类极端负载,观察后段输出是否还保持连贯、显存水位是否稳定、有没有出现重复或发散。把这类极端场景纳入回归,才能在上线前把崩坏风险挡在门外,而不是等到用户在生产环境里遇到才开始排查。尤其是产品化的长会话应用,用户在真实使用中往往会连续交互成千上万轮,测试用例应当模拟这种强度。