2w字解析量化技术,全网最全的大模型量化技术解析
📖阅读时长:50分钟
🕙发布时间:2025-01-31
这篇网页文章主要围绕机器学习中的量化技术展开,详细介绍了其在大语言模型中的应用、基础概念、技术方法及不同模型的量化策略。
量化是机器学习领域的一项关键优化技术,它能把高精度数值转化为低精度格式。在大型语言模型(LLM)的世界里,量化可以将32位浮点参数,巧妙地变成8位或4位整数这种更紧凑的形式,这可太厉害了,能让模型在资源有限的环境中轻松实现高效部署。
一、量化带来的诸多优势
- 降低内存占用:就像给模型做了一次“瘦身”,让它占用的内存空间大幅减少。
- 加快推理速度:模型推理时就像装上了“小马达”,速度更快了,能迅速给出结果。
- 减少能源消耗:变得更节能了,在运行时消耗的能源大大降低。
- 提升部署灵活性:不管是在什么样的设备上,都能更灵活地进行部署。
二、大语言模型的规模挑战
如今,现代大语言模型规模大得超乎想象!像Claude、Gemini、GPT和Llama系列这些模型,个个都包含了几十亿个参数。如此庞大的规模,对内存提出了超高要求。大部分硬件加速器,比如CPU、GPU和TPU,处理整数运算的速度比浮点运算更快,而且还更节能。要是把模型量化成8位、4位,甚至1位整数,推理速度会加快,耗电量也会降低,在移动设备和边缘设备上,响应速度更快,电池续航时间也更长。在计算中使用浮点数时,位宽(所用的位数)在平衡精度和内存使用方面起着关键作用。
(一)32位浮点(FP32):高精度的代表
32位浮点格式(也就是FP32),每个数字都用32位来表示。这种格式把这些位分成三个部分:符号位、指数位和尾数(小数部分)。FP32能提供高精度的数值表示,因为它分配了更多的位来精确捕捉数字的值。就拿圆周率π来说,FP32会把它存储为3.1415927410125732,和真实值非常接近。不过,每个数字都用32位来存储,这在处理大量数据时,内存占用就成了问题。
(二)16位浮点(FP16):在精度和内存间权衡
相比之下,16位浮点格式(FP16)就只用16位,这意味着尾数和指数位的位数都变少了。精度自然就降低了,还是以π为例,在FP16里它被存储为3.140625,虽然很接近,但确实没有FP32那么精确。不过,FP16的优势也很明显,它占用的内存只有FP32的一半。对于大规模数据集或模型来说,内存使用量能大幅减少,所以很适合那些硬件容量有限的设备,或者是对精度要求没那么高、更看重效率的应用场景。
虽说减少位大小有这些好处,可量化过程会引入近似误差,这就可能影响模型的准确性。最大的挑战就在于,要在享受减少内存占用和加快计算速度这些好处的同时,把误差降到最低。
三、量化的基础
(一)IEEE-754浮点标准
- 单精度(FP32)
- 符号位:1位
- 指数位:8位
- 尾数位:23位
- 总范围:±3.4×1⁰³⁸
- 半精度(FP16)
- 符号位:1位
- 指数位:5位
- 尾数位:10位
- 总范围:±65,504
- Bfloat16(BF16)
- 符号位:1位
- 指数位:8位
- 尾数位:7位
- 总范围:±3.4×10³⁸,和FP32差不多。
Bfloat16在训练和推理中经常用到,尤其是在像TPU这样的硬件中,它既有和FP32相似的大范围表示能力,又有FP16低内存占用的优点。
(二)整数格式
- INT8(8位整数)
- 总位数:8位
- 符号位(有符号INT8):1位
- 数值位(有符号INT8):7位
- 或者数值位(无符号INT8):8位
- 范围:
- 有符号:-128到 +127
- 无符号:0到255
- INT4(4位整数)
- 总位数:4位
- 符号位(有符号INT4):1位
- 数值位(有符号INT4):3位
- 或者数值位(无符号INT4):4位
- 范围:
- 有符号:-8到 +7
- 无符号:0到15
(三)量化过程
- 确定数值的动态范围:要搞清楚数据中数值的变化范围。
- 选择缩放因子(Δ):这个因子很关键,会影响量化的效果。
- 将浮点值映射为整数:按照规则把浮点形式的数字变成整数。
- 存储缩放因子用于反量化:方便之后把量化后的整数再变回浮点值。
(四)量化类型
- 对称量化:对称量化是用一个缩放因子把浮点值转化为整数的方法,量化后的值以零为中心对称分布。也就是说,量化值的范围在正数和负数之间是平衡的,从(-α)到(α)。
比如说,有这么一组FP32值:
4.72, 2.96, -6.48
0, -3.34, -5.26
8.58, 2.19, -3.67
计算缩放因子:在8位量化中,我们用的范围是 -127到127。根据最大绝对值来确定缩放因子。这里最大绝对值α = 8.58,量化位数(b)是8位,所以对称整数范围就是 -127到127。
量化数值:用缩放因子,把每个FP32值除以缩放因子再四舍五入取整,就得到量化后的INT8值。
70, 44, -96
0, -49, -78
127, 32, -54
反量化:要变回FP32值,就把每个量化后的整数乘以缩放因子。这个过程中会发现,有些值比如 -3.34和2.96,量化后再反量化回FP32格式时,会损失一些精度,和原来的值不完全一样。这个差值就是量化误差,通过计算原始FP32值和反量化值的差得到。一般来说,量化用的位数越少,量化误差就越大,因为位数少,能表示的值范围有限,近似的情况就更多,精度也就更低。
- 非对称量化:非对称量化不是以零为中心对称的。它把浮点范围的最小值(β)和最大值(α),映射到量化范围的最小值和最大值。还是用刚才那组FP32值来举例:
4.72, 2.96, -6.48
0, -3.34, -5.26
8.58, 2.19, -3.67
计算缩放因子和零点:先找出最小值β = -6.48和最大值α = 8.58,然后计算缩放因子和零点。
量化每个值:用缩放因子和零点,对每个值进行量化,得到下面这些量化后的INT8值:
190, 160, 0
110, 53, 20
255, 147, 47
反量化:要变回FP32值,就用相应的反量化公式计算。同样也可以计算非对称量化的误差,下面的表格能清晰对比两种量化方式。
(五)特定任务的量化建议
- 自然语言处理任务(NLP):训练时用BF16,推理时用INT8。
- 图像处理任务:训练时用FP16,推理时用INT8或INT4。
- Transformer模型:注意力层可以混合使用INT8和INT4。
(六)裁剪
在量化过程中,有一种常用方法是把向量值的范围映射到低比特表示。这样能以压缩格式捕捉向量所有的值,但遇到比其他数据大很多或者小很多的异常值时,就出问题了。想象有这么一个向量:[0.5, 1.2, -0.7, 2.0, 0.9, 10.5],这里10.5就是个异常值,比其他值大太多。要是把这个向量的整个范围映射到低比特格式(比如INT8),其他小的值最后可能都被压缩成相同或者相似的低比特表示,它们之间的差异信息就没了。这就是绝对最大(absmax)方法的特点,它是根据数据的最大绝对值来设置范围的。
1. 用裁剪处理异常值
为了减少异常值的影响,可以采用裁剪的方法,也就是定义一个自定义的动态范围,把极端值排除在外。比如说,手动把动态范围设为[-5, 5],那么任何超出这个范围的值都会被“裁剪”,不管它实际有多大,都被映射到目标范围的最大值或最小值。在这个例子里,10.5在INT8中会被映射为127,而在[-5, 5]范围内的值能保留更多精度和差异细节。裁剪的好处是,对于选定范围内(非异常值)的值,量化误差能大幅降低,不过异常值的量化误差会变高。在一些场景中,如果少数极端值会主导量化尺度,导致大部分数据精度损失,那这种技术就特别有用。
2. 校准
上面例子里,[-5, 5]这个动态范围是随便选的,只是为了展示裁剪。但实际上,选择一个最优范围的过程,也就是校准,对有效的量化至关重要。校准的目的是找到一个能包含尽可能多的值,同时又能最小化量化误差的范围。在量化机器学习模型时,这一点尤为重要,因为选了错误的范围会严重影响模型的准确性。
3. 不同类型参数的校准技术
不同类型的参数,校准要求也不一样:
- 权重(和偏差):在大语言模型(LLMs)里,权重和偏差可以看作是静态值,在模型部署前就确定了。权重是量化的主要关注点,因为它们占了模型大小的绝大部分(常常有几十亿个参数),而偏差数量相对较少,有时候会用更高的精度(比如INT16)来保存。权重的校准技术通常有:
- 基于百分位数的裁剪:设定一个范围,排除一定比例的极端值,重点关注数据点的中间百分位数范围。
- 均方误差(MSE)最小化:这种方法能最小化原始权重和量化后权重之间的均方误差,有助于保持模型的准确性。
- 最小化KL散度(熵):目的是最小化原始值和量化值之间的分布差异(KL散度)。
比如说,用基于百分位数的方法,会产生和前面讨论的裁剪类似的效果,把异常值排除掉,更好地表示数据的中心范围。
- 激活值:激活值就不一样了,它是动态变化的,会随着模型的每次输入而改变。激活值在每个隐藏层都要经过激活函数(比如sigmoid或ReLU),这就使得它们的值取决于当时正在处理的具体数据。这种可变性让应用单一的量化方案变得很困难,因为激活值会根据输入有很大的差异。激活值的校准一般有两种主要方法:
- 训练后量化(PTQ):在模型训练完成后进行量化,通常在一个有代表性的数据集上进行。
- 量化感知训练(QAT):在训练过程中就应用量化,让模型在量化值的约束下进行调整和学习,这样能提高模型的准确性。
四、量化技术
(一)训练后量化(PTQ)
训练后量化(PTQ)是一种应用非常广泛的技术,在模型训练完成后,用它来量化模型的参数(权重和激活值)。在PTQ中,权重和激活值可以调整为低精度,而且不需要额外的训练或微调。
- 权重的量化:权重可以用对称量化或者非对称量化。如果数据是以零为中心分布的,一般用对称量化;要是数据有偏差,非对称量化能更有效地表示不同的数据范围,就更合适。
- 激活值的量化:量化激活值比量化权重要复杂,因为激活值会随着输入和层的不同而变化。要想准确量化激活值,就得知道它们可能的范围,这只能通过让数据在模型中运行(推理)来确定。在PTQ中,激活值量化主要有两种方法:动态量化和静态量化。
- 动态量化
在动态量化过程中,当数据流经每个隐藏层时,会收集并分析激活值。具体过程如下:
- 激活值收集:数据通过隐藏层后,记录激活值的分布情况。
- 计算量化参数:根据这个分布,计算零点($z$)和缩放因子($s$),用这些参数把激活值映射到低精度格式。每次数据流经一个层,这个过程都会重复,也就是说每个层都有自己的$z$和$s$值,不同层的量化方案也不一样。
- 优势:动态量化更准确,因为$z$和$s$值是在推理时针对每个层确定的,能适应实际流经模型的数据。
- 缺点:不过,在每个层都重新计算这些值,会增加推理时的计算量,可能导致推理过程变慢。
下面这段代码展示了如何用PyTorch对预训练的ResNet18模型应用动态量化。动态量化只应用于特定的层(比如线性层),而且不需要校准数据集。
import torch
import torchvision
model = torchvision.models.resnet18(pretrained=True)
model.eval()
这段代码从torchvision
加载了一个预训练的ResNet18模型,并把它设置为评估模式(model.eval()
),这样能确保在推理时批归一化和随机失活层正常工作。
model_quantized = torch.quantization.quantize_dynamic(
model, {torch.nn.Linear}, dtype=torch.qint8
)
torch.quantization.quantize_dynamic
函数动态地对特定层进行量化,具体说明如下:
- 模型:
model
参数指定了要量化的模型。 - 要量化的层:
{torch.nn.Linear}
表示只对线性(全连接)层进行量化。动态量化通常应用于像线性层和LSTM层这样,大部分计算是矩阵乘法的层,因为这样可以减少内存占用,加快计算速度。 - 量化类型:
dtype=torch.qint8
指定在量化过程中,模型的参数会被转换为8位整数(INT8)。在动态量化中,权重在加载时就被量化为INT8,而激活值在推理时动态(实时)量化。这使得动态量化比全精度模型更快,占用内存更少,还不需要校准数据集。
torch.save(model_quantized.state_dict(), 'dynamic_quantized_model.pth')
最后,把量化后模型的状态字典保存到dynamic_quantized_model.pth
文件里,之后加载这个文件就能进行高效推理了。
- 静态量化
另一方面,静态量化在推理前就确定了$z$和$s$值。这涉及到:
- 校准数据集:用一个有代表性的数据集(校准数据集)在模型中运行,收集激活值的分布情况。
- 计算量化参数:根据这个分布,计算并存储$z$和$s$值。在推理时,这些值会在所有层和激活值中统一使用。
- 优势:静态量化通常更快,因为在推理时不用重新计算$z$和$s$值,依靠预先计算好的值,计算效率更高。
- 缺点:静态量化和动态量化比起来可能没那么准确,因为它在校准过程中只用了一组$z$和$s$值。如果在实际推理时激活值的分布变化很大,就可能导致更高的量化误差。
import torch
import torchvision
import torch.quantization as quant
model = torchvision.models.resnet18(pretrained=True)
model.eval()
这段代码加载了有预训练权重的ResNet18模型,并设置为评估模式(model.eval()
),这是推理和量化的必要步骤。
model.qconfig = torch.quantization.get_default_qconfig("fbgemm")
用torch.quantization.get_default_qconfig("fbgemm")
设置量化配置。FBGEMM后端针对x86 CPU进行了优化,通常使用8位整数(INT8)量化,能实现更快、更节省内存的推理。
model_prepared = torch.quantization.prepare(model, inplace=False)
prepare
函数会在模型的层中插入量化观察器,收集静态量化所需的激活统计信息。设置inplace=False
会创建模型的修改副本。
for _ in range(20):
input_tensor = torch.randn(1, 3, 224, 224)
model_prepared(input_tensor)
在校准过程中,样本数据(形状为(1, 3, 224, 224)
的随机张量,代表批量大小为1、分辨率为224x224的RGB图像)会通过模型。这些运行过程会收集激活统计信息,用于确定量化的缩放因子和零点。
输入形状说明:(1, 3, 224, 224)
是ResNet18期望的输入形状。在这个例子中,你可以自由调整批量大小。例如,将输入形状设置为(32, 3, 224, 224)
,将一次性处理32张图像,如果硬件允许,这可以提高推理时的吞吐量。不过,通道数(RGB图像为3)和分辨率(224x224)应保持不变,以与ResNet18兼容,因为它期望的图像具有这些维度。
model_quantized = torch.quantization.convert(model_prepared, inplace=False)
convert
函数应用收集到的量化参数(缩放因子和零点),创建量化版本的模型。这会用量化版本替换某些层,针对INT8操作进行优化。
torch.save(model_quantized.state_dict(), "quantized_model.pth")
最后,量化模型的状态字典(参数)被保存到文件中。之后加载这个文件,就能以量化形式进行高效推理。
4位量化
将模型精度降低到4位(甚至更低)一直颇具挑战,因为随着比特精度的每一次降低,量化误差都会增加。低比特量化虽然效率很高,但如果处理不当,可能会降低模型精度。不过,随着量化技术的进步,现在模型能够在保持性能的同时达到4位精度。
4位量化的方法
对于大型语言模型,4位量化通常采用两种主要方法:
- GPTQ:主要针对GPU进行优化,并使用加权量化误差校正。
- GGML和GGUF:这些技术旨在支持混合精度和CPU卸载,其中GGUF在GGML的基础上进行了改进,克服了其一些局限性。
- AWQ:专为高效4位量化而设计,采用激活感知方法,无需重新训练数据即可最小化精度损失,适用于在资源受限环境中的CPU和GPU上进行部署。
在实际场景中,这些方法适用于4位精度,但如果再降低精度(例如到3位或2位精度),可能会引入过多量化误差,在不显著降低模型精度的情况下很难维持其准确性。
GPTQ
GPTQ是在高性能模型中实现4位量化的常用选择。GPTQ采用非对称量化方法,一次对一层进行量化。通过独立处理每一层,GPTQ确保在进入下一层之前,每一层都能得到专门的处理。
以下是GPTQ处理量化的详细步骤:
- 独立层量化:GPTQ按顺序对每一层进行量化,为每一层应用独特的设置。首先使用逆海森矩阵对权重进行变换,这一过程可以表明每个权重对模型输出变化的敏感程度。
- 量化中的海森矩阵:在量化的背景下,尤其是对于神经网络,逆海森矩阵(模型损失函数的二阶导数)有助于我们了解模型输出对每个权重变化的敏感程度。逆海森值较低的权重更敏感,意味着这些权重的微小变化可能会对模型输出产生显著影响。在量化过程中,逆海森矩阵有助于对这些敏感权重进行优先级排序,以便更精细地分配精度,减少量化误差并保持模型精度。
- 基于逆海森敏感性的量化:计算完逆海森矩阵后,GPTQ对层内的每个权重进行量化,然后再反量化。这使得该方法能够计算量化误差($q$),捕捉将权重转换为低比特格式时损失的精度。
- 加权量化误差调整:GPTQ通过将量化误差与逆海森敏感性值($h$)进行权衡,进一步优化量化误差。这一调整过程会优先考虑对精度至关重要的权重,有效地生成加权量化误差,从而保留更重要的细节。其中,$q$是量化误差。
- 权重间的误差重新分配:GPTQ不会让量化误差孤立地存在于单个权重上,而是将误差在层内相关权重之间重新分配。这种方法确保即使在精度降低的情况下,模型的功能仍能保持一致。例如,权重$x_2$的误差为$0.3$,可以通过应用按$h_2$($x_2$的敏感性)缩放的$q$来平衡。
- 迭代优化:GPTQ对层内的每个权重迭代应用这一过程,使相关权重能够“分担”量化误差,直到所有值都以平衡精度和功能的方式完成量化。最终得到的层,其权重经过更新,能更好地适应4位量化的限制。
GPTQ敏感性感知方法的优势
GPTQ对权重敏感性的关注使其即使在较低精度下也能保持准确性。通过在相互关联的权重之间重新分配量化误差,它确保了模型性能的稳健性。这种方法特别有效,因为层内的权重通常具有复杂的关系,保留这些连接有助于保持模型的整体完整性。
GPTQ中的其他优化技巧
为了进一步提高4位量化的效率,GPTQ融入了一些专门的优化技术:
- 阻尼因子:降低逆海森矩阵中高敏感性的影响,防止某些权重变得过于不稳定。
- 延迟批处理:以计算高效的方式对更新进行分组,最小化处理开销。
- 乔列斯基分解:预先计算逆海森矩阵,以便更快地进行量化,利用数学分解技术实现更流畅的执行。
神经网络中的涌现特征
- 定义:涌现特征是在大型神经网络中观察到的一种现象,不同层在识别和标记数据中的特定特征时趋于一致。这种一致性表明数据的某些方面被有效地捕捉到了,它可以被视为网络所学“知识”的一种体现。
- 特征:
- 大权重:涌现特征通常表现为权重明显大于网络中平均权重。例如,如果网络中的平均权重约为$0.5$,涌现特征可能由$10.0$或$-8.5$这样的权重表示。这表明这些权重在模型的决策过程中起着至关重要的作用。
- 信息集中:这些大权重有助于网络处理和解释输入数据,有效地充当模型已学会识别的特征的指示符。
- 示例:考虑神经网络中的以下权重矩阵:
- 在这个矩阵中,权重$-10.0$、$8.5$和$-15.0$可以被视为涌现特征。它们的量级比矩阵中的其他权重明显更大。
- 这些权重对模型的输出至关重要,表明它们代表了模型进行预测所依赖的重要特征。
- 在量化中的重要性:在量化方面,涌现特征至关重要。代表涌现特征的权重通常保持较高精度,不进行量化。做出这一决策是为了保持模型的性能和准确性。通过以原始精度保留这些权重,网络可以保留其所学知识,并有效地响应输入数据,确保在量化过程中保留关键特征。
GGUF
GGUF是一种先进的量化方法,当GPU内存(VRAM)不足时,它能够将大型语言模型(LLM)的层卸载到CPU上。这种灵活性使模型能够高效利用CPU和GPU资源,便于在各种硬件配置上进行部署。
GGUF详细步骤解析
- 块结构:在GGUF中,每一层的权重被组织成层次化的块结构。
- 超级块:这些是较大的段,包含多个子块,为量化提供整体上下文和尺度。
- 子块:这些是超级块内的较小单元,其中的权重会被单独进行量化处理。
- 示例:假设有一个LLM层的权重如下。在这个例子中,我们可以定义一个包含三个子块的超级块。每个子块可能包含一行权重,权重划分如下:
- 超级块1:
- 子块1:[0.5, 0.3, -0.1]
- 子块2:[-0.7, 0.2, -0.6]
- 子块3:[0.8, 0.4, -0.1]
- 超级块1:
- 缩放因子提取:从这些块中提取两个关键组件:
- 缩放因子($s$):用于对子块中的权重进行量化的乘数。
- Alpha($\alpha$):表示每个子块内的最大绝对权重值。
- 示例:对于子块1,$\alpha = \max(|0.5|, |0.3|, |-0.1|) = 0.5$,子块的缩放因子$s$可以根据其值计算得出。
- 量化过程:为了量化一个“子”块,应用绝对最大(absmax)量化:量化后的权重 = $s$×权重。假设子块1的$s = 0.5$,每个权重的量化过程如下:
- 对于$0.5$:量化后的权重 = $0.5×0.5 = 0.25$
- 对于$0.3$:量化后的权重 = $0.5×0.3 = 0.15$
- 对于$-0.1$:量化后的权重 = $0.5× -0.1 = -0.05$
- 逐块量化:在量化过程中,超级块的缩放因子($s_{\text{super}}$)用于指导每个子块缩放因子($s_{\text{sub}}$)的量化。这种方法考虑到超级块提供的更广泛上下文,使得量化过程更加准确。例如,如果超级块的缩放因子$s_{\text{super}} = 1.0$,它可以用于调整子块的量化级别。
- 零点调整:在应用量化时,可能需要一个额外的最小值($m$)来调整量化的零点。这有助于保持模型输出的完整性,处理方式与缩放因子类似。
量化级别
GGUF具有通用性,能够适应各种量化级别(主要是4位)。每个级别可能涉及不同的量化策略,以平衡精度和资源效率。
GGUF是一种强大的量化方法,它使大型语言模型能够高效利用CPU和GPU资源。通过采用层次化块结构并利用缩放因子,GGUF即使在较低精度的约束下也能保持模型性能。这种在管理硬件资源方面的灵活性,使GGUF成为在不同环境中部署LLM的有吸引力的选择。
AWQ(激活感知权重量化)
AWQ是一种针对大型语言模型(LLMs)的训练后量化(PTQ)方法,专注于4位仅权重量化,旨在最小化推理成本的同时保持模型精度。与量化感知训练(QAT)方法不同,AWQ无需反向传播或重新训练,因此适用于大型模型的扩展。这种技术特别适合像边缘设备这样资源受限的硬件,通过有策略地保护对精度至关重要的权重来实现高效推理。
AWQ的关键组件
- 激活感知权重量化:AWQ识别并保护每一层中最重要的权重,这些权重对于维持模型性能至关重要。通过关注与高激活特征对应的权重,AWQ最小化了可能导致显著精度下降的量化误差。权重$w$的量化函数$Q(w)$定义为:
$$Q(w)=\text{clip}(\lfloor\frac{w}{\Delta}\rceil,\ -2^{N - 1},\ 2^{N - 1}-1)\times\Delta$$
其中,$\Delta$是由权重的最大绝对值确定的量化尺度,$N$是比特精度(例如,4位量化时$N = 4$)。 - 显著权重保护:AWQ根据激活幅度对1%最重要的权重进行选择性缩放,而不是对所有权重进行统一量化。这种策略通过防止高影响力权重的退化,保留了大型语言模型中的关键知识。与可能对校准集过拟合的GPTQ不同,AWQ的激活感知方法避免了过拟合,使其在不同任务和数据集上具有通用性。对于显著权重,AWQ使用缩放量化方法:
$$Q_{\text{salient}}(w)=s\times\text{clip}(\lfloor\frac{w}{\Delta}\rceil,\ -2^{N - 1},\ 2^{N - 1}-1)\times\Delta$$
其中$s>1$是用于保护关键权重的缩放因子,$x$代表与权重相关的激活。这通过对激活进行反向缩放来减少最重要权重的量化误差。 - 通过缩放最小化误差:AWQ按通道调整重要权重的缩放因子,在不使用额外训练数据的情况下降低量化误差。通过逐通道缩放,AWQ对量化过程进行微调,平衡显著权重和非显著权重之间的误差,以最小化总体损失。这种方法显著降低了量化误差并提高了性能,特别是在低比特设置下,如4位甚至3位组量化设置。缩放后显著权重的误差可以表示为:
$$\text{Error}{\text{scaled}}=\sum{i = 1}^{n}(w_i - Q_{\text{salient}}(w_i))^2$$
其中,显著权重的相对误差被最小化,使AWQ在保持关键模型精度方面更加稳健。 - 无数据优化:与一些可能对特定校准分布过拟合的PTQ方法不同,AWQ不需要大型校准集。通过仅分析每个通道的平均激活幅度,AWQ保持了稳定性,并能更好地泛化到不同的数据分布,确保LLMs在不同任务和领域中保持广泛的泛化能力。每一层的最优缩放因子可以通过优化得到:
$$L(s)$$
其中$L(s)$是表示量化误差的目标函数,逐通道缩放用于根据输入激活幅度调整权重。 - 层间灵活性:AWQ可以根据模型推理路径中不同层的相对重要性,对不同层应用不同的量化尺度。这种层间灵活性使AWQ能够避免在关键层进行过于激进的量化,确保即使在激进量化的情况下,整体精度也能得到保留。
- 部署优势:AWQ能够在CPU和GPU硬件上高效部署高性能的4位量化模型,降低内存和计算需求。对于资源受限的边缘和移动环境中的LLMs来说,AWQ是理想选择,因为它在性能与低内存和计算开销之间取得了平衡。AWQ的选择性权重缩放方法实现的效果可与混合精度技术(例如,将1%的权重保持为FP16)相媲美,但对于低功耗部署来说,它对硬件更加友好。
B. 量化感知训练(QAT)
量化感知训练(QAT)是一种在量化过程中用于提高神经网络准确性的技术。与训练后量化(PTQ)不同,PTQ是在模型训练完成后进行量化,而QAT则将量化直接融入训练过程中。这种整合使模型能够适应量化带来的变化,从而获得更好的性能。
QAT的工作原理
- 伪量化:在训练阶段,引入“伪”量化。这涉及将权重量化为较低精度(例如INT4),然后立即将它们反量化回浮点表示(例如FP32)。这使模型能够在仍以高精度进行训练的同时,模拟量化对权重的影响。
- 损失计算:模型在损失计算过程中使用这些量化后的权重,从而了解量化如何影响输出。通过这样做,模型调整其权重更新,不仅最小化标准损失,还最小化潜在的量化误差。
- 探索损失曲面:QAT旨在在损失曲面中找到“宽”的最小值。宽最小值表示更稳定的解决方案,因为权重的小变化不会显著影响输出。相比之下,窄最小值可能会导致更大的量化误差,因为权重的小变化可能会导致模型性能的大幅波动。
- 与PTQ的比较:虽然PTQ在高精度(如FP32)下可能会产生较低的损失,但QAT在较低精度格式(如INT4)下实现较低的损失。这对于将以较低比特精度部署的模型是有益的,确保它们在量化后仍能保持性能。
示例场景
假设有一个需要对图像进行分类的神经网络。在QAT过程中,如果模型学习到一个在FP32中能有效最小化损失,但在降低到INT4时会引入显著量化误差的权重,QAT会在整个训练过程中帮助调整该权重,以避免这个问题。这会产生一个即使在量化后也能有效运行的更新权重。
损失曲面:“宽”与“窄”最小值
损失曲面代表了模型权重与损失函数之间的关系,在量化中起着关键作用。
- 宽最小值:损失曲面中的这些区域具有较浅的斜率,这意味着权重值的小变化(如量化引入的变化)只会导致损失的最小增加。宽最小值对量化更具弹性,从而产生较低的量化误差。
- 窄最小值:这些区域具有陡峭的斜率,即使是微小的权重变化也会导致损失的显著增加。窄最小值对量化更敏感,会导致更高的量化误差。
量化感知训练(QAT)通过在反向传播过程中考虑量化效应,帮助模型找到这些宽最小值。相比之下,训练后量化(PTQ)在训练过程中不考虑量化,因此如果模型收敛在窄最小值处,可能会导致更高的量化误差。
BitNet:大语言模型1比特量化的时代
BitNet简介
超越常见的4比特量化,BitNet引入了极端量化,仅使用1比特值(即 -1和1)来表示权重。BitNet通过将量化直接集成到Transformer架构(大型语言模型(LLMs)的核心组件)中来实现这一点。这种方法显著降低了内存需求和计算开销,使得在资源受限的设备上大规模部署LLMs成为可能。
标准Transformer架构
大多数LLMs都构建在Transformer架构之上,该架构在计算中严重依赖线性层。这些线性层通常以更高的精度(例如FP16)表示,模型的大部分权重都存在于此。BitNet对此进行了修改,引入了BitLinear层,用1比特值取代了FP16权重。
BitLinear层
BitLinear层的操作与传统线性层类似,但权重仅用1比特(-1或1)表示,激活值用INT8表示 。BitLinear在训练期间使用一种“伪”量化来评估权重和激活量化的效果,类似于量化感知训练(QAT)。
BitLinear组件分步解析
- 权重量化:在训练过程中,权重存储为INT8值,然后使用符号函数将其量化为1比特:
这个函数将权重分布移动到以零为中心,将零左侧的值赋值为 -1,右侧的值赋值为1。另外还会跟踪一个值β(权重的平均绝对值),稍后用于反量化。
- 激活量化:BitLinear采用绝对最大(absmax)量化将FP16激活值转换为INT8,这确保了激活值在矩阵乘法中具有更高的精度。BitLinear还记录激活值的最高绝对值α,用于反量化步骤。
- 反量化:使用α和β,BitNet通过以下缩放因子将激活值反量化回FP16:
这一步将值重新调整回其原始比例,在保持计算效率的同时,实现更准确的激活值。
BitNet向1.58比特量化的转变
虽然最初的BitNet量化仅对权重使用 -1和1,但BitNet 1.58b版本引入了三进制表示:-1、0和1。这个小小的增加(允许权重也取0值)在计算效率方面带来了显著的改进。
1.58比特量化中添加零的好处
- 简化矩阵乘法:在典型的矩阵乘法中,每个权重都与一个输入相乘,然后将结果相加。在1.58比特量化中,矩阵乘法中的权重可以表示特定的操作:
- 1:加上相应的值
- 忽略该值
- -1:减去相应的值
通过消除不必要的乘法,三进制权重显著加快了计算速度,并实现了更高效的特征过滤。
- 量化策略:绝对均值量化:BitNet 1.58b使用绝对均值量化(absmax的一种变体),将权重分布压缩在绝对均值(α)附近,然后将权重量化为 -1、0或1:
与absmax不同,绝对均值量化确保在量化之前压缩权重分布,通过减少量化误差来提高性能。
关键要点
- 1比特量化:BitNet的原始方法将权重简化为 -1和1,在内存使用和计算能力方面实现了高效的LLMs。
- 带三进制权重的1.58比特量化:BitNet 1.58b中零的加入使计算速度更快,因为模型可以完全跳过某些操作。
- 缩放优势:BitNet的效率随着模型规模的增长而提高,较大的模型(例如,超过300亿个参数)在1比特和FP16量化之间的性能差距更小。
这种新颖的方法使BitNet能够提供轻量级、延迟优化的LLMs。据作者称,一个130亿参数的1.58比特精度的BitNet模型在延迟、内存和能耗方面,比一个小得多的30亿参数的FP16模型更高效。
本系列文章
1. LLM大模型架构专栏|| 从NLP基础谈起
2.LLM大模型架构专栏|| 自然语言处理(NLP)之建模
3. LLM大模型架构之词嵌入(Part1)
4. LLM大模型架构之词嵌入(Part2)
5. LLM大模型架构之词嵌入(Part3)
6. LLM架构从基础到精通之循环神经网络(RNN)
7. LLM架构从基础到精通之LSTM
8. LLM架构从基础到精通之门控循环单元(GRUs)
9. 20000字的注意力机制讲解,全网最全
10. 深入探究编码器 - 解码器架构:从RNN到Transformer的自然语言处理模型
11. 2w8000字深度解析从RNN到Transformer:构建NLP应用的架构演进之路
欢迎关注公众号 柏企阅文 如果您有任何问题或建议,欢迎在评论区留言交流!
评论