2. 量化基础:模型量化的基本原理(FP16、INT8、NF4),量化对模型精度的影响
聊QLoRA之前,咱们得先把量化这事儿掰扯清楚。
说实话,我第一次接触量化时,觉得这玩意儿挺玄乎的。把好好的32位浮点数硬塞进8位甚至4位的盒子里,精度不掉才怪呢。但后来我发现,模型参数里其实藏着大量冗余信息。就像一张高清照片,你把它压缩成JPEG,肉眼看着差别不大,但文件体积小了好几倍。量化干的,差不多就是这事儿。
2.1 为什么需要量化?
大模型动辄几十上百GB,你想想看,一张A100才80GB显存。要是跑个70B的模型,光加载参数就得140GB,根本塞不进去。量化就是来解决这个矛盾的。
我习惯把量化比作「降维打击」——把参数的表示精度降下来,换来更小的内存占用和更快的计算速度。说白了,就是用一点点精度损失,换取巨大的工程收益。
- 模型体积缩小4倍(FP32→INT8)
- 显存占用大幅降低
- 推理速度提升2-4倍
- 可以在消费级显卡上跑大模型
2.2 常见的量化精度格式
咱们从高精度往低精度捋一遍。我当年踩过的坑,也一并说给你听。
FP16(半精度浮点数)
FP16是16位浮点数,1位符号位,5位指数位,10位尾数位。它比FP32省了一半空间,但表示范围小了很多。FP32能表示到3.4e38,FP16只能到6.5e4。什么意思呢?就是数值太大或太小,FP16就扛不住了,直接变成Inf或0。
我在项目中遇到过,训练时loss突然变成NaN,查了半天才发现是梯度溢出了。后来加了梯度裁剪,问题才解决。所以用FP16时,一定要做梯度缩放,这是血的教训。
INT8(8位整数)
INT8只有256个取值(-128到127)。把FP32的数值映射到这么窄的范围,必然会有精度损失。但好处是,INT8的矩阵运算在GPU上有专门的Tensor Core加速,速度能翻倍。
INT8量化的关键是「校准」——找到合适的缩放因子,把浮点数映射到整数范围。常用的方法有:
- 对称量化:以0为中心,正负对称映射
- 非对称量化:根据实际数据分布,动态调整映射范围
我个人习惯用非对称量化,因为它能更好地适应权重分布不均匀的情况。比如有些层的权重集中在0附近,有些则分散得很开,非对称量化能分别处理,精度损失更小。
NF4(4位NormalFloat)
NF4是QLoRA论文里提出的新格式。它可不是随便把4位塞满就完事了。NF4的设计思路很巧妙——它假设模型权重服从正态分布,然后在这个假设下,把4位(16个取值)按照正态分布的分位数来分配。
你想想看,如果权重集中在0附近,那0附近的量化区间就应该更密,远处的可以稀疏一些。NF4就是这么干的。它把16个取值点,按照正态分布的概率密度函数,等概率地分布在数轴上。
2.3 量化对模型精度的影响
量化一定会损失精度,这是物理定律决定的。但损失多少,取决于几个因素:
- 量化位宽:位宽越低,精度损失越大。FP16几乎无损,INT8损失可控,NF4损失明显但可接受。
- 模型大小:模型越大,冗余越多,量化后精度损失越小。70B的模型量化到4位,可能只掉1-2个点;7B的模型量化到4位,可能掉5-10个点。
- 任务类型:生成式任务对量化更敏感,分类任务相对鲁棒。我做过对比,同一个模型量化后,在文本生成任务上掉点明显,在情感分类上几乎没变化。
2.4 量化误差的来源
量化误差主要来自两个方面:
- 舍入误差:把连续值映射到离散值时,必然有舍入。比如0.3映射到0.25,误差0.05。
- 截断误差:超出表示范围的值被截断。比如INT8最大127,128就被截成127。
这两种误差在模型的前向传播中会逐层累积。尤其是深层网络,误差会像滚雪球一样越滚越大。所以量化时,输入层和输出层通常保留高精度,中间层才做低精度量化。这是业界的通用做法。
2.5 量化感知训练(QAT) vs 后训练量化(PTQ)
这两种方法,我简单说说区别:
| 方法 | 原理 | 精度 | 成本 |
|---|---|---|---|
| PTQ | 训练完成后直接量化 | 较低,但速度快 | 低,只需少量校准数据 |
| QAT | 在训练过程中模拟量化 | 较高,接近原始精度 | 高,需要重新训练 |
QLoRA用的是PTQ的思路,但它不是简单的PTQ。它把量化后的权重冻结,只训练低秩适配器。这样既享受了量化的内存优势,又通过微调弥补了精度损失。嗯,这个设计确实很巧妙。
2.6 本章知识体系
下面这张图,帮你理清量化的核心逻辑:
这张图把量化的三个核心维度串起来了。左边是「用什么格式量化」,中间是「量化后精度会怎样」,右边是「怎么量化」。QLoRA选的是NF4格式,用PTQ的思路,再通过低秩适配器微调来弥补精度损失。这个组合拳,就是QLoRA的精髓。