前言
本文主要是个人对 商汤技术分享 的摘抄,并加上了一些个人理解,以及详解了一些视频中被快速省略的部分。
视频说的比较详细,如果想了解的更加细致建议移步视频。
本人仅供个人技术学习记录与快速分享使用。
LLM 的特性与不同点
如果你使用过chatgpt或者其他LLM模型,你就会知道,在你输入一段话的时候,他会有非常短的延迟,然后会逐字逐句的输出。在这个实际场景下,LLM模型有一个区别于传统语言模型的极大区别。就是他只有一次encoding,但会有N次的decoding。在提交问题到gpt回答的这个时间,其实是encoding的时间,而逐字生成答案的时间,都是decoding的时间。
这个设计给模型放松了很大的latency限制,也很符合人类本身对对话的实际情况。
在LLM中,encoding和decoding的基本上一致的,他们的结构见下图。
其中只有结构中的self attention,在具体实现上,encoding和decoding不相同。
在LLM中 encoding 的结构发生了变化,在论文中都称之为 prefill。这个变化源自于一个非常特殊的结构,叫做KV Cache。
KV Cache是在attention中途,将KV 进行存储或者使用。在prefill中是对其进行存储,而在decoding阶段,则是对它进行取出再使用以达到联系全文的目的。
以上是decoding的情况,decoding的输入只会是前文的最后一个字,也就是它的实际QKV三个矩阵都只有一维,但是在attention中,会取出KV Cache中的KV拼接到实际的KV中。
加速方案
1. 流水线编排与高性能采样
流水线编排比较简单,因为tokenize和detokenize都是cpu操作的,那么在上次decoding在detokenize时,这时候就可以进行gpu compute了。
并且这里使用了自写的更高性能的 sample 算子,传送门
这些内容我将会另开一篇文章细讲。
2. dynamic batch support
dynamic batch是服务端常用的优化技巧,这里的支持是针对多用户多请求支持的。见下图
多个用户的多个请求,首先decoding是完全可以拼batch的(因为结构完全一样嘛,输入长度不一样的话只需要padding一下就行了),而实际上 prefill 与 也可以进行merge。
上文有提到,prefill与decoding只有 attention 是不一样的,其他结构完全一致。那么只有这个地方需要进行gather然后分开处理,其他算子都是可以批处理的。
3. decoding attention
decoding attention 因为其独有的特性(q的长度为1,但k,v的长度可以不为1),目前的attention kernel优化都不太适用。因此重新写一个kernel专门优化很有必要。
4. cache 显存管理
kv缓存的存在使得显存管理很有必要
- pytorch: 每一个用户过来使用的时候,直接申请使用模型最大的显存。
- page attention: 将显存以分页的模式进行管理,每一个显存页都会有固定大小的显存。用户过来使用的时候,并不会分配显存,而是在用户输入之后,只分配一个显存,当处理的时候发现显存不够的使用继续分配,直到显存足够为止。
- VM Allocator: 增加一个独特的预测机制,用户来了之后并不分配显存,只有在提交输入的时候才会分配显存。显存分配的大小是有新增的预测机制决定的,这个预测机制会根据输入大概的预测显存使用。这个预测并不能100%预测正确,目标只需要90%即可。如果预测正确,那没问题,如果预测失败,则需要重新预测或者需要用户重新提交任务。
优缺点分析:
显存管理方式 | 优点 | 缺点 |
---|---|---|
pytorch | 最可靠,最简单 | 显存浪费最多 |
page attention | 理论用户无上限,可靠,显存浪费很小 | 1.随着batch size的增大或者用户的提升,性能会有回退。2.将分页显存链式连接是有代价的,只能靠软件支持 |
VM Allocator | 可靠,显存浪费小,代码实现相对来说比较简单 | 1.存在额外的预测,并且对这个预测精度有很强的依赖。2. 会牺牲少量的用户体验 |
这里对page attention多说明一下:
因为显存是动态申请的,而我们无法动态判断系统是否拥有接收处理下一个请求的能力,这可能会导致很多用户都同时缺显存,导致很多用户同时不能完整预测。这个时候的显存分配就会非常棘手。
这个问题我们进入到底层考虑,如果遇到多个预测同时缺显存,导致面临死锁的局面,那么此时我们只能释放部分显存,以小换大来满足大部分请求的显存。这个时候被释放的请求,他的latency就会大幅上涨。这时候就导致了随着batch size增大而吞吐反而可能降低的结果。
志佬有提出,ChatGLM2和Llama-2慎重开page attention。因为这些模型已经使用了GAQ或MQA技术压缩KV缓存,batch size会很大,这时候page attention反而可能会拖累性能。
4. KV Cache 量化
因为存在kv cache的存在,对于支持越长的文本长度的模型,kv cache的显存占用越高。
因此kv cache的量化也是有很必要的。
分组量化的精度很高并且很简单,比较推荐
5. 矩阵乘法量化
用的是 dynamic channel-token int8 量化方法
精度也极高
通用量化流程
- 权重量化,使权重精度降低
- 插入量化算子与反量化算子,使输入精度降低,但是输出精度恢复
- 将量化算子与量化前的算子融合,反量化算子与量化后的算子融合
6. 量化精度讨论
关于这块内容,我打算另开一篇文章详细讨论一下
后文中解释了 int8 量化 和 int4 weight only量化的区别
int4 weight only量化是仅用于压缩权重用的,中间需要使用解量化过程,这个过程同样非常耗时,因为会解量化,所以不会加速计算
但是int8量化不需要解量化,这样也可以加速计算。
(后面需要调研一下这个
weight only 量化一般直接使用打表即可。
动态批处理