量化简介
量化是将模型浮点数变为定点数运行的过程。通过一个原始float数值range(scale、min、max),将类似实属域的float数值映射到一个网格比较稀疏的int网络上,中间肯定会产生数值的偏移。
基本概念 :模型量化可以减少模型尺寸,进而减少在推理时的内存消耗,并且在一些低精度运算较快的处理器上可以增加推理速度。
常见方法:工业界目前最常用的量化位数是8比特,低于8比特的量化被称为低比特量化。1比特是模型压缩的极限,可以将模型压缩为1/32。
精度类型
低精度浮点数:在PyTorch中用torch.bfloat16和torch.float16表示。
双精度浮点数:在PyTorch中用torch.float64表示,或者在其他语言中也称为double类型,在LLM训练中一般比较少用
全精度浮点数:在PyTorch中用torch.float32表示
精度类型注意点
- bfloat16的小数部分较短,整数部分较长,这会有利于在训练中减少梯度爆炸的情况(即梯度累加值超过了最大值),但是这种数据类型是在N系列显卡Ampere系列才支持的,即30系列显卡。
- float16的小数部分较长,这意味着在精度控制上float16更好,但整数部分较短,比较容易梯度爆炸。
量化基本思路
量化的基本原理是根据每个tensor的浮点型最大值和最小值,将其映射为一个固定范围的整形数值集合,比如[-127~127]。
量化的精度损失
在后续计算或反量化为浮点型时存在无法完全还原的情况,这就是精度损失。
量化部分
按照量化发生的步骤区分
可以划分为PTQ(训练后量化,或离线量化)和QAT(训练感知型量化,或在线量化)。
按照量化方法可以划分
可以划分为线性量化、非线性量化(如对数量化)等多种方式,目前较为常用的是线性量化。其中线性量化又可以按照对称性划分为对称量化和非对称量化,非对称量化为了解决weight分布不均匀问题,其在公式中增加了zero_point项。
按照量化粒度划分
可以分为逐层量化(每层使用一套量化因子)、逐组量化(在每层中按照group使用一套量化因子)、逐通道量化(按channel划分量化因子)等几种方式。
按照量化最大值的阈值区分
可以分为饱和量化和不饱和量化两种。不饱和量化按照浮点数最大值和量化后最大值的比例计算量化因子,由于原始weight的非均匀性会导致某些整形数值范围存在权重空缺。饱和量化会计算一个中间值以计算出量化因子,因此会舍弃一部分不重要数据,将重要数据尽量均匀的分布到量化数值范围内。
混合精度训练
混合精度训练是一种深度学习的优化技术,旨在加速训练过程并减少内存使用,特别是在处理大规模神经网络模型时。它通过在训练中同时使用两种浮点数据类型——半精度(FP16)和单精度(FP32)。
具体策略:
-
权重的双精度备份:保持权重的FP32副本,用FP16进行前向传播,然后在反向传播时将梯度从FP16转换回FP32进行更新,以避免精度大幅下降。
-
损失缩放(Loss Scaling):放大损失值并以FP16存储,以解决小数值在FP16中表示的问题,之后在更新权重前进行反向缩放,减少梯度消失
方法适合大规模模型和大批次训练,但在小批次训练中可能不会提升速度,甚至可能因为额外的类型转换而变慢,因为小批次的训练瓶颈通常是I/O而非计算。
AutoAWQ
AWQ简介
AWQ是一个“激活感知权重量化(Activation-aware Weight Quantization)”的方法。主要思想就是权重对于LLM的性能不是一样重要,有多有少。存在约(0.1%-1%)salient(显著的)权重对大模型性能影响太大,通过跳过这1%的显著权重(salient weight)不进行量化,减少量化误差。
思想在于对模型进行INT3量化,但是保留一定权重通道的比例为FP16(大部分大语言模型的精度都在FP16)。混合精度数据类型会给系统实现带来困难(硬件效率低下)。因此,我们需要想出一种方法来保护重要的权重,而不将其实际保留为 FP16。但要找到显著的权重通道,我们应该根据激活分布而不是权重分布。与较大激活幅度(activation magnitudes)相对应的权重通道更加突出,因为它们处理了更重要的特征。
AWQ思路与总体流程
量化的针对元素分为:per-tensor、per-token、per-channel
实验首先通过四个对比试验:
①权重全部进行INT3量化,也就是RTN(round-to-nearest quantization)量化,直接四舍五入量化。 PPL=43.2
②对应更大的activation值的0.1%~3%比例weight channels不量化√效果最佳 PPL=13.0
③0.1%~3%比例最大的weight channels不量化
④0.1%~3%比例随机weight channels不量化
实验证明了表明越大量级的 input features 越重要,保留其对应的 FP16 权重能保存这些特征,从而保证更好的模型精度。
其中确定权重重要性的方法采用的是查看其大小或者L2范数。
注释:
①PPL:困惑度,衡量语言模型好坏的指标。根据每个词来估计一句话出现的概率,并用句子长度做为normalize。本质上就是计算句子概率,对于句子S(词语w的序列):
如果词库里有10个词,一个没有经过任何调整的模型针对于每个词的生成概率就是,那么PPL(困惑度)就是10。如果模型针对一个单词生成的概率为,那么模型的PPL(困惑度)就是2。
混合精度问题引入
如果使用混合精度进行处理,那么salient权重还是FP16,其余的non-salient为INT4,使用FP16进行训练时,梯度更容易溢出或不足,导致优化器计算不精确
解决方法
在salient权重所在的通道(chanel)上,乘以一个系数S(S>1,Activation-aware Scaling),将与此系数相乘之后的salient权重再与其他权重进行共同量化,就避免了混合精度导致的硬件局限性。
在计算时,首先将激活值对每一列求绝对值的平均值,再将平均值较大的一列对应的通道(channels)作为salient权重通道,并保留FP16精度。
salient权重的确定
通过统计激活值来确定salient权重,将大激活值对应的权重定义为salient权重,可以实现较好的量化性能。
主要研究问题:权重及激活的分布
缩放系数S(Scaling)的确定
round()表示对浮点数进行四舍五入。
首先定义线性操作函数为
W是FP16保存的权重,x是校准集的输入特征,也就是上一层的activation
那么量化后的公式就为,其中具体解释如下:
量化函数定义(不可导,无法使用反向传播方法)
公式中,w任意一组权重,N是量化的bits数,是量化缩放系数。
设,再乘以设置的缩放系数s>1,那么公式变化为:
那么如何找到,使用L2范数
L2范数
L2范数,也就是欧几里得范数,是向量元素的平方和的平方根。对于一个n维向量x,L2范数表示为:
那么却别在于L2范数计算了每个元素的平方和,所以,L2范数更偏重于大元素的影响。
一个二维向量x=[3,-4]
L2范数:||x||₂ = √(3² + (-4)²) = √(9 + 16) = √25 = 5
其中,是应用了s之后的量化缩放系数。
经过对比:
①误差大致均匀分布在0-0.5,平均误差为0.25
②放大某个单一的权重值不会影响该组权重的极大值,因此可以认为
③新误差
④那么将两者的误差继续对比,因为,且s>1,那么对比与那是的误差,新误差会进一步下降。
不同不是s越大越好,因为增加了非显著通道权重的量化误差就会增大。
最优scaling
目的是同时考虑显著权重和非显著权重,通过最小化某层量化前后的差值来寻找最优scaling。
最直接的方法是通过使用反向梯度传播进行求解,arg min这个符号表示选取范围中最小的值。
diag表示的是一个对角矩阵
s是per-channel scaling因子
Q是权重量化函数
W是初始FP16精度的权重
X是一个输入为了小型的自校正数据集的输入特征来自预训练数据集为了防止对特定的任务过拟合
是一个可以被融合至之前操作的算子
scaling 范围
因为Q不可微,通过梯度下降算法找到最优解,所以通过统计per-channel的最小激活值作为通道的缩放系数。
就是per-channel的平均激活幅值,则是超参数,用来平衡salient与non-salient的权重。
如何找到最优,通过fast grid search(网格搜索):在[0,1]区间平均取20个数,0, 0.05, 0.10, 0.15 …… 然后逐个计算不同下的MSE损失,损失最小的就是最优的。得到最优后,最小Scaling系数S也就确定。
注意:grid search根本是一个枚举法,我们增加的维度越多,搜索就会变得越困难。也就是在每个维度,定义可能的取值范围,搜索所有可能的配置,等待结果以建立最佳配置。
weight clipping用来减少新的误差,从而减少量化误差,进而减少MSE误差。
梯度裁剪
分组量化(grouped quantization)
分组量化(grouped quantization)针对的部分是某个层里的每一个权重(w)分组之后的部分做量化,属于同一个组的使用同样的,INT3-g12就是一组为128个权重,128个权重根据公式计算出一个局部的,组内的权重都会乘以此,之后就会得到,之后对所有组都进行相同的步骤,那么所有层都会完成量化。
ONNX
Tensortrt
FlashAttention
ExLlamav2