量化基础理论:定点数与浮点数、对称量化与非对称量化、量化参数
聊量化之前,咱们得先搞清楚一个根本问题:计算机里到底怎么表示数字?
你想想看,我们平时写代码,float、double 用得很顺手。但在嵌入式设备、AI 芯片上,这些浮点数太「奢侈」了。我刚开始做模型部署时,就吃过这个亏——模型在 GPU 上跑得飞快,一上手机端,直接内存爆炸。后来才明白,量化就是把「大数字」变成「小数字」的艺术。
1. 定点数与浮点数
先说浮点数。FP32(32位浮点)是咱们的老朋友,1位符号位、8位指数位、23位尾数位。它能表示的范围很大,精度也够。但问题是——它太占地方了。
定点数就简单多了。比如 INT8,就是 8 位整数,范围 -128 到 127。没有指数,没有尾数,就是一个整数。说白了,定点数就是「硬邦邦」的整数,浮点数则是「软绵绵」的科学计数法。
我个人的习惯是:能不用浮点就不用浮点。尤其是在边缘设备上,定点数运算快、功耗低、占内存少。但代价是精度会损失。嗯,这就是量化的本质——用精度换效率。
- 浮点数:动态范围大,精度高,但计算慢、占内存
- 定点数:范围固定,精度有限,但计算快、省资源
2. 对称量化与非对称量化
量化说白了就是找一个映射关系,把浮点数映射到整数上。这个映射有两种主流方式:对称量化和非对称量化。
对称量化
对称量化的特点是:0 映射到 0。也就是说,浮点数的 0 在量化后还是 0。它的映射范围是 [-127, 127](INT8 去掉 -128 是为了对称)。
我在项目中遇到过一个问题:当模型的权重分布比较对称时(比如经过 BatchNorm 后的激活值),对称量化效果很好。但如果分布偏了,比如 ReLU 之后全是正数,那对称量化就浪费了一半的整数范围。
非对称量化
非对称量化允许浮点数的 0 映射到整数上的任意位置。它用两个参数:scale 和 zero_point。zero_point 就是浮点数 0 对应的整数值。
举个例子:假设浮点数范围是 [0.0, 10.0],映射到 INT8 [0, 255]。那么 scale = 10.0 / 255 ≈ 0.0392,zero_point = 0。这样浮点数 0 就映射到整数 0,浮点数 10.0 映射到整数 255。
非对称量化的好处是:能充分利用整数范围。如果数据全是正数,它不会浪费一半的表示空间。我曾经在量化一个语音模型时,激活值全是 0 到 1 之间的小数,用对称量化效果很差,换成非对称量化后精度直接回升了 2%。
| 类型 | 0 映射 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 对称量化 | 0 → 0 | 权重、对称分布 | 实现简单 | 浪费范围 |
| 非对称量化 | 0 → zero_point | 激活值、非对称分布 | 充分利用范围 | 计算稍复杂 |
3. 量化参数:scale 和 zero_point
这两个参数是量化的核心。说白了,scale 决定了步长,zero_point 决定了偏移。
公式很简单:
量化公式:q = round(r / scale) + zero_point
反量化公式:r = (q - zero_point) * scale
其中:
- r:浮点数(real value)
- q:量化后的整数(quantized value)
- scale:缩放因子,浮点数
- zero_point:零点偏移,整数
scale 怎么算?很简单:
scale = (r_max - r_min) / (q_max - q_min)
zero_point 怎么算?
zero_point = round(q_min - r_min / scale)
嗯,这里要注意:zero_point 必须是整数。我刚开始做量化时,直接拿浮点数当 zero_point 用,结果反量化出来全是错的。后来才意识到,zero_point 是整数索引,不是浮点偏移。
知识体系总览
下面这张图,是我自己总结的量化基础框架。你看一眼就能明白整个知识脉络:
你看,整个量化基础其实就三块:数字怎么表示、量化怎么映射、参数怎么算。搞懂了这些,后面学 PTQ 就轻松多了。
公众号:蓝海资料掘金营,微信deep3321