3、量化核心概念:什么是量化?对称量化 vs 非对称量化
好,咱们今天聊聊量化的核心概念。说实话,很多刚接触模型部署的兄弟,一听到「量化」两个字就觉得头大。其实没那么玄乎。
量化,说白了就是「降精度」。
你想想看,我们训练模型的时候,权重和激活值通常都是 FP32(32位浮点数)。一个数占 4 个字节。但到了 STM32 这种单片机上,Flash 和 RAM 都金贵得很。我能不能用 8 位整数(INT8)甚至 4 位整数(INT4)来近似表示这些数?
能。这就是量化。
用 INT8 代替 FP32,模型体积直接缩到四分之一。内存带宽也降到四分之一。推理速度还能翻倍——因为 Cortex-M 内核有 SIMD 指令,一次能处理多个 8 位数据。
我在项目中遇到过最极端的情况:一个 2MB 的模型,硬塞进 512KB Flash 的 STM32F4 里。不做量化?门都没有。
量化的数学本质
量化的核心公式其实就一行:
q = round(r / scale) + zero_point
其中:
- r 是原始的浮点数(FP32)
- q 是量化后的整数(INT8/INT4)
- scale 是缩放因子(步长)
- zero_point 是零点偏移
反量化就是反过来:
r = (q - zero_point) * scale
嗯,这里要注意:round 操作会带来精度损失。你想想看,原本 FP32 能表示 2^32 种数值,INT8 只能表示 256 种。这中间的取舍,就是量化的艺术。
对称量化 vs 非对称量化
这两种方式,区别就在 zero_point 上。
对称量化
对称量化,zero_point 固定为 0。公式简化成:
q = round(r / scale)
这意味着量化后的整数范围是对称的。比如 INT8 就是 [-128, 127]。浮点数的 0.0 精确映射到整数的 0。
优点:
- 计算简单,没有偏移量,硬件实现快
- 卷积、全连接层的累加器不需要处理偏移
缺点:
- 如果数据分布不对称(比如全是正数),会浪费一半的量化范围
我个人的习惯: 对于权重,我几乎只用对称量化。因为训练好的权重通常分布接近正态,正负对称。我在 STM32N6 上部署 MobileNetV2 时,对称量化权重的精度损失不到 0.5%。
非对称量化
非对称量化,zero_point 可以不为 0。它把浮点数的最小值映射到整数的最小值,最大值映射到整数的最大值。
举个例子:假设激活值范围是 [0.0, 6.0],用 INT8 量化(范围 [0, 255]):
scale = (6.0 - 0.0) / (255 - 0) = 0.02353
zero_point = 0 - round(0.0 / 0.02353) = 0
如果范围是 [-3.0, 5.0]:
scale = (5.0 - (-3.0)) / (255 - 0) = 0.03137
zero_point = 0 - round(-3.0 / 0.03137) = 96
优点:
- 充分利用量化范围,精度损失更小
- 特别适合 ReLU 后的激活值(全是非负数)
缺点:
- 计算时需要处理 zero_point 偏移,稍微复杂一点
- 硬件加速器可能不支持(比如某些 NPU 只支持对称量化)
避坑指南: 我曾经在 STM32MP1 上踩过一个坑——激活值用对称量化,结果精度掉了 3%。后来发现激活值全是正数,对称量化把 [-128, 0] 那半边全浪费了。换成非对称量化,精度损失降到 0.8%。
什么时候选哪种?
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 权重量化 | 对称量化 | 权重分布通常对称,计算简单 |
| 激活值量化(ReLU后) | 非对称量化 | 全是非负数,充分利用范围 |
| 激活值量化(tanh/sigmoid后) | 对称量化 | 输出范围对称 [-1, 1] 或 [0, 1] |
| 硬件不支持偏移 | 对称量化 | 兼容性优先 |
| 精度要求极高 | 非对称量化 | 更精细的粒度 |
实际部署中的选择
在 STM32CubeAI 里,默认的量化策略是:
- 权重: 对称量化(per-channel)
- 激活值: 非对称量化(per-tensor)
为什么这么选?
权重用 per-channel 对称量化,每个输出通道有自己的 scale。这样能处理不同通道的权重范围差异。激活值用 per-tensor 非对称量化,因为激活值范围通常比较稳定,而且非对称能更好地匹配 ReLU 的输出。
我记得有一次调试一个语音识别模型,激活值范围特别大(从 -10 到 20)。用对称量化,scale 被拉得很大,小数值的精度全丢了。换成非对称量化后,精度直接回升了 2%。
注意: 非对称量化在 STM32 上做 INT8 矩阵乘法时,需要额外处理 zero_point。具体来说,卷积计算时要先减去 zero_point 再做乘加。这会增加一点点计算量,但通常可以忽略不计。
量化参数的计算
实际部署时,量化参数(scale 和 zero_point)怎么算?
有两种方式:
- 校准(Calibration): 用一小批数据跑一遍模型,统计每层的激活值范围。然后根据范围计算 scale 和 zero_point。
- 训练时量化(QAT): 在训练过程中模拟量化误差,让模型自己适应低精度。精度通常比校准高,但需要重新训练。
对于 STM32 部署,我个人建议先用校准。如果精度不够,再考虑 QAT。因为校准只需要几分钟,而 QAT 可能要跑好几个小时。
好了,总结一下:
- 量化就是 FP32 → INT8/INT4 的映射
- 对称量化:zero_point=0,适合权重和对称分布的数据
- 非对称量化:zero_point≠0,适合非对称分布的数据(如 ReLU 输出)
- STM32CubeAI 默认用权重对称 + 激活非对称的组合
下一章咱们聊聊「量化感知训练(QAT)」和「训练后量化(PTQ)」的具体实现。到时候我会拿一个实际的 STM32 项目来演示。