模型选型与量化策略:从浮点到定点
模型选型这件事,说白了就是「在精度和速度之间找平衡」。我刚开始做 STM32 部署时,总想把 PC 上跑得最好的模型直接搬过去,结果发现根本跑不动。后来才明白——嵌入式部署不是选最强的模型,而是选最合适的模型。
这一章,我们重点聊两个事:怎么选模型,以及怎么把浮点模型变成定点模型。这两步走对了,后面部署就顺了。
3.1 模型选型:不是越大越好
STM32 的资源非常有限。我见过有人把 ResNet-50 硬塞到 STM32H7 上,结果 Flash 爆了,RAM 也不够。嗯,这里要注意——选模型之前,先看芯片的规格书。
3.1.1 芯片资源约束
STM32 系列芯片的资源差异很大。我整理了一个常用型号的对比表,方便你快速判断:
| 芯片型号 | Flash (最大) | SRAM (最大) | 算力 (MACS) | 适合模型规模 |
|---|---|---|---|---|
| STM32F4 系列 | 2 MB | 384 KB | ~0.1 G | 极轻量模型(< 100 KB) |
| STM32L4 系列 | 1 MB | 320 KB | ~0.05 G | 极轻量模型(< 80 KB) |
| STM32H7 系列 | 2 MB | 1 MB | ~1 G | 轻量模型(< 500 KB) |
| STM32MP1 系列 | 外部存储 | 1 GB+ | ~10 G | 中等模型(< 5 MB) |
我个人习惯是:先算模型的理论内存占用。一个简单的公式:
模型大小 ≈ 参数量 × 量化位宽 / 8 (字节)
激活内存 ≈ 特征图大小 × 量化位宽 / 8 (字节)
举个例子:一个 100K 参数的模型,用 INT8 量化后,权重只占 100KB。但如果你用 FP32,那就是 400KB。差距很明显。
3.1.2 常见轻量模型对比
我推荐几个在 STM32 上表现不错的模型结构:
- MobileNetV1/V2:深度可分离卷积,参数量少,适合 STM32H7 以上。
- SqueezeNet:用 1x1 卷积替代 3x3,参数量极低,适合 F4 系列。
- ShuffleNetV2:通道混洗,计算效率高,适合 L4 系列。
- MicroNet:专门为微控制器设计的结构,参数量可以压到 10K 以下。
3.2 量化原理:从浮点到定点
量化,说白了就是把模型里的浮点数(FP32)变成整数(INT8/INT16)。为什么要这么做?因为 STM32 没有 FPU 或者 FPU 很慢,但整数运算快得多。
我记得第一次做量化时,以为就是把小数点直接去掉。结果模型精度直接崩了。后来才明白——量化不是简单的截断,而是重新映射。
3.2.1 量化映射公式
量化的核心公式其实很简单:
q = round(r / scale) + zero_point
其中:
r = 浮点数值
q = 量化后的整数值
scale = 缩放因子
zero_point = 零点偏移
反量化就是反过来:
r = (q - zero_point) × scale
嗯,这里要注意——scale 和 zero_point 的选择决定了量化精度。scale 越小,精度越高,但能表示的数值范围越小。
3.2.2 INT8 vs INT16 量化
我整理了一个对比表,帮你快速决策:
| 量化类型 | 位宽 | 精度损失 | 内存节省 | 计算速度 | 适用场景 |
|---|---|---|---|---|---|
| FP32 | 32 bit | 无 | 1x | 慢(无 FPU 时极慢) | PC 端训练 |
| INT16 | 16 bit | ~0.1% | 2x | 中等 | 对精度敏感的模型 |
| INT8 | 8 bit | ~0.5-2% | 4x | 快 | 大多数嵌入式场景 |
3.3 量化策略:怎么量化才不崩
量化不是一键搞定的事。我总结了三种常用策略,按推荐程度排序:
3.3.1 训练后量化(Post-Training Quantization, PTQ)
这是最简单的方法。训练完模型后,直接用校准数据集算 scale 和 zero_point。
步骤:
- 准备几百张校准图片(或数据样本)。
- 跑一遍推理,收集每层的激活值分布。
- 根据分布计算 scale 和 zero_point。
- 把权重和激活值都量化。
优点:快,不需要重新训练。
缺点:精度损失可能较大,尤其是小模型。
3.3.2 量化感知训练(Quantization-Aware Training, QAT)
这个方法更稳。在训练过程中模拟量化操作,让模型自己适应量化误差。
核心思路:
# 伪代码示例
for each batch:
# 前向传播时模拟量化
fake_quantized_weights = fake_quantize(weights)
output = forward(fake_quantized_weights)
loss = compute_loss(output, target)
# 反向传播时用直通估计器(STE)
loss.backward()
optimizer.step()
优点:精度损失小,通常能控制在 0.5% 以内。
缺点:需要重新训练,时间长。
3.3.3 混合精度量化
有些层对量化敏感,有些层不敏感。混合精度量化就是——敏感层用 INT16,不敏感层用 INT8。
我常用的判断方法:
- 第一层和最后一层:通常对量化最敏感,建议用 INT16 或 FP32。
- 残差连接层:容易累积误差,建议用 INT16。
- 中间卷积层:可以用 INT8。
3.4 在 STM32 上选择合适模型
选模型不是拍脑袋。我有一套固定的流程,分享给你:
3.4.1 三步选型法
- 第一步:确定芯片。先看项目用的是什么 STM32 芯片,查 Flash、RAM、算力。
- 第二步:估算模型大小。用公式算一下模型的理论内存占用,确保不超过芯片的 70%。
- 第三步:量化测试。先试 INT8 PTQ,如果精度不够,换 INT16 或 QAT。
3.4.2 实战案例
我之前做过一个关键词唤醒项目,用的是 STM32L4 系列。选型过程是这样的:
- 芯片:STM32L476,Flash 1MB,SRAM 128KB。
- 模型:先试了 MobileNetV1,参数量 4.2M,INT8 后 4.2MB,Flash 装不下。
- 换 SqueezeNet,参数量 1.2M,INT8 后 1.2MB,Flash 勉强够。
- 再试 MicroNet,参数量 0.3M,INT8 后 300KB,Flash 和 RAM 都够。
- 量化策略:INT8 PTQ,精度掉了 1.2%,但推理速度从 500ms 降到 80ms。
3.5 量化策略选择流程图
下面这张图总结了我常用的量化策略选择逻辑,你可以照着走:
3.6 量化后的验证
量化完一定要验证。我见过有人量化完直接部署,结果模型输出全是乱码。嗯,这里要注意——量化后的模型必须跑一遍完整的测试集。
验证步骤:
- 精度对比:对比量化前后模型在测试集上的精度。
- 输出分布检查:看量化后的输出分布是否和浮点模型一致。
- 边缘 case 测试:找一些极端样本(比如噪声很大的图片),看模型是否稳定。
3.7 本章小结
这一章我们聊了三个核心问题:
- 怎么选模型:先看芯片资源,再选轻量结构,最后量化测试。
- 量化原理:浮点到定点的映射,INT8 和 INT16 的取舍。
- 量化策略:PTQ 快但精度低,QAT 慢但精度高,混合精度是折中方案。
记住一句话:嵌入式部署不是做学术研究,而是做工程取舍。选对模型、用对量化策略,你的 STM32 就能跑出好效果。