描述留在内存中的内容
在自回归生成过程中,已处理 token 的键和值可以保留下来供后续步骤使用。该缓存属于注意力层。因此,它的体量取决于模型和所保留的历史,而不仅仅是参数数量。一段对话的上下文还包括应用加入的指令、之前的消息和文档。
您的第一个决策是操作层面的:需要有多少条序列保持活跃,最长到多少长度?一个包含二十个请求、其中两个同时执行的队列,未必意味着二十个常驻缓存。请记录请求的实际准入情况,以及生成过程中可能产生的额外序列。
准备一份说明,列出模型修订版本、注意力层、KV 头数、键和值的维度、它们的 dtype 以及缓存策略。在 tokenizer 和对话模板之后统计 token 数。字符数上限并不能描述这一分配。
使用 KV 头数,尤其是配合 GQA 时
查询头 Q 的数量与 KV 头的数量可以不同。在经典的多头注意力中,二者一致。使用 MQA 时,只有一个 KV 头被共享;GQA 则把多个 Q 头归到一个 KV 头之下。当配置中存在 num_key_value_heads 字段时,请读取它,并核实它对该架构的含义。
例如,四十个 Q 头与八个 KV 头形成每个 KV 组五个 Q 头。存储缓存的公式使用八,而不是四十。这个比例并不能描述注意力的全部内存:某些运算或转换可能会产生临时量。
不要仅仅为了降低一个已训练模型的内存需求而修改这个数字。注意力模式是其架构的一部分。两个头数不同的模型并不会通过一次容量计算就成为等价的变体;它们的质量必须单独评估。
技术来源: Hugging Face — LlamaConfig 字段以及 MHA、MQA、GQA 的区分 · PyTorch — Q/K/V 维度与 GQA 注意力约束
列出公式及其单位
在均匀情形下,L 是层数,Hkv 是 KV 头数,D 是其维度,T 是每个序列保留的位置数,B 是序列数,q 是每个值的字节数。系数二计入 K 和 V。这个近似假设键和值具有相同维度和相同格式,没有压缩或前缀共享。
只转换最终结果:一 Gio 等于 1 073 741 824 字节;一十进制 GB 等于 1 000 000 000 字节。在你的记录中保留字节数,以免四舍五入或单位变化掩盖差异。
对于不等长且无 padding 的情况,用实际存储的位置总和替换 B × T。对于异构层,逐层求和。带 padding 的密集存储、按块分配或静态预留都要求计入已分配的位置,而这些位置可能超过有效 token。
实例演算:六个序列,不测量 GPU
假设一个虚构架构:四十层、八个 KV 头、维度 128。再假设每个值两字节的均匀缓存。每个序列最多接收 3 072 个输入 token,并为额外 1 024 个 token 预留空间,即上限 4 096 个位置。这些是教学假设,不是某个模型已证实的配置。
每个位置、每个序列的计算成本为 2 × 40 × 8 × 128 × 2 = 163 840 字节。那么一个 4 096 位置的序列为 671 088 640 字节,即 0,625 Gio。六个序列为 4 026 531 840 字节,即仅缓存就达 3,75 Gio。
在该公式中,将保留长度加倍会使这一项翻倍。在其他假设不变的情况下,将八个 KV 头替换为四十个会使其乘以五。这些比例并不预示任何真实模型的加速、质量损失或兼容性。
| 序列数 B | 位置数 T | KV 头数 | 计算出的字节数 | GiB |
|---|---|---|---|---|
| 1 | 4 096 | 8 | 671 088 640 | 0,625 |
| 6 | 4 096 | 8 | 4 026 531 840 | 3,75 |
| 6 | 8 192 | 8 | 8 053 063 680 | 7,5 |
| 6 | 4 096 | 40 | 20 132 659 200 | 18,75 |
根据缓存策略调整预算
动态缓存会随着保留的位置而增长。静态缓存会预留一个最大容量:要为这个预留量定尺寸,而不只是为启动时观察到的那个短请求定尺寸。对于滑动窗口注意力,某些层可以将其历史长度封顶;而全注意力层则需要单独计算。
缓存量化以及将其卸载到 CPU 是另外一些策略,取决于模型和软件。它们会改变存储、传输或计算方面的约束。量化权重并不能证明缓存也具有相同的格式。
记下缓存类别及其显式参数。还要检查请求结束或取消后槽位是否被释放。对于混合了短序列和长序列的负载,统一的按最大值预留可能比仅按有用内容之和更占用资源。
按步骤验证代表性的负载
构建三种情况:常规输入、预期的长输入,以及允许的最大并发请求数。固定模型、tokenizer、模板、生成长度限制和结束规则。每次只改变一个维度;被截短的回复或被截断的文档会改变实际完成的工作。
分别记录加载、输入初始处理和生成。围绕计时阶段同步设备,保留起始内存水平和峰值。内存方法解释了为什么 allocated 和 reserved 不能相加,以及为什么它们最大值之差无法单独分离出缓存。
你的验证应得出一个有依据的上界和可接受的输出:已完成请求的 ID、错误、生成长度以及质量判据。只在短输入上跑通并不能验证最大并发。结束前的内存错误不构成对完整运行需求的度量。
排除会误导选择的错误
不要将计算出的缓存等同于 GPU 总容量。还要分析权重、临时激活、保留的输出以及软件开销。也不要自动将这个体积除以显卡数量:层或头的放置必须实际配置并在每块设备上验证。
IteraGPU Lab 笔记本是一个针对小型无注意力稠密网络的插桩练习。它有助于解读内存阶段;其 context 参数无法验证这一 KV 计算。请使用负载说明和推理资料来准备你自己模型的试验。
只有在区分了算术容量、实际观测到的最大值以及尚未测试的情况之后,才能比较各套餐的容量。租赁套餐组织你的工作窗口;它不保证任何上下文长度或生成速率。
- 混淆 Q 头和 KV 头:重新核对架构配置。
- 只为提示词做预算:把生成和实际预留也纳入进来。
- 把「4 位权重」当成「4 位缓存」:分别记录两种格式。
- 忽略并发:统计实际驻留的序列数。
- 承诺显卡之间共享内存:核实实际放置。
实际问题
我能仅凭参数数量就知道 KV 缓存吗?
不能。还需要注意力层的架构、KV 头、其维度、缓存格式、保留的位置以及驻留序列。两个规模相近的模型可能要求不同的 KV 预算。
静态缓存只消耗我请求的长度吗?
需要评估自己预留的容量。一次短请求无法推断出这个最大分配量。请记录缓存参数,并测量实际创建的配置。
公式的结果足以用来选择显卡吗?
它仅根据所声明的假设估算缓存。选择还必须考虑其他内存项、软件兼容性,以及一次具有代表性上下文、并发和质量要求的完整试验。