KV缓存从11.25GB缩到0.21GB:WARP中Kimi Delta Attention线性注意力原理剖析
【免费下载链接】warpRun the full 2.78-trillion-parameter Kimi K3 model, DeepSeek V4.1 Flash or GLM-5.3-Flash beyond available RAM by streaming activated weights directly from NVMe. A dependency-free, embeddable C inference engine.项目地址: https://gitcode.com/gh_mirrors/was/warp
WARP是一个零依赖、可嵌入的 C 语言推理引擎,能让 2.78 万亿参数的 Kimi K3 直接跑在 64GB 内存的笔记本上——它把模型主干放在内存里,只从 NVMe 流式读取每个 token 真正激活的专家。而它最巧妙的省钱技巧之一,就是把KV 缓存在 4K 上下文下从11.25GB 压到 0.21GB,整整 53 倍。这份差距来自两个协同机制:69 层Kimi Delta Attention 线性注意力,加上 24 层 MLA 全注意力的潜空间 KV 压缩。本文用最少代码把原理讲透。
为什么大模型的 KV 缓存会吃掉内存
要理解省下来的这 11GB 有多珍贵,先看传统做法为什么这么"浪费"。
在标准 softmax 注意力里,每来一个新 token,模型都要把它对应的 Key 和 Value 追加进一个越聊越长的缓存表。这意味着:
- 缓存大小与上下文长度成正比——聊得越久、读得越长的文档,占用的内存线性增长。
- 每个注意力头都要存一份——K3 有 96 个头,每个头都要存完整的 K 和 V。
- 长上下文直接爆内存——在 K3 上,展开式 KV 缓存到 128K 就要 360GB,到 1M 更是 2.81TB,远超任何笔记本。
💡 关键点:KV 缓存每多占 1GB 内存,专家缓存就少 1GB 可用空间;而专家缓存的命中率,恰恰直接决定了每秒能生成多少 token。省 KV 不是抠细节,而是把宝贵的 RAM 让给真正影响速度的地方。
Kimi Delta Attention:把注意力状态压成固定大小
K3 的 93 层里,69 层用的是 KDA 线性注意力,只有 24 层是传统全注意力(约 2.9:1)。这正是它能"线性"的根本原因。
线性注意力:状态与上下文长度无关
传统注意力的状态随 token 数增长,而 KDA 的状态是一个固定大小的矩阵,不管你聊了 1 句话还是 100 万 token,它占用的内存都一样。
具体到 K3 的形状:69 层 × 96 头 × 128(key 维)× 128(value 维)× 4 字节 ≈414 MiB——这个数字完全不随上下文增长。对比展开式 KV 缓存在 4K 时就要 11.25GB,量级天差地别。
KDA 递推公式:先衰减,再差分
每个 token 到来时,KDA 用三步更新状态矩阵S(见 src/kda.h 与内核 src/kda.c):
S' = Diag(exp(g_t)) · S_{t-1} # ① 沿 key 轴先做"遗忘衰减" S_t = S' + β_t · k_t (v_t − S'ᵀ k_t)ᵀ # ② 差分项:只修正"没记住"的部分 o_t = S_tᵀ q_t # ③ 读出输出(q 先 L2 归一化)三个设计让它在 C 里极其好算:
- 衰减在差分之前(
g是 log 空间,内核里才指数化)——这是和早期草稿的关键差异。 - 每个头每 token 只做两次向量点积 + 一次秩-1 更新,约
3·d_k·d_v次乘加。96 头 × 128×128 每层每 token 约 1.6 MFLOP,几乎可以忽略。 - q、k 在内核内部 L2 归一化——这一步改变了数值,C 内核必须精确复现,否则结果就偏了。
KDA 在 q/k/v 前还接了一段核宽为 4 的短卷积(SiLU 激活),解码时只需要一个 3-token 的环形缓冲,几 MB 而已,但要作为会话状态持久化。
414MB 的 KDA 状态从哪来
| 项 | 计算 | 大小 |
|---|---|---|
| 递推状态矩阵 S | 69 层 × 96 头 × 128 × 128 × 4B | 414 MiB(不随上下文变) |
| 短卷积环形窗口 | 每层 3 个投影 × 3 × 128 | 几 MB |
这部分内存是"一次性"的——不管你跑多长的对话,它都稳定在 414MiB 左右。
剩下 24 层全注意力:MLA 潜空间再压 53 倍
KDA 搞定了 69 层,但那 24 层全注意力(MLA)仍需 KV 缓存。WARP 在这里又压了一刀,把每 token 每层的缓存从120KB 降到 2.25KB。
只缓存 576 维潜向量,而不是每个头的 K/V
MLA 的核心思想:K 和 V 并不需要对每个头都存完整向量,它们都来自一个低秩潜向量(latent)。K3 里这个潜向量是kv_lora 512 + qk_rope 64 = 576个浮点数。于是每个 token 每层只需缓存 576 个数,而不是 96 头 ×(192 + 128)=30720个数。
576 / 30720 ≈ 53 倍——这就是标题里那个数字的出处。
把 kv_b_proj 吸收进 query:120KB → 2.25KB
真正让"只存潜向量"成立的是 WARP 的一个数学技巧(src/model.c):把投影矩阵kv_b_proj从"每个缓存 token 的路径"搬到"每步计算的路径"。依据是两条等式:
- 打分:
q_nope · (W_kb c) ≡ (W_kbᵀ q_nope) · c - 取值:
Σ a_s (W_vb c_s) ≡ W_vb (Σ a_s c_s)
也就是说,投影可以从"缓存里存展开结果"变成"算的时候现场投影"。代价是注意力算术变大约 3.2 倍(点积从 192/128 宽变成 576/512 宽),但换来内存流量降 53 倍——在 NVMe 受限的引擎里,这笔交易极其划算。
4K 上下文内存账本:0.21GB 是怎么来的
把两部分合起来,waste plan报告的 "KDA state + KV cache" 在 4K 上下文下就是:
| 组成 | 随上下文变化? | 4K 时大小 |
|---|---|---|
| KDA 递推状态 S | ❌ 恒定 | 414 MiB |
| MLA 潜空间 KV | ✅ 线性增长 | 216 MiB ≈ 0.21GB |
| 短卷积环形窗口 | ❌ 恒定 | ~30 MiB |
| 合计 | ≈ 659 MiB |
其中"0.21GB"专指那 24 层 MLA 的潜空间缓存(24 层 × 4096 token × 576 × 4B ≈ 216MiB)。完整的展开式 vs 潜式对比(docs/LEARNED.md §10):
| 上下文 | 展开式 KV(旧) | 潜式 KV(新) |
|---|---|---|
| 4,096 | 11.25 GB | 0.21 GB |
| 32,768 | 90.00 GB | 1.69 GB |
| 131,072 | 360.00 GB | 6.75 GB |
| 1,048,576 | 2.81 TB | 54.00 GB |
🎯 一句话总结:KDA 让 69 层根本不随上下文膨胀,MLA 潜空间让 24 层的膨胀也被压到 53 分之一。于是 1M 上下文的"地板价"从 2.81TB 掉到 54GB——长上下文第一次变得"能开"。
省下的内存去哪了:喂给专家缓存
省 KV 的最终目的不是好看,而是把 RAM 让给 MoE 专家缓存。K3 打开模型本身就要 29.19GB 常驻,剩下越多的空闲 RAM,专家缓存就能越大,命中率高,token/s 越高。
实测(docs/EFFICIENCY.md):KV 缓存从展开式换成潜式后,同样 46GB 预算下专家缓存从 5.64GB 涨到 16.68GB,命中率从 0% 升到 11%,logits 几乎不变(最大偏差 1.19e-05,argmax 与 top-5 完全一致)。同样的输出质量,多出了十几 GB 给缓存——这就是那 11GB 的真实价值。
相关源码与文档导航
想深入这块的,可以从这些入口读起:
- KDA 原理与内核规划:docs/KDA.md —— 递推公式、状态预算、验证方式全在这。
- KDA C 内核实现:src/kda.c(解码步进、短卷积、门控 RMSNorm)与头文件 src/kda.h。
- 内存预算计算:src/waste.c —— 注释直接写明"576 而非 30720",并给出
state_bytes的完整公式。 - MLA 吸收技巧:src/model.c —— 2.25KB vs 120KB 的来龙去脉。
- K3 架构全貌:docs/K3.md —— 93 层形状、69:24 分层比。
- 验证:tests/test_kda.c 配合 tools/kda_ref.py,与官方参考实现逐层比对(真实形状下最大偏差 4.1e-08)。
- 内存规划工具:tools/memplan.py —— 按真实形状算出各部分占用。
小结
WARP 把 KV 缓存从 11.25GB 压到 0.21GB,靠的不是单一技巧,而是两种机制叠加:
- KDA 线性注意力——69 层用一个固定 414MiB 的递推矩阵替代随上下文膨胀的 KV,状态与长度无关。
- MLA 潜空间 KV + 投影吸收——24 层全注意力只缓存 576 维潜向量,再用
kv_b_proj吸收把每 token 缓存砍 53 倍。
省下的 RAM 直接转化为专家缓存命中率,也就转化成了你看得见的 token/s。这正是 WARP 能在 64GB 笔记本上跑动 2.78 万亿参数模型的核心底气之一——内存省在注意力上,速度赢在专家缓存上。
【免费下载链接】warpRun the full 2.78-trillion-parameter Kimi K3 model, DeepSeek V4.1 Flash or GLM-5.3-Flash beyond available RAM by streaming activated weights directly from NVMe. A dependency-free, embeddable C inference engine.项目地址: https://gitcode.com/gh_mirrors/was/warp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考