2、模型量化基础:什么是量化、定点数与浮点数、量化带来的收益与挑战

好,咱们今天聊聊模型量化。说实话,这是嵌入式部署里最实用、也最绕不开的一环。

我刚开始接触量化时,觉得这东西挺玄乎的。把好好的浮点数模型变成整数,精度还能保住?后来踩过几次坑,才慢慢摸清门道。今天我就把这块掰开了讲清楚。

2.1 什么是量化?

量化,说白了就是「用更少的比特数,去表示原本更精确的数字」。

举个例子。你原来用 float32 表示一个权重,每个数占 32 个比特。量化后,你可能只用 int8,也就是 8 个比特。存储一下子缩到原来的四分之一。计算也快了,因为整数运算比浮点快得多。

但代价是什么?精度损失。你想想看,32 个比特能表示约 42 亿个不同的数,8 个比特只能表示 256 个。这中间的信息量差距,就是量化要面对的挑战。

核心定义:量化是将连续取值的浮点数,映射到离散取值的整数空间的过程。

2.2 定点数与浮点数

聊量化之前,得先搞清楚定点数和浮点数到底是个啥。我见过不少同学在这上面栽跟头。

浮点数(Floating Point)

浮点数,就是咱们平时用的 float32、float16。它的表示方式像科学计数法:

值 = (-1)^符号 × 尾数 × 2^指数

float32 里,1 位符号位,8 位指数位,23 位尾数位。它能表示的范围很大,从 1.4e-45 到 3.4e38。精度也高,大约 7 位有效数字。

但问题来了——浮点运算在嵌入式芯片上,功耗高、速度慢。尤其是没有 FPU 的 MCU,跑浮点简直是灾难。

定点数(Fixed Point)

定点数就简单粗暴了。它没有指数,只有整数部分和小数部分,位置是固定的。

比如 Q7.8 格式:1 位符号,7 位整数,8 位小数。每个数就是:

值 = 整数部分 + 小数部分 / 256

定点数的好处是:运算就是整数运算,快得很。坏处是:范围固定,精度固定。你选 Q7.8,最大只能表示 127.996,最小精度 1/256。一旦数值超出范围,直接溢出。

我的经验:我在一个语音识别项目里,一开始用 float32 跑得好好的,换到定点数后,有个中间层的激活值直接溢出了。排查了半天才发现,是某个卷积层的输出范围比预期大了 3 倍。后来我养成了习惯:量化前一定先跑一遍统计,看看每层的数值分布。

2.3 量化带来的收益

收益是实打实的,我列几个关键点:

  • 模型体积缩小:从 float32 到 int8,体积直接缩到 1/4。一个 100MB 的模型,量化后只有 25MB。这在 Flash 有限的嵌入式设备上,太关键了。
  • 推理速度提升:整数运算比浮点快 2-4 倍。我测过一个 MobileNetV2,在 STM32H7 上,float32 推理一次要 120ms,int8 只要 35ms。差距肉眼可见。
  • 功耗降低:整数运算单元比浮点单元省电得多。对于电池供电的设备,这能多撑好几个小时。
  • 内存带宽节省:数据量小了,从 DDR 搬运到计算单元的时间也短了。很多场景下,内存带宽才是真正的瓶颈。
指标 float32 int8 量化 收益
模型体积 100 MB 25 MB 减少 75%
推理速度 120 ms 35 ms 提升 3.4 倍
内存占用 256 KB 64 KB 减少 75%
功耗 500 mW 200 mW 降低 60%

2.4 量化带来的挑战

收益大,挑战也不小。我踩过的坑,一个一个说。

精度损失

这是最直接的。量化相当于把连续空间离散化,必然有信息丢失。尤其是那些数值分布很广的层,比如某些卷积层的权重范围是 [-10, 10],但大部分值集中在 [-0.1, 0.1]。量化后,那些小值可能直接被截断成 0。

为什么会这样?因为量化时,我们通常用 min-max 映射。范围大的值占用了大部分量化区间,小值就被挤到角落里了。

避坑指南:我曾经在一个目标检测模型上,量化后 mAP 掉了 8 个点。排查发现,是某个 BN 层后的激活值分布太不均匀。后来我改用 per-channel 量化,每个通道单独算 scale 和 zero_point,精度才恢复回来。记住:per-tensor 量化省事,但 per-channel 量化更保精度。

量化误差累积

单层量化误差可能不大,但网络一深,误差会一层层传下去。尤其是那些对数值敏感的结构,比如残差连接、注意力机制。

我建议你在量化后,逐层对比输出。看看哪一层误差最大,针对性地做处理。比如对敏感层保留 float16,或者用更精细的量化策略。

硬件支持差异

不同芯片的量化支持不一样。有些芯片只支持对称量化(zero_point = 0),有些支持非对称。有些只支持 int8,有些支持 int4、int16。

你想想看,你辛辛苦苦量化好的模型,换了个芯片,发现它的指令集不支持你的量化格式。那就得重新调。

我的习惯:选芯片之前,先看它的量化 SDK 文档。看看它支持哪些量化格式、有没有量化校准工具、支不支持混合精度。这些信息,能帮你省下至少一周的调试时间。

校准数据需求

量化不是简单地把 float 转成 int。你需要一组校准数据,去统计每层的数值范围。校准数据选不好,量化效果就大打折扣。

我见过有人随便拿 100 张图做校准,结果量化后模型直接崩了。为什么?因为校准数据分布和实际数据分布不一致。你拿猫的图片做校准,模型部署后识别狗,那肯定出问题。

我的建议是:校准数据要覆盖实际场景的多样性。至少 500-1000 张,而且最好是从实际部署环境中采集的。

2.5 小结

量化这东西,说白了就是「用精度换效率」。收益很诱人,但挑战也实实在在。我个人觉得,做量化最核心的是三点:

  1. 理解数值分布:量化前先跑统计,知道每层的 min、max、均值、方差。
  2. 选择合适的策略:per-tensor 还是 per-channel?对称还是非对称?int8 还是混合精度?
  3. 验证验证再验证:量化后一定要做精度对比,逐层排查误差来源。

下一章,我会带你手把手实现一个简单的量化工具。到时候咱们用代码说话,看看量化到底是怎么一步步把 float 变成 int 的。