量化基础:从FP32到INT4,精度是如何一步步被“压缩”的?
聊到大模型推理,有个话题绕不开——量化。
说白了,就是把模型里那些又大又沉的FP32参数,换成更轻量的INT8、INT4。你想想看,一个70B的模型,FP32要280GB显存,换成INT4直接降到70GB。这诱惑力,谁能顶得住?
但问题来了:精度是怎么被“压缩”的?代价又是什么?
这一讲,我就带你从底层拆解这件事。
1. 浮点数的“奢侈”生活
先看FP32。一个32位浮点数,由1位符号位、8位指数位、23位尾数位组成。
它能表示的范围大约是 ±1.18×10⁻³⁸ 到 ±3.4×10³⁸。精度呢?大约7位有效数字。
这很奢侈。你想想,模型里几万亿个参数,每个都占4字节。我当年第一次跑LLaMA-65B时,光加载权重就花了260GB显存,差点把A100集群干趴下。
核心矛盾:FP32精度高,但存储和计算开销太大。推理时,我们真的需要那么高的精度吗?
2. 量化的本质:映射与截断
量化,本质上是一个映射函数:
Q(x) = round(x / scale) + zero_point
其中:
- scale:缩放因子,决定每个量化步长代表多少浮点值
- zero_point:零点偏移,让浮点0能精确映射到某个整数值
反量化就是逆过程:
x_hat = (Q(x) - zero_point) × scale
嗯,这里要注意:round操作会引入误差。这个误差,就是精度损失的根源。
举个例子。假设原始权重范围是[-1.0, 1.0],要量化到INT8(-128到127):
scale = (1.0 - (-1.0)) / (127 - (-128)) = 2.0 / 255 ≈ 0.00784
zero_point = round(-(-1.0) / 0.00784) + (-128) = round(127.55) - 128 = 0
你看,原本连续的浮点值,现在只能取256个离散值。每个值之间的差距约0.00784。这就是量化步长。
个人经验:我习惯先看权重的分布。如果分布很均匀,量化误差就小。如果分布有长尾,那就要小心了——那些离群点会被严重截断。
3. 从FP32到INT8:对称 vs 非对称
INT8量化有两种主流方式:
| 类型 | zero_point | 适用场景 | 精度损失 |
|---|---|---|---|
| 对称量化 | 固定为0 | 权重分布对称(如经过LayerNorm后的值) | 较小 |
| 非对称量化 | 可调 | 激活值分布不对称(如ReLU后的正值) | 更小 |
对称量化实现简单,但遇到ReLU后的激活值(全是正数)就浪费了一半的表示范围。非对称量化通过调整zero_point,能把整个量化范围“对准”数据的实际分布。
我在项目中遇到过一个问题:用对称量化量化BERT的激活值,精度掉了2个点。换成非对称量化后,只掉了0.3个点。差别就在这里。
4. INT4:更激进的压缩
INT4只有16个取值(-8到7)。相比INT8的256个,信息量少了16倍。
这带来的问题是:量化步长变大,误差急剧增加。
举个例子,同样范围[-1.0, 1.0]:
INT8步长:0.00784
INT4步长:2.0 / 15 ≈ 0.1333
INT4的步长是INT8的17倍。这意味着每个量化值代表的浮点范围更大,精度损失更严重。
避坑指南:我曾经在量化一个代码生成模型时,直接用了INT4,结果生成的代码全是语法错误。后来发现是注意力层的softmax输出被严重截断。解决方案:对敏感层保留INT8,其他层用INT4。这叫混合精度量化。
5. 量化误差的传播
量化误差不是孤立的。它会随着网络层逐层传播。
你想想看:第一层量化误差1%,经过100层后,误差可能累积到10%甚至更高。这就是为什么有些模型量化后直接“崩了”。
我总结了几种常见的误差来源:
- 截断误差:超出量化范围的值被强行截断
- 舍入误差:round操作导致的精度丢失
- 零点漂移:zero_point计算不准确导致的系统性偏差
怎么缓解?
- 使用校准数据集,统计真实的激活值分布
- 采用逐通道量化(per-channel),每个通道独立计算scale和zero_point
- 对离群点做特殊处理,比如用FP16存储那些极端值
6. 一张图看懂量化流程
下面这张SVG图,展示了从FP32到INT4的完整量化流程:
7. 实战建议:怎么选量化精度?
没有银弹。选哪种量化,取决于你的场景:
- INT8:大多数场景的首选。精度损失通常在1%以内,速度提升2-4倍。我建议从INT8开始尝试。
- INT4:适合对精度要求不高的场景,或者模型本身很大(如70B+)。精度损失可能到3-5%。
- 混合精度:敏感层(如注意力、输出层)用INT8,其他层用INT4。这是目前工业界的主流做法。
我的习惯:先跑一遍校准数据集,看看每层的激活值分布。如果某层的分布范围特别宽(比如超过±10),我会单独处理这层,或者保留FP16。
量化不是玄学,是工程。理解每一步的误差来源,你就能在精度和速度之间找到那个平衡点。
公众号:蓝海资料掘金营,微信deep3321