ELAI S.r.l.

AI 助手已经读过的文本,为什么还在占用内存?

逐步推导 KV 缓存内存:上下文、并发请求、共享注意力头与容量限制,附可复现计数和明确假设。

AI 助手已经读过的文本,为什么还在占用内存?

模型装得下,四段对话却装不下

企业助手能正确回答简短问题,但四个人同时提交长文档后,回答尚未完成,内存就耗尽了。模型文件没有变化,增长的是什么?重要部分是上下文内存:生成时保留已处理文本的表示,以便复用。本文要弄清这些表示的成本,以及为什么对话数量与长度同样重要。

摘要。本文从数据形状推导稠密注意力因果解码器的键值缓存成本。一个 32 层、八个 KV 头的合成例子,为四个各 8,192 token 的序列需要 4 GiB。比较注意力头共享、公共前缀、量化,以及含权重和工作区的容量预算。所有数字都是计数,而非已分配内存或延迟实测;它们解释约束,但不保证某块 GPU 或某个运行时能执行该负载。

保存什么,为什么保存

自回归模型根据之前上下文逐个生成 token。在注意力中,当前 token 产生 query,即用于与历史比较的表示;已处理 token 提供比较用的 keys(键)和用于组合的 values(值)。它们不是抄在通讯录里的句子,而是模型内部数值向量。不同注意力头以不同表示并行进行比较。

这里的因果解码器中,早先 token 看不到未来 token。在模型与位置约定固定时,其键和值可在后续步骤复用。KV 缓存按层保存这些数据,避免重新计算,却不取消新 query 与历史的比较。因此读过的文本仍有成本:省去的部分重算,换成了内存存储。

先数数值,再算吉字节

定义 L 为缓存层数,B 为并发序列数,T 为每序列保留 token 数,H_KV 为键值头数,d 为每头维度,s 为每个数值的字节数。假设所有序列等长、各层配置相同,不共享前缀、不压缩。每层 K 张量有 B×T×H_KV×d 个元素,V 同样多。

M_KV = 2 × L × B × T × H_KV × d × s [byte]

因子 2 对应 K、V,L 表示每层重复保存,其余因子对应张量维度。这是缓存数据内存,不是进程总内存;不包括学习权重、临时激活、词表 logits、内存管理元数据或运行时预留。有效公式还必须说明未计入什么。

数值案例:四份各 8,192 token 的文档

取 L=32、B=4、T=8,192、H_KV=8、d=128、s=2 字节。这是示意参数,不是某个具名模型的规格。代入得到 4,294,967,296 字节,恰为 4 GiB。1 GiB 等于 2³⁰ 字节,不同于十进制 GB 的十亿字节。单序列同样计算得到 1 GiB。

每新增一个 token,每序列增加 2×32×8×128×2=131,072 字节,即 128 KiB。四段对话各增加一个 token,共增加 512 KiB。T 包括运行时保留的提示和已生成 token,而不只是初始输入。只按问题做预算,可能在长回答生成过程中超限。

序列BToken TKV头缓存GiB
1819281
4819284
481923216
41638488

为什么要数 KV 头,而不只是 query 头

并非每个 query 头都必须拥有独立键值。传统多头注意力 MHA 中数量相同;分组查询注意力 GQA 中,多个 query 头共享一个 KV 头。本例保留 32 个 query 头,把 KV 头从 32 减至 8,缓存从 16 降至 4 GiB,直接源于保存的独立向量数量减少。这不意味着可以删除任意模型四分之三张量而不改变行为。

Ainslie 等人的 GQA 发表于 EMNLP 2023。已阅读 2023 年 12 月 23 日 arXiv v3 的方法、实验与限制:实验采用编码器—解码器 T5、TPUv4 计时及转换后适配。不把这些结果推广到 GPU 或仅解码器模型,也不称 GQA 为 2026 年新技术;本文关注内存计数。

理论计数:32 层、四个序列、头维度 128、每元素两字节。两条线只比较 8 与 32 个 KV 头,不含权重和工作区,不是内存实测曲线。
理论计数:32 层、四个序列、头维度 128、每元素两字节。两条线只比较 8 与 32 个 KV 头,不含权重和工作区,不是内存实测曲线。

斜率回答延长上下文的成本:T 翻倍,这部分内存翻倍;B 翻倍同样如此。图中不是 T×T 注意力分数矩阵。避免显式形成该矩阵与保留 KV 缓存是不同问题,注意力实现高效不代表保存历史免费。

完整预算会改变允许并发请求数

再加入假设的六十亿参数模型,每参数两字节,权重独占一百二十亿字节,约 11.1759 GiB。另假设其他执行需求预留 2 GiB,可用容量 16 GiB;这些仍是选定值而非实测。四序列总计 11.1759+2+4≈17.1759 GiB,超出预算;三序列约 16.1759,仍超;两序列 15.1759。

“两序列在算术上装得下”不保证能运行。实际预留依赖运行时、内核、提示处理阶段、中间张量形状、碎片及其他分配。计数是规划依据,还须与实测峰值比较。本文未运行模型、未选择 GPU、未测峰值,JSON 明确称结果为 arithmeticFits,即算术可容纳性。

相同前缀必须保存四份吗?

四段对话可能以相同指令和文档开头。若运行时支持安全共享不可变前缀,可只计算一份前缀,再分别计算后缀。设每序列总 T=8,192 token,其中 P=4,096 公共 token。不共享时保存 BT=32,768 个位置,理想共享时:

T_stored = P + B(T − P) T_stored = 4096 + 4×4096 = 20480 M_shared = 2 L H_KV d s T_stored = 2.5 GiB

相较原来 4 GiB,理论节省 1.5 GiB。但屏幕上文字相同不够:token、权重与适配器、位置、决定缓存的注意力条件都必须一致。修改更早指令也可能改变后续表示,回答分叉后还必须分离后缀。本文未实现这种运行时;计数给出此假设下、未计元数据及分配块前的理想收益。

量化缓存,不等于总内存减半

最初每个 KV 数值两字节,若假设八位表示,仅数据载荷从 4 降至 2 GiB。但量化还可能需要缩放因子和元数据。这里纯计数方案为每 64 个值增加一个两字节缩放因子,不设零点,因此每值开销 2/64 字节,总量为 2×(1+2/64)=2.0625 GiB,并非恰好 2。

这是存储公式,不是经评估的量化算法。没有量化真实张量、测回答误差或证明加速;临时反量化副本可能影响峰值。本比较中减半 KV 也不会改变权重和预留,因此总内存节省比例更小。具体方法需要数值和任务层面的验证。

运行时有哪些变化,计数又不能说明什么

Hugging Face 文档描述动态、静态、滑动窗口缓存,以及掩码和生成循环,均已阅读。动态缓存增长,静态缓存可提前预留容量,窗口限制保留历史。未执行这些组件;T 应按实际分配长度与涉及层数调整。

缩短窗口不是对相同信息的免费压缩,而是依架构和策略失去对更早 token 的直接访问。把缓存移出加速器内存改变位置,不消除数据量,还可能增加传输。同样,KV 数据少四倍不保证延迟少四倍;投影、注意力运算、带宽、同步和实现都影响总时间。

结论,以及真实测量所需协议

已读文本占内存,是因为模型按层、按序列保存对后续 token 有用的表示。本例仅缓存已达 4 GiB,加上其他部分,四请求超出假设的 16 GiB 预算。业务规划应同时考虑长度、并发和缓存表示,而非只看下载模型大小。这是企业助手的设计分析,不是 EL-AI 基础设施实测报告。

真实测试需固定检查点、分词器、权重和 KV 精度、GPU、运行时版本、注意力后端、请求数、输入输出长度及分配策略,分别测已分配与预留内存、提示与生成阶段峰值、延迟和吞吐量。此协议仅提出,未执行。附件提供的是确定性整数计数、GiB 断言与图数据,无随机种子;每配置 O(1) 算术操作,不模拟张量。

主要来源与代码

Hugging Face Transformers — How caching works.

Ainslie, Lee-Thorp, de Jong, Zemlyanskiy, Lebrón, Sanghai — GQA, arXiv v3, 23 December 2023.

GQA — EMNLP 2023, ACL Anthology.

GiB = 2**30
layers, kv_heads, head_dim, bytes_per_value = 32, 8, 128, 2
def cache_bytes(batch, tokens):
    return 2 * layers * batch * tokens * kv_heads * head_dim * bytes_per_value
for batch in (1, 2, 3, 4):
    kv = cache_bytes(batch, 8192)
    total = 6_000_000_000 * 2 + 2 * GiB + kv
    print(batch, kv/GiB, total/GiB, total <= 16*GiB)

代码、数据与说明 · JSON. 教学计算使用 Python 3.14.0,图使用 Matplotlib 3.11.2。分析由 AI 辅助,不声称经过同行评审或人工审核。原创 ImageGen 封面仅作示意,不记录 EL-AI 人员、场所或实际安装。来源查阅于 2026 年 10 月 3 日。