窗口到底指什么
把大模型想成一个一次只能读一页纸的读者,这页纸有多大,就是它的上下文窗口。窗口里装的东西比名字暗示的多:系统提示、对话历史、检索来的资料、模型自己的回复,全部加起来不能超过上限。还有一点常被忽略:输出也占窗口,上下文 8K 的模型,输入加输出一共只有 8K,留给回答的空间往往比直觉少。
窗口长度按 token 计数。token 是模型词表里的最小单位,一个英文单词可能是一两个 token,一个汉字通常是一个 token,也可能被切碎。按 token 不按字,是因为模型从底层处理的就是 token:每个 token 变成一个向量参与计算,注意力机制(让序列中每个位置互相交换信息的模块)也建立在 token 之间。所以 128K 窗口指的就是 128K 个 token,换算成中文大致是几万到十几万字,具体取决于文本构成。
长度曾经为什么受限
自注意力有个先天代价:序列里每对 token 都要计算一次相关性,计算量随长度平方增长——长度翻四倍,计算翻十六倍。这解释了早期模型为什么普遍只有几 K 的窗口。
推理侧的麻烦更具体。生成是自回归的:每步只产出一个新 token,但注意力要回看全部历史。为了不重算历史,推理引擎把每层的 K、V(key 和 value,注意力里承担「被检索内容」角色的两组向量)缓存下来,这就是 KV Cache。它的显存占用随长度线性增长,往往才是长上下文推理的显存大头。
估算方法一句话说清:每 token 的 KV Cache = 2 × 层数 × KV 头数 × 每头维度 × 每元素字节数。以典型 7B 模型为例,取 32 层、32 个 KV 头、每头 128 维、fp16 存储,每 token 约 512 KB——这是估算值。代入两头:4K 上下文约 2 GB,128K 上下文约 60 GB,而 7B 权重本身用 fp16 也只要十几 GB。也就是说长上下文下缓存反超权重,两者相差三十多倍、接近两个数量级。若模型用了 GQA(分组查询注意力,让多个查询头共享一组 KV 头),数字会成倍缩小,但随长度线性增长的趋势不变。
做长窗口的三条路线
第一条是位置编码的外推与插值。模型靠位置编码知道 token 的先后顺序,RoPE(旋转位置编码,把位置信息旋进注意力计算里)是当前主流;直接外推到训练没见过的长度通常会崩,于是有 NTK 缩放、YaRN 一类插值方法,把超长位置「压缩」回模型熟悉的区间,少量微调甚至免训练就能把窗口拉长数倍。
第二条是注意力的稀疏化与近似:不让每个 token 看所有人,只看局部窗口或按规则挑出的关键位置,把平方压向线性。省算力,但可能漏掉远处的信息。
第三条是压缩 KV Cache 本身:低精度量化、淘汰不重要的旧 token、合并相似条目。思路统一——历史信息多留一点,显存少花一点。
塞得进去不等于用得好
即使算力和显存都撑得住,模型对超长上下文的利用质量也会衰减。研究者观察到的 lost in the middle 现象是:同一份材料,把关键答案放开头或结尾,模型召回良好;放在正中间,召回率明显下降——注意力天然偏向序列两端。超长输入下指令遵循同样会退化,埋在十万字深处的某句格式约束可能被直接忽略。
所以「支持 1M token」是能力上限,不是使用建议。窗口越长,每一部分内容的利用率越不可控。
工程上怎么用
RAG(检索增强生成,先从知识库检索相关片段再交给模型作答)与长上下文不是二选一。粗糙的分界线:资料量小、结构清晰,直接放进上下文;资料库大到放不下,先检索缩小范围。即便窗口够大,RAG 依旧省费用、省延迟,还能控制单次输入的信息密度——相关性低的片段会稀释注意力。
更实用的是给上下文做预算分配。以 128K 窗口为例,大致划四块:系统提示与工具说明留几 K,写清规则即可;长期记忆与用户偏好按需留几 K 到几十 K;检索内容弹性最大,但宁缺毋滥,相关的十段胜过无关的百段;再给输出留足展开推理的空间。重要的不是填满,而是每一块职责明确。
小结
上下文窗口的长度由注意力计算量、KV Cache 显存与位置编码方案共同决定,三条扩长路线各有取舍。lost in the middle 提醒我们塞得进去不等于用得好;预算分配是使用侧唯一能主动控制的变量。窗口是预算,不是仓库——把它花在刀刃上,比把它装满更重要。
读者留言
COMMENTS 暂无还没有留言,来说第一句?