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 知识体系结构图

下面这张图,把量化参数和两种量化方式的关系梳理清楚了。你可以把它当作一个快速参考。

量化原理知识结构 量化 (Quantization) 量化参数 量化方式 Scale (缩放因子) Zero-Point (零点) 对称量化 非对称量化 Zero-Point = 0 硬件友好 Zero-Point ≠ 0 精度更高 权重 → 对称量化 激活值 → 非对称量化

这张图里,我特意把“权重用对称,激活用非对称”这个经验法则放在了底部。你以后做量化方案选型时,直接照着这个框架来,基本不会跑偏。