资料摘要:personal chatgpt 20 — llama2 源码分析 cache KV(keys、values cache)加速推理
本讲由真实视频转录与作者 notebook/代码交叉整理。原始层:🔒 带时间戳视频转录;课程总览:personal chatgpt — LLMs 实践系列。
KV Cache 用于 decoder 的自回归生成:每个新 token 只重新计算自己的 Q/K/V,把新 K/V 追加到缓存,并复用所有历史 K/V。这样避免每一步都重新对整个前缀做 K/V 投影。课程结合 Llama 2 Attention 源码追踪缓存张量和 start_pos,再用 GPT-2 对比 use_cache=True/False 的生成耗时。
1. 自回归解码为什么重复计算
Section titled “1. 自回归解码为什么重复计算”生成第一个 token 时要处理完整 prompt;之后每一步把上一步输出追加到输入。当前 token 的 query 必须与全部历史 keys 做注意力,并对历史 values 加权。如果每步把完整前缀再次输入而不缓存,历史 token 的 K/V 投影会被反复计算。
KV Cache 只用于生成/decoder 阶段。课程举例:GPT 这类 decoder-only 模型支持;T5 在 decoder 部分使用;BERT 这类非生成 encoder-only 模型没有这种逐 token 解码缓存。
2. Q 与 K/V 的形状角色
Section titled “2. Q 与 K/V 的形状角色”增量解码时,当前 Q 可以只对应最新 token,而 K/V 必须覆盖当前 token 与全部历史 token。因此课程把当前 Q 描述为 vector,把累积 K/V 描述为沿 sequence 维增长的 matrices。
3. Llama 2 源码中的缓存
Section titled “3. Llama 2 源码中的缓存”Attention.__init__ 按 [max_batch_size, max_seq_len, n_kv_heads, head_dim] 预分配 cache_k/cache_v。每次 forward 用 start_pos:start_pos+seqlen 写入新 K/V,再读取 :start_pos+seqlen 的全部历史缓存。
首轮 prompt 可能一次写入多个 token;后续循环通常 token-by-token。课程示例 prompt 有 8 个 token,最大新生成 64 个 token,因此生成循环上界为 72。
4. GPT-2 基准
Section titled “4. GPT-2 基准”Notebook 对 use_cache=True/False 各执行 10 次、每次最多生成 1,000 个新 token。保存输出为:
- with KV caching:
6.636 ± 0.023 s - without KV caching:
8.283 ± 0.049 s
这是特定 GPU、GPT-2、prompt 和生成长度下的结果,只证明该环境中的差异,不能直接外推到其他模型或 batch。
公式 / 代码
Section titled “公式 / 代码”
self.cache_k[:bsz, start_pos:start_pos + seqlen] = xkself.cache_v[:bsz, start_pos:start_pos + seqlen] = xv
keys = self.cache_k[:bsz, :start_pos + seqlen]values = self.cache_v[:bsz, :start_pos + seqlen]for use_cache in (True, False): model.generate( **tokenizer("What is KV caching?", return_tensors="pt").to(device), use_cache=use_cache, max_new_tokens=1000, )更准确的复杂度边界是:KV Cache 消除了历史 token 的重复 K/V 投影,但生成第 个 token 时,新 Q 与 个历史 K 的点积仍是 ;它不是让 attention 对上下文长度变成常数时间。
| 时间 | 内容 |
|---|---|
| 00:00 | 引入 KV Cache 与推理加速 |
| 01:00 | K/V 指 scaled dot-product attention 中的 Key/Value |
| 02:00 | 只在 decoder/生成过程使用;BERT 不适用 |
| 03:32 | SOS、EOS 与逐 token 自回归 |
| 05:00 | 不缓存时长前缀带来的重复计算 |
| 06:08 | 新 token 计算 Q/K/V,仍需历史 K/V |
| 07:59 | 图示无缓存与有缓存的差异 |
| 09:00 | 跟踪 Llama 2 生成循环与首轮 prompt |
| 12:00 | cache_k/cache_v 沿 sequence 维写入与读取 |
| 13:47 | GPT-2 基准:约 6.6 秒对 8.3 秒 |
- 运行标签:conditional / source-bundle not-as-written。 需要 CUDA、Transformers、PyTorch、SentencePiece、网络下载 GPT-2,并执行 20 次长生成。
source_bundle/code/lesson_20缺少 6 张相对图片和./tokenizer.model;主仓库imgs/与tutorials/tokenizer.model存在。- Llama
Attention代码位于 Markdown 代码块,依赖ModelArgs、并行线性层等完整仓库上下文,不能单独复制即运行。 - 基准硬编码
device="cuda",每次max_new_tokens=1000,运行成本较高。 - 本次没有重跑基准;数值来自 Notebook 保存输出,转录约 13:47–13:54 也口述“6 秒/8 秒”。
与前后讲关系
Section titled “与前后讲关系”- 承接第 18、19 讲: Q/K 先应用 RoPE,随后 K 与 V 写入缓存;缓存中保存的是已投影、已处理位置的状态。
- 总结第 15–20 讲源码线: 从推理入口、block 组件、RoPE 到增量解码缓存,形成一条完整的 Llama 2 推理阅读路径。
- 后续延伸边界: 本讲没有讲 PagedAttention、GQA/MQA 或 KV 量化,不应按标题自行补入。
- KV Cache 避免了哪些重复计算,又没有消除哪些计算?
- 为什么增量解码时当前 Q 可以只对应新 token,而 K/V 要覆盖整个前缀?
start_pos在写入和读取缓存时分别起什么作用?- 为什么 BERT 不使用本讲所说的生成式 KV Cache?
- 课程的 6.636 秒与 8.283 秒基准为什么不能直接推广到 Llama 2 70B?
- 视频:llama2 源码分析 cache KV(keys、values cache)加速推理
- 转录:🔒 personal chatgpt 20 视频转录
- 课件 / 代码:🔒 llama2_src_cache_kv.ipynb
- 滴答清单抓取时学习状态:已完成。
- 视频正文采用自动语音识别;关键术语已用标题、notebook 与源码校正,无法确认的口语细节不扩写。
- notebook 未执行的 CUDA、权重下载、训练或外部 API 单元,不表述为已复现实验。