想象你在一座没有尽头的图书馆里,面前永远摊开一本《世界全书》。每一页都在不断新增新的知识,而你想回答的问题,往往依赖很久以前读过的某一页。传统的“全文精读”是每次读到新页,就把前面所有页从头再翻一遍——书页越来越多,最终寸步难行。于是出现了两种聪明的读法:一种叫“书签派”(StreamingLLM),只把第一页和最近几页放在手边;另一种叫“检索派”(InfLLM),请图书管理员按需把远处相关的几页取过来。这两种读法都遵循同一个原则:把有限的注意力献给最有价值的内容。对于 AI 从业者来说,理解它们,就等于拿到了让大模型处理无限长文本的两把钥匙。
掌握 InfLLM 与 StreamingLLM 的核心思路,能让你在生产环境中用近乎固定的显存处理超长输入,是构建长文档问答、流式对话与视频理解系统的关键技能。
可视化演示
展示StreamingLLM滑动窗口注意力、注意力汇聚锚点机制,以及KV缓存内存效率对比。
本节配有交互动效(进入视口后自动加载,约 3KB)。
滑动窗口注意力 窗口内token参与注意力计算 窗口外token的KV缓存被丢弃 复杂度O(w·d)非O(n²·d) 滑动窗口将线性计算量降为固定值
长上下文困境
想象你正在读《世界全书》,第 1000 页上出现了一个线索,但你必须同时对照第 1 页、第 37 页、第 512 页……如果每次读到新页都要把前面的所有页重新翻一遍,阅读速度就会越来越慢,直到彻底卡住。Transformer 的全注意力机制正是如此:每生成一个新 token,都要回头看所有历史 token,并计算它们两两之间的相关程度。页数越多,这种“回头看”的代价就越大。
更麻烦的是,模型还要把每一页的“读书笔记”(也就是 K 和 V 向量)都保存在内存里,以便后续快速复用。这意味着不仅计算量随序列长度急剧膨胀,显存占用也在不停上升。为了缓解这个问题,早期方案会直接截断历史,只看最近的一小段。可这样做的代价是:模型把更早的关键信息全忘了。还有一类问题是“位置编码漂移”——模型训练时见过的位置范围是有限的,一旦输入超出训练长度,页码本身都变得不可信。
回到图书馆的例子:书页无限增加的时候,“每次都重读全书”的方案显然不可持续。我们必须像聪明的读者一样,只保留最关键的几页,或按需去远处取回少数的几页。
- 序列长度 \(n\) 决定规模\(n\) 表示 token 的个数,例如一篇 1 万 token 的文档,\(n=10000\)。每一步注意力都要处理这些 token 之间的两两关系。
- 计算量二次方增长每个新 token 都要和之前所有 token 计算一次相似度,所以总操作数约等于 \(n^2\),再乘以向量维度 \(d\)。
- 缓存量线性增长每个历史 token 都会留下 K 和 V 两个向量,因此缓存容量随 \(n\) 线性增加,最终压垮显存。
注意力与KV缓存
在动手解决长上下文之前,要先理解模型“读笔记”的方式。假设你现在有一个问题,比如“这本书的定价是多少”,这就是查询 \(Q\)。图书馆里每页的标题、标签就是键 \(K\),而页面上真正的答案内容就是值 \(V\)。注意力机制做的,就是拿你的问题去和每页的标签做匹配,找出哪些页最值得阅读,然后把最有价值的内容汇总成回答。
模型的注意力公式可以拆成三步:先让 \(Q\) 和所有 \(K\) 做点积,得到初步的“相关分数”;再经过 softmax 归一化,让所有分数变成正数且加起来等于 1;最后用这些分数对 \(V\) 做加权求和。这个加权求和的过程,就像把若干张便签纸上的答案按可信度混合成一条新笔记。
理解了这套笔记机制,就能明白 KV Cache 的价值:过去页面的 \(K\) 和 \(V\) 一旦算好,就存在“缓存区”里,新问题来时只需要带上新的 \(Q\),不必重新计算历史页面的笔记。可如果缓存区无限膨胀,终究还是会撑爆内存。
- 查询与键点积\(QK^\top\) 计算每个问题和每个历史位置的相似度,分数越高代表那一页越值得关注。
- 缩放因子除以 \(\sqrt{d_k}\) 可以把分数拉回合理范围,避免极端值让 softmax 输出过于“非黑即白”。
- softmax 归一化把原始分数变成一组非负且总和为 1 的权重,相当于把“关注度”变成百分比。
- 对值加权求和用权重去混合 \(V\),得到最终的注意力输出。混合过程也叫注意力汇聚。
流式锚点方法:StreamingLLM
现在你决定采用“书签派”读法:手边永远放着《世界全书》的第一页,以及最近翻过的几页。第一页虽然内容不多,但它给出了全书的话题基调,所以模型在产生注意力时,会不自觉地给它分配很高权重。这种现象被称为“注意力汇聚”(attention sink),就像开会时大家都习惯性地看向主持人,即使主持人并没有说出关键信息。
StreamingLLM 正是利用这一点:无论序列已经写到多长,只保留初始 token 的 KV Cache 作为锚点,再结合一个滑动窗口,窗口里是最近 \(w\) 个 token 的 KV Cache。计算新 token 的注意力时,只看锚点和窗口,完全不看中间那些古老但非开头的 token。这样 KV Cache 不再随序列长度增长,真正实现了“无限流式处理”。
这一招的精妙之处在于:锚点提供了长期主题,窗口提供了最新语境,而中间的旧细节被主动遗忘。就像一位读者永远知道这本书讲的是“宇宙历史”,也记得刚刚读到的章节,至于第 300 页的某个数字,丢掉就丢掉了。
- 当前查询 \(q_t\)新 token 的查询向量,代表“我现在想知道什么”。
- 拼接锚点与窗口\([K_0;\,K_{t-w+1:t}]\) 把第 0 个 token 的键与最近 \(w\) 个 token 的键连成一个候选集合。
- 缩放点积注意力和标准注意力一样,先算相关性、再缩放、再 softmax,得到候选集合上的关注权重。
- 汇聚对应值把相同候选集合的 \(V\) 按权重加权求和,生成当前步的输出。
检索记忆方法:InfLLM
如果一个问题偏偏要依赖第 300 页的某个细节呢?书签派会无能为力。这时轮到“检索派”登场:请图书管理员根据你的问题,去书库深处把相关的那几页取回来。InfLLM 的思路正是如此——它并不试图记住所有历史 token,而是把历史 KV 按块存放在“记忆银行”中,每个块有一个精炼的“块级摘要”。
当新的查询到来时,InfLLM 先用查询和各个块摘要做相似度计算,选出最相关的 Top-k 个远距离 KV 块。随后,注意力只在“当前滑动窗口 + 这 k 个被检索出来的块”上进行。这样,模型既能保持滑动窗口的流式特性,又能主动触及很久以前的细节,而成本只由窗口大小和 k 决定,不随历史总长度增长。
InfLLM 的工程细节中,块级摘要通常不是把块内所有 token 都存一遍,而是通过“采样评估”的方式选取少量代表性 token 来计算块级键。这就像图书管理员只看目录和摘要,而不会把整本书背诵下来。于是检索成本大大降低,长程记忆却依然精准。
- 候选集合当前窗口和检索到的远端块共同构成注意力可以看的“候选页”。
- 相关性打分每个候选 token 的键 \(k_i\) 与当前查询 \(q_t\) 做缩放点积,得到原始相关分数。
- 权重归一化并汇聚用 softmax 把候选集合上的分数变成权重,再对值 \(v_i\) 做加权求和,得到最终输出。
方案对比分析
现在我们把“书签派”和“检索派”放在同一张书桌上比较。书签派(StreamingLLM)最大的优点是零检索开销:它只需要锚点加窗口,显存是固定大小,部署简单、推理速度快。缺点也很明显:除了初始 token,中间的历史细节全部丢失,遇到“第 300 页的细节”这种问题时只能靠猜。
检索派(InfLLM)则多了一道“管理员检索”工序。它需要额外的记忆银行、块级摘要和 Top-k 排序,因此实现更复杂,但只要检索准确,就能覆盖更远的内容。两者的共同点是都避开了全注意力的二次方增长,改为“局部注意力 + 有限补充”。
选择哪个方案,取决于你的场景:如果任务只依赖最近的对话和系统提示,StreamingLLM 是轻量首选;如果任务需要回答文档深处的细节,InfLLM 的检索能力则更有价值。两者甚至可以在同一产品里配合使用。
- StreamingLLM 缓存量只保留 1 个锚点 token 和 \(w\) 个窗口 token,所以显存是 \(O(w+1)\)。
- InfLLM 缓存量除了窗口 \(w\),还额外加载 \(B\) 个远端块,所以是 \(O(w+B)\)。
- 共同点两者都摆脱了对 \(n\) 的依赖,因此可以处理任意长度的输入,只是“记忆精度”不同。
工程实践要点
把论文里的方法变成可用的系统,还需要一套“阅览室规则”。首先是窗口配置:窗口太小,模型连当前话题都接不上;窗口太大,显存和延迟又会上升。实践中通常根据任务类型扫描 512、1024、2048 等窗口大小,同时观察困惑度(Perplexity,PPL)和下游任务指标。
其次是缓存管理:StreamingLLM 要保证锚点永远不被淘汰,窗口内的旧 token 按先进先出淘汰;InfLLM 要设计好块大小、块摘要的采样数量以及 Top-k 的召回策略。实现时还要注意检索延迟,不能让管理员找书的时间拖慢整个阅读节奏。评测指标里,PPL 是最常用的语言模型质量度量,它衡量模型对文本的预测能力。
工程实践的核心,是把“书签”和“检索”变成一套可监控、可调参的机制。锚点位置不能丢,窗口淘汰要平滑,检索块要控制时延。调试的路子走对了,无限长文本就真的能在产品里跑起来。
- 单步对数似然\(\log p_\theta(x_t \mid x_{<t})\) 是模型预测第 \(t\) 个 token 的对数概率,越大越好。
- 对所有 token 取平均把整个序列每一步的对数概率加起来再除以 \(T\),得到平均对数似然。
- 取负并求指数负号把“越大越好”变成“越小越好”,取指数后得到更直观的 PPL 数值。
应用前景展望
当“书签派”和“检索派”的能力合在一起,我们几乎拥有了一本永远不会读完、也永远不会读不完的书。长文档问答不再需要把全文塞进模型,多轮对话可以无限延续而不爆显存,代码仓库的理解也不必把整个项目复制到上下文窗口里。
在这些应用中,模型的有效上下文范围可以用一个简单的公式表达:
未来,无论是一本百万字的书、一整天不间断的语音流,还是一整个大型代码库,AI 都可以用近乎恒定的资源去阅读。书签派保证当下的流畅,检索派保证远方的记忆,两条路正在共同通向真正的“无限上下文”。
- Streaming 有效范围\(w + 1\) 表示最近窗口 \(w\) 个 token 加上 1 个初始锚点。
- InfLLM 有效范围\(w + |\mathcal{B}| \cdot b\) 表示窗口与检索块覆盖的总 token 数,其中 \(b\) 是每个块的大小。
AI 应用场景
智能客服的无限轮对话
阶段:流式推理与部署
解决的问题:传统多轮对话会把整个会话历史反复输入模型,随着对话轮数增加,延迟和显存不断上升。StreamingLLM 用系统提示词作为锚点,加上最近几轮消息的滑动窗口,让客服机器人永远只用固定大小的 KV Cache 回应,省去历史重放。
具体案例:某电商客服系统每天处理千万级会话,接入 StreamingLLM 后,单次请求平均时延下降 40%,长会话不再触发 OOM。用户问“刚才说的优惠券怎么用”时,系统仍能通过最近窗口回忆起刚聊过的内容。
企业知识库的长文档问答
阶段:离线索引 + 在线检索增强问答
解决的问题:全注意力问答系统在遇到“请总结第 42 页第 3 段的结论”时,要么把整个文档截断导致信息丢失,要么计算量爆炸。InfLLM 的块级记忆银行把历史 KV 分块存储,只检索与问题相关的远端块。
具体案例:某律所的合同审查系统将每份 10 万 token 的合同按 512 token 分块,InfLLM 只加载当前窗口和 Top-k 相关块,就能准确回答“第 8 条违约责任是什么”,显存占用比全量注意力降低 90%。
视频流的实时内容理解
阶段:多模态流式分析
解决的问题:监控视频不断产生新的帧,模型不能无限堆积全部历史帧。InfLLM 把历史帧的视觉 KV 放进记忆银行,当前分析窗口负责实时事件,检索模块负责回溯“10 分钟前是否有人靠近过门禁”。
具体案例:智慧园区安防系统使用 InfLLM 风格的长记忆流,对 24 小时视频流持续分析,当新事件出现时自动检索相关历史片段,生成异常行为报告。
Python 演示
# -*- coding: utf-8 -*-"""Simulate StreamingLLM and InfLLM attention selection."""import mathimport random def softmax(scores): m = max(scores) exps = [math.exp(s - m) for s in scores] total = sum(exps) return [e / total for e in exps] def attention(q, keys, values, indices): scores = [sum(q[d] * keys[i][d] for d in range(len(q))) for i in indices] weights = softmax(scores) out = [0.0 for _ in range(len(q))] for pos, i in enumerate(indices): for d in range(len(q)): out[d] += weights[pos] * values[i][d] return out, indices def full_attention(q, keys, values): return attention(q, keys, values, list(range(len(keys)))) def streaming_attention(q, keys, values, window=4): n = len(keys) if n == 1: return attention(q, keys, values, [0]) start = max(1, n - window) indices = [0] + list(range(start, n)) return attention(q, keys, values, indices) def infllm_attention(q, keys, values, window=4, block_size=2, top_k=2): n = len(keys) if n <= window + top_k * block_size: return streaming_attention(q, keys, values, window) start = max(1, n - window) window_indices = list(range(start, n)) used = set(window_indices) blocks = [] for b in range(0, start, block_size): if b + block_size <= start: score = sum(q[d] * keys[b][d] for d in range(len(q))) blocks.append((score, b)) blocks.sort(reverse=True) retrieved = [] for _, b in blocks[:top_k]: for i in range(b, b + block_size): if i not in used: retrieved.append(i) used.add(i) indices = window_indices + retrieved return attention(q, keys, values, indices) if __name__ == "__main__": random.seed(0) n = 20 dim = 4 keys = [[random.random() for _ in range(dim)] for _ in range(n)] values = [[random.random() for _ in range(dim)] for _ in range(n)] q = [random.random() for _ in range(dim)] _, full_idx = full_attention(q, keys, values) _, stream_idx = streaming_attention(q, keys, values, window=4) _, infllm_idx = infllm_attention(q, keys, values, window=4, block_size=2, top_k=2) print("full attention tokens:", len(full_idx)) print("streaming tokens:", len(stream_idx)) print("infllm tokens:", len(infllm_idx)) print("streaming selected:", stream_idx) print("infllm selected:", infllm_idx)
scores = [sum(q[d] * keys[i][d] for d in range(len(q))) for i in indices]计算当前查询与每个候选键的点积,作为相关分数。weights = softmax(scores)把原始分数变成和为 1 的注意力权重,模拟“关注度分配”。indices = [0] + list(range(start, n))这是 StreamingLLM 的核心:固定锚点 \(0\) 加上最近窗口。blocks.sort(reverse=True)按块级相关分数排序,模拟图书管理员先看摘要再取书。indices = window_indices + retrieved把当前窗口和检索到的远端块合并,作为 InfLLM 的注意力候选集合。if n <= window + top_k * block_size:当序列本身足够短时,退化为 StreamingLLM,避免过度设计。
常见误区
误区既然叫长上下文方案,那过去每个 token 应该都被记住了吧?模型的回答应该能引用几千轮之前的所有细节。
正解StreamingLLM 只保留初始锚点 token 和最近窗口中的 token,其余历史 KV 会被主动丢弃。它记住的是“全书主题”和“当前语境”,而不是每个具体的旧细节。第一页书签能提醒你这本书讲什么,却不能替你背诵第 300 页的表格。
为何常见:“无限上下文”这个名字容易让人误以为模型拥有了无限记忆,实际上它提供的是无限流式处理能力,而非无限内容存储。
误区InfLLM 要动态检索,那它得先和所有历史 token 比较一遍,复杂度不还是 \(O(n)\) 吗?
正解InfLLM 检索时比较的是“块级摘要键”,而不是每个 token 的原始键。块摘要由块内少量采样 token 生成,所以扫一遍所有块摘要的成本远低于逐 token 计算。就像图书管理员只看书架上的标题卡片,而不是拆开每本书逐字阅读。
为何常见:人们容易把“检索”理解成“遍历全部内容”,忽略了摘要和索引的作用。
误区既然窗口能提供上下文,那把窗口调到 8192、16384 甚至更大,效果一定更好。
正解窗口增大会同时增加计算量、显存和延迟,而且过大的窗口会让注意力权重被稀释,模型反而抓不住重点。StreamingLLM 的卖点正是“小窗口 + 锚点”也能保持稳定,InfLLM 则用检索来补足长程,而不是无限扩大窗口。
为何常见:“更多上下文 = 更好效果”是直觉思维,忽略了注意力的分配规律和硬件成本。
误区KV Cache 是模型训练出来的权重,保存下来就能直接用于所有输入。
正解KV Cache 是推理过程中由输入 token 计算得到的中间缓存,不是可训练参数。不同的输入会有不同的 K/V,StreamingLLM 和 InfLLM 的核心工作就是决定“哪些 K/V 该留、哪些该丢”。
为何常见:KV Cache 和模型权重都存在内存或显存里,初学者容易把两种存储混为一谈。
面试题
Q1. StreamingLLM 选择哪个位置作为固定锚点?
- A.序列最末尾的 token
- B.序列最开头的 token← 正确答案
- C.注意力分数最低项
- D.随机抽取的 token
答案:B。StreamingLLM 保留初始 token 的 KV Cache 作为锚点,因为初始 token 能吸引大量注意力,形成稳定的注意力汇聚。末尾 token 会随窗口滑动,C 和 D 与论文观察不符。
Q2. StreamingLLM 的滑动窗口主要保留什么?
- A.离当前最近的若干 token← 正确答案
- B.距离最远的若干 token
- C.全量历史 token 缓存
- D.注意力权重最大的 token
答案:A。滑动窗口保留的是当前时刻附近最近的 \(w\) 个 token,让模型掌握最新语境。最远 token 和全量缓存正是 StreamingLLM 要避免的。
Q3. InfLLM 针对远端信息采用什么策略?
- A.永远只保留初始 token
- B.动态检索相关 KV 块← 正确答案
- C.把所有历史块平均合并
- D.完全丢弃远端信息
答案:B。InfLLM 把历史 KV 分成块并建立块级摘要,用当前查询动态检索 Top-k 相关块,而不是简单保留或丢弃。
Q4. 朴素全注意力的计算复杂度约为?
- A.与序列长度线性相关
- B.与序列长度平方相关← 正确答案
- C.与序列长度对数相关
- D.与序列长度完全无关
答案:B。每个 token 都要与之前所有 token 计算注意力,因此总计算量约为 \(O(n^2)\)。这正是长上下文方案要突破的核心瓶颈。
Q5. 关于 KV Cache 的描述,正确的有哪些?
- A.它是推理阶段的临时缓存← 正确答案
- B.它属于需训练的模型参数
- C.它保存历史 token 的 K/V← 正确答案
- D.它能避免重复计算旧值← 正确答案
答案:ACD。KV Cache 是推理时生成的历史 K/V 缓存,不是模型权重。它让新 token 不再重新计算历史投影,因此能显著加速解码。
Q6. 注意力汇聚现象在 StreamingLLM 中表现为?
- A.初始 token 获得大量注意力← 正确答案
- B.末尾 token 占据全部权重
- C.所有 token 权重完全相等
- D.注意力权重会快速消失
答案:A。论文发现初始 token 即使不含重要信息,也会吸引大量注意力分数。利用这个现象,StreamingLLM 把它作为固定锚点来稳定长流式生成。
Q7. 与 StreamingLLM 相比,InfLLM 的优势有哪些?
- A.能主动获取远端相关信息← 正确答案
- B.显存必然随长度线性增长
- C.更适合长程依赖型任务← 正确答案
- D.完全不需要任何初始 token
答案:AC。InfLLM 通过动态检索访问远端 KV 块,因此能处理需要远距离记忆的任务。它的显存由窗口和 Top-k 块决定,不随历史长度线性增长;它仍然需要局部上下文。
Q8. InfLLM 的块级检索键通常如何构造?
- A.对块内全部 K 做平均池化
- B.采样若干 token 的 K 汇总← 正确答案
- C.只取块内最后一个 K 向量
- D.对每个 token 逐一生成索引
答案:B。InfLLM 的块级键通过从块内采样少量 token 并计算其 K 的汇总得到,兼顾检索效率和代表性。平均池化和单 token 都可能丢失关键信息,逐 token 索引又会带来过高成本。
Q9. StreamingLLM 的显存开销随序列长度如何变化?
- A.近似恒定,不随长度增长← 正确答案
- B.线性增长且无法控制
- C.平方增长且持续累积
- D.指数增长迅速耗尽
答案:A。StreamingLLM 只保留锚点 + 窗口,所以 KV Cache 大小固定,近似 \(O(w+1)\)。超长输入不会带来额外显存增长。
Q10. 以下哪些任务更适合用 InfLLM 思路?
- A.回答长文档中的历史细节← 正确答案
- B.只依赖最近消息的闲聊
- C.跨代码文件依赖分析← 正确答案
- D.固定系统提示词的流式生成
答案:AC。长文档问答和跨代码文件分析都依赖较早的远端信息,适合用 InfLLM 检索历史 KV 块。只依赖最近消息或固定提示词的场景,用 StreamingLLM 更简单高效。