2、模型量化基础(上):量化的数学原理、对称量化与非对称量化、量化参数(scale/zero-point)的计算
好,咱们正式开始聊模型量化。
说实话,量化这块内容,我当年刚接触的时候也觉得有点绕。一堆数学公式,什么 scale、zero-point,看着就头疼。但后来我在一个嵌入式视觉项目里,硬是把一个 200MB 的模型压到了 50MB,精度只掉了 0.3%。那一刻我才真正体会到——量化不是玄学,是实实在在的工程利器。
今天这一讲,咱们先把量化的数学底子打牢。你搞懂了原理,后面实操才不会慌。
2.1 为什么要量化?—— 从浮点到整数的“降维打击”
模型在训练的时候,参数和激活值默认都是 FP32(32位浮点数)。这玩意儿精度高,但占地方、跑得慢。你想想看,一个参数占 4 个字节,一亿个参数就是 400MB。在服务器上还行,放到手机或者 MCU 上,直接内存爆炸。
量化的核心思路很简单:用更少的比特数去近似表示原来的浮点数。最常见的就是从 FP32 量化到 INT8,也就是 4 倍压缩。不光模型变小了,推理速度也能快 2-4 倍,因为整数运算比浮点运算快得多。
我打个比方。你原来用一把毫米刻度的尺子量东西,现在换成一把厘米刻度的尺子。精度肯定下降了,但量得快、尺子也小。量化就是这个道理。
- 模型体积缩小:FP32 → INT8,体积变为原来的 1/4
- 推理速度提升:整数运算比浮点运算快 2-4 倍
- 内存带宽降低:更少的数据搬运,更省电
2.2 量化的数学原理 —— 一个线性映射
量化的本质,就是一个数学映射。把浮点数范围 [r_min, r_max] 映射到整数范围 [q_min, q_max]。
这个映射是线性的,公式长这样:
r = S * (q - Z)
其中:
r是真实的浮点数值q是量化后的整数值S是缩放因子(scale),一个浮点数Z是零点(zero-point),一个整数
反过来,从浮点量化到整数的公式是:
q = round(r / S + Z)
嗯,这里要注意。round 操作会带来精度损失,这是量化误差的主要来源。我在项目中遇到过,有些层对量化特别敏感,就是因为 round 误差被放大了。
2.3 对称量化 vs 非对称量化
根据零点 Z 的取值,量化分为两种:对称量化和非对称量化。这是面试常考的点,也是你选型时要做的第一个决策。
2.3.1 对称量化
对称量化,说白了就是让零点 Z = 0。公式简化成:
r = S * q
q = round(r / S)
它的特点是:浮点数的正负范围对称映射到整数的正负范围。比如 INT8 的 [-128, 127] 映射到浮点数的 [-128*S, 127*S]。
优点:实现简单,硬件友好。很多 NPU 和 DSP 只支持对称量化。
缺点:如果浮点数的分布不对称(比如 ReLU 后的激活值全是非负的),对称量化会浪费一半的整数表示范围。
2.3.2 非对称量化
非对称量化允许零点 Z 不为 0。这样整数范围可以完全覆盖浮点数的实际分布范围,不浪费任何编码空间。
举个例子。假设浮点数范围是 [0.0, 10.0],量化到 INT8 [0, 255]。那么:
- S = (10.0 - 0.0) / (255 - 0) = 0.0392
- Z = 0(因为浮点 0 正好对应整数 0)
但如果浮点数范围是 [2.0, 12.0],量化到 INT8 [0, 255]:
- S = (12.0 - 2.0) / (255 - 0) = 0.0392
- Z = round(0 - 2.0 / 0.0392) = round(-51) = -51
你看,零点变成了 -51。这就是非对称量化的灵活性。
2.4 量化参数的计算 —— scale 和 zero-point
好,现在咱们来手算一下 scale 和 zero-point。这是基本功,必须会。
假设我们要把浮点数范围 [r_min, r_max] 量化到整数范围 [q_min, q_max]。
第一步:计算 scale
S = (r_max - r_min) / (q_max - q_min)
第二步:计算 zero-point
Z = round(q_min - r_min / S)
或者用浮点 0 对应的整数:
Z = round(0 - r_min / S)
注意,Z 必须被裁剪到 [q_min, q_max] 范围内。
咱们来个具体的例子。假设有一组浮点激活值,范围是 [-1.5, 6.0],要量化到 INT8 [-128, 127]。
S = (6.0 - (-1.5)) / (127 - (-128))
= 7.5 / 255
= 0.02941
Z = round(0 - (-1.5) / 0.02941)
= round(51.0)
= 51
所以,量化参数是 S=0.02941,Z=51。
验证一下:浮点 0 量化后是 q = round(0/0.02941 + 51) = 51,反量化回来 r = 0.02941 * (51 - 51) = 0。完美。
- Scale 决定了量化的“粒度”,S 越小,精度越高
- Zero-point 负责对齐浮点 0 和整数 0,保证 0 的量化是无损的
- 实际部署时,S 和 Z 会作为常量固化在模型里,推理时直接查表或计算
2.5 量化误差的来源与应对
量化不是无损的。误差主要来自三个方面:
- 舍入误差:round 操作导致的。这个没法避免,但可以通过更精细的校准数据集来缓解。
- 截断误差:浮点数超出量化范围时被截断。我建议你在校准阶段统计好 min/max,尽量覆盖 99% 以上的数据分布。
- 精度损失:小数值被量化成 0 或 1,丢失了细节。对于某些层(比如 attention 的 softmax 输出),这种损失影响很大。
我曾经在一个 NLP 模型上,因为量化后精度掉了 5%,排查了两天。最后发现是某一层的激活值范围特别小,量化后全变成了 0。解决办法是给那一层单独设置更大的量化范围,或者改用更高精度的量化(比如 INT16)。
- 先做逐层量化敏感性分析,找出“脆弱层”
- 对脆弱层保留 FP32 或使用混合精度量化
- 校准数据集要足够有代表性,别用训练集,用验证集
2.6 小结
这一讲咱们把量化的数学基础捋了一遍。核心就三件事:
- 理解线性映射公式
r = S * (q - Z) - 区分对称量化和非对称量化的适用场景
- 会手算 scale 和 zero-point
下一讲,咱们会深入聊量化校准方法、量化感知训练,以及如何在 TensorRT 和 TFLite 里实操。到时候我会拿一个实际项目案例,带你一步步把模型量化部署到开发板上。
嗯,今天就到这儿。你先把这些公式和概念消化一下,有问题随时翻回来看看。