3、量化原理详解:对称量化与非对称量化、量化参数(scale, zero-point)、量化公式推导
好,我们直接进入正题。量化这件事,说白了就是用更少的比特数去近似表示浮点数。你想想看,一个32位的浮点数,在芯片上做乘法,功耗和面积都大得吓人。换成INT8,面积直接砍到四分之一,速度还能翻倍。但代价是什么?精度损失。所以,理解量化原理,就是搞清楚“怎么丢精度丢得最聪明”。
3.1 量化参数:Scale 和 Zero-Point
量化不是简单地把小数点挪个位置。它需要两个核心参数:scale(缩放因子)和zero-point(零点)。我习惯把 scale 想象成“步长”,它决定了浮点数和整数之间的映射粒度。而 zero-point,则是浮点0值对应到整数域的那个偏移量。
举个例子。假设我们有一组浮点激活值,范围在 [-1.0, 2.0] 之间。要量化到 INT8(范围 -128 到 127),我们需要一个 scale 和一个 zero-point 来建立映射关系。
公式很简单,但很重要:
// 量化:浮点 -> 整数
q = round( r / scale + zero_point )
// 反量化:整数 -> 浮点
r = (q - zero_point) * scale
这里 r 是原始浮点数,q 是量化后的整数。scale 和 zero-point 一旦确定,整个张量的映射就固定了。我在项目中遇到过,很多人只关注 scale,却忽略了 zero-point 的校准,结果模型精度崩得一塌糊涂。
核心要点:Scale 控制精度粒度,Zero-Point 保证浮点0能被无损映射。浮点0必须能精确表示为整数,否则 ReLU 等操作后的 padding 值会引入偏差。
3.2 对称量化 vs 非对称量化
这两种方式,区别就在于 zero-point 是否为零。
3.2.1 对称量化
对称量化,zero-point 固定为0。公式退化为:
q = round( r / scale )
r = q * scale
它的特点是量化范围关于0对称。比如浮点范围 [-1.0, 1.0] 映射到 INT8 [-127, 127]。注意,这里没用 -128,因为要保持对称性,通常会把 -128 空出来或者特殊处理。
优点:实现简单,硬件友好。卷积核的权重量化几乎都用它。为什么?因为权重分布通常关于0对称,用对称量化不会浪费表示范围。
缺点:如果数据分布严重偏斜(比如全是正数),对称量化会浪费一半的整数表示范围。你想想看,如果激活值都在 [0, 2.0] 之间,对称量化却要用 [-127, 127] 去映射,那负半轴的表示能力全浪费了。
我的经验:权重量化我几乎只用对称量化。激活值嘛,得看情况。如果是 ReLU 后的输出,非对称量化往往更香。
3.2.2 非对称量化
非对称量化,zero-point 不为0。它可以根据数据的实际分布,灵活调整映射范围。公式就是最前面那个带 zero-point 的版本。
举个例子。激活值范围 [0.0, 3.0],量化到 [0, 255](UINT8)。那么:
scale = (3.0 - 0.0) / (255 - 0) = 0.01176
zero_point = 0 - round(0.0 / 0.01176) = 0
你看,如果最小值就是0,zero-point 自然就是0。但如果范围是 [0.2, 2.8],zero-point 就不是0了。
优点:充分利用整数表示范围,精度更高。尤其适合激活值这种分布不对称的数据。
缺点:计算复杂一些。推理时,zero-point 需要参与运算,对硬件设计不太友好。我记得有一次调试一个端侧芯片,非对称量化导致卷积计算单元多了一级加法,延迟直接增加了5%。
避坑指南:我曾经在量化一个轻量级分类网络时,为了省事,对激活值也用了对称量化。结果精度掉了2个点。换成非对称量化后,精度损失降到了0.3%以内。所以,别偷懒,激活值该用非对称就用非对称。
3.3 量化公式的完整推导
我们从头推一遍,这样你心里有底。
假设浮点数的真实范围是 [r_min, r_max],量化后的整数范围是 [q_min, q_max]。对于 INT8,q_min = -128,q_max = 127。对于 UINT8,q_min = 0,q_max = 255。
第一步,计算 scale:
scale = (r_max - r_min) / (q_max - q_min)
第二步,计算 zero-point:
zero_point = q_min - round(r_min / scale)
这里用 round 取整,保证 zero-point 是整数。
第三步,量化:
q = round( r / scale + zero_point )
第四步,反量化:
r = (q - zero_point) * scale
嗯,这里要注意。反量化后的浮点值,和原始浮点值之间是有误差的。这个误差就是量化误差,它等于 scale / 2 左右。scale 越小,误差越小,但整数表示范围也越小。
关键洞察:量化误差的本质是“舍入误差”。你无法消除它,只能通过选择合适的 scale 和 zero-point 来最小化它对最终结果的影响。
3.4 对称 vs 非对称:一张表说清楚
| 特性 | 对称量化 | 非对称量化 |
|---|---|---|
| Zero-Point | 固定为0 | 非0整数 |
| 计算复杂度 | 低,硬件友好 | 高,需额外处理 zero-point |
| 表示效率 | 数据对称时高,偏斜时低 | 始终高效,适应任意分布 |
| 典型应用 | 权重(Weight) | 激活值(Activation) |
| 精度损失 | 数据对称时小 | 通常更小 |
3.5 知识体系结构图
下面这张图,把量化参数和两种量化方式的关系梳理清楚了。你可以把它当作一个快速参考。
这张图里,我特意把“权重用对称,激活用非对称”这个经验法则放在了底部。你以后做量化方案选型时,直接照着这个框架来,基本不会跑偏。