模型选型与量化策略:从浮点到定点

模型选型这件事,说白了就是「在精度和速度之间找平衡」。我刚开始做 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 以下。
我的经验: 在 STM32F4 上,我建议优先试 SqueezeNet 或 MicroNet。H7 系列可以上 MobileNetV2。别一上来就试 ResNet,大概率跑不动。

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 快 大多数嵌入式场景
避坑指南: 我曾经在一个语音识别模型上直接用了 INT8 量化,结果精度掉了 5%。后来换成 INT16,精度只掉了 0.3%。所以——如果模型对精度非常敏感,先试 INT16。

3.3 量化策略:怎么量化才不崩

量化不是一键搞定的事。我总结了三种常用策略,按推荐程度排序:

3.3.1 训练后量化(Post-Training Quantization, PTQ)

这是最简单的方法。训练完模型后,直接用校准数据集算 scale 和 zero_point。

步骤:

  1. 准备几百张校准图片(或数据样本)。
  2. 跑一遍推理,收集每层的激活值分布。
  3. 根据分布计算 scale 和 zero_point。
  4. 把权重和激活值都量化。

优点:快,不需要重新训练。
缺点:精度损失可能较大,尤其是小模型。

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% 以内。
缺点:需要重新训练,时间长。

我的建议: 如果时间充裕,优先用 QAT。我在一个手势识别项目里,PTQ 掉了 3% 的精度,换成 QAT 后只掉了 0.2%。这差距在嵌入式场景里可能就是「能用」和「不能用」的区别。

3.3.3 混合精度量化

有些层对量化敏感,有些层不敏感。混合精度量化就是——敏感层用 INT16,不敏感层用 INT8。

我常用的判断方法:

  • 第一层和最后一层:通常对量化最敏感,建议用 INT16 或 FP32。
  • 残差连接层:容易累积误差,建议用 INT16。
  • 中间卷积层:可以用 INT8。

3.4 在 STM32 上选择合适模型

选模型不是拍脑袋。我有一套固定的流程,分享给你:

3.4.1 三步选型法

  1. 第一步:确定芯片。先看项目用的是什么 STM32 芯片,查 Flash、RAM、算力。
  2. 第二步:估算模型大小。用公式算一下模型的理论内存占用,确保不超过芯片的 70%。
  3. 第三步:量化测试。先试 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。
经验之谈: 选模型时,别只看参数量。还要看激活值大小。有些模型参数量小,但特征图很大,RAM 会爆。我建议用 STM32Cube.AI 的「内存分析」功能,提前模拟一下。

3.5 量化策略选择流程图

下面这张图总结了我常用的量化策略选择逻辑,你可以照着走:

量化策略选择流程图 量化策略选择流程图 开始量化 精度要求高吗? 是 训练时间充裕吗? 是 使用 QAT 量化 否 混合精度量化 否 使用 PTQ 量化 INT8 量化 部署到 STM32 提示:如果 INT8 精度不够,回退到 INT16 或混合精度。如果还不行,考虑换模型结构。

3.6 量化后的验证

量化完一定要验证。我见过有人量化完直接部署,结果模型输出全是乱码。嗯,这里要注意——量化后的模型必须跑一遍完整的测试集。

验证步骤:

  1. 精度对比:对比量化前后模型在测试集上的精度。
  2. 输出分布检查:看量化后的输出分布是否和浮点模型一致。
  3. 边缘 case 测试:找一些极端样本(比如噪声很大的图片),看模型是否稳定。
避坑指南: 我曾经在一个模型上发现,量化后大部分样本精度正常,但有个别样本输出完全错误。后来查出来是某个层的激活值分布太宽,scale 选得太大。解决办法是——用更大的校准集,或者手动调整 scale。

3.7 本章小结

这一章我们聊了三个核心问题:

  • 怎么选模型:先看芯片资源,再选轻量结构,最后量化测试。
  • 量化原理:浮点到定点的映射,INT8 和 INT16 的取舍。
  • 量化策略:PTQ 快但精度低,QAT 慢但精度高,混合精度是折中方案。

记住一句话:嵌入式部署不是做学术研究,而是做工程取舍。选对模型、用对量化策略,你的 STM32 就能跑出好效果。