4、模型量化基础:INT8/INT4量化原理与对内存占用的影响

说到边缘AI系统的内存优化,量化绝对是个绕不开的话题。我刚开始做嵌入式AI那会儿,模型动不动就几十上百兆,根本塞不进芯片里。后来接触了量化,才算是打开了新世界的大门。

说白了,量化就是把模型里那些高精度的浮点数,换成低精度的整数。你想想看,一个float32是4个字节,换成int8就变成1个字节,内存直接缩到四分之一。这还不算完,int4更是只要半个字节。嗯,代价就是精度会掉一些,但很多时候这个代价是完全可以接受的。

4.1 为什么要做量化?

先说说我个人的理解。模型在训练的时候,我们需要高精度来保证梯度更新的准确性,所以用float32甚至float64。但推理的时候呢?其实没那么娇气。

我在项目中遇到过这样一个场景:一个目标检测模型,float32版本要跑在树莓派上,帧率只有可怜的3帧。量化到int8之后,帧率直接飙到15帧,内存占用也从120MB降到了35MB。最关键的是,mAP只掉了0.8%。你说这买卖划不划算?

量化的好处主要有三点:

  • 内存减半再减半:int8是float32的1/4,int4是1/8
  • 带宽需求降低:从DDR搬数据的时间少了,推理自然就快了
  • 计算效率提升:整数运算比浮点运算快得多,很多硬件还有专门的int8加速单元

核心观点:量化不是简单的精度取舍,而是用最小的精度损失,换取最大的性能收益。在边缘设备上,这往往是唯一的选择。

4.2 INT8量化原理

INT8量化,就是把float32的数值范围映射到[-128, 127]这个区间。怎么映射呢?核心就两个参数:缩放因子(scale)零点(zero point)

公式其实很简单:

q = round(r / scale) + zero_point

其中r是原始的浮点数,q是量化后的整数。反过来,反量化就是:

r = (q - zero_point) * scale

这里有个坑,我曾经踩过。scale的选择非常关键,选大了精度损失大,选小了容易溢出。我一般会先跑一遍校准数据集,统计出每一层激活值的最大值和最小值,然后根据这个来定scale。

量化的方式主要有两种:

  • 对称量化:zero_point固定为0,适合权重这种正负对称的数据
  • 非对称量化:zero_point可以不为0,适合激活值这种非对称分布的数据

我的经验:对于权重,我习惯用对称量化。对于激活值,尤其是ReLU之后的,用非对称量化效果更好。这个组合我在多个项目里验证过,精度损失最小。

4.3 INT4量化原理

INT4量化就更激进了。int4只有16个取值,从-8到7。你想想看,要把float32里几十亿个可能的值,压缩到16个格子里,这精度损失能不大吗?

但为什么还要用int4呢?因为有些场景下,精度不是第一位的。比如一些简单的分类任务,或者作为模型剪枝后的进一步压缩手段。

INT4量化的实现方式跟INT8类似,但有个关键区别:分组量化。因为单个数值的表示能力太弱了,所以需要把一组数值放在一起,共享同一个scale和zero_point。

// 伪代码示例:INT4分组量化
group_size = 32  // 每32个数值为一组
for each group in weights:
    scale = (max(group) - min(group)) / 15
    zero_point = -round(min(group) / scale)
    for each value in group:
        q = round(value / scale) + zero_point
        q = clamp(q, -8, 7)  // 限制在int4范围内

这里要注意,int4在硬件上并不像int8那么普及。很多芯片没有原生的int4计算单元,需要做额外的bit packing和解包操作。我曾经在一个项目里强行上int4,结果因为解包开销太大,速度反而没比int8快多少。

避坑指南:我曾经在STM32上尝试int4量化,发现内存是省了,但推理时间反而增加了。后来一查,原来是MCU没有int4指令集,所有运算都要拆成int8来做。所以,上int4之前,一定要确认你的硬件支不支持。

4.4 量化对内存占用的影响

这部分我直接上数据,大家看得更清楚。假设一个模型有1000万个参数:

精度 每个参数大小 总内存占用 相比float32节省
float32 4 bytes 40 MB 0%
float16 2 bytes 20 MB 50%
int8 1 byte 10 MB 75%
int4 0.5 byte 5 MB 87.5%

但这只是权重部分。别忘了,推理过程中还有中间激活值。这部分内存占用往往比权重还大。量化同样能压缩激活值,但要注意:激活值的分布是动态的,不像权重那样固定。

我个人的做法是:

  1. 先量化权重,这个基本无损
  2. 再量化激活值,但保留一些关键层的精度
  3. 最后用校准数据集跑一遍,看看精度是否达标

实际案例:我之前优化过一个语音识别模型,原始float32版本占用180MB。量化到int8后降到48MB,再配合int4量化部分层,最终只有28MB。成功塞进了一款只有32MB RAM的音频处理芯片里。

4.5 量化后的精度补偿技巧

量化一定会掉精度,但我们可以想办法把损失降到最低。这里分享几个我常用的技巧:

  • 量化感知训练(QAT):在训练过程中模拟量化效果,让模型自己适应低精度。效果比训练后量化好很多,但需要重新训练。
  • 逐通道量化:每个卷积核单独算scale,而不是整个层共享。精度能提升0.5-1%。
  • 混合精度量化:敏感层用int8,不敏感层用int4。怎么判断敏感层?跑一遍逐层精度分析就知道了。

小技巧:如果你不想重新训练,可以试试偏置校正。量化后统计一下输出分布的偏移,然后调整偏置项。这个方法我试过多次,简单有效,精度能回来不少。

最后说一句,量化不是银弹。有些模型对精度极其敏感,比如医疗影像或者自动驾驶,量化后可能无法满足要求。但大多数边缘AI场景,比如智能家居、工业检测、可穿戴设备,量化都是最优解。

嗯,关于量化的基础就讲到这里。下一章我会深入讲量化工具链的使用,包括TensorRT、ONNX Runtime这些实际工具怎么配置量化参数。到时候再跟大家分享一些踩坑经验。