挽幽
2025-6-8 21:54:04
探秘Transformer系列之(36)--- 大模型量化方案
目录
探秘Transformer系列之(36)--- 大模型量化方案
0x00 概述
0x01 8位量化
1.1 LLM.int8()
1.1.1 动机
1.1.2 方案
8位数据类型和量化
逐向量量化(vector-wise quantization)
混合精度分解(mixed precision decomposition)
1.1.3 缺点
1.2 ZeroQuant
1.2.1 主要贡献
1.2.2 方案
用于激活的按token量化
用于权重矩阵的分组量化
逐层知识蒸馏
1.3 SmoothQuant
1.3.1 动机
1.3.2 方案
1.3.3 不足
0x02 4位量化
2.1 GPTQ
2.1.1 背景
2.1.2 GPTQ 方案
特色
取消贪心算法
Lazy Batch-Updates
Cholesky(乔莱斯基) 分解
总体算法
2.2 AWQ
2.2.1 动机
2.2.2 方案
选择权重
激活感知缩放
应用
与 SmoothQuant 的关系
2.3 LLM-QAT
2.3.1 动机
2.3.2 方案
Data-free 蒸馏
知识蒸馏
量化函数
KV Cache的量化感知训练
结论
2.4 QLoRA
2.4.1 动机
2.4.2 方案
4位正态浮点量化
双重量化
优化器状态分配分页内存
2.5 FlatQuant
0x03 低位量化
3.1 SqueezeLLM
3.2 SpQR
3.3 BitNet
3.4 BitNet b1.58
3.5 OneBit
3.5.1 主要贡献
3.5.2 挑战
3.5.3 方案
1-bit线性层架构
符号-值独立分解(SVID)
知识转移
0xFF 参考
0x00 概述
继前一篇介绍了大模型量化基础之后,本篇我们来看看一些量化方案。
因为大家目前都用压缩到某个bit来衡量量化方案,因此我们接下来就按照量化比特来进行分类学习。
0x01 8位量化
因为目前硬件 (例如 NVIDIA GPU、Intel CPU、高通 DSP 等) 普通都支持INT8 GEMM,因此为了加快推理速度,研究人员提出了将 weight 和 activation 量化为 INT8 (即 W8A8)的方案。
下图给出了几种8bit量化方案的对比。
本节介绍的三种方案特点摘要如下。
方法量化权重与激活特点LLM.int8()W8A8混合精度量化,离群点保持FP16,其它值量化为INT8ZeroQuantW8A8应用Dynamic per-token activation 量化和分组 weight 量化。SmoothQuantW8A8提出了一种按通道缩放的方法,将高精度量化的复杂性从激活转移到权重,借此平滑激活异常值。1.1 LLM.int8()
LLM.int8()出自论文“ LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale”,是 LLM 量化早期工作之一。论文的核心思想是分而治之。对绝大部分权重(Per-channel)和激活(Per-token)用8bit量化(vector-wise)。对离群特征的几个维度保留16bit,不进行量化。
1.1.1 动机
作者首先提出了activation outlier概念,这是后续大模型量化一直都在注重的点,这两年大模型的优化主要集中在保持activation outlier对应的权重的基础上如何做量化。
LLMs的激活非常难以量化,是因为激活中会出现具有大幅度的activation outlier,导致较大的量化误差和准确性下降。这些离群值与正常值相比会有数百倍的数值差距。activation outlier影响巨大,容易导致量化失败。因为如果按照张量量化的方案,会一个tensor共享一个缩放值,这样,只要有一个异常值就能破坏量化的精度。试想一下,张量的大部分值都在0到1之间,假设有一个异常值等于一万,量化之后,这一个异常值就把其它的值拉到0了,会产生很大的精度损失。但同时又有研究表明,这部分离群值会对模型的性能产生显著影响,因此必须想办法保留离群值而不是直接清零,这就产生了一个难以调和的矛盾。
作者发现激活中的异常值集中在一小部分通道中,且outliers占据的维度很少(不到1%)。于是作者思考,是否可以把异常值单独分出来处理。剩下的部分只使用一个缩放值就可以得到很好的结果了。另外,作者认为,其实换个角度来看,矩阵乘法可以看成是左边矩阵的行和右边矩阵的列在做点乘。如果我们把左边矩阵按行做量化,右边矩阵按列做量化,那么就可以得到更加精确的量化值,可以进一步得到更高的精度。
1.1.2 方案
基于以上思考,论文提出了一个两步量化方法 LLM.int8()。
逐向量量化 。具体是:
从输入的隐含状态中,按列提取异常值 (离群特征,即大于某个阈值的值)。
根据输入通道内的离群值分布将激活和权重分成离群值和普通值两个不同的部分。包含激活值和权重的异常数据的通道以FP16格式存储,其他通道则以INT8格式存储。后续会对每个向量内积使用不同的归一化常数。
混合精度矩阵分解。具体是:
对绝大部分权重和激活的正常特征仍然用8bit量化(vector-wise)后计算(W8A8),计算完成后再量化为FP16。对离群特征的几个维度保留FP16,对其做高精度的矩阵乘法(W16A16)。
把反量化非离群值的矩阵乘结果与离群值矩阵乘结果相加,获得最终的 FP16 结果。
具体如下图所示,通过离群(Outlier)检测,把输入X和权重W中包含异常值的行、列挑选出来直接做fp16的浮点矩阵乘法,然后剩下的正常点(X每一行用absmax进行量化、W每一列用absmax进行量化)量化后进行int8乘法再反量化回fp16,最后把它们累加起来作为最终结果输出。
8位数据类型和量化
我们首先看看8位数据类型和量化的特点。这里介绍的是高精度非对称量化 (Zeropoint quantization) 和对称量化 (Absmax quantization)。虽然 Zeropoint 量化通过使用数据类型的全位范围来提供高精度,但由于实际的限制,它很少使用。Absmax 量化是最常用的技术。
下图(a)表示Absmax量化,b)表示Zeropoint量化。[β,α]分别表示区间最小和最大值。
Absmax量化:除以绝对值的最大值乘127从而把输入缩放到8位宽 [−127,127] , ⌊⌉ 表示取最近邻整数,记\(s_{xfl16}\)为缩放系数。该方法即为缩放量化。
Zeropoint量化:上述量化方案对于ReLU的输出只有正区间被使用,而Zeropoint提出使用动态区间 ndx 缩放,使用零点 zpx 平移,从而可使用全区间数值。该方法即为仿射量化,更准确但由于实际限制该方法较少使用。
逐向量量化(vector-wise quantization)
当进行Absmax量化时,会为每个 tensor 使用一个 scaling 常数。由于每个tensor只使用一个缩放常数,激活中存在单个离群点会导致缩放系数变小,最终致使其他非离群点数值精度降低。如果每个张量有多个 scaling 常数就可以缓解此问题。因此,作者使用了 Vector-wise Quantization。
Vector-wise Quantization具体为:将矩阵乘法看成一系列向量(独立的行与列)的内积,可对每个内积使用不同的归一化常数。对于给定输入 \(X_{f16}\)和权重 \(W_{f16}\),输入为FP16的int8矩阵乘法,我们为输入 \(X_{f16} \in R^{s×ℎ}\) 的每行、权重 \(W_{f16} \in R^{ℎ×o}\) 的每列分配不同的缩放常数 \(c_{xf16}\) 和 \(c_{wf16}\) ,反量化时把量化的内积除以缩放常数积的倒数。对于整个矩阵乘法,这相当于外积\(c_{xf16}⊗c_{wf16}\)的反规范化(denormalization)。并且,可以在下一个操作前,通过反归一化恢复矩阵乘法的输出。
混合精度分解(mixed precision decomposition)
参数规模达到十亿级的 8-bit Transformer 的一个重要问题是,它们具有异常值特征,需要高精度的量化。然而, Vector-wise Quantization(量化隐藏状态的每一行)对异常值特征依然不够有效。作者观察到,这些异常值特征在实践中既非常稀疏又有规律,仅占所有特征维度的 0.1%。因此,作者开发了一种新的矩阵乘法的混合精度分解技术:将大于阈值 \(\alpha\) 的离群点的维度放入一个集合O,对集合内的特征不做量化、直接做FP16乘法,而其他维度进行8位乘法。实验发现,当\(\alpha\)=6.0,就足以抵消量化带来的降级影响(performance degradation) 。因为异常值特征维度通常小于7即 |O|≤7 ,因此分解操作基本只消耗很少内存。
1.1.3 缺点
论文不足点如下:
很难在硬件加速器上高效实现outlier的分解,而且运行时进行异常值检测,这导致推理速度比fp16还慢
模型量化仅到8bit,仍是4bit的2倍大。
1.2 ZeroQuant
1.2.1 主要贡献
ZeroQuant的主要贡献如下:
对激活按 per-token 的动态量化,每个token都有独立的量化参数。对权重矩阵进行分组量化,每组都有独立的量化参数。这样兼顾了性能和精度。
提出一种无需访问原始训练数据的逐层知识蒸馏算法(LKD)来提取量化网络,降低了显存开销。
1.2.2 方案
用于激活的按token量化
现有 PTQ 中对激活进行量化常见做法是静态量化,其中最小/最大范围是在离线校准阶段计算的。
对于激活范围方差较小的小模型来说,这种方法可能就足够了。然而,LLM的激活范围存在巨大差异。因此,静态量化方案(通常应用于所有tokens/样本)将导致准确度显著下降。克服这个问题的一个办法就是采用更细粒度的token-wise量化并动态计算每个token的最小/最大范围,以减少激活引起的量化误差。然而,使用现有的深度学习框架直接应用 token-wise 量化会导致显著的量化和反量化成本,因为 token-wise 量化引入了额外的操作,导致 GPU 计算单元和主存之间产生昂贵的数据移动开销。
为了解决这个问题,ZeroQuant 构建了一个高度优化的推理后端,用于Transformer模型 token-wise 量化。例如,ZeroQuant 的推理后端采用算子融合技术将量化算子与其先前的算子(如Layer Norm)融合,以减轻 token-wise 量化的数据移动成本。类似地,在将最终 FP16 结果写回到下一个 FP16 算子(如:GeLU)的主存之前,使用权重和激活量化 scales 缩放 INT32 accumulation,可以减轻不同 GeMM 输出的反量化成本。
Token-wise 量化可以显著减少量化激活的表示误差,它不需要校准激活范围,对于ZeroQuant 的量化方案(INT8 权重和 INT8 激活)不存在与量化相关的成本(例如,激活范围校准)。
用于权重矩阵的分组量化
将 INT8 PTQ 应用于 BERT/GPT-3 模型会导致准确性显著下降。关键的挑战是 INT8 的表示无法完全捕获权重矩阵中不同行和不同激活Token的不同数值范围。解决这个问题的一种方法是对权重矩阵(激活)使用group-wise(token-wise)量化。
分组量化最先在Q-BERT中提出,其中权重矩阵被划分为 g 个组,每个组单独量化。然而,在Q-BERT中,作者仅将其应用于量化感知训练。更重要的是,他们没有考虑硬件效率约束,也没有系统后端支持,因此没有降低时延。
ZeroQuant 的设计中考虑了 GPU Ampere 架构(例如: A100)的硬件约束,其中计算单元基于 Warp Matrix Multiply and Accumulate (WMMA) 的分片(Tiling)大小,以实现最佳加速。与单矩阵量化相比,ZeroQuant 的分组量化由于其更细粒度的量化而具有更好的精度,同时极大的降低了延迟。
逐层知识蒸馏
知识蒸馏(KD)是缓解模型压缩后精度下降的最有力方法之一。然而,当应用到LLM上时,KD 存在一些局限性:
KD 需要在训练过程中将教师和学生模型放在一起,这大大增加了内存和计算成本;
KD 通常需要对学生模型进行充分训练。因此,需要在内存中存储权重参数的多个副本来更新模型;
KD 通常需要原始训练数据,有时由于隐私/机密问题而无法访问。
为了解决这些限制,ZeroQuant 提出了逐层蒸馏(LKD)算法缓解精度损失。该算法将原始模型作为教师模型,量化后的模型作为学生模型,通过逐层传递知识的方式,引导学生模型模仿教师模型的输出。这种方法不需要原始训练数据,且能够在不增加额外计算成本的情况下,有效提升量化模型的精度。
第二版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 模块量化,同时采用不同量化精度对各个模块进行量化组合,按需选取量化等级。
1.3 SmoothQuant
SmoothQuant由论文“SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models”给出,其量化位宽为 W8A8,即权重和激活都是用 8bit 量化。
SmoothQuant 的核心思路是,激活 X 之所以难以量化,是因为存在离群值拉伸了量化的线性映射范围,导致大部分数值的有效位数减少。而且,不同token在激活通道之间具有相似变化。因此,论文提出了一种按通道缩放的方法,在离线阶段引入平滑因子 s 来平滑激活中的异常值,并通过数学上的等效转换将量化的难度从激活迁移至权重 W 上(逐通道对权重乘上缩放因子,在激活上乘上缩放因子矩阵的逆,从而保持整体的输出不变,激活值整体变小,权重整体变大,激活值更好量化了),从而降低激活的量化难度。经过平滑处理的激活 X 和调整后的权重 W 均易于量化。
1.3.1 动机
现状
针对异常值,ZeroQuant 和 LLM.int8() 提出了各自的解决办法:
ZeroQuant 应用了动态逐 token 激活量化和分组权重量化来解决激活离群值问题。虽然该方法实现效率较高,但对于 175B 参数的 OPT 模型的准确性较差。
LLM.int8() 通过引入混合精度分解(即对异常值保持 FP16,其他激活使用 INT8)解决了该准确性问题,但是这种方法在工程上很难在 AI 加速器上高效实现,是一种硬件不友好的量化方案。
因此,需要寻找到一种高效、对硬件友好且无需训练的量化方案,使得 LLMs 中所有计算密集型操作均采用 INT8。
量化方案的问题
根据量化参数 s(数据量化的间隔)和 z(数据偏移的偏置)的共享范围,即量化粒度的不同,量化方法也可以分为逐层量化(per-tensor)、逐通道(per-channel)量化,逐token(per-token)量化和逐组量化(per-group,Group-wise)。SmoothQuant 作者对比了这几种量化方案,得出结论如下:
<ul>per-tensor量化是在硬件上最高效的实现方式。
per-channel量化保留了精度,但是它与 INT8 GEMM 内核不兼容(要求权重和激活都是INT8)。即per-channel量化不能很好地映射到硬件加速的GEMM内核(硬件不能高效执行,从而增加了计算时间)。在这些内核中,缩放只能沿着矩阵乘法的外部维度执行 (即激活的 token 维度、权重 \(
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
相关推荐