问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

从速度和精度角度的 FP8 vs INT8 的全面解析

创作时间:
作者:
@小白创作中心

从速度和精度角度的 FP8 vs INT8 的全面解析

引用
网易
1.
https://m.163.com/dy/article/JGVQJ4Q90552BFKV.html?spss=dy_author

本文将深入探讨FP8与INT8在TensorRT-LLM中的量化对比,从速度和精度两个维度进行详细分析。文章将介绍NVIDIA Hopper架构上的FP8数据类型、量化工具TensorRT Model Optimizer的使用方法,以及FP8在不同场景下的具体应用和优化。

速度和精度

在讲解精度之前,先介绍NVIDIA Hopper架构上的数据类型FP8,它有两种数据类型:E5M2和E4M3,在TensorRT-LLM中目前支持E4M3。对Tensor Core硬件来说,相比于FP32/FP16作为输入,FP8在数据传输上具有优势。另外,GEMM运算可直接使用8比特,相对于16比特的TF32或者FP16更快,且支持更高累加精度以保证精度。

在Perf内容之前,需重申在做PTQ量化时需对哪些OP进行量化。以经典的Transform结构为例,量化主要围绕红色、蓝色和绿色框进行,涉及4种GEMM运算和Multi-Head Attention的量化。

PTQ量化需计算Scaling Factor,Multi-Head Attention中的GEMM运算在Scaling Facotr为1就可以保持不错的精度(目前,TensorRT-LLM中为了提高精度,在该部分做了Scaling Factor不为1的实现,本文内容是以FMHA的Scaling为1的情况下的分析)。而蓝色和红色GEMM运算需进行Scaling计算。除此之外,我们要保存kvcache,也可对kvcache进行8bit量化,但需进行Scaling计算。

计算Scaling Factor的方法是使用Quantize脚本,添加如上图所示两个参数(--qformat fp8,--kv_cache_dtypefp8)即可进行FP8 Scaling计算。对于FMHA Attention无需Scaling,生成Engine使用“--use_fp8_context_fmha enable”即可快速生成FP8、kvcache和GEMM运算功能。

第三步为评估,使用MMLU进行估计。

针对第三步,做精度评估时,如图所示,第一个小红框对MMLU 78个子数据集做了评估。因为篇幅较大,省略了中间的数据集,只展示其中的一部分。第一行代表了所做的量化方案。第一列是baseline,GEMM运算采用的是FP16,在整个表中,我们对比了Attention以外的4种GEMM运算和对应的kvcache开启FP8情况下的精度。

首先是FP8、INT8 weightonly + FP16 kvcache及最后一列对应的绿色框。可以看到,除了纯FP8方案能够保持精度比较好的量化方式,其他的比如INT8_sq,或者是INT8 weightonly + INT8 kvcache并不能保持很好的精度。

再看蓝色框部分,对比纯FP16和纯FP8方案的精度情况,以及最后一行红色框展示了平均的精度比较。

我们再看看加速比,第一列对比了FP8和FP16,性能提升1.5~1.7倍。另外两种方式的加速比都比较不错,但是仍然没有FP8高。采用INT8 sq或者INT8 sq + INT8 kvcache对于精度的保持可能并不会太好。因此,我们优先推荐纯FP8的方案。

这里还测试了GEMM + kvcache+FMHA方案。当对FMHA进行FP8 GEMM运算enable时,对比纯FP8与FP16 FMHA和FP8 FMHA这两种方式的精度,采用纯FP8方案,当开启FMHA时,它的精度保持也是比较高的。

GEMM + kvcache + FMHA对应的性能:因为开启FMHA的FP8仅是针对首token的优化,首token的计算一般情况是一个computer bound问题,结果如上图所示。我们在某款GPU上测试了Llama2 7B模型,input sequence越大,开启FMHA的FP8,带来的加速比越来越明显。

再来看下做量化的耗时情况,我们在CNNDaily数据集上做了测试。在这个数据集中,我们首先推荐用512的数据量,就可以很好的完成FP8保持精度的calibration,其概耗时是40毫秒。这是在另一款GPU上做的测试,如果显存比较大,我们可以让Batch size变大一点,这时calibration的时间可以变成秒级。

量化工具AMMO/Modelopt

接下来介绍下量化工具AMMO,它的最新的名字是Modelopt。FP8 PTQ量化的方式,可以总结为三个步骤:

  1. Calibrate pytorch model
  2. 生成model_config
  3. 生成engine

其中:

第一步最重要的API是Quantized API,通过Quantized API可以生成Scaling的计算过程。关于这个过程,我们可以传入一个模型,设置量化的config,比如设置成FP8。最后,准备好需要的calibrate数据。

第二步主要是帮助我们生成一个Json文件和一组weight文件。Json文件主要存储模型结构或者元数据。在weight文件中,group的大小主要受Tensor Parallelism和Pipeline Parallelism影响,weight则主要用来存储对应的参数。这步最重要的是API,直接调用一个API,就可以转成model config,方便TensorRT生成engine时使用。

第三步也是通过一个API就可以完成,也就是加载上一步的model config,直接生成engine结果。在这过程中,有一些隐藏的参数,比如训练的模型TP/PP比较大或者并行比较,在推理时,可以通过API让TP/PP变小。图中是我们用Modelopt工具做PTQ量化时,一些简单的API。

如何Debug?

在使用过程中,如果遇到问题,该如何debug?具体的debug过程如下:

  1. 找到想要输出的tensor做注册,这里的注册通过一个API就可以完成。
  2. build engine。
  3. 直接打开debug model进行打印即可。

如上图所示,展示了一个简单的debug过程。

另外,debug可能会遇到一些经验性问题:

  1. 在debug过程中,可能发现GEMM的输出不对。这时,我们可以检查weights的通道是否保持一致。因为Huggingface下载的不同模型,通道保持可能不太一样。
  2. Attention输出不对时,可以查看attention使用的plugin的参数,设置的是否正确。

Deep Dive

接下来,对FP8的workloads进行deep dive,看模型什么地方用了FP8,以及采用FP8之后的具体收益和为什么要这么用。最后介绍用FP8 build出的engine中Scaling factor和tensor core是怎么调用的。让大家了解FP8的底层原理,进而放心的去使用。

接下来介绍下从FP16模型build FP8 Tensor-LLM engine的过程。图中黄色部分代表通过Modelopt toolkit做FP8的权值转换,存出Model_config,再通过TensorRT-LLM中的From_json_file和Build_and_save组件,将Model_config转成TensorRT-engine。

在这个过程中,大模型通常会有6个部分用到FP8。其中模块1,4,5,6为矩阵乘,2是FMHA,主要是context phase中的batch GEMM会用到FP8。3是MMHA中的kvcache会用FP8来存储,以节省显存。

上图展示了从FP16矩阵乘变成FP8矩阵乘的过程:绿色代表FP16精度,黄色代表FP8精度,蓝色是FP32精度,灰色代表融合的过程。

我们刚开始拿到的是FP16的矩阵乘,针对这个矩阵乘的Input和Weight插入QDQ节点。对于Output,如果使用FP8的kvcache,也需要在QKV GEMM后面插入QDQ节点。如果不做FP8的kvcache,或者矩阵乘是QKV之外的矩阵乘,由于GEMM的输出是half型数据,因此不需要插入QDQ节点。

当把QDQ节点都插好之后,类似TensorRT的流程做calibration,使用量化校准数据集作为模型的输入,对每一个activation的A-max值做统计。我们并不是直接把FP16的数据cast成FP8,而是通过一个量化的过程来完成。这里借助Modelopt工具中的QDQ来计算量化参数,也叫Scaling Factor。有了Scaling Factor,可以把左侧插完QDQ的计算过程转换成右侧的计算过程。

其中输入部分还是一个浮点的输入,Quantized节点可以把输入量化成FP8,在量化的过程中会尽可能与其他算子融合以减少数据传输。另外,权重矩阵用weight跟Quantized scaling factor乘完之后,存成一个FP8的值在显存中。当计算矩阵乘时,可以把FP8 weight load进来,再把量化之后的input用FP8的tensor core进行计算。这里FP8只有tensor core支持,CUDA core是没有FP8的。用FP8 tensor core计算完之后,再做一个反量化,得出FP16的值。当然,输出值的类型是根据实际需要来配置的,也可以是其他的数据类型。

在国内能买到的支持FP8的H20 GPU中,INT8和FP8的算力峰值都是一样的,都是296 tflops。但实测中,FP8用Plug-in或者用TensorRT融合的myelin graph运算,都会发现FP8比INT8快。这是因为FP8的计算是根据Hopper硬件的一些特性来做的计算。但是INT8很多的计算没有参考最新Hopper的架构。所以,软件优先级的问题也导致FP8矩阵乘的运算比INT8要快。当后续软件层面也会优化INT8,这个Gap将不存在。

除了矩阵乘,Attention部分也可以借助FP8做运算。主要有两个:

  1. Fused Multi-Head Attention:做Context phase时,Attention计算中的batch GEMM可以用FP8计算。因为FMHA是一个融合的kernel,由两个batch GEMM和中间的softmax组成。由于softmax是累加的过程,所以必须用高精度FP32处理。但对于batch GEMM,可以直接借助FP8的Tensor Core计算,最终输出是一个FP8的输出。这样输出的原因是FMHA kernel后,紧跟着一个FP8的矩阵乘project GEMM,可以直接接收FP8的输出,所以直接输出一个FP8即可,减少了一次量化。

  2. 对于FMHA,为什么不用INT8?这里我们做过相应的实验,INT8的FMHA在精度上比FP8有很大的下降。所以,INT8由于精度问题用不了,而FP8的精度更鲁棒。同时,也因为FP8在绝对值相对较小的情况下,打点比INT8的数据分布更密集。但当绝对值很大时,对于离群点部分,INT8不区分离群点和非离群点的打点密集程度,而FP8在离群点的地方打点很疏,在非离群点打点很密集,所以FP8的精度更鲁棒。

  3. FP8中的Quantized和Dequantized,有一个per tensor量化参数就可以搞定。不需像INT8 per token + per channel这样复杂,FP8就可以保持精度,这也是用FP8显而易见的好处。

  4. Masked Multi-Head Attention:Generation phase计算Attention模块时,需要用融合的算子。因为MMHA的计算量比FMHA小很多,虽然也需要做batched GEMM,batched GEMM的batch维度都是BS * HEAD_NUM,区别在于,context phase的GEMM是[length, head_size] * [head_size, length],而generation的GEMM是[1, head_size] * [head_size, length],因此batch GEMM并不是计算密集型的计算过程,所以换FP8的收益不大,直接用浮点即可。但是加载KV-cache的模块可以通过FP8量化来节省显存。KV-cache有INT8 KV-cache,也有FP8 KV-cache。相比INT8,FP8的精度更鲁棒,在Hopper硬件架构下,FP8 KV-cache转出浮点的速度比INT8快。所以,FP8 KV-cache的MMHA速度比INT8 KV-cache的MMHA要快。

借助NVIDIA NCU工具,对比在未打开XQA情况下的MMHA。图中蓝色代表FP8 KV-cache,绿色代表INT8 KV-cache。可以看到,INT8的MMHA kernel在XU pipe上的利用率非常高,也就是所有的kernel运算,都会卡在这个地方,产生较高的瓶颈。(这里的XU是做INT8数值转换用到的一个pipe。)

FP8主要用ALU和FMA,bound情况好于INT8。所以,FP8 KV-cache在数值转换的bound程度相比INT8 KV-cache轻,所以FP8 KV-cache MMHA好于INT8 KV-cache MMHA。

以上就是FP8在模型中的应用场景、优势以及使用原因的简要总结和介绍。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号