大模型基础之量化
概述
量化,Quantization,机器学习和深度学习领域是一种用于降低计算复杂度、减少内存占用、加速推理的优化方法。定义:将模型中的数据从高精度表示转换为低精度表示。主要目的是为了减少模型的存储需求和计算复杂度,同时尽量减少精度损失。具体来说:
- 减少存储需求:大模型通常包含数亿甚至数十亿个参数,占用大量的存储空间;
- 提高计算效率:量化后的模型在推理时的计算量更少,能够加快推理速度;
- 降低功耗:量化后的模型需要的计算资源更少,从而降低能耗。尤其是在移动设备和嵌入式系统中,可延长设备的使用时间,减少散热需求;
- 减少带宽需求:量化后的模型可以更快地完成传输,提高数据传输效率。
量化对象:模型的权重和激活。
方便理解的几个示意图
INT是Integer缩写,即整数类型。
FP是Floating Point缩写,浮点数类型,用于表示带有小数部分的数。由三部分组成:符号位(表示正负)、指数位和尾数位(决定浮点数精度)。
常见的数据类型,包括:
- FP32:全精度,标准精度。一种高精度的浮点数,1个符号位、8个指数位和23个尾数位,总共32位。可表示非常大和非常小的数,缺点是占用的存储空间大,计算速度慢;大部分硬件都支持FP32运算,所以在训练和推理中广泛使用。
- FP16:一种低精度的浮点数,1个符号位、5个指数位和10个尾数位,总共16位。精度比FP32低,占用存储空间少一半,计算速度也更快;容易出现上溢和下溢问题,可通过一些技巧来缓解这些问题。
- BF16:Bfloat16,另一种16位浮点数,指数位数和FP32相同,动态范围较大,但精度比FP16低。在处理大数值时表现更好,但在精度上会有所损失。
- FP8:1个符号位、3(或4)个指数位和4(或3)个尾数位,总共8位。主要用于深度学习中的高效计算。
- INT8:8位整数,只能表示从-128到127的整数。存储空间更小,计算速度也更快,但精度更低。常用于模型量化。比如图像处理中的像素值,通常在0到255之间,用INT8就可以很好地表示。
- INT4:4位整数,表示的范围更小,从-8到7。可用于对精度要求不是特别高的场景,如某些简单的神经网络层。
公式
量化公式如下: x q = r o u n d ( x f ∗ s c a l e + z ) x_q=round(x_f * scale+z) xq=round(xf∗scale+z)
其中:
- x f x_f xf是原始的浮点数;
- x q x_q xq是量化后的低精度浮点数;
- z z z是零点;
- s c a l e scale scale是缩放因子,用于将浮点数映射到低精度范围;
- r o u n d round round表示四舍五入到最近的值。
计算缩放因子前,需要找到权重或激活的最小值(min)和最大值(max)。 s c a l e = ( m a x − m i n ) / ( 2 n − 1 ) scale=(max-min)/(2^n-1) scale=(max−min)/(2n−1), z = r o u n d ( − m i n / s c a l e ) z=round(-min/scale) z=round(−min/scale),确保浮点数0映射到整数0,避免负数溢出。
有量化公式,自然还有反量化公式: x f = ( x q − z ) / s c a l e x_f=(x_q-z)/scale xf=(xq−z)/scale
应用
包括:
- 模型训练加速:在模型训练过程中,使用低精度数据类型如 FP16 或 FP8 可以显著加快训练速度。例如,NVIDIA 的 Hopper 架构 GPU 支持 FP8 精度的 Tensor Core 计算,与传统的 FP32 训练相比,FP8 训练速度可以提升 2-3 倍。这种加速对于大规模模型的训练尤为重要,因为它可以大幅减少训练时间和计算资源的消耗。
以大模型训练为例,Inflection AI 的 Inflection2 模型采用了 FP8 混合精度在 5000 个 NVIDIA Hopper 架构 GPU 上进行训练,累计浮点运算次数高达约 1025 FLOPs。与同属训练计算类别的 Google 旗舰模型 PaLM 2 相比,在多项标准人工智能性能基准测试中,Inflection-2 展现出了卓越的性能。 - 模型推理优化
在模型推理阶段,量化可以显著降低模型的存储需求和计算复杂度。例如,将 FP32 模型量化为 INT8,存储需求可以减少 75%,推理速度可以提升数倍。这对于在边缘设备或移动设备上部署模型尤为重要,因为这些设备的计算资源和存储空间通常有限。
例如,Google 与 NVIDIA 团队合作,将 TensorRT-LLM 应用于 Gemma 模型,并结合 FP8 技术进行了推理加速。使用 Hopper GPU 进行推理时,FP8 对比 FP16 在吞吐量上能够带来 3 倍以上的收益。 - 模型压缩与部署
量化还可以用于模型的压缩和部署。通过将高精度模型量化为低精度模型,可以减少模型的大小,从而更容易将模型部署到资源受限的环境中。例如,零一万物基于 NVIDIA 软硬结合的技术栈,完成了在大模型的 FP8 训练和验证。其大模型的训练吞吐相对 BF16 得到了 1.3 倍的性能提升。
量化后的模型不仅在存储和计算上具有优势,还可以通过特定的硬件加速来进一步提升性能。例如,NVIDIA 的 Transformer Engine 已经集成到 PyTorch、JAX、PaddlePaddle 等基础深度学习框架中,为量化模型的推理提供了高效的硬件支持。
优势与局限
优势
- 计算效率提升:低精度量化可以显著加快计算速度,减少计算资源的消耗。如,FP16和FP8的计算吞吐量比FP32高出数倍。这种加速效果在大规模模型训练和推理中尤为明显。
- 存储需求降低:量化可以大幅减少模型的存储需求。如将FP32模型量化为INT8,存储需求可减少75%。这对于在资源受限的环境中部署模型非常有帮助。
- 功耗降低:低精度计算需要的计算资源更少,从而降低了能耗。在移动设备和嵌入式系统中,功耗是一个重要考虑因素。量化后的模型可以延长设备的使用时间,同时减少散热需求。
- 模型优化:量化促使模型在训练和推理过程中进行优化和压缩,进一步降低部署成本。例如,FP8 的使用可以促使模型在训练过程中进行更细致的量化,从而提高模型的效率。
局限性
- 精度损失:量化会带来一定的精度损失,尤其是在低精度格式下。例如,FP8 的精度比 FP16 和 FP32 低,可能会在某些任务中导致性能下降。虽然通过合理选择缩放因子可以在一定程度上减少精度损失,但完全避免精度损失是不可能的。
- 硬件支持有限:并非所有硬件都支持低精度计算。如FP8和FP16的计算需要特定的硬件支持,如NVIDIA的Hopper架构GPU。如果硬件不支持低精度计算,量化的优势将无法充分发挥。
- 复杂性增加:量化过程本身会增加模型的复杂性。例如,在量化过程中需要计算缩放因子,并且需要对模型进行校准和调整。这可能会增加模型开发和部署的难度。
- 适用场景有限:量化并不适用于所有场景。例如,在需要高精度计算的任务中,如某些科学计算或金融建模任务,量化可能会导致不可接受的精度损失。
未来趋势
未来,模型量化将在精度优化、硬件适配、动态适应性以及与其他技术的结合等方面取得更大的突破。
- 精度优化
- 混合精度量化:结合浮点型和整型运算的优点,对模型的不同部分使用不同的量化精度。这种方法可以在保证推理速度的同时减少精度损失,提高量化模型的性能。例如,对于一些对精度要求较高的层,可以使用较高的量化精度,而对于一些对精度要求较低的层,可以使用较低的量化精度。这种灵活的量化方式能够更好地平衡精度和效率。
- 量化算法的改进:以更好地处理数据分布不均匀的情况。例如,通过引入更复杂的映射函数或优化算法,可以进一步提高量化精度。此外,研究人员还将探索新的量化方法,如非线性量化,以适应不同类型的数据分布。
- 硬件适配
- 专用硬件的优化:随着硬件技术的发展,专用硬件加速器将更好地支持模型量化。例如,未来的 GPU 和 FPGA 将提供更高效的量化操作支持,减少量化过程中的计算开销。这将使得量化模型在硬件上的运行更加高效,进一步提高推理速度。
- 跨平台适配:未来,量化算法将更加注重跨平台适配。这意味着同一个量化模型可以在不同的硬件平台上高效运行,无需针对每个平台进行单独的优化。例如,通过开发通用的量化框架和接口,可以实现量化模型在移动设备、嵌入式系统和数据中心等不同场景下的无缝部署。
- 动态适应性
- 动态量化技术的改进:以更好地适应输入数据的变化。例如,通过引入更高效的量化参数计算方法,可以减少动态量化过程中的计算开销。此外,研究人员还将探索新的动态量化策略,如自适应量化,以进一步提高量化精度。
- 动态模型的支持:例如,对于动态神经网络,量化算法将能够根据模型结构的动态变化实时调整量化参数。这将使得量化模型在处理动态任务时能够更好地保持精度和效率。
- 与其他技术的结合
- 与模型剪枝的结合:例如,通过先对模型进行剪枝,去除冗余的参数和连接,然后再进行量化,可以进一步减少模型的存储空间和计算复杂度。这种组合优化方法能够更好地提高模型的部署效率。
- 与知识蒸馏的结合:例如,通过将量化模型作为学生模型,将原始浮点模型作为教师模型,进行知识蒸馏,可以进一步提高量化模型的精度。这种结合方式能够更好地平衡量化模型的精度和效率。
算法
分类
按阶段可分为:PTQ和QAT
PTQ:训练后量化(Post-Training Quantization)
- 定义:在模型训练完成后,直接对模型的权重和激活值进行量化。不需要对原始训练过程进行修改,简单易行,适合在实际生产环境中快速部署量化模型。
- 优点:过程简单,不需要额外的训练资源,对部署人员的技术要求较低。例如,对于一些已经训练好的大型模型,如ResNet50,使用PTQ可以在短时间内完成量化,使其更适合在移动设备上运行。
- 缺点:由于没有在训练阶段对量化误差进行建模,量化后的模型精度可能会有一定的损失。可通过一些优化手段,如校准(calibration)和量化策略的选择,在一定程度上缓解精度下降的问题。
QAT:量化感知训练(Quantization Aware Training)
- 定义:在模型训练过程中引入量化操作,使模型在训练阶段就对量化误差进行学习和适应。通过模拟量化过程,让模型在训练时就考虑到量化带来的影响,从而在量化后能够更好地保持精度。
- 优点:通常能够获得较低的精度损失,适合对模型精度要求较高的场景。例如,在一些需要高精度的图像识别任务中,使用 QAT 可以在量化后仍然保持较高的准确率。
- 缺点:需要对训练过程进行修改,增加训练的复杂性和计算成本。此外,QAT 的实现相对复杂,对研究人员的技术水平要求较高。
QAT:
按量化方式可分为:对称量化(Symmetric Quantization)和非对称量化(Asymmetric Quantization)
对称量化
- 定义:对称量化是将浮点数的最大绝对值映射到量化后的最大值,将最大绝对值的负值映射到量化后的最小值。例如,对于 int8 量化,将浮点数的最大绝对值映射到 127,其负值映射到 -128。
- 优点:计算简单,易于实现。在数据分布较为均匀的情况下,对称量化能够较好地利用量化范围,保持数据的精度。例如,当模型的权重或激活值在零点附近均匀分布时,对称量化的效果较好。
- 缺点:对于数据分布不均匀的情况,对称量化可能无法充分利用量化范围,导致精度损失较大。例如,当数据主要集中在正数或负数区域时,对称量化可能会浪费一部分量化范围,影响量化效果。
非对称量化
- 定义:非对称量化通过收缩因子(scale)和零点(zero point)将浮点数的最小值和最大值分别映射到量化后的最小值和最大值。例如,对于 int8 量化,将浮点数的最小值映射到 -128,最大值映射到 127。
- 优点:能够更好地处理数据分布不均匀的情况,适用于各种数据分布形态。例如,当数据主要集中在正数区域时,非对称量化可以通过调整零点和收缩因子,将量化范围更多地分配给正数区域,从而更好地保留数据信息。
- 缺点:计算相对复杂,需要引入零点的概念。在某些情况下,非对称量化可能会引入额外的计算开销,尤其是在硬件实现时。例如,一些专用的硬件加速器可能对非对称量化支持不够完善,导致性能下降。
PyTorch
作为主流的深度学习框架,PyTorch提供完善的支持:
Dynamic PTQ:
import torch
from torch.quantization import quantize_dynamic
# 加载预训练模型
model = torch.load('model.pth')
model.eval()
# 动态量化(量化Linear和LSTM层)
quantized_model = quantize_dynamic(model, {torch.nn.Linear, torch.nn.LSTM}, # 指定量化层类型dtype=torch.qint8
)
Static PTQ:
from torch.quantization import prepare, convert
# 准备校准数据集
def calibrate(model, data_loader):model.eval()with torch.no_grad():for inputs in data_loader:model(inputs)
# 配置量化参数
model.qconfig = torch.quantization.get_default_qconfig('fbgemm')
model_prepared = prepare(model) # 插入Observer节点
calibrate(model_prepared, data_loader) # 校准激活值范围
quantized_model = convert(model_prepared) # 转换为量化模型
QAT:
from torch.quantization import prepare_qat, FakeQuantize
# 定义QAT模型
model.qconfig = torch.quantization.get_default_qat_qconfig('fbgemm')
model_prepared = prepare_qat(model) # 插入伪量化节点
# 训练阶段(模拟量化误差)
optimizer = torch.optim.SGD(model_prepared.parameters(), lr=0.01)
for inputs, labels in train_loader:outputs = model_prepared(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()
# 转换至最终量化模型
quantized_model = convert(model_prepared)
算法简介
对称量化算法
一种简单且广泛应用的量化方法,其核心思想是将浮点数的最大绝对值映射到量化后的最大值,将最大绝对值的负值映射到量化后的最小值。优点:计算简单,易于实现,且在数据分布较为均匀的情况下能够较好地保持数据精度。例如,当模型的权重或激活值在零点附近均匀分布时,对称量化的效果较好。
公式:
x i n t = r o u n d ( x / s ) x_{int}=round(x/s) xint=round(x/s)
x Q = c l a m p ( − N l e v e l / 2 , N l e v e l / 2 − 1 , x i n t ) x_Q=clamp(-N_{level}/2,N_{level}/2-1,x_{int}) xQ=clamp(−Nlevel/2,Nlevel/2−1,xint)(有符号)
x Q = c l a m p ( 0 , N l e v e l − 1 , x i n t ) x_Q=clamp(0,N_{level}-1,x_{int}) xQ=clamp(0,Nlevel−1,xint)(无符号)
其中, x x x是原始浮点数, s s s是缩放因子, N l e v e l N_{level} Nlevel是量化后的数值范围。
缺点:对于数据分布不均匀的情况,例如数据主要集中在正数或负数区域,对称量化可能会浪费一部分量化范围,导致精度损失较大。例如,当数据主要集中在正数区域时,对称量化可能会将一部分量化范围分配给负数区域,而这些负数区域的数据实际上很少出现,从而影响量化效果。
非对称量化算法
通过收缩因子和零点将浮点数的最小值和最大值分别映射到量化后的最小值和最大值。例如,在int8量化中,浮点数的最小值被映射到 -128,最大值被映射到 127。
公式:
x i n t = r o u n d ( x / s ) + z x_{int}=round(x/s)+z xint=round(x/s)+z
x Q = c l a m p ( 0 , N l e v e l − 1 , x i n t ) x_Q=clamp(0,N_{level}-1,x_{int}) xQ=clamp(0,Nlevel−1,xint)
其中, x x x是原始浮点数, s s s是缩放因子, z z z是零点, N l e v e l N_{level} Nlevel是量化后的数值范围。
优点是能够更好地处理数据分布不均匀的情况,适用于各种数据分布形态。例如,当数据主要集中在正数区域时,非对称量化可以通过调整零点和收缩因子,将量化范围更多地分配给正数区域,从而更好地保留数据信息。缺点:计算相对复杂,需要引入零点的概念。在某些情况下,非对称量化可能会引入额外的计算开销,尤其是在硬件实现时。例如,一些专用的硬件加速器可能对非对称量化支持不够完善,导致性能下降。
动态量化算法
一种在推理过程中动态调整量化参数的方法。与静态量化不同,动态量化不需要在量化前对整个数据集进行校准,而是根据输入数据的实时分布动态调整量化范围。
公式:
x i n t = r o u n d ( x / s ) x_{int}=round(x/s) xint=round(x/s)
x Q = c l a m p ( − N l e v e l / 2 , N l e v e l / 2 − 1 , x i n t ) x_Q=clamp(-N_{level}/2,N_{level}/2-1,x_{int}) xQ=clamp(−Nlevel/2,Nlevel/2−1,xint)(有符号)
x Q = c l a m p ( 0 , N l e v e l − 1 , x i n t ) x_Q=clamp(0,N_{level}-1,x_{int}) xQ=clamp(0,Nlevel−1,xint)(无符号)
其中, x x x是原始浮点数, s s s是动态计算的缩放因子, N l e v e l N_{level} Nlevel是量化后的数值范围。
优点是能够根据输入数据的实时分布动态调整量化范围,从而更好地适应输入数据的变化,提高量化精度。例如,在处理不同批次的输入数据时,动态量化可以根据每个批次的数据分布动态调整量化参数,避免因数据分布差异导致的精度损失。缺点:由于需要在推理过程中动态计算量化参数,这会增加计算开销,尤其是在硬件实现时。动态量化可能会导致量化后的模型在不同输入数据下表现出不同的精度,这需要在实际应用中进行仔细的测试和调整。
静态量化算法
一种在量化前对整个数据集进行校准的方法。通过校准过程,可确定最优的量化参数,从而在推理过程中保持固定的量化范围。
公式:
x i n t = r o u n d ( x / s ) x_{int}=round(x/s) xint=round(x/s)
x Q = c l a m p ( − N l e v e l / 2 , N l e v e l / 2 − 1 , x i n t ) x_Q=clamp(-N_{level}/2,N_{level}/2-1,x_{int}) xQ=clamp(−Nlevel/2,Nlevel/2−1,xint)(有符号)
x Q = c l a m p ( 0 , N l e v e l − 1 , x i n t ) x_Q=clamp(0,N_{level}-1,x_{int}) xQ=clamp(0,Nlevel−1,xint)(无符号)
其中, x x x是原始浮点数, s s s是通过校准过程确定的缩放因子, N l e v e l N_{level} Nlevel是量化后的数值范围。
优点是量化参数固定,计算简单,适合在硬件上实现。例如,通过校准过程确定的量化参数可以在推理过程中保持不变,从而减少计算开销,提高推理速度。缺点:由于量化参数在量化前已经确定,无法根据输入数据的变化动态调整,这可能会导致在某些情况下精度损失较大。例如,当输入数据的分布与校准数据的分布差异较大时,静态量化可能会导致量化后的模型精度下降。
零点量化
Zero-Point Quantization,通过线性映射,将原始数据的最小值和最大值对应到目标数据类型的最小值和最大值。将 FP16 数值转换为 Int8 时,就会用到这种量化方法。下图展示了这一过程。
绝对最大值量化
Max Absolute Quantization,absmax,对称量化方法,将原始数据中的最大绝对值映射到目标数据类型的有符号范围。通过找到数据中正负范围内的绝对最大值,然后使用这个最大值来确定量化的缩放因子。
GPTQ
Group-wise Precision Tuning Quantization,分组精确调整量化,首次成功地将BLOOM或OPT-175B等175B规模的大模型量化为4-bit。
GPTQ采用逐行量化(per-row quantization),即独立地量化权重矩阵的每一行,以找到最优的量化权重,从而最小化量化误差。为了实现高精度量化,GPTQ 利用 Hessian 矩阵计算剩余权重的最优更新,从而减少量化带来的信息损失。需要注意的是,GPTQ 保留嵌入层(embedding layer)和输出层(output layer)为 FP16 以维持模型精度。
AWQ
Activation-aware Weight Quantization,激活感知权重量化,
选择
选择哪种量化算法,需要综合考虑精度、推理速度、存储空间等多方面因素。