1. 先搞清楚 ST 的 Model Zoo 到底装了什么
ST 官方这几年在嵌入式 AI 这条线上铺得很快,从 STM32Cube.AI 到 NanoEdge AI Studio,再到 X-CUBE-AI 扩展包,整个工具链已经能覆盖从模型转换、量化、部署到性能评估的完整流程。Model Zoo 就是这套体系里的一个“样板间”,里面预置了一批已经验证过、可以直接跑在 STM32 上的模型,涵盖关键词唤醒、人体活动识别、图像分类、异常检测等常见场景。
很多人第一次打开 Model Zoo 的仓库,看到一堆 .tflite、.onnx 和对应的 C 代码,第一反应就是:既然官方都给我准备好了,我直接拿来用不就行了,还费劲自己设计模型干嘛?这个想法不能说错,但只对了一半。Model Zoo 里的模型本质上是“参考实现”,它的价值在于让你快速验证工具链能不能跑通、板子能不能带动、内存够不够用,而不是让你直接拿去量产。
我见过不少团队,拿到 Model Zoo 里的关键词识别模型,直接烧进板子做产品 demo,结果现场环境稍微复杂一点,误唤醒率就飙到没法看。原因很简单:Model Zoo 的模型是在特定数据集上训练的,它的输入特征、采样率、噪声分布都和你实际场景对不上。模型结构可以复用,但权重和部分超参数必须根据你的数据重新调整。
所以这一节先把结论说清楚:Model Zoo 解决的是“从零到一”的问题,它让你不用从搭环境开始,但它不解决“从一到好”的问题。你要做产品,该设计的模型还是得设计,该调的参数还是得调。
1.1 Model Zoo 里到底有哪些类型的模型
目前 ST 生态里能见到的预置模型大致分三类。第一类是音频类,比如关键词识别(KWS)和音频事件检测,输入通常是 MFCC 或 Mel 频谱特征,模型体量很小,几十 KB 到几百 KB 不等,适合跑在 STM32F4、L4 这类中低端芯片上。第二类是传感器类,比如基于加速度计的姿态识别、跌倒检测、电机异常振动检测,输入是时序数据,常用 1D-CNN 或小型 RNN 结构。第三类是视觉类,比如手写数字识别、简单物体分类,输入是低分辨率灰度图,模型稍大,一般需要 STM32H7 或带 NPU 的 STM32N6 才能跑得流畅。
这三类模型的共同特点是:输入维度低、网络层数浅、参数量小。这不是 ST 偷懒,而是嵌入式设备的 RAM 和 Flash 就那么点,你不可能把 ResNet-50 塞进 STM32F103。Model Zoo 里的模型都是经过剪枝和量化处理的,很多还是 8 位整型量化版本,这样才能在 MCU 上跑到可接受的推理速度。
1.2 为什么有人觉得“不需要自己设计模型”
这个错觉主要来自两个地方。一是 ST 的 Cube.AI 工具确实做得太顺滑了,你把 ONNX 模型拖进去,它自动帮你转成 C 代码,还告诉你每层占多少 RAM、推理需要多少周期。整个过程不需要你写一行神经网络代码,看起来就像“模型设计”这件事已经被工具替代了。二是 Model Zoo 里的模型在官方测试集上表现都不错,准确率动辄 95% 以上,让人误以为拿过来就能用。
但官方测试集是干净的、受控的。你实际部署的环境里有风扇噪声、有振动、有温度漂移、有电源纹波。这些因素在测试集里不存在,但在现场天天发生。Model Zoo 的模型没有见过这些干扰,它的鲁棒性边界在哪里,你不做实验根本不知道。
2. 自己设计模型到底在设计什么
很多人把“设计模型”理解成“设计网络结构”,觉得就是要发明一个新的层或者一个新的连接方式。这个理解太窄了。在嵌入式 AI 场景下,模型设计至少包含四个层面:输入特征设计、网络结构选型、量化策略设计、后处理逻辑设计。这四个层面里,真正需要你从头发明的东西很少,大部分工作是在做选择和调优。
2.1 输入特征设计:比网络结构更重要的一步
我做过一个电机异常检测的项目,一开始直接拿原始振动信号喂给 1D-CNN,训练集准确率能到 98%,但现场测试只有 70% 出头。后来把输入换成 FFT 后的频域幅值,同样的网络结构,现场准确率直接拉到 92%。这个例子说明什么?说明在嵌入式场景下,特征工程往往比网络结构更能决定最终效果。
Model Zoo 里的模型通常已经固定了输入特征格式,比如 KWS 模型要求输入是 49 帧 × 13 维的 MFCC。但你的麦克风采样率、预加重系数、窗函数类型、帧移长度,这些参数都会影响 MFCC 的质量。如果你直接套用 Model Zoo 的预处理代码,但硬件配置和它不一样,特征分布就会偏移,模型表现自然下降。
所以自己设计模型的第一步,其实是设计一套和你的硬件匹配的特征提取流程。这个流程可以用 C 手写,也可以用 CMSIS-DSP 库加速。关键是你要保证训练时的特征提取和推理时的特征提取完全一致,否则量化后的模型会出现严重的精度损失。
2.2 网络结构选型:在精度和资源之间找平衡
嵌入式 AI 的网络结构选型,本质上是一个多目标优化问题。你要同时考虑准确率、推理延迟、RAM 占用、Flash 占用、功耗。这四个指标互相拉扯,你不可能同时让它们都最优。
我一般会先定一个资源预算。比如老板说这颗芯片只有 256KB RAM 可用,那你的模型激活值峰值就不能超过这个数。然后根据任务复杂度选一个基准结构,比如 KWS 任务先用 DS-CNN(深度可分离卷积)搭一个 5 层的小网络,跑一遍基线。如果准确率不够,再考虑加宽通道数或者加深层数;如果延迟超标,就减通道或者换更激进的量化策略。
这里有个经验值可以参考:在 STM32F4 上跑 1D-CNN,每层卷积的乘加次数控制在 100 万次以内,基本能保证 10ms 级别的推理延迟。超过这个量级,要么换 H7,要么就得重新设计结构。
2.3 量化策略设计:不是所有模型都能无损转 int8
Cube.AI 支持 float32 和 int8 两种推理模式。int8 的推理速度通常是 float32 的 3 到 5 倍,RAM 占用也小得多。但 int8 量化是有代价的,某些对数值精度敏感的层,比如 softmax 之前的全连接层,量化后会出现明显的精度下降。
我的做法是先用 float32 跑通整个流程,确认模型结构和特征提取没问题,然后再做 int8 量化。量化的时候逐层看 Cube.AI 给出的精度报告,如果某一层量化误差特别大,就考虑把这层保留为 float32,或者在这层前面加一个小的缩放因子。这种混合精度的做法在 Cube.AI 里是支持的,虽然会稍微增加一点代码复杂度,但能换来更好的精度表现。
2.4 后处理逻辑设计:模型输出不等于最终结果
Model Zoo 里的模型输出通常是一个概率向量,比如 KWS 模型输出 12 个关键词的概率。但你的产品逻辑可能要求“连续 3 帧置信度超过 0.8 才触发”,或者“检测到关键词后 500ms 内忽略其他输入”。这些逻辑不在模型里,而在你的应用代码里。
后处理设计的好坏,直接影响用户体验。我见过一个智能开关项目,模型本身准确率很高,但因为没有做去抖处理,用户说一句话就触发好几次。后来加了一个简单的滑动窗口投票机制,误触发率直接降了一个数量级。这个改动没有动模型一行代码,但效果比重新训练模型还明显。
3. 从 Model Zoo 到自有模型的完整实操路径
这一节我按实际项目流程走一遍,从选基准模型到最终部署,每一步都给出具体的操作和参数。你照着做,基本能复现一个可用的嵌入式 AI 模型。
3.1 第一步:选一个最接近的 Model Zoo 模型做基准
不要一上来就自己搭网络。先去 ST 的 Model Zoo 仓库里找和你任务最接近的模型。比如你要做关键词识别,就找 KWS 的模型;要做姿态识别,就找 HAR 的模型。找到之后,先不改任何东西,直接跑一遍官方提供的测试脚本,确认你的开发环境、Cube.AI 版本、板子配置都能正常工作。
这一步的目的是排除工具链问题。我遇到过好几次,模型精度不对,查了半天发现是 Cube.AI 版本和模型导出时的版本不匹配。先跑通官方流程,再改自己的东西,能省很多排查时间。
3.2 第二步:用你的数据重新训练基准模型
基准模型跑通后,把最后一层或者最后几层的权重重新训练。具体来说,你可以冻结前面的卷积层,只训练全连接层和输出层。这样做的好处是训练速度快,需要的样本量也少。如果你的数据量和官方数据集分布差异很大,那就解冻所有层做微调,但学习率要设小一点,比如 1e-4 或者 1e-5。
训练的时候要注意,你的验证集必须来自实际场景,不能只用公开数据集。我一般会留出 20% 的现场数据做验证,如果现场数据太少,就用数据增强来扩充,比如加噪声、做时间偏移、调整幅度。这些增强手段在音频和传感器数据上都很有效。
3.3 第三步:用 Cube.AI 做模型转换和资源评估
训练好的模型导出为 ONNX 格式,然后拖进 Cube.AI。Cube.AI 会给你一份详细的报告,包括每层的参数量、激活值大小、推理周期数。你要重点看两个数:一个是 RAM 占用峰值,一个是 Flash 占用总量。这两个数必须小于你芯片的可用资源,否则后面部署一定失败。
如果 RAM 超标,优先考虑减少网络层数或者降低通道数。如果 Flash 超标,优先考虑更激进的量化,比如把权重从 int8 降到 int4。但 int4 的精度损失比较大,一般只在极端资源受限的场景下用。
3.4 第四步:在板子上做实际推理测试
Cube.AI 生成的 C 代码集成到你的工程里,然后写一个简单的测试程序,把一段已知的测试数据喂进去,看输出和 PC 上的推理结果是否一致。如果不一致,大概率是特征提取的预处理代码有问题。这时候你要逐帧对比 PC 和板子上的中间特征值,找到第一个出现偏差的地方。
这个调试过程比较枯燥,但非常关键。我一般会在预处理代码里加一个宏开关,打开后把中间特征通过串口打印出来,和 PC 上的 Python 脚本输出做逐行对比。只要特征提取对齐了,后面的推理结果基本不会有大问题。
3.5 第五步:现场数据回流和迭代
模型部署到设备上之后,一定要留一个数据回流的通道。比如设备可以把推理置信度低的数据片段保存下来,定期上传到服务器。这些数据就是你下一轮迭代的训练素材。我做过的一个项目,第一版模型现场准确率只有 85%,回流了两轮数据重新训练后,第三版模型现场准确率到了 96%。
这个迭代过程是 Model Zoo 给不了你的。Model Zoo 的模型是静态的,而你的产品是动态演进的。只有建立起数据回流的闭环,你的模型才能越用越准。
4. 常见问题与排查技巧实录
嵌入式 AI 的坑很多,有些是工具链的问题,有些是硬件的问题,有些是算法本身的问题。这一节我把实际项目中遇到的高频问题整理出来,附上排查思路和解决方法。
4.1 模型转换失败:先看算子支持列表
Cube.AI 不是所有 ONNX 算子都支持。如果你用了自定义算子或者比较新的算子,转换的时候会报错。这时候先去 ST 的文档里查算子支持列表,确认你的模型里有没有不支持的算子。如果有,要么换一个等价的算子组合,要么自己写一个自定义层。
我遇到过一次,模型里用了HardSwish激活函数,Cube.AI 不支持,后来换成ReLU6就通过了。虽然精度稍微掉了一点,但整体影响不大。
4.2 推理结果和 PC 不一致:九成是预处理没对齐
这是最常见的问题。PC 上用 Python 做推理,结果正常;板子上跑同样的模型,结果完全不对。原因通常是预处理代码的差异,比如归一化系数不同、特征提取的窗函数不同、数据类型转换时的舍入方式不同。
排查方法很简单:在 PC 和板子上分别打印预处理后的第一个特征向量,逐元素对比。如果第一个元素就不一样,那就从采样率、窗长、帧移这些参数开始查。如果前面几个元素一样,后面开始不一样,那可能是浮点精度或者累加顺序的问题。
4.3 推理速度太慢:先看时钟配置和缓存
板子上推理一帧要几百毫秒,远超预期。这时候先检查芯片的主频是不是跑到了标称值,有些工程默认用的是内部 RC 振荡器,主频只有 16MHz,改成外部晶振加 PLL 之后能到 168MHz 甚至更高。然后检查 Cube.AI 生成的代码有没有启用 CMSIS-DSP 加速,有些配置下默认是不启用的。
如果时钟和加速都正常,但速度还是慢,那就得看模型本身了。用 Cube.AI 的报告看哪一层的周期数最多,通常是第一个卷积层或者全连接层。针对性地减少这层的通道数或者输入维度,往往能带来明显的速度提升。
4.4 精度突然下降:检查量化校准集
int8 量化需要一个校准集来统计每层的数值分布。如果你用的校准集和实际数据分布差异很大,量化后的精度就会崩。我一般会从训练集里随机抽 100 到 200 个样本做校准,确保校准集覆盖各种工况。
还有一个坑是校准集的预处理。如果你在校准时用了和推理时不一样的归一化参数,量化误差会非常大。这个细节很容易被忽略,但影响很致命。
4.5 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方向 |
|---|---|---|---|
| 模型转换报错 | 算子不支持 | 查 Cube.AI 算子支持列表 | 替换等价算子或自定义层 |
| PC 和板子结果不一致 | 预处理未对齐 | 逐元素对比中间特征 | 统一预处理参数和数据类型 |
| 推理速度慢 | 主频低或未启用加速 | 检查时钟树和 CMSIS-DSP 配置 | 提高主频、启用 DSP 加速 |
| int8 精度下降 | 校准集分布偏差 | 检查校准集来源和预处理 | 用现场数据做校准 |
| RAM 溢出 | 激活值峰值过高 | 看 Cube.AI 内存报告 | 减层、减通道、改量化策略 |
| 现场误触发多 | 后处理太敏感 | 统计触发间隔和置信度分布 | 加去抖、加滑动窗口投票 |
5. 我的个人经验:什么时候该用 Model Zoo,什么时候该自己设计
这个问题没有标准答案,但有一个判断原则:如果你的任务和 Model Zoo 里的某个模型高度相似,而且你的硬件配置和官方推荐一致,那你可以先用 Model Zoo 的模型快速验证产品概念。这个阶段的目标是“跑通”,不是“跑好”。
一旦进入产品化阶段,你就必须自己设计模型。因为产品化意味着你要面对真实的噪声、真实的用户行为、真实的硬件差异。这些因素 Model Zoo 没有覆盖,也覆盖不了。你自己设计的模型,哪怕结构比 Model Zoo 简单,只要特征提取和你的硬件匹配、后处理和你的产品逻辑匹配,最终效果大概率会比直接套用 Model Zoo 好。
我个人的习惯是:Model Zoo 用来学工具链,用来做基线对比,用来快速出 demo。真正要出货的模型,一定是从自己的数据里长出来的。这个过程没有捷径,但每一步都有方法可循。上面写的这些步骤和参数,都是我实际项目中验证过的,你照着走一遍,基本能避开大部分常见的坑。