微信扫码
与创始人交个朋友
我要投稿
本文主要对近年流行和经典的LLM PTQ量化算法论文进行一些汇总和分析。由于每篇详解的文章很多,本文不会逐篇做非常细致的讲解,主要对LLM量化算法的发展演进和核心思想方法做一些归纳,希望能触发一些思考和讨论。
GPTQ: ACCURATE POST-TRAINING QUANTIZATION FOR GENERATIVE PRE-TRAINED TRANSFORMERS (https://arxiv.org/abs/2210.17323)
Code: https://github.com/IST-DASLab/gptq
Type:W4A16
首先介绍GPTQ,它在LLM PTQ方向的历史地位已经毋庸置疑了,因其出色的效果得到广泛应用。它的背景非常悠久(OBD->OBS->OBQ->GPTQ),从最早的 Yann LeCun 在 1990 年提出的 OBD 算法,到 OBS、OBQ算法,再到GPTQ ,也算是站在巨人的肩膀上。GPTQ的出发点很朴素,就是试图最小化weight量化后和量化前的层函数误差,经过一系列复杂的数学求解后结果如下所示,代表最优先进行量化的weight,代表对剩下权重的更新,H是二阶的Hessian matrix(海森矩阵),这样量化误差会最小。
论文里关于数学公式推导描述地比较简单,详细数学推理公式可以参考文章杨远航:QLoRA、GPTQ:模型量化概述 (https://zhuanlan.zhihu.com/p/646210009) 。求解出来后,GPTQ的做法就是对每个weight进行逐步量化,每组参数量化后需要适当调整其他未量化的参数,以弥补量化造成的error,而弥补的数值需要参考最优化求解的结果。为了加速量化过程,GPTQ还做了一些优化,主要有:
舍弃了筛选参数进行量化的贪心策略,直接顺序方式对所有参数进行量化。
在量化-更新-量化-更新的迭代中使用批处理更新从而延迟一部分参数的更新,缓解 I/O 压力。
用 Cholesky 分解求海森矩阵的逆,在增强数值稳定性的同时,不再需要对海森矩阵做更新计算,进一步减少了计算量。
最终算法描述如下,详细的代码解析可以参考进击的Killua:GPTQ & SmoothQuant & AWQ 代码解析 (https://zhuanlan.zhihu.com/p/697860995)。GPTQ很好地解决了weight优化的问题,所以在开源社区多数大模型都有其对应的GPTQ 量化实现,但是它没有涉及解决异常值问题和 Activation 量化(给后人留了空间),而且利用calibration set进行量化的过程中存在一定过拟合风险。
LLM.int8()发现LLM规模变大后会出现少量非常重要的离群点(即异常值),这些点对attention效果有较大影响,同时对量化带来挑战。
论文的核心思想非常简单直接,就是分而治之。通过离群(Outlier)检测,把输入X和权重W中包含异常值的行、列挑选出来直接做fp16的浮点矩阵乘法,然后剩下的正常点用矢量化方法(X每一行用absmax进行量化、W每一列用absmax进行量化)量化后进行int8乘法再反量化回fp16,最后把它们累加起来作为最终结果输出,如下图所示。
这种简单的处理方式当然也会带来一些问题,比如离群点的检测、拆分以及合并会带来一些额外的计算开销,同时随着离群点增多混合精度的计算复杂度会显著提升,对性能提升的幅度会明显下降。当然这只是个开端,后面有很多研究是围绕这些优化展开的。
SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models (https://arxiv.org/abs/2211.10438)
Code:https://github.com/mit-han-lab/smoothquant 、https://github.com/Guangxuan-Xiao/torch-int
Type:W8A8
SmoothQuant是MIT 韩松老师实验发表的,已经集成到Pytorch、TensorRT-LLM等框架中成为W8A8的业界主流。它发现权重比较容易量化,激活值不易量化,因为有前文提到的离群值,激活值中某些channel异常地高。那么对激活值用per-channel的方式进行量化精度下降可能会比较小,但是这样就无法很好地使用TensorCore进行硬件加速了。于是作者就提出先用smooth的方式对weight和channel维度activation进行调整,平滑后就比较容易量化了,如下图所示。
猜测作者可能是借鉴了Cross Layer Equalization (https://arxiv.org/abs/1906.04721)论文的思想在LLM上也撸了一遍,这种平滑的处理方式其实在做常规PTQ量化的同学已经想到过了,只不过面向的场景不太一样,CLE是在不同layer之间做smooth,而这里是在权重和激活值上做smooth。平滑的具体做法如下所示,在激活值上进行缩放,同时在权重上进行扩增,这样总的结果是保持不变的,但是计算时候激活值的波动就变小了,这里还有个超参用来调整缩放的大小。详细的代码解析可以参考进击的Killua:GPTQ & SmoothQuant & AWQ 代码解析(https://zhuanlan.zhihu.com/p/697860995)
AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration (https://arxiv.org/abs/2306.00978)
Code:https://github.com/mit-han-lab/llm-awq
Type:W4A16
AWQ是一种LLM低比特权重量化方法,MLSys 2024的best paper,也是韩松老师实验室发表的,可以认为是当前W4A16的SOTA,已经被应用到很多低比特量化框架中,如TensorRT-LLM、FastChat、vLLM、TGI、LMDeploy。它和SmoothQuant有些像,只是SmoothQuant侧重于对matrix multiplication场景的weight和activations进行INT8量化(W8A8),而AWQ关注在low bit(INT4) weight量化(W4A16),主要被应用在linear layer(包含最多的参数),详细的代码解析可以参考进击的Killua:GPTQ & SmoothQuant & AWQ 代码解析 (https://zhuanlan.zhihu.com/p/697860995)
它核心的贡献:
发现weight对模型的重要程度存在极强的不均衡性,1%的参数可能主导的量化过程中损失的性能,假如我们在量化中保护这1%的参数,就能极大程度保护模型性能不受影响,但是混合精度(FP16+低比特)对硬件不友好。
那么如何发现重要weight?不用权重!用激活值来发现重要的weight。
不用混合精度的话如何保护重要weight?对weight进行per-channel的scale同时对激活值除以scale。
如何确定最合适的scale?量化误差函数不可微,没法用梯度下降求解,所以退而减少搜索空间。取和激活值相关的值进行grid search,找到那个让量化误差最小的scale。
ZeroQuant: Efficient and Affordable Post-Training Quantization for Large-Scale Transformers(https://arxiv.org/abs/2206.01861)
ZeroQuant-V2: Exploring Post-training Quantization in LLMs from Comprehensive Study to Low Rank Compensation(https://arxiv.org/abs/2303.08302)
ZeroQuant-FP: A Leap Forward in LLMs Post-Training W4A8 Quantization Using Floating-Point Formats(https://arxiv.org/abs/2307.09782)
ZeroQuant-HERO: Hardware-Enhanced Robust Optimized Post-Training Quantization Framework for W8A8 Transformers(https://arxiv.org/abs/2310.17723)
Code:https://github.com/microsoft/DeepSpeed
ZeroQuant系列其由微软DeepSpeed团队于2022年推出,是一个端到端的量化推理流水线,具有很强的工程实践特性,至今已经发展成了一个系列量化算法,应用还是比较广泛的。
第一版的ZeroQuant主要包含以下三个方法:
对weight进行per-group量化,对activiation应用per-token的动态量化,兼顾了性能和精度。
使用了逐层知识蒸馏来提取量化网络,降低了显存开销。
使用cutlass prelogue进行量化算子fuse,使用epilogue进行反量化算子fuse,降低量化带来的性能损失。
第二版ZeroQuant-V2更多的是一些分析和总结,它分析了权重量化和激活值量化对精度的敏感性,比较了常用PTQ算法的模型效果,最后引入了一种称为低秩补偿(LoRC)的优化技术,它可以与 PTQ 的协同工作,以最小的模型参数大小的增加来改善整个模型质量的恢复,但这种方法的扩展性似乎不是很好。
第三版ZeroQuant-FP主要探索了浮点(FP)量化的可行性,特别关注FP8和FP4格式。对于LLM,FP8激活在性能上优于INT8,而在权重量化方面,FP4在性能上与INT4相比具有可比性,甚至更优越;LoRC有助于提升W4A8的整体表现。同时为了解决由权重和激活之间的差异(从FP4到FP8)引起的挑战,ZeroQuant-FP要求所有缩放因子为2的幂,并将缩放因子限制在单个计算组内。
第四版ZeroQuant-HERO是一种新的硬件增强型训练后 W8A8 量化框架,它考虑了内存带宽限制和计算密集型运算,总体偏工程方向,将transformer中更多子模块进行了量化改造,分别是:Embedding层量化、Attention模块量化、MLP 模块量化,同时采用不同量化精度对各个模块进行量化组合,按需选取量化等级。
SpQR: A Sparse-Quantized Representation for Near-Lossless LLM Weight Compression(https://arxiv.org/abs/2306.03078)
Code: https://github.com/Vahe1994/SpQR
Type: W4A16
首先它用类似OBC(Optimal Brain Compression)的方式来计算参数的敏感度,求解结果如下所示。
通过统计发现了敏感weight位置分布的一些特性,大多具有特定的结构(行、列、注意力头、非结构化等),为此SpQR对两类weight进行特殊处理:敏感值group和单个异常值,然后将它们存储在更高的精度中;将所有其他权重压缩为3-4比特。利用这些发现提出一个压缩表示,可以支持所有这些不同的离群值类型。
该压缩表示的内容为:
dense的普通权重(低比特量化)
group的量化敏感权重值(高精度)
sparse的离群单点(高精度)
下图是单个weight张量的 SpQR 表示的高级概述,右侧描述了所有存储的数据类型及其维度。作者还为 SpQR 格式设计了一个基于 GPU 的高效解码实现。SpQR效果还是挺好的,在某些大模型上的效果比AWQ还要好一些,但它比AWQ复杂性高出很多,所以应用广泛度不及AWQ。
OWQ: Outlier-Aware Weight Quantization for Efficient Fine-Tuning and Inference of Large Language Models (https://arxiv.org/abs/2306.02272)
Code: https://github.com/xvyaward/owq
Type: W4A16
OWQ也是延续了LLM.int8()和GPTQ的思想,weight的某些channel特别敏感最好使用fp16来存储,但是OWQ的筛选的方法是使用类似GPTQ的海森矩阵,而不是LLM.int8的outlier feature统计。OWQ对交叉维的每个channel计算敏感度指标如下:
其中是海森矩阵的第j个对角线元素。选择敏感度最大的top-k看成weak columns转化为fp16存储,其余部分用GPTQ的低比特方案存储(例如4比特),本质上也是混合精度的方式。具体操作如下:
把原始 ? 转换成全量的低比特矩阵,weak column(敏感列)用0填充。
单独额外存储fp16的weak columns列,并且把列号附在最后一个元素后面。
论文同时提出对特定任务的finetune采取对低比特矩阵冻结,仅对weak column微调的方式来进行,能够做到在保持精度的同时节省内存开销的目的。
SqueezeLLM: Dense-and-Sparse Quantization(https://arxiv.org/abs/2306.07629)
Code:https://github.com/SqueezeAILab/SqueezeLLM
Type: W4A16
SqueezeLLM也是一个权重量化算法,它基于的事实也已成为标准,即
LLM模型推理的主要瓶颈是带宽,存在内存墙。
权重存在不同敏感度,少量权重非常敏感需要针对性进行量化。
和之前的AWQ、SpQR等的区别体现在:
1. 它采用近似的Fisher information来度量敏感度,这是一种新的度量方式。然后基于敏感度进行低比特非均匀量化,使用kmeans聚类来生成靠近敏感值的量化定点,其它点以MSE最小来安置,从而最小化量化误差。
2. 稠密和稀疏分解,高效的稀疏格式来存储outlier和敏感权重(FP16),稠密格式来存储大量的低比特常规权重值。分别开发了kernel来处理稠密和稀疏的矩阵向量乘kernel。
在困惑度方面,战胜了RTN,GPTQ,AWQ这些权重量化的方法,但是在加速与内存节省方面稍逊于GPTQ。
RPTQ: Reorder-based Post-training Quantization for Large Language Models(https://arxiv.org/abs/2304.01089)
Code: https://github.com/hahnyuan/RPTQ4LLM
Type: W4A4/W4A8/W4A4KV
RPTQ主要研究的是激活值的量化,它发现即使除去outlier,不同channel的range变化还是很大,如果用per-tensor的方式去量化误差较大,而per-channel的方式执行效率低,所以采取折中的方式per-group量化。
RPTQ的主要思想是对激活值的channel进行kmeans聚类重排,把(min,max)近似的channel聚成一个group,在这个基础上进行量化。
内存重排的开销还是比较大的,为了减少这部分开销提出了一些工程优化:
把重排操作融入到了layernorm层中,这个想法比较简单,直接在layernorm刷回内存时按新排序的index进行写入。
调整了weight顺序,允许线性层直接接受重排的激活值,同时自身也输出重排的激活值。
矩阵乘和线性层也都需要input channel对齐。
transformer block的reorder如图所示。整个流程稍微有点绕,真正要落地到LLM大规模工程实践还有一些工作要做。
ATOM: LOW-BIT QUANTIZATION FOR EFFICIENT AND ACCURATE LLM SERVING(https://arxiv.org/pdf/2310.19102)
Code: https://github.com/efeslab/Atom
Type: W4A4
ATOM几乎是W4A4类型的SOTA,也是之前各项工作的集大成者,提出了一系列针对LLM特性的量化策略和工程实践:
1. 进行混合精度量化,outlier activation值使用INT8,normal值使用INT4,复用了RPTQ的reorder方法对outlier进行重排并进行了算子融合。
2. 对weight和activation中的normal值使用group量化,由于各group scale不一致,所以先分别计算各group的中间结果,再反量化为fp16计算最终结果。为了降低计算开销也使用了融合GEMM技术。
3. 为提高精度对激活值进行动态量化,减少计算开销的方式也是采用了前算子融合的技术。
4. 对KVCache进行低比特非对称量化,在FP16计算前进行反量化。
OliVe: Accelerating Large Language Models via Hardware-friendly Outlier-Victim Pair Quantization(https://arxiv.org/abs/2304.07493)
Type:W4A4
Olive从硬件层面上对离群值的计算进行了优化,之前的离群值感知量化方案采用的基本都是稀疏编码技术,即将离群值与正常值分开存储,其中过程需要全局协调。这将导致复杂的编码和解码硬件逻辑,它不是硬件最优的。OliVe提出了一个硬件友好地处理离群值和正常值的方式,它关键发现是离群值是重要的,而它们旁边的正常值则不太重要,因此可以牺牲这些正常值(称为受害者)以适应离群值,以本地化的方式处理离群值,提高硬件效率。
OliVe首先将与离群值相邻的正常值修剪为零。这些被修剪过的正常值被称为受害者,为离群值腾出了空间。然后,我们利用受害者提供的额外空间,将异常值嵌入到低精度矩阵中,如下图所示。
Olive将这里的encoder和decoder过程做的非常精细,从硬件层面进行了架构设计,一般想要复现论文效果技术门槛很高,但这个算法和硬件架构协同设计的实现方案确实威力巨大,可以看看后续硬件架构的演进路线。
encodder时对(outlier, victim), (outlier,outlier),(normal,normal)pair进行分别处理,设计了victim的identifier, 设计了normal的数据类型(int4,fint4,int8),为outlier设计了专门的数据格式abfloat,关键思想是通过向指数上添加适当的偏差,所有编码值都可以跳过normal所在的区间,为outlier提供更多的范围。
deocder时对TensorCore等硬件特性进行了充分集成,使用Verilog设计了门电路进行解码、设计了针对(outlier,victim) pair的矩阵乘指令,优化层级非常深。
Outlier Suppression: Pushing the Limit of Low-bit Transformer Language Models(https://arxiv.org/abs/2209.13325)
Outlier Suppression+: Accurate quantization of large language models by equivalent and optimal shifting and scaling(https://arxiv.org/abs/2304.09145)
Code:
https://github.com/wimh966/outlier_suppression
https://github.com/ModelTC/Outlier_Suppression_Plus
Type: W8A8
之前的量化算法基本上都是对outlier进行保护处理以保证它对精度的影响,而outlier suppression做法相反,它另辟蹊径地想要抑制outlier对最终精度的影响。论文发现:
1.LayerNorm结构中的尺度参数γ作为一个离群值放大器,它放大了输出中的离群值。
2. 一些覆盖大面积的更激进的离群值可以被安全地剪切,不会降低精度。
于是它提出的优化手段:
Gamma Migration,通过将离群值放大器γ迁移到后续模块中来产生一个更易于量化的模型,有点像smoothquant。
对outlier进行token-wise的剪切能够有效地找到一个使最终量化损失最小的剪切范围。
而outlier suppression+发现了异常值的一个新特征,即它们在不同通道间保持不对称的形状,这种离群的不对称呈现可能导致Tensor更广泛分布,更难于量化。于是它提出了通道shift+通道scale的操作来改造activation的分布,同时引入了一个统一的迁移模式,将这些操作的影响转移到后面的模块中,以维护等效的浮点运算逻辑,如下图所示。本质上其实和SmoothQuant的方向是一致的,让weight和activation做等效变化从而让它们更易于量化,只是outlier suppression+手段显得更有力一些,在多个模型和评测集上也超过了SmoothQuant的效果。
本文提到的PTQ量化算法分类如下所示:
Type | W4A16 | W8A8 | W4A4 |
---|---|---|---|
Algorithm | GPTQ AWQ SpQR OWQ SqueezeLLM | LLM.int8() ZeroQuant SmoothQuant outlier suppression+ | RPTQ ATOM Olive |
从这些研究可以看出LLM PTQ量化方向已经形成了不少共识性的方法和研究方向。
对于权重量化,一个共识是需要区分不同的敏感度来进行差异性量化,而敏感度的度量和处理方式各有千秋。有用激活值来进行度量,有用Hessian矩阵进行度量的,有用近似的Fisher information来度量;而处理方式上有平滑的方式,有混合精度稠密稀疏分解的表示方式,以及围绕混合精度设计不同的硬件友好的表示方式。
对于激活值量化,一个共识需要对离群值做特殊处理,处理的方式有平滑的方式、内存重排的优化方式和混合精度的处理方式,甚至还有裁剪的方式,这些都能在一定程度上降低量化的难度,进而减少离群值对精度的影响。
工程实现上也有了大量的实践,最常用的量化、反量化算子融合和重排算子融合就是其中的代表。
以上方法都能够比较好地解决权重和激活值量化的问题,然而算法应用的广泛度不仅仅取决于算法的效果,易用性和简单性也是不可忽视的重要因素,这里AWQ就是一个比较好的例子,效果不错且应用简单因而得到较为广泛的推广。
随着LLM模型体量的增大相信接下来还会有更多W4A4类型的低比特量化算法出现,同时能够基于硬件进行深度优化,在保持精度的前提下进一步降低推理耗时,让我们拭目以待。
53AI,企业落地应用大模型首选服务商
产品:大模型应用平台+智能体定制开发+落地咨询服务
承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2024-05-28
2024-08-13
2024-04-26
2024-08-21
2024-07-09
2024-06-13
2024-08-04
2024-04-11
2024-07-18
2024-07-01