✏️ LLM 定价的数学原理 04:拆开 KV cache 这个「反派」
到目前为止,KV cache 一直以 L(上下文长度)和 b(每 token 字节数)两个符号出现在咱们的方程里。咱们说过它「摊不掉」,但还没真正打开看里面是什么。这一篇咱们正面把它拆开:每 token 那几十 KB 字节到底装的是什么、GQA / MLA / 跨层共享这些技术分别在解决什么问题、以及最有意思的——从 Gemini 公开的长上下文定价反推它的内部架构。最后咱们退一步,把「算得快 vs 经济学优化 vs 硬件常数」这套三分框架交给你,让你能给任何架构创新分类。
前情提要:上一篇 从推理耗时到推理成本 咱们换到运营方视角,看清了同一套方程在 Y 轴换成「每 token 成本」后能讲什么故事。但 KV cache 这个反派一直只以符号出现,没真正拆开看——这一篇咱们补上。
一、KV cache 里到底装了什么?
回想第一篇的比喻:KV cache 是模型「对已读 token 做的笔记」。但具体每条笔记长什么样?这一节咱们打开看。
模型怎么「看」历史
Transformer 的核心是 attention(注意力机制)。每一个被喂进模型的 token 都要拿自己的 Q(Query,查询向量) 去 attend 前文——跟历史 token 的 K 做相似度匹配,按相似度加权融合它们的 V,得到自己位置的一份 hidden state,用来预测再下一个 token。
一个容易绕晕的点:每个 token 都是先被预测出来,下一轮被喂回模型时才算自己的 Q/K/V。真实流程是:
- 把所有上下文输入模型 → 计算出最后一个位置的 hidden state
- 通过 hidden state 预测出新 token
- 此时新 token 只是一个刚被采样出来的 id——还没有自己的 Q/K/V
- 等下一轮计算,新 token 作为输入被喂回模型,才会算它的 Q/K/V:把 K/V 写入 cache,把 Q 拿去 attend 前面所有 K,得到下一个 hidden state,预测再下一个 token
配一个完整时间线——假设 prompt 是「我 喜欢 吃 苹果」,模型最终生成「, 尤其 甜 的」:
prefill:
输入:我 喜欢 吃 苹果
生成 cache:我/喜欢/吃/苹果 的 K/V
计算“苹果”位置的 Q,得到 hidden state,输出预测:,
⬇️
decode step 1:
输入:,
生成 cache:, 的 K/V
计算“,”位置的 Q,得到 hidden state,输出预测:尤其
⬇️
decode step 2:
输入:尤其
生成 cache:尤其 的 K/V
计算“尤其”位置的 Q,得到 hidden state,输出预测:甜
⬇️
decode step 3:
输入:甜
生成 cache:甜 的 K/V
计算“甜”位置的 Q,得到 hidden state,输出预测:的每一步同一个 pattern:当前输入 token → 算它的 Q/K/V → K/V 写进 cache → 用 Q 拉前文得到 hidden state → 预测下一个 token。
🔑 K 和 V 是 token 的两个会被长期保存的角色——K 是「让未来 token 检索我时用的指纹」,V 是「被检索到时取出的内容」。Q 是每步当场算、用过即弃。这就是为什么叫 KV cache——只缓存 K 和 V。
K 和 V 里装着什么?
K 和 V 都不是文字标签,而是几百维的数字向量——每个维度记录 token 在某种语义特征上的取值(「它是不是名词」「跟时间相关吗」「能不能被评价」⋯⋯)。下面用中文描述每份向量「装着什么」,只是帮你抓住直觉,真实运算是数字之间的点积。
模型生成「今天天气真 ___」的下一个字时,cache 里大致存着:
- 「天气」
- K(被检索的”指纹”)= 「我是名词 / 可以被评价 / 跟季节、室外相关 / 有时序属性」
- V(取出的”内容”)= 「冷热好坏可以形容我 / 跟心情和环境挂钩 / 有具体语义概念」
- 「今天」
- K = 「我是时间副词 / 定位『何时』」
- V = 「我指代当下这个具体时间」
- 「真」
- K = 「我是副词 / 表强调」
- V = 「我在强调评价的真实性」
轮到**最后一个已有 token「真」**做 attention。它算出自己的 Q(大致是「我是评价副词,前面哪个对象是被我评价的?」),然后两步:
- 「真」的 Q 跟每个 K 做点积:跟「天气」K 匹配最高,跟「今天」/「真」自己相对弱
- 按相似度加权融合所有 V:「天气」的 V 拉得最重
得到「真」位置的 hidden state → 模型用它预测下一个字(「好」/「热」/「冷」之类)。
🔑 整个过程里在工作的是**「真」的 Q**,不是新字「好」的 Q——「好」此时还没出生。这正是上面 ⚠️ Callout 流程的具体演示。
一条「笔记」有多重?
注意:不是简单的一组 K/V。模型有多个层(layers),每层都有自己的 attention 机制;每层里还有多个注意力头(heads)。所以一个 token 的 KV cache 实际是:
每 token KV 字节数 = 层数 × KV head 数 × 每个 head 的维度 × 2(K 和 V 各一份) × 精度字节数代入 Llama 3 70B 的典型数字:
- 80 层
- 8 个 KV head(GQA 后)
- 每个 head 128 维
- FP8 = 1 字节
🔑 每个 token 的笔记是 160 KB 这个量级。100K 上下文 = 16 GB 的 KV。这是个庞然大物。
这就是为什么上一篇咱们说「KV 主导成本」——它不是抽象的「摊不掉」,而是字面意义上的几十 GB 数据要每生成一个 token 都从显存搬运一遍。
二、降 KV 成本的四条路
公式回顾:
b = 层数 × KV head 数 × head 维度 × 2 × 精度字节数这五个变量里,每一个都被研究过怎么降。咱们看四条最主流的路。
路 1:减少 KV head 数(GQA)
最早大家用 MHA(Multi-Head Attention,多头注意力):有多少个 Q head 就有多少个 K head 和 V head,每个 head 一份独立的 K/V。
后来发现一个浪费:多个 Q head 其实可以共享一份 K/V。这叫 GQA(Grouped Query Attention,分组查询注意力)。
比如 Llama 3:32 个 Q head 但只有 8 个 KV head——每 4 个 Q head 共用一组 K/V。KV 大小直接降 4 倍,质量损失几乎没有。
🔑 GQA 已经是现在所有主流模型的标配——Llama、Mistral、Qwen、Claude 全都在用。
路 2:跨层共享 KV(Character.AI / Gemma 风格)
更激进的想法:不同层之间共享 KV。
比如 80 层里,每两层共享一组 KV → 等效层数变 40 → KV 大小再降 2 倍。
🔑 Character.AI 把这种思路推到极致——「global context」在所有层共享一份 KV(只在某些「local」层用独立 KV)。他们之所以做这种激进设计,是因为 chatbot 场景下用户经常有超长对话历史,必须把 KV 压到极致才能保住经济学。
路 3:MLA(Multi-head Latent Attention,DeepSeek 的招)
DeepSeek 的创新。不直接存 K 和 V,而是存一个压缩过的潜在向量,用的时候再解压。
类比:别存原图,存 JPEG;用的时候再解码。
🔑 MLA 把 KV 大小降到 GQA 的 1/4 左右,而且质量几乎没损失。这是 DeepSeek V3 能用极小 KV 跑超长上下文的关键。
路 4:稀疏 attention(只看一部分历史)
前面三条都在压「每个 token 的 KV 大小」。稀疏 attention 走的是另一条路:新 token 不去看所有历史 token,只看一个子集。
如果稀疏模式选得好,KV 搬运成本能从 O(L) 降到 O(√L)。
🔑 代价:模型可能漏掉远处的关键信息。所以质量上是有损的,只能在「大部分长程信息不重要」的场景用。
四条路对比
| 技术 | KV 降幅 | 质量损失 | 现实使用 |
|---|---|---|---|
| GQA | 4-8× | 几乎无 | 几乎所有主流模型 |
| 跨层共享 | 2-N× | 小到中等 | Character.AI、Gemma |
| MLA | 4× | 几乎无 | DeepSeek 系列 |
| 稀疏 attention | √L 改进 | 中等 | 少数实验性部署 |
三、从 API 定价反推架构——最漂亮的一段
现在你已经有了反推的全部前置知识。咱们就拿 Gemini 做案例。
已知线索
历史上 Gemini 1.5 Pro 在长上下文阈值(128K / 200K 这一档)之后用过阶梯定价(典型做法是单价翻倍)。
为什么定价要分档? Gemini 的运营方想保证:无论上下文多长,每段都不亏本。在两个区域(KV 主导 vs 算力主导)成本结构不同,所以定价也得分两档。
🔑 阶梯拐点的位置直接暴露了底层成本曲线的形状——它就是 KV 项追上算力项的瞬间。咱们能用这一点反推架构。
找拐点条件
在拐点处,KV 搬运时间正好等于算力时间(假设 Batch 已经足够大,参数项可忽略):
解 b
代入 L = 200K(拐点位置),N_active 估计 100B(对 Gemini 这个量级模型的合理猜测):
🔑 得到 ~2 KB / token——这就是 Gemini 这个模型每个 token 的 KV 字节数。
这个 2 KB 意味着什么?
用第二节「四条路」的知识反推它的架构:
b = 层数 × KV head 数 × head 维度 × 2 × 精度字节数
2000 ≈ 层数 × 8 × 128 × 2 × 1解出:等效层数 ≈ 1。
🔑 这意味着 Gemini 极有可能用了跨层共享 KV(类似 Character.AI 风格)——所有层共享一组 KV,所以「等效层数」被压到 1。
咱们刚才做了什么?
🔑 仅凭 Google 公开的 API 定价(200K 阶梯),咱们反推出了 Gemini 内部架构使用了跨层共享 KV。这就是这套工具的核心威力——任何 AI 公司的定价文档,都是关于他们内部架构的间接情报。
四、再来一次:从输入/输出价差反推 decode 瓶颈
另一个反推:很多 provider 的输出 token 比输入 token 贵约 5×。这告诉咱们什么?
区分 prefill 和 decode
- Prefill(处理输入):一次性吃进 N 个 token,并行计算所有它们的 KV
- Decode(生成输出):一次只生成 1 个 token,但每次都要把所有历史 KV 读一遍
关键差异:
| 阶段 | 计算量 | 内存读取量 |
|---|---|---|
| Prefill 处理 N tokens | N × N_active | 搬 1 次参数 |
| Decode 生成 N tokens | N × N_active | 搬 N 次参数 + 搬 N 次 KV |
计算量是一样的,但内存读取 decode 是 prefill 的 N 倍以上。
平均到每 token 的成本
- Prefill:成本被 N 个 token 一起摊销(一次搬运服务 N 个 token)
- Decode:每个 token 都要单独搬一次
🔑 Prefill 是 compute-bound,Decode 是 memory-bound。同一个硬件、同一个模型,只是 workload 形态不同,经济学完全不同。
5× 价差意味着什么?
如果一家 provider 的 input/output 价差是 5×,那意味着:decode 的实际成本约是 prefill 的 5×。
回到 roofline 分析:两阶段花的时间比例约 5:1。也就是:
🔑 Decode 阶段,内存时间是计算时间的约 5 倍——也就是 GPU 工人 80% 时间在闲着等数据。
这就是「decode 严重 memory-bound」的精确含义——不是泛泛说「内存慢」,而是具体到 5 倍这个倍数。
把每个定价数字当 X 光片读
有了这两段反推训练,你现在能从任何 provider 的定价表读出几件事:
- 有没有 long-context 阶梯定价?
- 有 → provider 用了某种长上下文优化(GQA / 跨层共享 / MLA),且拐点位置暴露了 b 的大小
- 无 → 要么 provider 不在乎长上下文成本(可能是离线 batch 服务),要么用了极致压缩让拐点不存在
- Input/Output 价差多大?
- 5-10× → 标准 decode-bound 情况
- 价差很小(< 2×)→ 可能这家走 prefill-heavy workload(比如批量文档分析)
- 价差很大(> 10×)→ 可能用了非常激进的 decode 优化(MLA、speculative decoding),或者硬件
C/BW比值偏高
- Cache hit 折扣多少?
- 5-10× 便宜 → 把高频前缀放在 HBM
- 几十× 便宜 → 放在 DDR / Flash(更慢但更便宜的存储层)
🔑 每个定价数字背后,都是 provider 内部架构选择的具体后果。
五、退一步:架构创新到底在改什么?
讲到这里你可能注意到一个事实:前面提到的所有创新最终都在「降成本」——GQA、MLA、跨层共享、稀疏 attention、MoE、量化⋯⋯全是。
那它们之间有什么本质区别?为什么有的叫「算得快」,有的叫「经济学优化」?
咱们退一步,给所有 AI 架构创新做一个统一分类。回到核心公式:
任何创新最终都在改这个公式里的某个量。但改不同的量,带来的成本下降方式根本不同——这就是分类的基础。
路径 1:改「工作量」——直接降算力时间
典型代表:更稀疏的 MoE、剪枝、量化、speculative decoding。
机制:让 N_active 变小,或让等效计算量变小。
几何效果:T_compute 那条线整体下移,甜蜜点位置也跟着移。
特点:
- 成本下降在所有 Batch、所有上下文长度下都成立
- 通常伴随质量损失(剪掉的、量化掉的、跳过的,都是模型本来该做的工作)
- 是「算得快」型——你确实减少了机器要做的工作
🔑 判别信号:有质量代价,且代价随激进程度增长。
路径 2:改「摊销结构」——让 Batch 经济学不崩
典型代表:GQA、MLA、跨层共享 KV。
机制:让 b(KV bytes / token)变小。
几何效果:不改变甜蜜点位置,但改变「长上下文下能否到达甜蜜点」。
特点:
- 短上下文下,这些创新几乎看不出效果(因为 KV 项本来就被算力项盖住了)
- 长上下文下效果显著放大
- 通常质量损失极小(只是改了 KV 的表示方式,不是改了模型在做什么)
🔑 判别信号:质量代价极小,但只在特定 workload(长上下文)下才显出价值。
路径 3:改「硬件常数」——让物理墙整体后退
典型代表:更大的 scale-up domain、更快的 HBM、FP8 / FP4、更多 GPU 互联。
机制:让 C 变大,或 BW 变大。
几何效果:整张图横纵双向缩放——甜蜜点移动、成本下界下降、延迟下界下降,全都改。
特点:
- 没有质量代价(纯硬件)
- 但需要重做硬件(贵、慢、需要供应链配合)
🔑 判别信号:完全没质量代价,但成本和时间巨大。
把过去几年的创新放进这个表
| 创新 | 改的是 | 路径 | 判别证据 |
|---|---|---|---|
| MoE(稀疏 expert) | N_active 变小 | 1:算得快 | 整体算力下降,有轻微质量代价 |
| GQA | b 变小 | 2:经济学优化 | 短上下文几乎无感,长上下文显效 |
| MLA | b 变小 | 2:经济学优化 | 同上 |
| 跨层共享 KV | b 变小 | 2:经济学优化 | 同上 |
| 稀疏 attention | 等效 L 变小(√L) | 1 + 2:双重收益 | 有中等质量代价 |
| FP8 / FP4 量化 | 等效 BW 变大 | 3:硬件常数 | 整张图缩放,可能微小质量代价 |
| Speculative decoding | 等效 N_active 变小 | 1:算得快 | 加速 decode,无质量代价但有失败率 |
| Flash Attention | 优化中间张量读写 | 接近 3:硬件常数 | 内存读取顺序改善,大幅省 HBM 带宽 |
| Blackwell 大 scale-up | BW 变大 | 3:硬件常数 | 整张图缩放,无质量代价 |
🔑 真实创新经常是几条路径的组合,这个三分法不是死框架,是个分析角度。
这个区分有什么用?
回到一开始的疑问:「既然都是降成本,为什么要区分?」
🔑 因为不同路径的创新,适用场景和组合方式完全不同。
场景 1:你想在长上下文 agent 场景下省钱
- ✅ 路径 2(MLA、GQA)→ 直接救你的命
- ⚠️ 路径 1(MoE)→ 有帮助,但不能替代路径 2(MoE 不解决 KV 项)
- ⚠️ 路径 3(换 Blackwell)→ 有效但贵
场景 2:你想压低简单 chatbot 的成本
- ⚠️ 路径 2 → 几乎没用(短上下文 KV 不主导)
- ✅ 路径 1(MoE、量化)→ 直接降本
- ✅ 路径 3 → 有效
场景 3:你在评估一家 provider 的可持续性
- 看它定价的形状(有无 long-context 拐点、input/output 价差)
- 形状告诉你它用了哪些路径的创新
- 路径决定了它的可持续性——纯靠路径 1(激进量化)的 provider,在质量竞争激烈时可能扛不住
一个更深的洞察
你可能会问:「为什么过去几年所有创新都好像围着成本转?」
🔑 AI 推理领域已经过了「求速度」的阶段,进入了「求经济学」的阶段。
GPT-2 时代,大家在乎「模型能不能跑」;GPT-3 时代,在乎「跑得快不快」;GPT-4 之后,所有玩家都在乎「每百万 token 多少钱才能盈利」。
所以你看到的「所有创新都在降成本」不是错觉——这是当前阶段的事实。区分类型不是为了否定这个事实,是为了在这个事实里看清楚「谁在用哪条路径」。
六、一段话总结
KV cache 一句话:每 token 几十到几百 KB 的笔记,跟着 batch 和上下文长度线性放大,没办法被 Batch 摊销。
降本四条路:GQA / 跨层共享 / MLA / 稀疏 attention。
反推核心招:定价形状(拐点位置、价差大小)是内部架构的间接情报。
架构创新三分法:
- 「算得快」型 = 改公式里的「工作量」变量
- 「经济学优化」型 = 改公式里的「摊销结构」,让某项不至于失控
- 「硬件常数」型 = 让物理墙整体后退
三者最终都降成本,但通过完全不同的几何路径。能区分这三类是真·工程师品味。
七、留几个可以自己想的问题
- 某 provider 报价:输入 $0.5 / 1M tokens、输出 $5.0 / 1M tokens(10× 差距)、无 long-context 阶梯定价。从这三个数字能推出关于这家 provider 内部 workload 和硬件配置的什么信息?它适合什么类型的应用?
- MLA 大幅压缩了
b(每 token KV 字节数)。回到第二篇的甜蜜点公式B = 300 × (N_total / N_active),MLA 会影响这个甜蜜点的位置吗?为什么? 进一步:如果 MLA 不影响甜蜜点,它的真正价值到底在哪里? - 两家 provider 用 70B 模型,整体定价相近,但其中一家的输入/输出价差是 3×,另一家是 8×。它们用的可能是同一个模型吗?如果是,它们的硬件配置(
C/BW比值)差异如何? - Flash Attention 严格说不属于上面三条路径里的任何一种(它优化的是中间张量读写顺序)。如果你要把它归类,会归到哪条路径?理由是什么?
下一篇预告:到目前为止,咱们都还在「一台 GPU 内部」看问题。下一篇咱们走出这一台机器,进入多 GPU 集群的世界——三种并行(expert / pipeline / tensor)分别在切什么、NVLink 比 InfiniBand 快 8 倍意味着什么、为什么 MoE 强烈偏好「一个机柜内」、为什么 Blackwell 把 scale-up 从 8 推到 72,以及最关键的——「为什么模型规模卡在 1T 这么多年才动起来」的答案。