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 小结
量化这东西,说白了就是「用精度换效率」。收益很诱人,但挑战也实实在在。我个人觉得,做量化最核心的是三点:
- 理解数值分布:量化前先跑统计,知道每层的 min、max、均值、方差。
- 选择合适的策略:per-tensor 还是 per-channel?对称还是非对称?int8 还是混合精度?
- 验证验证再验证:量化后一定要做精度对比,逐层排查误差来源。
下一章,我会带你手把手实现一个简单的量化工具。到时候咱们用代码说话,看看量化到底是怎么一步步把 float 变成 int 的。