量化技术详解
量化原理
模型量化是将浮点型参数转换为更低精度的定点型参数,以减少模型的存储和计算复杂度。
核心思想:将连续的浮点值映射到离散的低精度整数值区间,用更少的比特数存储每个参数,从而减少内存占用和加速计算。
主要收益:
- 减少模型存储空间:FP16→INT8减少一半,→INT4减少1/4
- 减少内存/显存占用:可以加载更大模型,支持更高并发
- 加速推理:低精度计算更快,尤其是支持整数计算单元如Tensor Core
- 降低功耗:更少的数据传输和计算,能耗更低
**代价:**量化会引入一定精度损失,需要在精度和效率之间权衡。
浮点类型基础
FP32(32位单精度浮点数)
- 1位符号位,8位指数,23位尾数
- 占4字节,高精度
- 训练时通常参数、梯度、优化器状态用FP32
FP16(16位半精度浮点数)
- 1位符号位,5位指数,10位尾数
- 占2字节
- 表示范围:6.10×10−5 ~ 6.55×104
- 混合精度训练中模型参数用FP16,节省一半内存
BF16(bfloat16)
- 1位符号位,8位指数,7位尾数
- 指数位数和FP32一样,表示范围相同
- 大模型训练常用,比FP16数值更稳定
TF32(TensorFloat-32)
- 1位符号位,8位指数,10位尾数 → 共19位
- NVIDIA Ampere架构后引入,在Tensor Core中使用
- 兼顾范围和精度,比FP16更稳定
INT8 / INT4
- INT8:8位整数,范围 -128 ~ 127,占1字节
- INT4:4位整数,范围 -8 ~ 7,占0.5字节
量化分类
按量化时机分类
静态量化:
- 在推理前对所有权重进行量化,离线完成
- 不需要校准数据
- 推理时直接使用量化后的权重
- 实现简单,速度快
动态量化:
- 激活值在线量化,权重离线量化
- 根据实际输入动态量化激活
- 精度更高,但推理时额外有量化开销
按量化粒度分类
逐张量量化:
- 整个张量用一组缩放因子和零点
- 计算最简单,但粒度太粗,误差大
逐通道量化:
- 每个输出通道一组缩放因子和零点
- 粒度细,误差小,计算量增加不多
- 目前主流方法
逐token量化:
- 每个token一组参数
- 粒度最细,误差最小,开销最大
主流量化方法
INT8 量化(LLM.int8())
bitsandbytes提出的LLM.int8()方法:
- 向量级量化:每个向量按绝对值动态量化
- 混合精度分解:将异常值(outlier)用FP16存储,其余用INT8
- 大部分权重用INT8计算,保持精度,同时节省内存
- 可以直接在消费级显卡上推理大模型
实现要点:
- 发现注意力输出中存在少数outlier,占比不到1%但贡献了大部分幅值
- 将outlier单独分离出来用FP16计算,其余用INT8
- 精度几乎无损,显存减半
GPTQ
- 基于训练后量化(PTQ)的方法
- 逐行量化权重,最小化重构误差
- 利用目标函数:min∣∣WX−W^X∣∣22
- 通过顺序量化和启发式搜索快速找到最优量化点
- 支持INT4量化,精度下降很小
- 实际推理速度快,广泛使用
AWQ(Activation-aware Weight Quantization)
- 激活感知的权重量化
- 观察到大部分激活分布不均匀,少数权重对激活输出贡献大
- 对重要权重保留更高精度,对不重要权重可以更 aggressive 压缩
- 比GPTQ精度更好,尤其是在低比特量化下
- 不需要回溯,更快,更适合大规模模型
GGUF / GGML
- llama.cpp项目使用的格式
- 支持多种量化级别:2-bit, 3-bit, 4-bit, 5-bit, 8-bit
- 设计适合CPU推理,优化内存对齐
- 文件格式友好,容易加载
- 非常流行,使得大模型可以在普通笔记本甚至手机上运行
NF4(Normalized Float 4-bit)
- bitsandbytes提出的针对神经网络权重分布优化的4-bit格式
- 权重通常是近似正态分布,NF4按照正态分布分位点量化
- 比均匀量化的INT4精度更高
- 是QLoRA的基础技术,支持4-bit基础模型上做LoRA微调
bitsandbytes 使用
bitsandbytes是自定义CUDA函数的轻量级包装器,特别提供8比特优化器、矩阵乘法和量化功能。
主要特性:
- 支持混合精度分解的8比特矩阵乘法
- LLM.int8()推理
- 8比特优化器(Adam、AdamW、RMSProp等),节省75%内存
- 支持4-bit量化:FP4、NF4
- 快速分位数估计,比其他算法快100倍
使用示例:NF4量化加载模型
1from transformers import BitsAndBytesConfig, AutoModelForCausalLM
2
3nf4_config = BitsAndBytesConfig(
4 load_in_4bit=True,
5 bnb_4bit_quant_type="nf4",
6 bnb_4bit_use_double_quant=True,
7 bnb_4bit_compute_dtype=torch.bfloat16
8)
9
10model = AutoModelForCausalLM.from_pretrained(
11 model_id,
12 quantization_config=nf4_config
13)
使用示例:FP4量化加载模型
1from transformers import BitsAndBytesConfig
2
3quantization_config = BitsAndBytesConfig(
4 load_in_4bit=True,
5 bnb_4bit_compute_dtype=torch.bfloat16
6)
只要模型包含torch.nn.Linear层,就可以开箱即用,支持任何模态(LLM、ViT、Whisper、Blip2等)。
量化对模型性能影响
| 量化方法 | 显存占用 | 推理速度 | 模型精度 | 适用场景 |
|---|
| FP16 | 100% | 基线 | 最佳 | 训练、追求最高质量 |
| FP16 + BF16 | ~100% | 稍快 | 最佳 | 大模型训练 |
| INT8 | 50% | 快一些 | 几乎无损 | 部署,显存紧张 |
| INT4 (AWQ/GPTQ) | 25% | 更快 | 轻微下降,可接受 | 消费级显卡部署 |
| INT4-GGUF | 25% | CPU友好 | 可接受 | CPU端部署 |
经验结论:
- INT8量化在现代大模型上几乎不会造成可感知的精度损失
- INT4量化通常只有很小的perplexity上升,生成质量依然可用
- 量化对困惑度(perplexity)影响小,对生成人类评估的影响更小
- 4-bit量化+LoRA微调(QLoRA)可以达到全微调精度的99%以上
模型压缩和加速其他方法
量化只是模型压缩加速的一种方法,其他常见方法:
| 方法 | 原理 | 效果 |
|---|
| 剪枝 | 删除冗余参数/神经元,减少参数量 | 减少模型大小,加速推理 |
| 知识蒸馏 | 用大模型(教师)指导小模型(学生)训练 | 小模型获得接近大模型的性能 |
| 低秩分解 | 将大权重矩阵分解为两个小矩阵乘积 | 减少参数量和计算量 |
| 结构剪枝 | 层次化剪枝,整层整通道剪枝 | 大幅减少计算量 |
面试常见问题
Q1: 什么是量化?为什么要量化?
A: 量化是将高精度浮点参数转换为低精度整数(或更少比特)表示的技术。主要目的是减少模型存储和显存占用,加速推理,让大模型能在有限硬件上部署。INT8可节省一半显存,INT4可节省3/4。
Q2: INT8量化原理是什么?LLM.int8()有什么特别之处?
A: INT8就是用8位整数表示原本FP16的参数,通过缩放因子将浮点范围映射到-128~127。LLM.int8()特别之处在于发现了注意力输出中存在少量outlier,采用混合精度分解:outlier用FP16,其余用INT8,做到精度几乎无损。
Q3: GPTQ和AWQ有什么区别?
A: GPTQ是逐行量化,最小化重构误差;AWQ是激活感知,根据激活幅度给不同权重不同的量化优先级,重要权重保留更高精度。AWQ通常比GPTQ精度更好,尤其是在低比特下。
Q4: NF4和INT4有什么区别?
A: INT4是均匀量化,将整个范围均匀分成16份;NF4是针对权重是正态分布这个特点,按照正态分布的分位点进行非均匀量化,更符合权重分布,所以精度比INT4更高,是QLoRA使用的技术。
Q5: 量化一定会降低模型精度吗?有没有办法避免?
A: 量化理论上会引入误差,但INT8量化在大模型上通常精度损失可以忽略,几乎不可感知。通过更好的量化算法(如AWQ、GPTQ)可以把精度损失控制在极小范围,4-bit量化都能获得可接受的结果。
Q6: 什么时候不适合量化?
A: 如果有足够大显存和计算资源,不需要量化,全精度能获得最好效果。对精度要求极高的场景(如医疗、金融关键决策),谨慎使用低比特量化。训练阶段一般不需要对参数量化(除了QLoRA这种特殊情况)。
Q7: bitsandbytes主要做什么?
A: bitsandbytes是一个提供自定义CUDA操作的库,主要支持8-bit量化、8-bit优化器、4-bit量化(FP4/NF4),让大模型可以在消费级显卡上推理和微调。