你有 16GB 显存,想跑一个 70B 参数的大模型。光权重 FP16 就有 140GB,根本塞不进去。有人告诉你:量化啊,把权重从 16 位砍到 4 位,体积缩了四倍。于是你试了 INT8,模型缩了一半,能跑了,精度还凑合。你贪心,换成 INT4,结果模型开始胡言乱语。你又听说有个叫 NF4 的东西,号称 4 位里精度最好。但你不明白:NF4 和 INT4 不都是 4 位吗?差别到底在哪?

这个问题我当初也卡了很久。直到我搞懂“线性度”这三个字,才豁然开朗。
量化,本质上是把连续分布的浮点数,映射到一组有限的离散值上。最常用的方式叫线性量化(也叫均匀量化)。假设你的权重分布在 [a, b] 区间,你想用 256 个整数来表示。那就把 [a, b] 均匀切成 256 段,每段宽度为 scale,每段对应一个整数。反算回来就是:
q = round((r - zero_point) / scale)
scale 是步长,zero_point 是偏移。这个方案最大的好处是简单:硬件可以直接用整数乘加,快得很。PyTorch 的量化文档里,INT8 量化就是按这个路子实现的。
但问题出在“均匀”这两个字上。大模型的权重分布从来不是均匀的,而是近似正态分布——大部分权重挤在 0 附近,少数权重散在两端。用均匀步长去切,就相当于给一条大街每隔一米设一个垃圾桶,但 90% 的垃圾都堆在市中心,而两端的垃圾桶基本是空的。8 位还好,256 个桶够多,浪费得起。到了 4 位,只有 16 个桶,大部分权重可能被挤进同一个桶,模型直接失忆。
这就是线性度的代价。所谓线性度,指的是量化映射是否为线性函数——均匀步长就是线性映射。线性映射好实现,但它不尊重数据分布。
那有没有办法让量化尊重分布?有,就是非线性量化。NF4 是其中最有名的一个。
NF4 的全称是 4-bit NormalFloat,出自 2023 年的 QLoRA 论文。它的核心思想很直观:既然权重像正态分布,那我就按正态分布的分位数来切区间。分位数是啥?就是把概率分布切成等面积区间的点。正态分布 0 附近面积大,所以切出来的区间窄;两边面积小,区间就宽。这样,0 附近的值获得更精细的表示,而尾部的大值只占少数几个区间。
论文里给了一个对比:NF4 在 4 位精度上,效果明显好于普通 INT4,接近 INT8。原因很简单:它把 16 个桶的资源集中用在了高频区域。
但这里有一个很容易被忽略的代价:NF4 量化出来的值不是整数,而是浮点数。比如它可能用 4 位索引对应一个数组 [-1.0, -0.7, -0.45, -0.2, 0.1, 0.3, ...]。你存储的时候存索引,但计算时得先查表反量化回浮点,再做矩阵乘法。也就是说,NF4 不能像 INT8/INT4 那样直接调用 GPU 上的整数张量核心。省了显存,却可能更慢。
我最早以为 NF4 只是把小数四舍五入到 4 个档位。后来看了 QLoRA 的论文才发现,它是在 16 个分位数上做文章。当时我心想:这不就是归一化后再查表吗?直到自己动手写代码,才发现难点在于如何把权重缩放到标准正态分布——QLoRA 里为此做了一个 absmax 归一化。
更让我意外的是,当我拿 NF4 去跑推理时,发现速度反而比 INT8 慢。那时我才意识到:量化方案的选择,必须结合硬件来考虑。NF4 的目标场景是微调——QLoRA 把基座模型冻结成 NF4 存进显存,省出空间给可训练的低秩适配器。它不是为了推理加速设计的。
| 方案 | 量化方式 | 线性度 | 精度表现 | 计算速度 | 典型用途 |
|---|---|---|---|---|---|
| INT8 | 均匀量化 | 线性 | 损失很小,几乎无感 | 快(硬件支持) | 推理加速、显存减半 |
| INT4 | 均匀量化 | 线性 | 损失大,尤其小模型 | 较快(需特殊处理) | 极限压缩(如GPTQ) |
| NF4 | 分位数量化 | 非线性 | 4bit中最好,接近INT8 | 慢(需反量化) | 大模型微调(QLoRA) |
看到这里你可能觉得 INT8 是最优解。但 INT8 也有自己的坑——不是权重,而是激活值。大模型的激活值在个别维度上会出现巨大的离群点,比如几百,而其他值在个位数。均匀量化为了覆盖离群点,步长被迫拉大,普通值的精度就遭殃了。LLM.int8() 的解决思路很有意思:把离群点所在的维度单独拆出来,用 FP16 计算,其余维度走 INT8。这就是混合量化。所以你在用一些库时,会看到“8-bit + outlier”这样的说法。
那 INT4 是不是完全不能用?也不尽然。GPTQ 是一种针对权重的二阶近似量化方法,它通过最小化量化误差的二次项来逐个调整权重,让 4 位均匀量化也能达到可用水平。GPTQ 还是线性量化,但它在数学上做了补偿。所以在实际部署中,GPTQ 的 INT4 常常比朴素的 INT4 强很多。
这就引出一个微妙的问题:NF4 和 GPTQ 谁更好?如果你只看精度,NF4 通常更好,因为它更贴近分布。但 GPTQ 是线性量化,计算时可以直接整数运算,推理速度更快。QLoRA 的论文里有一张表,显示 NF4 在微调场景下优于 GPTQ 的 4 位版本,但那是微调,不是推理。
为什么 NF4 比 INT4 精度好这么多?
因为 INT4 用均匀步长,把大量离散级别浪费在尾部低概率区域。NF4 用分位数,让每个级别覆盖概率相近的区间,相当于把信息量最大化。
NF4 能直接加速推理吗?
不能。NF4 只是存储格式,计算前必须反量化回浮点。所以它省显存、省带宽,但不省算力。要加速推理,INT8/INT4 结合算子融合更可靠。
量化后模型一定会变笨吗?
不一定。如果量化方案匹配分布,且位宽足够(如8位),损失可能只有 1% 以下。但 4 位是极限压缩,对分布不敏感的方案会损失明显。
所以回到最初的问题:INT8、INT4、NF4 怎么选?我的判断是——
如果你在做推理,且显存不是极端紧张,INT8 是最稳的答案,精度高、速度快。如果必须压到 4 位,GPTQ 比朴素 INT4 好,但 NF4 通常能保留更多精度,代价是速度。如果你在做大模型微调,QLoRA 的 NF4 是目前少有的能让你在单卡上跑 65B 的方案。
技术永远在边界上跳舞。NF4 证明了 4 位可以接近 8 位的精度,但它靠的是对分布的先验假设。当模型权重分布与正态分布偏差过大时,NF4 的优势也会减弱。未来硬件如果原生支持非线性查表运算,也许会出现更快的 NF4 实现。但在那一天到来之前,线性度与精度的取舍,依然是每个工程师必须做的功课。
原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/216.html