2. 量化原理精讲:浮点数与定点数、对称量化与非对称量化、量化粒度
好,咱们直接进入正题。量化这东西,说白了就是给模型“减肥”。但减肥不能减到残废,对吧?你得知道哪些肉能减,哪些骨头不能动。这一章,我就带你把这套“减肥”的底层逻辑彻底搞明白。
2.1 浮点数 vs 定点数:模型世界的两种“语言”
模型里存的是什么?是权重和激活值。这些值最初都是浮点数,也就是带小数点的数。比如 0.1234567,或者 -3.1415926。浮点数精度高,但占地方大,计算也慢。
那定点数呢?你可以把它想象成“整数+缩放”。比如 12345,我告诉你这个数要除以 1000,那它实际代表 12.345。定点数就是这种思路——用整数来模拟小数。
我刚开始做量化的时候,总觉得浮点数转定点数就是“四舍五入”那么简单。后来踩了个大坑——有个模型量化后直接崩了,查了半天才发现是定点数的精度不够,导致梯度爆炸。嗯,从那以后我再也不敢小看这个转换过程了。
核心区别一句话:
- 浮点数:动态范围大,精度高,但存储和计算开销大。
- 定点数:动态范围固定,精度有限,但存储小、计算快。
你想想看,大模型动辄几十亿参数,每个参数都存成 float32,那得多少显存?所以量化就是要把这些浮点数“翻译”成定点数,用更少的比特去表示。
2.2 对称量化 vs 非对称量化:两种“翻译”策略
好,现在我们知道要把浮点数转成定点数了。但怎么转?这里有两种主流策略:对称量化和非对称量化。
2.2.1 对称量化
对称量化,顾名思义,就是量化后的整数范围关于 0 对称。比如 int8 的范围是 [-128, 127],那对称量化就是把浮点数的最大值和最小值映射到 [-127, 127](通常不用 -128,留一个给特殊值)。
公式很简单:
scale = max(|x_max|, |x_min|) / 127
x_quant = round(x / scale)
我个人习惯在权重量化时优先用对称量化。为什么?因为权重的分布通常比较对称,而且对称量化在推理时计算更简单——不需要处理零点偏移。
我的经验:
我在项目中遇到过,有些模型的权重分布严重不对称(比如全是正数)。这时候用对称量化,有一半的量化范围就浪费了。说白了,就是精度会下降。所以对称量化不是万能的。
2.2.2 非对称量化
非对称量化就灵活多了。它允许量化后的整数范围不关于 0 对称,通过引入一个“零点”(zero point)来偏移。
公式:
scale = (x_max - x_min) / (q_max - q_min)
zero_point = round(q_min - x_min / scale)
x_quant = round(x / scale) + zero_point
非对称量化特别适合激活值。为什么?因为激活值经过 ReLU 后全是非负数,分布天然不对称。用非对称量化,就能把整个量化范围都利用起来。
避坑指南:
我曾经在量化一个 NLP 模型时,对激活值用了对称量化,结果精度掉了 5 个点。后来换成非对称量化,精度只掉了 0.5 个点。差别就这么大。所以,权重用对称,激活用非对称,这是一个很实用的经验法则。
2.3 量化粒度:per-tensor vs per-channel
量化粒度,说白了就是“量化参数(scale 和 zero point)共享的范围有多大”。
2.3.1 Per-tensor 量化
整个张量(比如一个权重矩阵)只用一个 scale 和一个 zero point。这是最简单的量化方式,计算量最小。
但问题也很明显:如果张量内部数值分布不均匀,那量化精度就会很差。比如一个矩阵里,大部分值在 0.1 左右,但有几个异常值是 10.0。那 scale 就会被这几个异常值拉大,导致大部分值的量化精度严重下降。
2.3.2 Per-channel 量化
每个通道(channel)单独计算 scale 和 zero point。比如一个卷积层的权重形状是 [64, 3, 3, 3],那 per-channel 量化就会对 64 个输出通道分别计算量化参数。
这样做的好处很明显:每个通道都能“量身定制”自己的量化范围,精度损失更小。代价就是需要存储更多的量化参数,计算也稍微复杂一点。
我的建议:
在 GGUF 量化中,我通常这样选:
- 权重:用 per-channel 量化。权重的分布在不同通道间差异很大,per-channel 能显著提升精度。
- 激活值:用 per-tensor 量化。激活值的分布相对稳定,per-tensor 够用,而且能减少计算开销。
2.4 知识体系图:量化原理全景
下面这张图,我把这一章的核心逻辑串起来了。你一看就明白:
2.5 实战中的选择策略
好了,理论讲完了,咱们来点实际的。你在做 GGUF 量化时,到底该怎么选?
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 权重量化(int8) | 对称 + per-channel | 权重分布对称,per-channel 精度高 |
| 激活量化(int8) | 非对称 + per-tensor | 激活值非负,per-tensor 够用且快 |
| 低比特量化(4-bit) | 非对称 + per-channel | 低比特下精度损失大,per-channel 能挽回一些 |
| 嵌入式设备部署 | 对称 + per-tensor | 计算资源有限,优先保证速度 |
一个小技巧:
我在量化 LLM 时,经常先用 per-tensor 快速跑一遍,看看精度损失。如果损失在可接受范围内,就保持 per-tensor。如果损失太大,再切换到 per-channel。这样能节省不少调参时间。
嗯,这一章的内容就到这。量化原理听起来复杂,但说白了就是“用更少的比特,尽可能保留更多的信息”。你只要记住:权重用对称+per-channel,激活用非对称+per-tensor,这个组合在大多数场景下都是最优解。