本文基于百度昆仑第二代硬件
前提
当前百度硬件tensor core支持的输入:int16, int8, bfp16
当前百度硬件tensor core支持的计算方式: int16, fp16,int8
常见的LLM大模型激活类型:fp16,bfp16
cache类型:fp16, bfp16,int8,int4
量化
这里列举一下当前常用的方式以及量化方法
激活 | cache | 计算方式 | |
---|---|---|---|
类型1 | fp16 | fp16 | 量化到int16,然后做int16*int16的计算,输出反量化到fp16 |
类型2 | fp16 | int8 | 输入量化到int16(实为int8 * 2),与cache 做两次计算,最后输出反量化到fp16 |
这里主要讨论一下cache 的量化方式:
-
当用类型1时,cache为fp16,此时需要量化到int16,此时需要一个per cache的 max/scale值来进行量化。(计算量 cache len)
这里可以做一个优化,保存一个per cache 的max值,每次插入数据时更新per cache max值。(计算量 1)
然而,当遇到动态插入情况时,如下图图2,Q4结束了,Q6插入,但这时Q4的max值还在cache max值中,这里有一个解决版本,保存per batch 的max,当某个batch 结束了,置空该值,然后在送入attention算子之前提前更新per cache max值 (计算量 batch)
-
当用类型2时,输入时int8,主要涉及到的是反量化,用到了静态量化方式。
常见的静态量化为:per channel,百度采用per head量化,(seqlen, head_num, head_dim)在head_num维度做量化。
题外
当然这些量化方式对硬件来说不需要关注,硬件只需要知道这一批数据max值是多少,量化还是反量化,输入输出类型是啥。怎么用就属于算子层实现,
例如:per head量化方式
for (int i = 0; i < seqlen; i++) {for (int j = 0; j < head_num; j++) {float scale = head_num_sacle[j];for (int k = 0; k < head_dim; k++) {dequant_or_quant(x, y, scale);}}
}