2、浮点模型基础:了解FP32、FP16格式,它们在MCU上的代价
好,咱们正式开始聊量化。在动手量化之前,有个基础必须打牢——浮点数格式。
你可能会想:“浮点数嘛,不就是小数吗?有什么好讲的?”
嗯,我刚开始也这么觉得。直到有一次,我在一个STM32H7项目上,把训练好的FP32模型直接烧进去,结果发现推理一次要3秒多,内存还爆了。那时候我才意识到——浮点数的代价,比你想象的大得多。
2.1 FP32:标准的“重量级选手”
FP32,全称是单精度浮点数,占用32个bit。这是深度学习模型最常用的格式。
它的结构是这样的:1位符号位 + 8位指数位 + 23位尾数位。
说白了,它能表示的范围大约是 ±1.18×10⁻³⁸ 到 ±3.4×10³⁸,精度大约7位有效数字。
FP32在MCU上的代价:
- 存储开销:每个参数4字节。一个100万参数的模型,光权重就要4MB。很多MCU的Flash才1-2MB,根本放不下。
- 计算开销:大部分Cortex-M内核没有硬件FPU,或者只有单精度FPU。一次浮点乘加运算,可能需要几十甚至上百个时钟周期。
- 内存带宽:从Flash读一个FP32数,要4次总线传输。在低主频的MCU上,这往往是瓶颈。
我记得有一次调试一个语音识别模型,模型本身只有500KB,但运行时中间激活值占用了大量RAM。最后不得不把批大小从32降到1,才勉强跑起来。这就是FP32的“内存黑洞”效应。
2.2 FP16:瘦身一半,代价几何?
FP16,半精度浮点数,只占16个bit。结构是:1位符号位 + 5位指数位 + 10位尾数位。
它的范围小很多:±6.55×10⁻⁵ 到 ±6.55×10⁴,精度大约3-4位有效数字。
| 特性 | FP32 | FP16 |
|---|---|---|
| 位宽 | 32 bit | 16 bit |
| 存储减半 | 4字节 | 2字节 |
| 表示范围 | ±3.4×10³⁸ | ±6.55×10⁴ |
| 精度 | ~7位小数 | ~3位小数 |
| MCU原生支持 | 部分有FPU | 几乎无 |
你可能会问:“FP16存储减半,那计算是不是也快一倍?”
答案是否定的。在MCU上,FP16的处境很尴尬。
FP16在MCU上的真实代价:
- 没有原生硬件支持:绝大多数Cortex-M内核没有FP16指令。你存的是16位,但计算时CPU会先把它转成FP32,算完再转回去。这一来一回,反而更慢。
- 精度损失不可忽视:我曾经在一个温度传感器校准模型上试过FP16,结果推理误差从0.1°C飙到了0.8°C。因为模型里有些权重值很小,FP16直接给截断成0了。
- 内存对齐问题:有些MCU对16位访问不对齐,会触发异常或性能惩罚。
嗯,这里要注意。FP16在GPU上是香饽饽,因为NVIDIA有Tensor Core专门加速它。但在MCU上,它更像一个“存储优化方案”,而不是“计算加速方案”。
2.3 实战对比:一个简单的卷积层
咱们来看一个具体的例子。假设有一个卷积层:输入通道32,输出通道64,卷积核3×3。
参数数量:32 × 64 × 3 × 3 = 18432 个权重。
// FP32版本:每个权重4字节
uint32_t weight_fp32_size = 18432 * 4; // 73,728 字节 ≈ 72KB
// FP16版本:每个权重2字节
uint32_t weight_fp16_size = 18432 * 2; // 36,864 字节 ≈ 36KB
// 但注意!计算时中间结果还是FP32
// 所以激活值内存并不会减半
你看,权重存储确实减半了。但运行时,每一层的输入输出激活值,如果还是用FP32存,那RAM节省有限。
我的个人建议:
在MCU上,除非你的Flash实在不够用,否则不要单独用FP16。更好的做法是:
- 权重用FP16存储,推理时转成FP32计算(牺牲一点速度换存储)
- 或者直接上INT8量化,这才是MCU的“甜点”
2.4 避坑指南:浮点数比较的陷阱
我曾经在调试一个PID控制器时,发现输出总是不对。查了两天,最后发现是浮点数比较的问题。
FP32和FP16的精度不同,同一个数值,用两种格式存,比较结果可能不一样。
// 错误示范
float a = 0.1f;
__fp16 b = 0.1f; // FP16格式
if (a == (float)b) {
// 这里可能不会执行!
// 因为0.1在FP16中会被截断,变成0.099609375
}
所以,如果你在模型里混合使用FP32和FP16,一定要小心边界条件。我习惯的做法是:在关键路径上统一用FP32,只在存储层面用FP16。
2.5 小结:MCU上浮点格式的选择策略
说了这么多,总结一下我的经验:
- Flash充足时:直接用FP32,省心,精度有保障。
- Flash紧张时:考虑FP16存储+FP32计算,但要做好精度验证。
- RAM也紧张时:别犹豫,直接上INT8量化。FP16在MCU上是个过渡方案,不是终极方案。
- 永远不要假设:每种模型对精度的敏感度不同。我见过一个图像分类模型,FP16几乎不掉点;但同一个模型做目标检测,FP16直接废了。
你想想看,MCU的资源就那么点,每一字节、每一时钟周期都要精打细算。理解浮点数的代价,是做好量化的第一步。
下一章,咱们就正式进入INT8量化的世界。那才是MCU上真正能打的技术。