拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32嵌入式AI:Model Zoo之外,为何仍需自定义模型设计

STM32嵌入式AI:Model Zoo之外,为何仍需自定义模型设计 1. 当Model Zoo摆在面前为什么我还是选择自己动手第一次在STM32CubeMX的AI扩展包里看到ST官方Model Zoo的时候说实话我是有点兴奋的。里面已经预置了图像分类、目标检测、人体姿态估计、音频事件检测这些常见任务的模型配套的X-CUBE-AI工具链也能直接把训练好的模型转成C代码一键部署到STM32H7、STM32F4这些主流芯片上。乍一看好像真的不需要自己从头设计模型了——拿来即用省时省力。但实际项目做下来我的结论是Model Zoo是起点不是终点。它解决的是从零到一的问题让你快速验证嵌入式AI的可行性但真正落到产品里你几乎一定会遇到通用模型跑不动精度不够内存爆了推理时间超标这些具体问题。这时候自己设计或者改造模型的能力就变成了分水岭。这篇文章我想聊的不是要不要用Model Zoo而是在什么场景下Model Zoo够用、什么场景下必须自己动手、以及自己动手时到底该改什么。如果你正在用STM32做嵌入式AI相关的项目或者正在纠结要不要入坑边缘计算这篇内容应该能帮你少走一些弯路。2. Model Zoo到底给了我们什么又拿走了什么2.1 开箱即用的便利性背后是通用性的妥协ST的Model Zoo本质上是一批在公开数据集上训练好的参考模型覆盖了视觉和音频两大类任务。以视觉为例里面有人脸检测、手写数字识别、CIFAR-10分类这些经典模型输入分辨率从32x32到224x224不等参数量从几万到几百万都有。你只需要在CubeMX里勾选对应的模型X-CUBE-AI会自动帮你做量化、剪枝、内存布局优化最后生成一个network.c和network.h直接塞进工程就能跑。这个流程的便利性毋庸置疑。我试过在STM32H743上部署一个MobileNetV1的量化版本从选模型到跑通推理前后不到两个小时。但问题也恰恰在这里Model Zoo里的模型是为了展示能力设计的不是为了解决你的具体问题设计的。举个例子Model Zoo里有一个基于CIFAR-10的10分类模型输入32x32的RGB图像。如果你的项目是做工业质检要区分的是金属表面的划痕、凹坑、污渍这三类缺陷那这个模型对你来说基本没有直接价值。你需要的是在自己的数据集上重新训练而重新训练就意味着你要自己设计网络结构、自己调参、自己做量化感知训练。Model Zoo在这里能提供的只是一个参考架构和工具链验证。2.2 模型压缩的边界量化不是万能药很多人觉得Model Zoo里的模型已经做过量化了直接拿来用就行。但量化本身是有精度损失的尤其是当你的任务和原模型的训练分布差异较大时量化后的精度下降会更明显。我做过一个对比实验用Model Zoo里的MobileNetV1量化模型做CIFAR-10分类准确率大概在88%左右但当我用同样的架构在自己的数据集上重新训练并量化后准确率能到93%。这5个百分点的差距在嵌入式场景里往往就是能用和不能用的区别。更关键的是量化后的模型对输入数据的分布非常敏感。如果你的摄像头采集的图像亮度、对比度和训练集差异较大量化模型的输出会变得很不稳定。这时候你需要做的不是换模型而是重新设计数据增强策略和量化校准集。2.3 内存和算力的硬约束Model Zoo不会告诉你的事ST的Model Zoo在文档里会标注每个模型的参数量和FLOPs但不会告诉你在实际芯片上跑起来需要多少RAM和Flash。这个差距有时候是惊人的。以STM32F429为例它的SRAM只有256KBFlash是2MB。Model Zoo里一个稍微大一点的模型比如MobileNetV2的量化版本权重加上激活值很容易就超过300KB。这时候你面临的选择只有两个要么换芯片要么自己设计一个更小的模型。我自己就踩过这个坑。当时选了一个Model Zoo里的音频分类模型文档说参数量只有50K看起来很小。但实际部署后发现中间层的激活值占用了大量RAM导致系统频繁触发HardFault。后来用X-CUBE-AI的内存分析工具一看峰值RAM占用到了180KB而我的芯片只有128KB。最后只能自己重新设计了一个更紧凑的网络结构把峰值RAM压到了60KB以下。3. 什么情况下必须自己设计模型3.1 任务特异性强通用模型无法覆盖如果你的任务是一个高度垂直的场景比如基于STM32的电机异常振动检测、超声波测距中的多路径干扰识别、特定化学传感器的响应模式分类那Model Zoo里几乎不可能有现成的模型。这些任务的输入数据格式、特征分布、类别定义都是你独有的你必须自己设计网络结构。我做过一个基于STM32F407的超声波测距项目需要区分真实回波和多路径反射造成的虚假回波。这个任务本质上是一个二分类问题但输入不是图像而是ADC采集到的时间序列信号。Model Zoo里没有任何一个模型能直接处理这种一维时序信号。最后我自己设计了一个1D-CNN输入是512点的ADC采样序列经过三层卷积和两层全连接参数量控制在20K以内在F407上推理一次只要3ms。3.2 资源约束到了Model Zoo无法满足的程度STM32的产品线跨度很大从低端的F0、G0系列到高端的H7系列RAM从几KB到1MB不等。Model Zoo里的模型主要是为H7和F4系列设计的如果你用的是F0或者G0那基本不用考虑直接部署。我手头有一个基于STM32G031的项目芯片只有32KB Flash和8KB RAM。这种资源条件下任何Model Zoo里的模型都跑不起来。你必须自己设计一个极简的网络比如只有一层卷积加一层全连接参数量控制在2K以内输入分辨率降到16x16甚至8x8。这种设计没有现成的参考只能根据任务特点一点点试。3.3 需要在线学习或自适应调整Model Zoo里的模型都是静态的部署后权重就固定了。但如果你的应用场景需要在线学习或者自适应调整比如根据环境变化自动调整阈值、根据用户习惯个性化推荐那你就需要设计一个支持增量学习的网络结构。在STM32上做在线学习是很有挑战的因为RAM和算力都有限。我试过用STM32H7做简单的在线微调只更新最后一层的权重用SGD优化器学习率设得很小。虽然效果有限但在一些缓慢变化的场景里比如温度漂移补偿确实有用。这种场景下Model Zoo完全帮不上忙你必须自己设计网络的可训练部分和冻结部分。4. 自己设计模型时我在STM32上踩过的那些坑4.1 输入分辨率不是越高越好刚开始做嵌入式视觉的时候我总想着把输入分辨率设高一点觉得这样精度会更好。结果在STM32F429上跑一个128x128的输入推理一次要200多毫秒完全达不到实时性要求。后来我做了个对比实验同一个任务输入分辨率从128x128降到64x64准确率只掉了2个百分点但推理时间缩短到了原来的四分之一。再降到32x32准确率掉了8个百分点但推理时间又缩短了一半。最后我选了64x64作为折中方案。这里的关键是嵌入式场景里输入分辨率对推理时间的影响是平方级的。因为卷积层的计算量和特征图面积成正比而特征图面积和输入分辨率的平方成正比。所以每降低一半分辨率计算量就降到四分之一。4.2 通道数要抠着用Model Zoo里的模型动辄用64、128、256这样的通道数但在STM32上通道数直接决定了权重大小和计算量。我的经验是第一层卷积的通道数可以稍微多一点比如16或32因为输入分辨率高通道数少了会丢失太多信息后面的层通道数要严格控制8或16就够了。我设计过一个用于STM32F103的极简CNN结构是这样的输入32x32灰度图第一层卷积8个3x3核第二层卷积16个3x3核然后全局平均池化最后全连接输出。整个模型参数量只有3K左右在F103上跑一次大概15ms。这个性能对于很多低速场景比如每秒采集几次的传感器分类已经足够了。4.3 激活函数的选择有讲究ReLU是默认选择但在量化模型里ReLU的输出范围是[0, ∞)这会导致量化时的动态范围很大精度损失也大。我后来改用ReLU6也就是把输出限制在[0, 6]量化后的精度明显更稳定。另外sigmoid和tanh在嵌入式场景里要慎用因为它们的计算涉及指数运算在STM32上没有硬件加速的话会非常慢。如果非要用可以考虑用查表法或者分段线性近似。4.4 池化层的位置和类型最大池化比平均池化更常用因为它在量化时更友好。但池化层的位置很关键如果在网络早期就做池化会丢失太多空间信息如果在网络后期才做特征图太大计算量下不来。我的经验是对于32x32的输入可以在第二层卷积后做一次2x2的最大池化把特征图降到16x16然后再做一次卷积再池化到8x8最后用全局平均池化代替全连接层这样参数量最少。5. 从Model Zoo到自定义模型一个完整的改造实例5.1 任务定义与基线选择假设我们要做一个基于STM32F407的手势识别项目输入是30x30的灰度图像输出是5类手势石头、剪刀、布、OK、点赞。Model Zoo里有一个基于CIFAR-10的10分类模型输入32x32我们可以把它作为基线。基线模型的结构大概是Conv(32, 3x3) - Conv(32, 3x3) - MaxPool(2x2) - Conv(64, 3x3) - Conv(64, 3x3) - MaxPool(2x2) - FC(256) - FC(10)。参数量大概在200K左右在F407上跑一次大概80ms。5.2 结构精简砍掉冗余层首先CIFAR-10是10分类我们只需要5分类所以最后一层全连接从10改成5。然后两个连续的3x3卷积可以合并成一个5x5卷积或者直接砍掉一个因为对于30x30的小输入两层3x3卷积的感受野已经覆盖了大部分区域。我最后的做法是Conv(16, 3x3) - MaxPool(2x2) - Conv(32, 3x3) - MaxPool(2x2) - Conv(32, 3x3) - GlobalAvgPool - FC(5)。参数量降到了15K左右推理时间降到了12ms。5.3 量化感知训练让精度不掉直接对训练好的浮点模型做训练后量化精度通常会掉3到5个百分点。我的做法是在训练阶段就模拟量化过程也就是量化感知训练。具体来说在TensorFlow或PyTorch里插入伪量化节点让网络在训练时就适应量化带来的误差。这一步很关键因为STM32上的X-CUBE-AI默认就是做8位整数量化。如果你不做量化感知训练部署后的精度损失可能会让你怀疑人生。我实测下来做了量化感知训练后精度只掉了0.5个百分点基本可以接受。5.4 内存布局优化X-CUBE-AI的隐藏技巧X-CUBE-AI在生成代码时会默认把权重放在Flash里激活值放在RAM里。但你可以通过配置把一些中间层的激活值也放到Flash里用时间换空间。具体操作是在CubeMX的X-CUBE-AI配置里把Memory pool选项改成Flash然后设置合适的对齐参数。另外权重的排列顺序也会影响推理速度。X-CUBE-AI默认是按行优先排列但STM32的硬件加速器比如H7系列的Chrom-ART可能对特定的排列方式更友好。这个需要根据具体芯片的手册来调。6. 自己设计模型时那些文档里不会写的经验6.1 先跑通工具链再优化模型很多人一上来就想着设计一个完美的网络结构结果在工具链上卡了好几天。我的建议是先用Model Zoo里的一个简单模型跑通整个流程从训练、量化、转换到部署确保每一步都走通了然后再去改网络结构。这样做的好处是当你遇到问题时你可以快速判断是模型结构的问题还是工具链的问题。我见过太多人把工具链的bug当成模型的问题白白浪费了很多时间。6.2 数据比模型重要在嵌入式AI里数据的重要性怎么强调都不为过。你的模型再精巧如果训练数据不能覆盖实际场景的多样性部署后一定会翻车。我的做法是在数据采集阶段就考虑量化后的影响。比如如果我知道部署时会做8位量化那我在采集数据时就会刻意增加一些低对比度、高噪声的样本让模型在训练时就适应这些恶劣条件。6.3 留出足够的余量嵌入式系统的资源是动态变化的你的模型可能只占用了当前RAM的70%但加上系统任务、通信缓冲、日志输出之后就可能超过100%。所以设计模型时一定要留出至少30%的资源余量。我一般会这样估算模型峰值RAM占用不超过总RAM的50%Flash占用不超过总Flash的60%。这样即使后续增加功能也不会因为资源不足而被迫重构。6.4 测试要覆盖边界条件嵌入式AI的测试和纯软件测试不一样你需要考虑电压波动、温度变化、电磁干扰这些物理因素对推理结果的影响。我做过一个实验同一个模型在室温下准确率是95%但在-20℃的环境下准确率掉到了82%。原因是ADC的参考电压随温度漂移导致输入数据的分布发生了变化。所以如果你的产品要在恶劣环境下工作一定要做温度循环测试和电压拉偏测试确保模型在这些边界条件下仍然稳定。7. 关于还需要自己设计模型吗的最终判断回到标题的问题ST已经有Model Zoo了我们还需要自己设计模型吗我的答案是取决于你的项目阶段和资源约束。如果你是在做原型验证、教学演示、或者资源非常充裕的高端芯片项目Model Zoo完全够用甚至可以说是最佳选择。它帮你省去了大量重复劳动让你能快速看到嵌入式AI的效果。但如果你是在做量产产品、资源受限的低端芯片、或者任务高度垂直的场景那自己设计模型几乎是必然的选择。Model Zoo在这里的角色更多是提供一个参考架构和工具链验证的起点而不是终点。我自己在实际项目中的体会是先用Model Zoo跑通流程建立信心然后根据具体约束逐步替换和优化模型结构最后通过量化感知训练和内存布局优化把性能压到极致。这个过程没有捷径但每一步的踩坑经验都会成为你后续项目的宝贵资产。最后再分享一个小技巧如果你不确定自己的模型设计是否合理可以先用X-CUBE-AI的Validation功能在PC上模拟推理对比浮点模型和量化模型的输出差异。如果差异在可接受范围内再部署到芯片上。这样可以避免反复烧录调试的时间浪费。
返回列表