大模型压缩——量化实现

发布时间:2023-06-12 07:00

背景介绍

随着越来越多高质量的大模型参数及其训练过程开源,我们一方面关注大模型在大算力集群上的高性能部署,另一方面也关注相对较小的模型在资源受限情况下的低代价适配。这会涉及模型、推理框架、核心算子实现、硬件资源调度等多个层面的协同优化,而首先需要解决的就是“放得下”以及“放更多”的问题。由于大模型参数本身占据大量存储空间,故对其进行压缩是一个十分有效的手段,其中一种简单高效的方法是模型参数量化,对于降低存储空间要求及访存带宽压力都有立竿见影的效果。

 

最近很多开源的大语言模型都提供了4bit的版本,基本上都是使用GPTQ[1]实现的。图1展示了HuggingFace上4bit模型的搜索结果。

 

图1 托管在HuggingFace上的4bit模型

图片来源:https://huggingface.co/models?sort=downloads&search=4bit

 

GPTQ可以将训练好的模型参数从16bit压缩到4bit或3bit,并保持较好的模型效果。表1为LLaMA-7B和LLaMA-65B使用GPTQ量化的情况,这里使用Wikitext2数据集[3]上测试的Perplexity值(越小越好)衡量模型效果,可以看到对于4bit量化,RTN(rounding all weights to the nearest quantized value)量化方法对7B模型的影响较大(5.68->6.29),对65B的模型影响较小(3.53->3.92),而GPTQ可以进一步减少量化对模型效果的影响(7B: 5.68->5.85, 65B: 3.53->3.65)。对于3bit的情况,RTN方法对模型效果产生了很大影响(7B: 5.68->25.54, 65B: 3.53->10.59),而GPTQ可以显著减少这些影响(7B: 5.68->6.61, 65B: 3.53->4.17)。量化后的模型所需存储空间大大降低。

 

表1 LLaMA 7B/65B使用GPTQ量化的情况 (Wikitext2一列表示在该数据集上测试的Perplexity值,越小越好)

数据来源:https://github.com/qwopqwop200/GPTQ-for-LLaMa

虽然在维持模型效果方面,GPTQ相对于RTN已经有很大改善,但如何进一步缩小量化模型与原始模型之间的差距,如何更好地存储及使用8bit以下的数据,还有待进一步研究。本文先介绍GPTQ方法,再结合SpQR继续讨论上述问题。

 

GPTQ

 

GPTQ的核心思想是逐一量化模型的各个层,对于每个层寻找可以满足下式的量化结果:

 

 

 

即对于每个需要被量化的层(对应参数W),希望量化前后该层输出变化尽量小。

 

为了达到这个目标,GPTQ对W进行分块量化,每个块中逐一量化各个列,量化该列导致的输出变化通过调整块内还未量化的其他列的值来弥补,完成整个块的量化后,量化该块导致的输出变化通过调整W还未量化的部分来弥补,调整方法基于Hessian矩阵进行设计,感兴趣的读者可以参考论文中的推导,整体量化流程如图2所示。

 

图2 GPTQ量化流程[1]

图片来源:https://arxiv.org/pdf/2210.17323.pdf

 

 

GPTQ量化过程有一定的计算量,如表2所示,完成OPT-175B模型量化需要4.2小时,BLOOM-176B模型量化需要3.8小时。

 

 

表2 GPTQ使用单块A100量化不同大小的模型所需时间[1]

图片来源:https://arxiv.org/pdf/2210.17323.pdf

 

 

通过将模型参数量化到3bit,OPT-175B模型参数存放大概需要63GB显存,对于2048的序列长度,大概还需要9GB显存用于存放key和value,这一配置下可以使用单块80GB的A100实现推理,而16bit的情况至少需要5块80GB的A100。大语言模型推理速度通常是受限于显存带宽,模型参数量化降低了显存带宽的压力,同时对于较大的模型减少了所需GPU数量,也减少了卡间通信,有助于降低推理延迟。对于3bit的情况,GPTQ提供了动态反量化的矩阵乘向量实现,相对于16bit矩阵乘的配置可以有明显的速度提升,具体数据见表3。不过从数值上看,这里16bit的情况应该是使用了流水并行,推理过程中每次只有一个GPU在运行,利用率较低,如果使用张量并行,推理速度通常会有5倍以上的提升,在这种配置下GPTQ可以带来多少收益还有待探究。

 

 

表3 OPT-175B不同配置下生成1个token的平均耗时(序列长度为128)[1]

图片来源:https://arxiv.org/pdf/2210.17323.pdf

 

 

下面我们讨论GPTQ量化对模型效果的影响,图3展示了不同大小的OPT模型及BLOOM模型分别使用GPTQ和RTN方法量化到4bit及3bit的情况,模型效果衡量使用LAMBADA数据集[4]上测试的准确度(Accuracy)。可以看到越大的模型参数量化越容易,4bit RTN对OPT-175B和BLOOM-176B进行量化时模型效果没有受到太大影响,而对较小的模型进行量化时模型精度会有明显下降。另外,不同模型对量化的敏感度有很大区别,OPT在1.3B和66B这两个配置下对量化十分敏感,4bit RTN就带来了明显精度损失,而BLOOM不同配置下敏感度比较相似,不过BLOOM没有66B这样的配置,这样不好做比较。3bit量化的情况下,RTN在两种模型不同配置上都会带来明显精度损失,GPTQ则可以大大减少精度损失。但也可以看到,特别是对于较小的模型,GPTQ量化后还是与16bit模型有一定差距。

 

 

 

图3 不同大小的OPT模型及BLOOM模型使用不同量化方法时在LAMBADA数据集上的准确度变化[1]

图片来源:https://arxiv.org/pdf/2210.17323.pdf

SpQR

在上述讨论中我们可以看到,GPTQ对于较小的模型精度的维持还有一定的改进空间。SpQR(Sparse-Quantized Representation)[2]进一步分析了模型参数的分布规律,提出使用更细粒度的量化方法,对于不同大小的模型都可以更好地维持模型效果。

 

首先定义模型参数对量化的敏感度,参考GPTQ的思路,如果对模型参数W中的某个值w_ij做了量化,可以通过调整剩下的值来尽量减小其对输出的影响,那么量化敏感度较高的值可以定义为,不管怎么调整剩下的值,都没法很好地弥补量化w_ij带来的影响,SpQR中对这一过程的数学描述如下:

 

 

 

使用GPTQ的求解方法可以得到:

 

 

由此可以计算模型各个层中各个参数的敏感度,图4 是使用GPTQ量化的LLaMA-65B最后一个自注意力层中out projection参数敏感度的可视化示例,从中可以找到一些规律:

 

  1. 有一些高敏感度的参数分布在同一行或同一列中,也有一些出现在某行的一部分,与注意力头(attention head)有关;

 

  1. 有一些注意力头整体上敏感度比较高,但在其内部敏感度也有比较大的差别;

 

  1. 有一些以64为周期的敏感度变化模式,应该与Rotary embedding[5]相关,整体体现为Rotary embedding定义中低频部分对应的区域敏感度高于高频部分;

 

  1. 还有一些没有固定模式的高敏感度值,这些值更频繁地出现在列号较大的位置,即图中靠右的位置,可能与GPTQ量化方法有关(靠后的列更晚被量化,吸收了更多之前完成量化的列带来的误差)。

 

图4 LLaMA-65B最后一个自注意力层中out projection参数敏感度可视化,深色代表敏感度高,中间两个小图是该层局部放大的结果,最右边两个小图是其他层某个局部的结果[2]

图片来源:https://arxiv.org/pdf/2306.03078.pdf

 

 

基于上面的观察,粗粒度的分组方式(单个或多个行/列为一组)很难保证不同分布模式的元素分到不同组,所以这里使用了更细粒度的量化:每8-32个元素为一组进行量化。但这样会导致量化参数(每个量化组会有一个16bit的scale,非对称量化还会增加一个16bit的zero point)数量大大增加,为减小存储量化参数带来的开销,对其再进行一次量化,分组为每16个元素一组。使用细粒度分组的情况下还是有少数高敏感度元素会带来较大影响,对于这些元素,SpQR单独将其保存为16bit,整体流程如图5所示。

 

 

图5 SpQR整体流程:高敏感度的outliers存储为高精度,剩下的元素进行细粒度分组两级量化[2]

图片来源:https://arxiv.org/pdf/2306.03078.pdf

 

SpQR量化后需要存储的内容包括量化部分(图5中quantized weights, scales and zeros)和非量化部分(图5中outliers)。

 

量化部分没有专门移除outliers的位置,在进行分组量化时,计算量化参数会忽略outliers的值,但还是会对其进行量化,而单独存储的outliers中保存的是其对应的量化误差。这样量化部分具体要存的是一个b_w bit的weights,每个量化组有b_q bit的scale和zero,以及对scale和zero进行量化的16bit量化参数。假设b_w=b_q=3,weight量化及scale/zero量化分组均为16,量化过程可以描述为,weight矩阵每个组包含16*16=256个元素,这256个元素会被量化到3bit,每16个元素有一个3bit的scale和一个3bit的zero,共16个scale和16个zero,各对应1个16bit的二级scale和1个16bit的二级zero,即4个16bit数值。量化的weights和对应的两级scale/zero会连续存储,方便按块取用。

 

非量化部分按先行后列的顺序排列,对于每个outlier值,存储一个16bit数值和一个16bit列号,对于每行,存储一个32bit数值,用以记录包括本行在内的outlier数目。这一方式下每个outlier开销为32.03-32.1 bits。

 

对于上述数据存储结构,GPU推理实现思路是反量化到16bit进行矩阵乘,outliers部分可以使用PyTorch提供的稀疏矩阵乘(cuSPARSE),但效率较低,SpQR提出了更优化的实现,速度上相对于16bit baseline可以提升20%-30%。其大致流程为,首先将weight矩阵分成大小相同的块,然后对于每个thread block:

 

(1)加载较大的一块outliers数据到SRAM中;

 

(2)判断outliers属于weight矩阵的那一块;

 

(3)加载对应的weight矩阵块完成矩阵乘。

 

由于outliers是先行后列存储,矩阵乘计算过程中通常有比较好的局部性,推理速度比较见表4。

 

表4 SpQR不同实现方式下推理速度(tokens/s)比较[2]。scratch表示从0开始生成100个token,prefix 1024表示prompt长度为1024的情况下生成100个token。

图片来源:https://arxiv.org/pdf/2306.03078.pdf

 

 

表5给出了三个数据集(Wikitext2[3],C4[6],PTB[7])上测试的Perplexity值,可以看到SpQR对不同大小的模型进行量化均能更好地保持模型效果。

 

表5 LLaMA不同配置量化效果对比(指标为Perplexity,越小越好)[2]图片来源:https://arxiv.org/pdf/2306.03078.pdf

 

总结与思考

本文讨论了两种对于大语言模型参数使用更低数值精度(4bit/3bit)进行量化的方法,这对于进一步推动大语言模型相关应用落地有很大帮助,比如3bit量化可以支持175B大小的模型在单块80GB的A100上完成2048序列长度的推理,大大降低了大语言模型的推理部署代价。

 

GPTQ的核心思想是逐层量化,每层的量化分块进行,并通过更新未量化部分的数值来弥补量化误差,最终达到量化前后各层同样的输入对应的输出变化尽量小的效果。这里每层的量化分组粒度还是比较粗的,通常是各层参数中一列或多列为一组。目前8bit以下的数值计算还没有很好的硬件支持,GPTQ提供了相应的推理实现方案,相对于16bit的情况,在将所需A100数量从5降至1的基础上,推理速度得到了3.24倍的提升。但根据其提供的数值可以大致判断出这里16bit的测试是使用了流水并行,如果改用张量并行通常会有5倍以上的速度提升,在此情况下GPTQ能带来的速度收益有待进一步研究。

 

SpQR更细致地分析了各层模型参数对量化的敏感度,提出使用更细粒度的量化分组方式,并通过两级量化,即对细粒度分组量化产生的量化参数再进行量化的方式来降低量化导致的额外存储开销。对于一些即使细粒度分组也无法覆盖的模型参数,SpQR选择单独使用高精度数值对其进行存储。由此会引入特殊的存储格式,SpQR也提供了存储建议及对应的高效推理实现方案,相对于量化前可以带来20%-30%的推理速度提升。

 

除了从量化过程本身去考虑如何更好地保持模型效果,在大量优质的低代价微调开源方案推动下,近期也看到一些基于量化后的模型做进一步训练或微调的工作,一方面可以更好地弥补量化带来的误差,另一方面也能以更低的成本实现大语言模型在各个领域的适配。

参考文献

[1] Frantar, Elias, et al. "GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers." arXiv preprint arXiv:2210.17323 (2022).

[2] https://doi.org/10.48550/arXiv.2306.03078

[3] Merity, Stephen, et al. "Pointer sentinel mixture models." arXiv preprint arXiv:1609.07843 (2016).

[4] Paperno, Denis, et al. "The LAMBADA dataset: Word prediction requiring a broad discourse context." arXiv preprint arXiv:1606.06031 (2016).

[5] Su, Jianlin, et al. "Roformer: Enhanced transformer with rotary position embedding." arXiv preprint arXiv:2104.09864 (2021).

[6] Raffel, Colin, et al. "Exploring the limits of transfer learning with a unified text-to-text transformer." The Journal of Machine Learning Research 21.1 (2020): 5485-5551.

[7] Marcus, Mitch, et al. "The Penn treebank: Annotating predicate argument structure." Human Language Technology: Proceedings of a Workshop held at Plainsboro, New Jersey, March 8-11, 1994. 1994.

 

 

 

上一个: 大语言模型微调:定制自己的微调数据集

下一个: 通用AI模型的未来:深度强化学习(deep reinforcement learning)

近期文章

大模型推理:从模型分析到计算优化(二)

大语言模型被普遍认为可以带来无数充满想象力的应用场景,也正是因为如此,它被视为是通用人工智能的一个重要的前进方向。本系列文章将围绕大语言模型的推理,分析推理背后的算法逻辑和特征,为算法实现提供简便的评估方案,再结合几种典型的硬件场景展开评估与分析,为打算实现模型推理的用户提供一些参考。本篇文章将定量讨论在单卡上大语言模型推理性能的估算,并呈现在典型的场景下各个性能指标跟随设定参数产生的丰富变

2023-09-04

多模态大语言模型:让AI看图说话

让AI具备看图说话的能力一直是AI研究的热点。从早期的RCNN,到影响很大的CLIP,AI对图像的语言理解技术一直在迭代。随着大语言模型的能力提升,将视觉语义模型与LLM结合,以获得优秀的看图说话能力成为当前探索的热点方向。本文主要分析了看图说话的多模态模型的构成,探索了多模态语言模型的前沿发展,并归纳了实现多模态理解能力的关键技术。

2023-08-07

查看更多