编程 窗口 1M 不等于用得好:token 实测、KV Cache 显存账与针入草堆评测

2026-09-30 00:03:10

窗口 1M 不等于用得好:token 实测、KV Cache 显存账与针入草堆评测

长上下文能力指模型在更长输入中定位、整合和使用信息的能力,不等同于配置文件中的最大 token 数。配置文件里的长度上限只说明模型「能接收」多长的输入,不保证它「能用好」其中的信息。

这一区分来自 Lost in the Middle 等评测工作的核心发现:模型即便在技术上接受很长的输入,对长文中部信息的利用率也远低于首尾。因此「长上下文」逐步被拆成三个可独立验证的问题——位置编码是否在超长长度下仍有效、注意力的显存与计算是否可控、模型是否真的能定位并利用长文中的信息。扩窗方法的演进也沿这三条线展开。

快速开始

先统计真实 token 长度:用与训练一致的 tokenizer 计算输入到底占多少 token,而不是按字符数或字数估算。不同语言与内容的 token 化差异很大,字符数相同的两段文本 token 数可能差数倍。

评测时覆盖短、中、长样本,分别记录任务成功率和延迟。短样本用于确认扩窗没有破坏原有能力,中样本看逐步增长时的退化趋势,长样本才反映真正的长上下文能力;只看长样本无法区分「扩窗失败」和「本来就退化」。

扩窗后必须同时验证三件事:位置编码在新长度下是否失配、注意力显存是否随长度爆炸、以及模型是否还能在长文本中定位关键信息。三者任一不满足,扩窗都不算成功。

挑战

全注意力成本随长度平方增长:注意力矩阵大小为 n×n,计算与显存都是 O(n²)。当 n 从 4K 扩展到 32K,注意力矩阵面积扩大 64 倍,即便采用 KV Cache,存储历史键值对仍随长度线性累积并可能耗尽显存。KV 缓存的显存可近似估算为:

mem = 2 × 层数 × 头数 × 头维度 × n × 字节数

其中系数 2 对应 K 与 V 两套缓存。随着 n 增大,这一项会先于权重显存成为主要瓶颈,这也是稀疏注意力与低精度缓存被广泛采用的原因。

位置外推是第二个瓶颈:在短长度上训练的位置编码直接应用到更长序列时,token 会落在训练时未见过的位置区间,导致注意力失效。常见的 RoPE 缩放通过调整旋转频率把更长位置压缩进训练见过的区间,代表性方法包括把频率按倍率缩放的 NTK-aware 方案,以及 YaRN 提出的「按维度分段、并引入温度校正」的组合策略;不同缩放方法的效果与副作用各不相同。

模型还可能在长文本中丢失中间信息,即「lost in the middle」现象:位于长输入中部的关键事实,被前文与后文的注意力稀释,召回率明显低于首尾。该现象由 Liu 等人系统评测提出,说明「注意力覆盖全部位置」并不等于「每个位置都被同等利用」。评测长上下文时应分段统计首部、中部、尾部的成功率,而不是只看整体。

滑动窗口、稀疏注意力、压缩和检索可缓解不同瓶颈:滑动窗口把注意力限制在局部邻域以控制显存,稀疏注意力保留部分全局连接(如 Longformer 的「局部窗口 + 全局 token」、BigBird 的「随机 + 窗口 + 全局」),压缩把历史概括成紧凑表示,检索则把长文档切块后只喂给模型相关片段。这些方法在显存、质量与实现复杂度之间各有取舍。

案例:针入草堆

针入草堆 (Needle in a Haystack) 是评测长上下文检索能力的常用方法,最早由 Greg Kamradt 的工具库推广:把一条可验证事实(针)插入不同位置的长文档(草堆),要求模型返回事实及出处。典型做法是把「针」分别放在首部、中部、尾部,并逐步加长文档,观察模型能否准确定位。构造样本的伪代码如下:

needle = "最关键的密码是 42,请记住它。"
haystack = ("这是一段用于填充上下文的无关文本。 " * fill_count)

depth = 0.5                                  # 0 表示开头,1 表示末尾
pos = int((len(haystack) - len(needle)) * depth)
context = haystack[:pos] + needle + haystack[pos + len(needle):]
prompt = f"{context}\n请回答:最关键的密码是多少?"

分别统计首部、中部和尾部成功率,绘制「长度 x 插入位置」的成功率热力图。若中部位置显著低于两端,说明模型存在 lost-in-the-middle 退化;若超过训练长度后成功率骤降,说明位置外推失败。

只测一个位置无法说明长上下文质量:把针放在开头,模型可能只是凭开头偏好答对,并未真正检索整段文档。排查时先确认模型是否真的读到了全文(输出出处或引用原文),再检查 tokenizer 是否把文档截断、以及检索增强是否误触发了无关片段。

推荐文章

程序员茄子在线接单