3. 模型选型原则:在边缘设备的镣铐下跳舞

做嵌入式AI,说白了就是戴着镣铐跳舞。你模型再牛,跑不起来也是白搭。我刚开始接触STM32Cube.AI时,就犯过这个错——在PC上跑得飞快的模型,一部署到芯片上直接内存溢出。嗯,从那以后我学乖了,选模型前先看三样东西:算力、内存、量化方式。

3.1 边缘设备的算力约束

STM32系列芯片的算力跨度很大。从低端的Cortex-M0到高端的Cortex-M7,甚至带NPU的STM32N6,性能差了几十倍。选模型前,你得先搞清楚目标芯片的算力天花板。

芯片系列 典型频率 算力(DMIPS) 适合模型规模
STM32F0/G0 48 MHz ~40 极轻量(<10K参数)
STM32F4 168 MHz ~210 轻量(<100K参数)
STM32H7 480 MHz ~1027 中等(<500K参数)
STM32N6 800 MHz + NPU ~6000 较大(<5M参数)

我个人习惯的做法是:先估算模型的计算量(MACs),再除以芯片的峰值算力,留出30%的余量。举个例子,一个MobileNetV1大约需要569M MACs,在STM32H7上跑,理论推理时间大约是569M / (1027M * 0.7) ≈ 0.79秒。如果应用要求实时性,这个数字就得重新掂量了。

小技巧: 用STM32Cube.AI的Benchmark功能,可以直接在目标芯片上跑模型,得到真实的推理时间。别光看理论值,实际跑一遍最靠谱。

3.2 内存与Flash限制

边缘设备的内存和Flash是硬约束。STM32F4系列通常只有128KB-1MB的Flash和64KB-256KB的SRAM。模型参数、中间激活值、输入输出缓冲区,全得挤在这点空间里。

我遇到过最头疼的情况:模型参数占用了80%的Flash,结果输入图像缓冲区没地方放了。后来只能把输入图像从640x480压缩到320x240,虽然精度掉了点,但总算能跑了。

这里有个经验公式:模型参数量(字节) + 中间激活值(字节) + 输入输出缓冲区(字节) ≤ 芯片可用内存的80%。剩下的20%留给系统栈和RTOS。

内存占用估算:
  • 模型参数:参数量 × 量化位宽(如INT8为1字节)
  • 中间激活值:每层输出特征图大小 × 量化位宽
  • 输入输出缓冲区:图像分辨率 × 通道数 × 量化位宽

3.3 量化感知训练(QAT)与后训练量化(PTQ)

量化是让模型在边缘设备上跑起来的关键。说白了,就是把FP32的模型变成INT8甚至INT4的,这样模型体积能缩小4倍,推理速度也能快2-4倍。但量化会掉精度,怎么选?

量化方式 精度损失 训练成本 适用场景
PTQ(后训练量化) 1-3% 低(只需校准集) 模型较大、训练数据不足
QAT(量化感知训练) 0.5-1% 高(需重新训练) 精度要求高、模型较小

我个人建议:优先尝试PTQ。为什么?因为省事。你只需要准备几百张校准图片,跑一遍校准流程,就能得到量化后的模型。如果PTQ后的精度满足要求,那就没必要折腾QAT。

但如果你发现PTQ后精度掉了超过3%,或者模型本身就很轻量(比如MobileNetV2),那QAT就是更好的选择。QAT在训练过程中模拟量化误差,让模型学会适应量化噪声,精度损失通常能控制在1%以内。

避坑指南: 我曾经在一个语音识别项目上直接用了PTQ,结果精度掉了5%。后来发现是因为模型中有大量BatchNorm层,量化后分布偏移严重。换成QAT后,精度只掉了0.8%。所以,如果模型结构复杂(有残差连接、注意力机制等),建议直接上QAT。

3.4 模型选型决策流程

说了这么多,到底怎么选?我总结了一个决策流程,你照着走就行。

模型选型决策流程 确定目标芯片 评估算力与内存上限 选择候选模型结构 尝试PTQ量化 精度是否满足要求? 部署PTQ模型 改用QAT 说明: PTQ优先,精度不满足时再考虑QAT。 复杂模型(含残差/注意力)建议直接上QAT。

3.5 实战中的量化选择

在STM32Cube.AI中,PTQ和QAT都有对应的工具链支持。PTQ可以直接在STM32Cube.AI Developer Desktop中完成,导入模型后选择量化选项,提供校准数据集就行。

QAT则需要你在训练框架(如TensorFlow、PyTorch)中完成。以TensorFlow为例,你需要使用tf.quantization.quantize_model或tfmot库进行量化感知训练。

# TensorFlow QAT示例
import tensorflow_model_optimization as tfmot

# 加载预训练模型
model = tf.keras.models.load_model('model.h5')

# 应用量化感知训练
quantize_model = tfmot.quantization.keras.quantize_model
qat_model = quantize_model(model)

# 重新训练(少量epoch即可)
qat_model.compile(optimizer='adam', loss='categorical_crossentropy')
qat_model.fit(train_data, train_labels, epochs=5)

# 导出为TFLite
converter = tf.lite.TFLiteConverter.from_keras_model(qat_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()

嗯,这里要注意:QAT训练时,学习率要调小一点,我一般用原学习率的1/10。不然量化模拟的噪声会让模型震荡,反而掉精度。

核心原则总结:
  • 算力不够?换更轻量的模型(如MobileNetV3、EfficientNet-Lite)
  • 内存不够?降低输入分辨率或减少通道数
  • 精度不够?从PTQ切换到QAT
  • 还不行?考虑模型剪枝或知识蒸馏

最后说一句:没有完美的模型,只有合适的模型。在边缘设备上,你永远在精度、速度、大小之间做权衡。我的经验是,先跑通再优化,别一开始就追求极致精度。毕竟,能跑起来的模型,才是好模型。