机制分析
生成一步,包含两类不同的工作
先处理四个已知位置 A—D。每个位置产生相应的键和值;最后位置 D 的查询 Q 与可见的 K 匹配,经过缩放点积、softmax 和对 V 的加权,得到用于后续预测的表示。本页把生成结果标为 E,强调处理位置与预测下一个位置之间的顺序。
当 E 成为下一次输入位置,因果约束使先前位置不需要因为它而重新计算自己的 K/V。保留缓存时,A—D 的四组可以直接复用,只新增 E 的一组;不保留缓存的对照则重新计算 A—E 的五组。
| 本例计数 | 保留缓存 | 不保留缓存 |
|---|---|---|
| 先处理 A—D | 计算4组 K/V | 计算4组 K/V |
| 再处理 E | 新增1组 | 重新计算5组 |
| 累计 K/V 投影组数 | 5组 | 9组 |
| E 的查询读取 | 可见的5组 K/V | 可见的5组 K/V |
5与9只统计这个示意层的 K/V 计算组数。模型还包含其他投影、注意力操作和网络层,真实服务还有调度与数据传输,因此不能据此声称延迟下降到5/9。缓存滚筒表达保存与追加,实际数据位于推理引擎管理的内存中。
缓存也不是模型的长期记忆
K/V 是一次推理过程中的中间状态,模型参数没有因此变化。它与把用户偏好保存到外部数据库不是同一种持久化,也不能因为页面保留了几组向量,就认为下一个独立请求会自动复用这些状态。
工程取舍
少做重算之后,瓶颈可能转移
计算节省与内存占用怎样权衡?
保存更长的可见历史,需要更多 K/V 存储;同时服务多个序列时,这些状态会竞争内存容量。即使每步只新增少量位置,读取历史的工作也仍存在。是否值得保留、能同时接纳多少请求,需要结合上下文长度、模型结构与设备资源测量,不能只看缓存命中数量。
减少历史,是缓存优化还是上下文策略?
直接丢弃缓存中的旧位置,会改变可用的注意力范围,不能当成对语义完全透明的内存回收。滑动窗口或局部注意力可以限制读取范围,但那是相应模型结构和运行策略的条件;本例展示的是全历史因果注意力,不能外推为所有架构的统一行为。
同一次生成的复用,与跨请求复用有什么区别?
本页只讨论一次自回归生成内的缓存。跨请求前缀复用还要判断前缀及运行条件是否兼容,并由服务实现管理生命周期与隔离。应用日志里出现相同文字,不足以证明底层复用了同一份缓存;相关收益需要在对应服务与配置下单独验证。
落到实现
把性能问题拆成可测量的问题
比较方案时,应固定模型、输入和输出条件,分别观察首次处理输入的时间、逐步生成的耗时以及缓存内存占用。真实请求的吞吐、等待和尾延迟还受批处理与并发影响,单条示意计算不能代替服务测量。
页面实际计算二维投影后的注意力运算,A—E 代表位置,不代表一个汉字就是一个 token;E/F 用于说明生成顺序,没有运行完整语言模型。各层通常各自维护相应的缓存,图中只展示一层的一头。
缓存让旧结果可以复用,却没有免除使用这些结果的工作。理解这一区别,才能解释为什么少了重算,长上下文仍然可能很贵。