跳转至

三十五:长上下文窗口分割

来源:http://mp.weixin.qq.com/s?__biz=MzYyNTk3Njg1NA==&mid=2247484435&idx=1&sn=2735258b47682b1e7c1c1f3e455cec83&chksm=f01eb76ac7693e7c5a99e41082189dbbf05c1fd99de68c6c82dfeba728f170c0ae98f5e72e5c#rd

1. 学习范围

本日主题是上下文窗口分割,重点是 StreamingLLM。前两天讨论的是 RoPE scaling,也就是让模型的位置编码更适合长上下文;今天讨论另一条路线:不一定修改模型位置编码,而是把长上下文拆成多个窗口,通过并行窗口、概率融合或流式缓存策略,让有限上下文模型处理更长输入。 需要掌握: - 上下文窗口分割解决什么问题。

  • Sliding window、chunking、parallel windows 的区别。

  • PCW 如何通过并行上下文窗口扩展输入。

  • NBCE 如何用朴素贝叶斯思想融合多个窗口的预测。

  • StreamingLLM 的 attention sink 发现。

  • StreamingLLM 如何用“初始 sink tokens + 最近窗口”保持流式生成稳定。

  • 这些方法不能解决哪些长上下文问题。

2. 参考资料

  • PCW 原论文:https://arxiv.org/abs/2212.10947

  • 苏剑林 NBCE 讲解:https://spaces.ac.cn/archives/9617

  • StreamingLLM 原论文:https://arxiv.org/abs/2309.17453

3. 上下文窗口分割的定位

长上下文扩展有几条路线:

位置编码扩展:
  RoPE PI / NTK / YaRN / LongRoPE

注意力机制改造:
  sparse attention / sliding attention / S2-Attention

上下文窗口分割:
  把长输入拆成多个窗口,分别处理或流式处理

提示压缩:
  Selective Context / LLMLingua / LongLLMLingua

RAG:
  检索相关片段,只给模型最需要的上下文
上下文窗口分割的基本想法:

模型原生窗口有限。
长文本可以分块。
每块仍在模型可处理长度内。
再用某种机制聚合窗口信息或保持生成状态。
它的优点是通常不需要从头训练模型;缺点是跨窗口信息交互弱,不能天然保证全局推理。

4. 常见窗口策略

4.1 固定 chunking

把长文档按长度切成多个 chunk:

doc -> chunk_1, chunk_2, ..., chunk_n
常用于 RAG。优点是简单;缺点是跨 chunk 关系被切断。

4.2 滑动窗口

窗口按 stride 滑动:

window_1: tokens 0..4095
window_2: tokens 2048..6143
window_3: tokens 4096..8191
优点是重叠区域减少边界切断;缺点是计算重复,且窗口之间仍缺少全局交互。

4.3 并行窗口

多个窗口并行输入同一个模型,让它们共享某些后续 query 或生成位置。PCW 属于这个方向。

4.4 流式窗口

对于无限流输入,只保留部分历史 cache,例如最近窗口和少量开头 token。StreamingLLM 属于这个方向。

5. PCW:Parallel Context Windows,并行上下文窗口

PCW 的目标是在不继续训练的情况下,让模型利用多个上下文窗口。它尤其用于 in-context learning 场景:示例很多、总长度超过原窗口时,把示例分到多个并行窗口。 在标准的 Transformer 架构中,全局自注意力的计算复杂度是 $O(N^2)$,且极度依赖预训练时的最大序列长度。PCW 绕过了这一瓶颈,特别适合在 Prefill 阶段并行处理相互独立的文本(如多个检索文档或大量的 Few-shot 示例)。 img

核心思路:

context_1  context_2  ...  context_n
    \         |              /
        shared task/query

img

PCW 会让每个窗口内的 token 使用相同或重置的位置范围,并通过 attention mask 限制不同窗口之间的交互。最后的 query 或生成 token 可以 attend 到多个窗口,从而综合多个窗口的信息。 PCW 对标准自回归 LLM 仅做极其轻量级的修改,具体包含以下四个步骤: - 上下文分块(Context Chunking) 将超长的输入文本切分成多个独立的块(Windows)。每个块的长度都控制在模型原生的上下文限制之内。

  • 位置编码复用(Positional Embedding Reuse) 由于大模型对超出预训练长度的位置编码泛化能力很差,PCW 摒弃了全局递增的位置编码。相反,它为每个 Window 独立分配位置编码。例如,三个 Window 都会各自使用 p_1, p_2, ..., p_c 的位置特征。

  • 块内局部注意力(Restricted Attention) 在处理这些前置上下文时,通过稀疏掩码将各个 Window 严格隔离。Window A 中的 Token 只能与 Window A 内部的 Token 计算注意力,完全看不到 Window B 的内容。

  • 任务 Token 的全局注意力(Global Task Attention) 在序列的最末尾,会放置“任务 Token”(例如用户的最终提问或触发生成的 Prompt)。这部分 Token 的注意力权限被彻底放开,它们被允许同时关注到前面所有并行的 Context Windows。位置编码则顺延前面的长度继续分配。

直觉: - 每个窗口都在模型熟悉的位置长度内。

  • 不强迫模型看到超出训练长度的 position id。

  • 多个窗口像多个并行证据块。

  • query 作为聚合点读取这些窗口。

PCW 的限制: - 窗口之间没有完整 token-level 交互。

  • 更适合多个独立示例或相对独立的证据块。

  • 对需要严格跨窗口链式推理的任务较弱。

  • attention mask 和 position id 实现复杂。

但是, PCW 并非适用于所有长文本任务(例如它不适合需要从头到尾连贯阅读的长篇小说连载),它的优势在于各个上下文片段相互独立的场景: - 检索增强生成 (RAG): 召回了 20 篇独立的参考文档,可以将每篇文档放入一个独立的 Window,最后让模型结合所有文档回答问题。

  • 大规模上下文学习 (Many-Shot ICL): 当模型的原生窗口只能塞下 5 个样例时,使用 PCW 可以并行塞入 50 个样例,显著提升分类和推理任务的准确率。

  • 多跳问答 (Multi-hop QA): 将不同维度的线索散列在多个并行窗口中,让底部的 Task Token 进行最终的逻辑聚合。

6. NBCE:Naive Bayes Context Extension

NBCE 是一种从概率角度扩展上下文的方法。它是一种在“解码层(Logits/Probabilities)”进行后处理的方法,而不是在“特征层(Attention)”做文章。它把长上下文拆成多个窗口,分别计算模型在每个窗口下对下一 token 的预测,然后用朴素贝叶斯假设融合这些预测。 设上下文窗口为:

C_1, C_2, ..., C_n
目标是估计:

P(y | C_1, C_2, ..., C_n)
朴素贝叶斯直觉:

多个上下文窗口提供相对独立的证据。
可以把每个窗口对 y 的支持合并起来。

img

img

img

常见 log 概率形式:

log P(y | C_1...C_n)
  ≈ Σ_i log P(y | C_i) - (n - 1) log P(y)
这里 P(y) 是无条件或较弱条件下的先验,用来避免重复计算语言模型自身的 token 偏好。 NBCE 的价值: - 不需要修改模型结构。

  • 可并行跑多个窗口。

  • 对生成下一个 token 的分布做融合。

限制: - 条件独立假设很强。

  • 多窗口之间没有真正交互。

  • 计算成本随窗口数增加。

  • 对需要跨窗口组合证据的复杂推理有限。

7. StreamingLLM 的问题设定

长文本推理中最让人头疼的工程梦魇:KV Cache 的线性膨胀,以及朴素滑动窗口(Sliding Window)带来的崩溃式退化。 标准 Transformer 自回归推理依赖 KV cache。如果序列越来越长,cache 会线性增长:

cache length = all previous tokens
memory cost grows with sequence length
一种自然想法是 sliding window:只保留最近 W 个 token 的 KV cache,丢掉更早历史。 但 StreamingLLM 发现:简单 sliding window 会导致模型崩溃式退化,即使被丢掉的早期 token 看起来语义不重要。 这引出 attention sink。

8. Attention Sink

为什么朴素滑动窗口会崩溃?

在标准的 Transformer 中,注意力机制的核心是 Softmax 函数,它的数学性质要求所有 Token 的注意力权重之和必须等于 1。

在 LLM 的很多注意力头(Attention Heads)中,并不是每个 Token 都需要强烈关注上文。有时候,某个 Token 只需要关注它自己,或者根本不需要提取什么上下文信息。但是,Softmax 不允许“不关注”。它强制模型必须把 100% 的注意力权重分配出去。 这就产生了一个问题:这些多余的、无处安放的注意力权重,该丢给谁? 模型在预训练时极其聪明地找到了一个“垃圾桶”——序列最开头的最初几个 Token(比如 <s> 或者系统提示词的第一句)。 为什么是它们?因为在自回归的因果掩码(Causal Mask)下,最初的几个 Token 是唯一对后续所有 Token 都绝对可见的。久而久之,这些头部 Token 就被训练成了 Attention Sink,它们吸收了大量本不该分配的“无用注意力”,以此来维持整个 Softmax 分布的稳定。 如果像朴素滑动窗口那样,直接把前几个 Token 的 KV Cache 丢掉,相当于突然移除了模型赖以生存的注意力垃圾桶。这会导致那一大部分无处安放的注意力权重,被迫倾泻到滑动窗口内其他有实际语义的 Token 上,瞬间打乱了原本精确的注意力分布,导致困惑度(Perplexity)激增,模型直接开始胡言乱语。 Attention sink 指的是:模型在生成时会持续把一部分 attention 分配给序列最开头的若干 token,哪怕这些 token 没有明显语义价值。 原因直觉: - Softmax attention 每一层每一头都需要把概率质量分配出去。

  • 初始 token 在训练中总是存在,容易成为稳定的 attention 汇聚点。

  • 某些 attention heads 依赖开头 token 作为“锚点”或 sink。

如果滑动窗口把最开头的 sink tokens 丢掉,模型内部 attention 分布会突然改变,生成质量可能严重下降。 StreamingLLM 的关键发现:

流式推理时,保留少量初始 tokens + 最近窗口,
比只保留最近窗口稳定得多。

9. StreamingLLM 的缓存策略

StreamingLLM 的 KV cache 保留两部分:

sink cache(沉淀缓存):
&nbsp; 序列开头的若干 token,例如前 4 个 token(实验证明,通常4个就够了)

recent cache(滚动缓存):
&nbsp; 最近的 W 个 token
中间历史被丢弃:

[sink tokens] [discarded middle tokens] [recent window]
生成新 token 时,新 token 可以 attend 到: - 初始 sink tokens。

  • 最近窗口 tokens。

不能 attend 到: - 被丢弃的中间 tokens。

这使模型能在无限流输入中维持稳定的局部生成能力,同时显著限制 KV cache 大小。当然要注意,这个方法没有真正扩展模型的上下文记忆能力。它的核心贡献是维持大模型在超长对话中的语言流畅度和生成稳定性,而不至于崩溃。

10. StreamingLLM 的能力边界

StreamingLLM 解决的是: - 长时间流式生成的稳定性。

  • KV cache 内存随时间增长的问题。

  • 只需要近期上下文的对话或连续文本场景。

它不解决: - 从被丢弃的中间历史中检索事实。

  • 对很久以前的细节做精确问答。

  • 长文档全局推理。

  • 多段远距离证据综合。

如果任务需要访问任意历史位置,就需要 RAG、外部记忆、摘要记忆或更强长上下文模型。

11. StreamingLLM 与 RoPE

StreamingLLM 和 RoPE scaling 是不同层面的技术: - RoPE scaling 让模型更可能处理更长位置。

  • StreamingLLM 控制 KV cache,只保留 sink 和最近窗口。

对于使用绝对位置或 RoPE 的模型,流式窗口还要处理 position id: - recent window 的 token 不能简单重置位置,否则和训练/缓存体系冲突。

  • 如果丢弃中间 cache,position id 仍可继续递增。

  • 不同框架可能使用 position rolling 或 attention mask 技巧。

重点是:StreamingLLM 的核心不是位置插值,而是 attention sink 和 cache eviction policy。

12. 与 RAG、摘要和提示压缩的关系

上下文窗口分割适合处理输入过长的问题,但不能自动判断哪些信息重要。 常见组合:

RAG:
&nbsp; 从长文档中检索相关片段,再放入窗口。

摘要记忆:
&nbsp; 把旧历史压成摘要,保留近期原文。

提示压缩:
&nbsp; 删除低信息 token,降低窗口压力。

StreamingLLM:
&nbsp; 保留 sink + recent cache,保证流式生成稳定。
真实系统通常组合使用,而不是只靠一种方法。

13. 面试表达模板

可以这样回答 StreamingLLM:

StreamingLLM 研究的是 LLM 在无限长输入流中的推理问题。普通 KV cache 会随序列长度线性增长,简单滑动窗口只保留最近 token 又会导致模型退化。论文发现模型存在 attention sink,即开头少量 token 会稳定吸收注意力,像注意力锚点一样。StreamingLLM 因此在 cache 中保留两部分:最开始的 sink tokens 和最近窗口 tokens,中间历史可以丢弃。这样能让模型在流式生成中保持稳定,同时把 KV cache 控制在固定大小。

但它不是万能长记忆方案。被丢弃的中间内容无法再被模型直接访问,所以它适合连续流式生成和近期上下文任务,不适合需要精确检索任意历史细节的长文档问答。

14. 紧凑总结

  • 上下文窗口分割通过分块、并行窗口或流式缓存绕开原生窗口限制。

  • PCW 把多个上下文窗口并行放入模型,并用 query 聚合信息。

  • NBCE 把多个窗口的预测分布用朴素贝叶斯思想融合。

  • StreamingLLM 发现 attention sink,保留开头 sink tokens 和最近窗口。

  • StreamingLLM 解决流式生成稳定性,不解决任意历史检索。

  • 长上下文系统常把窗口分割、RAG、摘要、提示压缩和 RoPE scaling 组合使用。

            预览时标签不可点
    

    <div class="