1
LLM推理:使用transformer解码器生成token
2
输出 logits 的最后一层通常被称为LM头
文本生成的两个阶段:
- 预填充阶段:将prompt的所有tokens表示作为输入,生成第一个输出token。
- 解码阶段:将生成的token加到输入序列后面,整体作为输入生成第二个输出token。如此重复,直到生成结束token(如EOS)或达到配置的最大序列长度。
3
单注意力头
我们为序列中的每个token生成一个相同维度的表示,这个表示包含了其他token的信息,叫做上下文表示。
由于掩码的存在,对于给定的token,其输出表示仅由前序tokens的表示生成。由于前序tokens在迭代中是相同的,该給定token的输出表示在所有后续迭代中也将是相同的,这意味着冗余计算。
图:解码阶段注意力层中的冗余计算(浅红色和浅紫色)
KV缓存
举个例子,在使用 “What color is the sky? The sky is” 作为输入的迭代中,我们之前步骤中唯一还没有计算的表示是输入序列中的最后一个词 “is”。 更具体地,需要什么材料来计算?
- A query vector for “is“.
- Key vectors for “What”, “color”, “is”, “the”, “sky”, “?”, “The” “sky” and “is” to compute attention scores.
- Value vectors for “What”, “color”, “is”, “the”, “sky”, “?”, “The” “sky” and “is” to compute the output.
除了 “is” 以外的健和值向量已经在之前的迭代中计算过,可以缓存并重用它们,这个优化就叫 KV caching。这时如下计算 “is” 的输出表示:
- Computing a query, a key and a value for “is ”.
- Fetching key and value vectors for “What”, “color”, “ is”, “ the”, “sky”, “?”, “The” and “sky” from the cache and concatenating them with the key and value we just computed for “is”.
- Computing the attention scores using the “is” query and all the keys.
- Computing the output vector for “is” using the attention scores and all the values.
观察输入,实际上不再需要先前的tokens,只需要它们的Key和Value向量。当使用KV缓存时,模型的实际输入是 最后生成的token 和 KV缓存。
图:启用KV缓存的生成步骤
4
尝试减少KV缓存的内存占用
减少注意力头的数量?
GQA:把查询头分为g组,一组内的查询头共享一个KV头
量化
只有那些量化权重和“激活”(即任何不是权重的项)的算法,才能生成量化后的KV缓存。
注意,在同时处理权重和激活的量化算法中,其中一个目的是以较低的精度执行计算密集型的矩阵乘法。这在计算受限的情况下(如训练期间)会带来性能提升,但推理的自回归阶段实际上是内存带宽受限的,因此能够更快地计算并不会带来太多价值。由于推理是内存带宽受限的,我们实际上只对减少内存占用感兴趣,因为这意味着更少的数据传输。
将负载卸载到更丰富但速度较慢的存储(CPU内存、磁盘)
由于它涉及使用速度较慢的存储,卸载会以较大的延迟为代价,因此显然不应优先考虑对延迟敏感的使用场景。卸载系统通常用于优化吞吐量的使用场景,如离线批处理。
用多个GPU,模型分片
- 流水线并行:模型和KV缓存在层维度上分片
- 张量并行:KV缓存在(多头注意力中的)头维度上分片
5
训练和推理的预填充阶段通常计算受限,推理的解码阶段通常内存带宽受限。