3、模型量化入门:什么是量化、量化的数学原理(对称/非对称量化)、量化精度与性能的权衡
好,咱们进入第三章。量化。
说实话,很多做AI的同行一听到「量化」两个字,第一反应就是「掉精度」。我刚开始接触嵌入式AI时也这么想——好好的FP32模型,干嘛非要折腾成INT8?这不是自找麻烦吗?
后来我在一个智能门锁项目上被现实狠狠教育了一回。模型跑在Cortex-M4上,推理一次要3秒,用户都按第二遍指纹了,结果还没算完。嗯,从那以后,我再也不敢小看量化了。
3.1 什么是量化?说白了就是「省着算」
量化,本质上就是把连续的浮点数,映射到离散的整数上。
你想想看,神经网络里那些权重和激活值,大部分都是FP32的。每个数占4个字节。一个MobileNetV2大概有3.5M个参数,光权重就14MB。很多MCU的Flash才512KB,根本塞不下。
量化之后呢?INT8每个数只占1个字节。模型体积直接缩到1/4。更关键的是,整数运算比浮点运算快得多——很多硬件有专用的SIMD指令,一条指令能同时算4个、8个甚至16个数据。
核心思想:用更少的比特数表示数值,换取更小的模型体积和更快的推理速度。
我在项目中遇到过最极端的例子:一个3MB的模型量化到INT8后只有750KB,跑在H7系列芯片上,推理时间从1.2秒降到了180毫秒。精度只掉了0.3%。值不值?你自己判断。
3.2 量化的数学原理
量化不是随便把浮点数四舍五入成整数。它有一套严谨的数学映射关系。
3.2.1 基本公式
假设我们有一个浮点数 r,要量化成整数 q。公式长这样:
q = round(r / S + Z)
反过来,反量化公式:
r = (q - Z) * S
这里有两个关键参数:
- S(Scale,缩放因子):决定每个整数步长代表多大的浮点范围
- Z(Zero Point,零点):浮点0映射到哪个整数
我习惯把S想象成「尺子的刻度」,Z就是「尺子的起点」。你想想看,一把尺子,刻度粗细和起点位置决定了它能量什么范围的东西。
3.2.2 对称量化 vs 非对称量化
这里有个重要的分岔路口——对称还是非对称?
| 特性 | 对称量化 | 非对称量化 |
|---|---|---|
| 零点Z | 固定为0 | 可以不为0 |
| 映射范围 | [-max, +max] | [min, max] |
| 计算复杂度 | 低(无零点偏移) | 高(需处理零点) |
| 适用场景 | 权重(通常对称分布) | 激活值(ReLU后全为正) |
对称量化:
Z固定为0。公式简化为:
q = round(r / S)
r = q * S
说白了,就是把浮点范围[-a, a]映射到[-127, 127](INT8)。
我个人的习惯是:权重用对称量化。为什么?因为训练好的权重分布通常以0为中心,左右对称。你硬要加个非零的零点,反而浪费了整数的表示能力。
非对称量化:
Z可以不为0。公式就是最前面那个带Z的版本。
激活值我建议用非对称量化。你想想看,ReLU之后全是正数,范围可能是[0, 6.7]。如果用对称量化,负半轴那部分表示能力全浪费了。非对称量化可以把整个整数范围[0, 255]都用来表示正数,精度更高。
我的经验:权重用对称,激活用非对称。这是最稳妥的搭配。我曾经试过全用对称量化,结果某个层的激活值分布偏得厉害,精度直接掉了2%。后来改成非对称,精度就回来了。
3.2.3 量化参数的计算
S和Z怎么算?需要先知道浮点数的范围[min, max]。
# 对称量化
S = max(|min|, |max|) / 127
Z = 0
# 非对称量化
S = (max - min) / 255
Z = round(-min / S)
这里有个坑——min和max怎么确定?
- 权重:直接取训练好的权重值,min和max是确定的
- 激活值:需要跑一批校准数据(calibration dataset),统计每层的输出范围
我曾经犯过一个错误:随便拿了10张图片做校准,结果激活值范围没覆盖全。模型在测试集上精度还行,一到实际场景就崩了。后来我学乖了,至少用500-1000张有代表性的数据做校准。
3.3 量化精度与性能的权衡
量化不是免费的午餐。你省了算力和存储,就得付出精度的代价。
3.3.1 精度损失从哪来?
说白了就两个原因:
- 截断误差:浮点范围太大,超出整数能表示的范围,直接截断
- 舍入误差:浮点数落在两个整数之间,四舍五入时丢失精度
你想想看,INT8只有256个取值。FP32有40多亿个。用256个值去近似40亿个值,怎么可能没损失?
3.3.2 量化到什么程度合适?
我整理了一个表格,方便你快速决策:
| 量化位宽 | 模型体积缩减 | 推理加速比 | 典型精度损失 | 推荐场景 |
|---|---|---|---|---|
| FP32→INT8 | 4x | 2-4x | <1% | 大部分嵌入式场景 |
| FP32→INT4 | 8x | 4-8x | 1-5% | 对精度要求不高的场景 |
| FP32→INT2/1 | 16-32x | 8-16x | 5-15% | 极端资源受限,且任务简单 |
注意:以上数据是典型值,实际效果取决于模型结构和数据分布。我见过某些模型INT8量化后精度反而提升了(因为量化起到了正则化作用),也见过掉3%的。一定要实测验证。
3.3.3 如何平衡?我的实战建议
嗯,这里分享几条我踩过坑之后总结的经验:
- 先做INT8:90%的嵌入式场景,INT8就够了。别一上来就追求INT4或二值化
- 逐层分析:不是所有层对精度都敏感。第一层和最后一层通常最敏感,可以保留FP32。中间层大胆量化
- 混合精度:敏感层用INT8,不敏感层用INT4。我有个项目就是这么干的,模型体积缩小了6倍,精度只掉了0.5%
- 量化感知训练(QAT):如果后训练量化(PTQ)精度掉太多,就用QAT。在训练过程中模拟量化误差,让模型自己适应
我记得有一次做语音唤醒词检测,模型很小,PTQ量化后精度掉了3%。我换成QAT,精度只掉了0.2%。虽然训练时间多了30%,但效果值得。
3.4 小结
量化是嵌入式AI推理加速的基石。说白了,就是用精度换速度、换体积。对称量化适合权重,非对称量化适合激活值。INT8是性价比最高的选择,但具体用哪种方案,一定要结合你的模型和硬件实测。
下一章,我们会深入代码,手把手教你用TensorRT和TFLite做量化。到时候你会看到,理论落地到代码其实没那么复杂。