2、模型量化入门:FP16/INT8/INT4量化原理、量化对显存的影响、量化工具(bitsandbytes、GPTQ)
2.1 量化到底在做什么?
很多人一听到「量化」就觉得高深莫测。其实说白了,就是给模型权重「瘦身」。
我打个比方。你有一张高清照片,原始文件几十MB。但发朋友圈时,系统会自动压缩成几百KB。你看上去差别不大,对吧?量化干的也是类似的事。
模型训练时,参数通常用FP32(32位浮点数)存储。每个数占4字节。推理时,我们其实不需要这么高的精度。换成FP16(2字节),甚至INT8(1字节)、INT4(0.5字节),模型照样能工作。
为什么会这样?因为神经网络对噪声有天然的鲁棒性。权重稍微变一点,输出结果不会崩。我在项目中遇到过,一个70B的模型用INT4量化后,回答质量几乎没下降,但显存占用直接砍到原来的四分之一。
核心结论:量化就是用更少的比特数表示模型参数,以精度换显存和速度。
2.2 FP16/INT8/INT4 量化原理
2.2.1 FP16 量化
FP16是最温和的量化方式。它把32位浮点数砍成16位。指数位从8位减到5位,尾数位从23位减到10位。
嗯,这里要注意。FP16的数值范围比FP32小很多。FP32能表示约3.4e38,FP16只能到6.5e4。如果模型权重分布太散,直接转FP16可能会溢出。
我个人习惯的做法是:先检查权重的最大值和最小值。如果最大值超过6.5e4,或者最小值小于-6.5e4,那就得做一下缩放。不过大多数预训练模型都没这个问题,放心转就行。
2.2.2 INT8 量化
INT8就更有意思了。它只有256个离散值(-128到127)。要把连续的浮点数映射到这256个格子里,需要做两件事:
- 确定缩放因子:找到权重的最小值和最大值,算出缩放比例
- 四舍五入:把浮点数映射到最近的整数格点上
举个例子。假设权重范围是[-1.0, 1.0],要映射到[-128, 127]。缩放因子就是127 / 1.0 = 127。权重0.5就变成0.5 * 127 ≈ 64。
你想想看,这个过程肯定有精度损失。但好消息是,模型通常能扛得住。我在项目中试过,INT8量化后的模型,准确率下降通常不超过1%。
我的经验:INT8量化时,最好用「逐通道量化」而不是「逐张量量化」。每个通道单独算缩放因子,精度更高。虽然计算量大了点,但效果值得。
2.2.3 INT4 量化
INT4是最激进的。它只有16个值(-8到7)。说实话,我第一次用INT4时心里也没底。但试过之后发现,大模型(尤其是70B以上的)对INT4的容忍度出奇地高。
INT4量化的难点在于:4比特没法直接表示浮点数。所以通常的做法是分组量化。比如每32个权重为一组,共享一个缩放因子。这样每个权重只用4比特,但组内精度还能保持。
我曾经踩过一个坑:用INT4量化小模型(比如1.5B的)。结果模型直接「失智」了,输出全是乱码。后来才明白,小模型参数少,冗余度低,扛不住这么大的精度损失。大模型反而更适合INT4。
避坑指南:我曾经在7B模型上直接套INT4量化,结果推理时频繁出现NaN。排查了半天,发现是某些层的激活值范围太大,超出了INT4的表示范围。解决方案是加一个「动态裁剪」步骤,把异常值先截断再量化。
2.3 量化对显存的影响
量化最直接的好处就是省显存。咱们算笔账:
| 精度 | 每参数比特数 | 70B模型显存占用 | 相对FP32节省 |
|---|---|---|---|
| FP32 | 32 | ~280GB | 0% |
| FP16 | 16 | ~140GB | 50% |
| INT8 | 8 | ~70GB | 75% |
| INT4 | 4 | ~35GB | 87.5% |
看到没?INT4量化后,70B模型只要35GB显存。一张A100(80GB)就能跑起来。这在以前想都不敢想。
但要注意,显存节省不是免费的。量化后的模型,推理速度通常会变快(因为数据量小了,内存带宽压力小),但精度会下降。我个人的经验是:
- FP16:几乎无损,推荐所有场景使用
- INT8:轻微损失,适合大多数生产环境
- INT4:有明显损失,但大模型可用,适合显存紧张的场景
2.4 量化工具:bitsandbytes
bitsandbytes是我用得最多的量化库。它由Tim Dettmers开发,专门为PyTorch设计。安装很简单:
pip install bitsandbytes
用法也直接。加载模型时指定量化类型就行:
import torch
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
# 配置INT4量化
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4", # 使用NF4量化类型
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True # 双重量化,进一步省显存
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-70b-hf",
quantization_config=bnb_config,
device_map="auto"
)
这里有个细节。bnb_4bit_quant_type有两个选项:fp4和nf4。nf4是bitsandbytes自创的量化格式,专门针对正态分布的权重做了优化。我实测下来,nf4比fp4精度高0.5-1%。
小技巧:开启bnb_4bit_use_double_quant=True,可以再省约0.4比特/参数。虽然不多,但积少成多。70B模型能省下约3.5GB显存。
2.5 量化工具:GPTQ
GPTQ是另一种主流量化方法。它和bitsandbytes的思路不太一样。
bitsandbytes是「训练后量化」(PTQ),直接对权重做映射。GPTQ也是PTQ,但它多了一步:用一小批校准数据来优化量化过程。
说白了,GPTQ会观察模型在真实数据上的表现,然后调整量化策略,让精度损失最小化。我试过,GPTQ量化的模型,在相同比特数下,精度通常比bitsandbytes高0.3-0.5%。
使用GPTQ需要安装auto-gptq库:
pip install auto-gptq
用法示例:
from auto_gptq import AutoGPTQForCausalLM
from transformers import AutoTokenizer
model = AutoGPTQForCausalLM.from_quantized(
"TheBloke/Llama-2-70B-GPTQ",
use_safetensors=True,
device="cuda:0",
use_triton=False
)
tokenizer = AutoTokenizer.from_pretrained("TheBloke/Llama-2-70B-GPTQ")
嗯,这里要注意。GPTQ量化后的模型,推理速度比bitsandbytes稍慢一点。因为GPTQ的量化格式更复杂,反量化计算量更大。但精度确实更好。
我个人习惯是:
- 追求速度:用bitsandbytes
- 追求精度:用GPTQ
- 显存极度紧张:bitsandbytes + INT4 + 双重量化
2.6 本章知识体系
下面这张图总结了量化的核心逻辑:
量化不是银弹。它是在显存、速度、精度三者之间找平衡。我建议你从FP16开始,如果显存不够再降INT8,最后才考虑INT4。每一步都要实测,别光看理论。
好了,这一章就到这里。量化入门其实不难,关键是动手试。拿个7B模型跑一遍,你就能感受到量化的威力了。
一句话总结:量化是突破显存瓶颈最直接的手段。FP16无脑用,INT8放心用,INT4谨慎用。
想要更多资料,联系可可