Antkillerfarm Hacking V7.5

DL acceleration » NN Quantization(二)——TF32, FP8, W4A16, FP4, OCP Formats, Posit, 量化策略

2019-07-26 :: 5878 Words

Flexpoint(续)

参考:

https://www.intel.ai/flexpoint-numerical-innovation-underlying-intel-nervana-neural-network-processor/

Flexpoint: Numerical Innovation Underlying the Intel Nervana Neural Network Processor

https://zhuanlan.zhihu.com/p/33580205

Flexpoint——利用一种自适应的数据类型加速神经网络训练

https://mp.weixin.qq.com/s/z4OEPrAAtaNmBQoyvEd7Nw

从春秋到战国—论Nervana的倒掉

TF32

这是Nvidia推出的格式,相当于把FP32的指数和FP16的底数拼到了一起。有BF16珠玉在前,这个的设计只能说中规中矩了。

优点:底数精度虽然不如Dynamic Range重要,但对于运算结果还是有一定的影响的。这点在CNN中不太显著,但在RNN/Transformer中还是有所体现的。

缺点:毕竟不是16位,运算速度只有FP16/BF16的一半,但比FP32快一些。

BF16和TF32的先例一开,各种格式如火山爆发一般涌现。例如AMD的fp24,Pixar的pxr24,Enflame的ef32。

壁仞原创定义了TF32+,相较于TF32,在满足同样动态表示范围的前提下,增加了5位尾数。实际上就是pxr24。。。

参考:

https://zhuanlan.zhihu.com/p/143499632

NVIDIA A100 GPU中的TF32将AI训练与HPC速度提升20倍

https://www.cnblogs.com/zhouronghua/p/15170247.html

AI中各种浮点精度概念集合:fp16,fp32,bf16,tf32,fp24,pxr24,ef32

https://zhuanlan.zhihu.com/p/449857213

那些年,AI芯片里的浮点(FloatPoint)格式

x86 Extended Precision Format

Intel在早期的8087芯片上引入了一种80bit的浮点格式:1 Sign + 15 Exponent + 80 Significand

这个格式设计不知道是否启发了BF16,因为它采用了和IEEE 754中128bit相同的Exponent,正如BF16使用FP32的Exponent一样,都是高一个档次的Exponent搭配低档次的Significand。

FP8

FP8包括两种常见的变种:E4M3(4位指数和3位尾数)和E5M2(5位指数和2位尾数)。

类似的还有FP6:E2M3和E3M2。

NVIDIA在H100中,添加了FP8的支持,但是去掉了对INT1/INT4的支持。。。看起来后两者还是实用价值偏低了。


Blackwell使用了比Hopper更精细的scaling factor,用以降低量化误差。


参考:

https://zhuanlan.zhihu.com/p/521631165

Nvidia H100中的FP8

https://zhuanlan.zhihu.com/p/714933476

大模型量化技术原理:FP8

BF8 and Tesla CFloat

当我们将浮点的继续降低到8-bit的时候,BF8遇到的挑战越来越大。

在论文HFP8中,模型inference的时候,FP8(1-4-3)在mobilenet和transformer任务上明显的精度降低。

对于training来说,遇到的挑战进一步增大,weight/gradients/activation的范围相差更大,没有办法选择一个合适的格式来满足所有数值的要求。

HFP8就提出了一种Hybrid的方式:forward的时候用FP-1-4-3,backward的时候用FP-1-5-2。forward的时候,更关注精度,backward的时候更注重范围。这样的话,HFP8就能够在训练的过程中获得接近FP32的表现。

在工业界Tesla DoJo提出了一种可配置的CFloat,exponent和mantissa的位数可以动态的调整,只要满足总共的bit数就可以了。这样,由软件来选择合适的浮点类型CFormat,来最大化的利用硬件的性能。

《8-BIT NUMERICAL FORMATS FOR DEEP NEURAL NETWORKS》

CFormat这种动态调整exponent和mantissa位数的量化方式,又被称为Dynamic Fixed Point Quantization。

NV推出了一个叫做TransformerEngine的库,专门用于Hopper GPU上的FP8加速。

https://github.com/NVIDIA/TransformerEngine

W4A16

这种量化方式只对Weight使用4bit量化,而其他Tensor仍然使用16bit的浮点数。A是Activation的意思。

W4A16主要有GPTQ和AWQ等实现。

上图是QuaRot提出的一种W4A16在LLM领域的实践方法,其中还包含了KV Cache的4bit量化。

这是韩松团队的另一篇类似的论文:

《QServe: W4A8KV4 Quantization and System Co-design for Efficient LLM Serving》

参考:

https://zhuanlan.zhihu.com/p/681578090

大模型量化技术原理-AWQ、AutoAWQ

https://zhuanlan.zhihu.com/p/680212402

大模型量化技术原理-LLM.int8()、GPTQ

q4f16 & q3f16

q3f16(q3指使用Quantize 3 bit来量化,f16是指核心计算使用fp 16来计算)。

FP4

IEEE版本的FP4(E2M1)如上图所示,但由于过于粗糙,一般使用更多的是所谓的NormalFloat的NF4。

NF4只能表示[-1, 1]之间的浮点数。由于在神经网络中,预训练的权重通常具有零中心的正态分布。所以根据正态分布的累积分布函数来量化,比均匀量化,效果要好一些。

所有可能的NF4值为:

[-1.0, -0.6961928009986877, -0.5250730514526367, -0.39491748809814453,
  -0.28444138169288635, -0.18477343022823334, -0.09105003625154495, 0.0,
  0.07958029955625534, 0.16093020141124725, 0.24611230194568634, 0.33791524171829224,
  0.44070982933044434, 0.5626170039176941, 0.7229568362236023, 1.0]

注意:0左侧和右侧的量化是非对称的,因为[-1, 0]之间有8个值,而[0, 1]之间有9个值。

NF4是QLoRA引入的,而FP4目前只有NV的GPU支持。


b4int3的scale复用前3位的数据,再加上自己的1位。但不管怎么变,还是只有16个取值,只是动态范围比前面几种FP4,要大的多。

OCP Formats

Open Compute Project提出了一种有别于IEEE标准的浮点数格式。

f6E3M2FNU:

  • f6:6bit浮点数。
  • E3:3bit指数。
  • M2:2bit底数。
  • FN:没有infinity/NaN。
  • U:没有0/负数。

官方文档:

https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf

OCP Microscaling Formats (MX) Specification

Posit

上图是Posit格式的示意图,除了符号位、指数和底数之外,它还包括了regime bits。

regime bits不知道怎么翻译,这里不妨意译为超指数。它的公式是:

\[useed^k\]

其中,

\[useed=2^{2^{es}}\]

es表示指数位的宽度,这里是3,所以\(useed=2^{2^{3}}=2^8=256\)

k的表示没有采用补码,而是一种特殊的方法:

k=从左面开始数0或者1的个数。

需要注意的是,同样总位宽的Posit格式,其每个部分(符号位除外,固定占1位)的宽度是不定的。除了regime bits是必须有的(但宽度不定)之外,指数和底数都是可选项。

Posit的设计思路其实是很自然的:

  • 底数增加1位,Dynamic Range增加2倍。

  • 指数增加1位,Dynamic Range增加\(2^2\)倍。

  • 如果还想增加Dynamic Range,自然就需要引入超指数了。

https://www.sigarch.org/posit-a-potential-replacement-for-ieee-754

Posit: A Potential Replacement for IEEE 754

常见的软件实现:

https://github.com/cjdelisle/libposit

https://github.com/stillwater-sc/universal

量化策略

上面主要讲了量化格式,这里再讲一下量化相关的策略问题。

Saturate Quantization

上述各种量化方法都是在保证数值表示范围的情况下,尽可能提高fl或者scale。这种方法也叫做Non-saturation Quantization。

NVIDIA在如下文章中提出了一种新方法:

http://on-demand.gputechconf.com/gtc/2017/presentation/s7310-8-bit-inference-with-tensorrt.pdf

8-bit Inference with TensorRT

Saturate Quantization的做法是:将超出上限或下限的值,设置为上限值或下限值。

如何设置合理的Saturate threshold呢?

可以设置一组门限,然后计算每个门限的分布和原分布的相似度,即KL散度,选择最相似分布的门限即可。

参考:

https://blog.csdn.net/u013010889/article/details/90295078

int8量化和tvm实现

Trainning Quantization

除了上面这些无条件Quantization之外,训练中的Quantization也是一大类算法。

比如下面提到的PACT量化,不仅对weight进行量化,还通过不断训练,限制每一层tensor的数值范围。

参考:

https://mp.weixin.qq.com/s/7rMnzbvp1hjDLuw_oifbng

我们是这样改进PACT量化算法的

双重量化

过去的量化算法每一层额外附带两个参数,现在的量化算法一般采用了分组量化的方式。例如,取128个参数作为一组,每一组都会额外增加两个参数。

量化参数(最小值、缩放比例)本身还能再进行量化,称为双重量化。QLoRA采用了这种方式。

https://zhuanlan.zhihu.com/p/665601576

用bitsandbytes、4比特量化和QLoRA打造亲民的LLM

per-channel & per-group

一般情况下,一个Tensor共享同一个Scale。有的时候为了提升精度,也可以一个channel共享同一个Scale,这也被称为per-channel quantization。如果不共享Scale,则退化为普通的浮点数表示。

经过观察,在正态分布下,绝对值很大的参数的比例会很少,所以一起归一会使得大多数参数变得很小,从而使得量化过程中的一些数字范围对应的int8没有被充分利用,导致更多的信息丢失。

把参数划分为了小Block,在进行量化的时候,按照block内绝对值最大的数对这个block进行归一化,使得所有参数都落在 [-1, 1] 这个范围,这就是Block-wise Quantization。Block的值如果在内存中连续,则这种quantization也叫做per-group quantization。

Activation Quantization

早期的Quantization一般是W和A采用相同量化格式,然而由于LLM的特殊性(层数深,迭代次数多),Activation的量化一直是一个大问题。W4A16就是目前传统方法所能达到的最好水平了。

seq2seq对于数值精度要求高,这在早期的LSTM时代,就已经很突出了。当时CNN普遍已经8bit量化,但在LSTM中,起码要16bit才能达到可接受的效果。

研究表明,有些Token的异常值会显著高于其他Token,有了之前Weight上的per-channel & per-group的策略,Activation上自然也可以使用per-token的策略。

然而Activation Quantization的这些高阶策略,和输入的内容密切相关,因此并不能进行离线量化,同时量化参数由于是运行时才确定的,相当于是动态图,这给后端的AI硬件的调度带来了一定的挑战。

相关算法:SmoothQuant、ZeroQuant、SpQR。

Fork me on GitHub