大模型基础概念:LLM技术名词
简要介绍
1 Model
1.1 Rope
一种Transformer的position注入方案。Transformer本身是不感知输入序列token顺序的,原版transformer需要positional embedding引入位置信息,这种方案要求infer时序列的长度不能大于训练时,而LLM需要进行长度外推。
Rope认为好的postional机制应该满足:1.attention值和相对位置有正相关关系;2.对attention值的影响和相对位置(两个token的位置之差有关),但对Q和K的modify只和绝对位置有关(这样modify才是可复用的)。
Rope的解决方案是:根据绝对位置n,将Q和K旋转n倍theta角
1.2 MQA/GQA/MLA
MHA计算太复杂了,需要一些简化;KV-cache存储空间太大,长序列显存不够用了。因为Q是用过即抛的,所以压缩都集中在KV上。
MQA:K和V只用一个head,大幅降低了kv-cache存储。
GQA:MQA走得太远了,折中一下(KV多于1个,Q-head数量的几分之一)。
MLA:把经典的low-rank省空间/算力加入进来(类似lora、matrix-factorization、LDA、Glove等等),hidden-state先映射到Q和KV的两个相对低维的latent空间,再通过矩阵升维升上来。这样kv-cache只保留一份kv共享latent向量就可以了;并且K的升维矩阵可以被Q吸收,直接在latent空间做attention。这里为了兼容Rope,在实现上保留了一部分MHA的部分去apply-rope。
1.3 MoE
保持激活参数不变、扩大总参数量的一种方法;同时影响了训练过程中的显存、算力消耗关系。
在Transformer层中,Attention后面有一个 MLP(Activation(MLP(X)))操作。这里的MLP是multi-layer-perceptron,就是乘一个矩阵。LLM-MoE把这里修改为,有N个可选MLP和少量必选MLP,通过router根据当前hidden从N中选择少数几个可选MLP与必选MLP分别乘以hidden,再把运算结果求和作为输出。
通过控制实际选择MLP和必选MLP的数量,可以有效控制激活参数量;增加可选MLP可以扩大LLM的总参数量。
2 Inference
2.1 Dynamic Batching
LLM处理请求时会将一个时间窗口内的多个请求组和起来统一运算(batching),这样可以利用GPU上较多的计算单元(SIMD),提高吞吐量。优于LLM给batch中每个request生成多少个新token是无法提前知道的,因此一个batch部分完成时存在算力浪费。
2.2 Dynamic Batching
将组batch的控制级别从request级别下降到token(也称为iteration)级别,使新到的request马上就可以得到处理,解码完成的request马上就可以构造response发给client,降低了latency;同时解决了上述batch部分完成时的算力浪费问题,提高了算力利用率和吞吐量。为了在iteration级别对batch内容进行调整,request之间差异较大的attention计算不进行batch计算,实际对效率影响很小。
2.3 Paged Attention
KV-cache因为要参与矩阵运算,因此一般用一个Tensor存储,Tensor这个数据类型决定了其在显存中是连续的;此外,KV-cache是通过估计将要生成的长度预分配的。
这导致了三重浪费:1.多次申请整块内存导致碎片化;2.预估时多估计了;3.KV预分配显存的尾部,即使最后用到了,在整个解码过程的大部分时间中这部分显存是浪费了的。
PagedAttention打破了KV-cache在显存空间必须连续的前提,利用操作系统中经典的内存page方案,在server启动时直接申请一整块显存自己管理,按需按block申请,解决了上述三重浪费,大幅提高了吞吐量。此外,在n-sampling/beam-search等场景下,存在基于request-id的prefix-kv-cache共享。
2.4 Radix-Tree prefix caching
sglang提出。LLM请求通常有一个很长的共享prompt,因此跨request也有很高的kv-cache复用空间。sglang用一棵内存上的radix-tree基于请求的文本内容管理kv-cache,实现了跨request复用。
radix-tree是一种边是字符串的trie树,prefix查询效率非常高。
显存满时,通过LRU淘汰已有内容的kv-cache块。
2.5 PD分离
LLM推理时,prefix是固定的prompt拼接用户的输入,这部分一次性拿到了大量token,运算特点类似于bert/encoder/LLM-train,称为prefix-filling;在实际生成回复时,整个LLM走一遍只能解码出一个token,运算特点类似于decoder,称为decoding阶段。因为这两个阶段的负载特点存在显著差异(尤其是prefix长度非常大时,比如一些宣传超长context的功能),所以针对这两种负载的特性分别开发inference-engine并部署在不同的机器上可以提高算力利用率。