3. 模型准备与转换:从训练框架到STM32的桥梁

说实话,模型准备这一步,看着简单,坑却不少。我见过太多人花了两周训出一个漂亮的模型,结果发现导不进STM32——那种感觉,就像菜都炒好了发现没开火。今天咱们就把这条路捋顺了。

3.1 支持哪些框架?

STM32Cube.AI目前主要支持三个来源:Keras、TFLite和ONNX。我个人习惯用Keras做快速原型,用ONNX做跨平台部署。

框架 文件格式 STM32Cube.AI版本支持 我的推荐场景
Keras (TensorFlow 2.x) .h5 全部版本 快速验证、教学演示
TFLite .tflite ≥ 7.0.0 量化模型、移动端迁移
ONNX .onnx ≥ 7.1.0 PyTorch用户、跨框架协作
小经验:如果你用的是PyTorch,别硬转Keras。先导出ONNX,再喂给STM32Cube.AI,这条路我走过十几回了,最稳。

3.2 模型导出格式详解

每种框架的导出方式不太一样。我挑最常用的三种说一下。

3.2.1 Keras (.h5)

最简单,一行代码的事:

model.save('my_model.h5')

嗯,这里要注意:save和save_weights是两回事。前者保存完整图结构+权重,后者只存权重。STM32Cube.AI要的是前者,我刚开始就搞混过。

3.2.2 TFLite (.tflite)

这个格式的好处是自带量化信息。导出时记得指定输入输出形状:

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8
tflite_model = converter.convert()
with open('model_quant.tflite', 'wb') as f:
    f.write(tflite_model)
避坑:我曾经导出一个TFLite模型,在PC上推理完全正常,烧到STM32里输出全是0。查了两天,发现是输入张量的量化参数(zero_point和scale)没对齐。导出时一定要打印出来核对一遍。

3.2.3 ONNX (.onnx)

PyTorch用户看这里:

import torch
dummy_input = torch.randn(1, 3, 32, 32)
torch.onnx.export(model, dummy_input, "model.onnx",
                  input_names=['input'],
                  output_names=['output'],
                  dynamic_axes={'input': {0: 'batch_size'},
                                'output': {0: 'batch_size'}})

为什么我推荐ONNX?因为它的算子覆盖面广,而且STM32Cube.AI对ONNX的支持越来越好了。说白了,ONNX就是模型界的通用语言。

3.3 输入输出张量约束

这是最容易翻车的地方。STM32的RAM和Flash有限,对张量形状有硬性要求。

约束项 具体要求 踩坑记录
输入维度 固定4维 (NCHW或NHWC) 我试过3维输入,直接报错
Batch size 必须为1 推理时batch>1会内存溢出
数据类型 float32 / int8 / uint8 float16不支持,别踩
最大张量尺寸 取决于芯片RAM(通常≤256KB) F4系列建议输入≤128x128x3
核心原则:输入输出张量的形状、数据类型、内存布局,必须在导出前就固定下来。STM32不像PC有虚拟内存,它没有「试错」的余地。

3.4 量化感知训练(QAT)简介

你想想看,一个float32的模型,直接转成int8,精度掉多少?我做过测试,有的模型掉5%,有的直接掉到没法用。这时候就需要QAT了。

QAT说白了就是:在训练过程中模拟量化误差,让模型自己学会适应低精度。这样部署到STM32上时,精度损失能控制在1-2%以内。

3.4.1 QAT怎么做?

TensorFlow里有一套完整的QAT API:

import tensorflow_model_optimization as tfmot

# 在原始模型上应用QAT
quantize_model = tfmot.quantization.keras.quantize_model
qat_model = quantize_model(original_model)

# 用较小的学习率继续训练
qat_model.compile(optimizer='adam', loss='categorical_crossentropy')
qat_model.fit(x_train, y_train, epochs=5, batch_size=32)

# 导出时自动包含量化信息
converter = tf.lite.TFLiteConverter.from_keras_model(qat_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_qat_model = converter.convert()

3.4.2 什么时候必须用QAT?

  • 模型对精度极其敏感(比如关键部位检测)
  • 部署芯片RAM极小(比如STM32F0系列)
  • 后训练量化(PTQ)掉点超过3%
我的习惯:先做PTQ试试水。如果精度掉得不多(<2%),就不折腾QAT了。如果掉得厉害,再上QAT。别一上来就QAT,训练时间翻倍不说,调参也麻烦。

3.5 知识体系总览

下面这张图,把模型准备到部署的整个流程串起来了。我建议你保存下来,每次做项目前看一眼。

模型准备与转换流程 训练框架 Keras / PyTorch / TF 训练 + 验证 导出格式 .h5 / .tflite / .onnx 固定输入输出形状 量化决策 PTQ vs QAT 精度评估 STM32部署 Cube.AI导入 RAM/Flash优化 关键检查点 ① 输入输出形状固定 ② 数据类型对齐 ③ 量化参数核对 ④ 内存预算确认

3.6 本章小结

模型准备这一步,说白了就是给STM32「喂对饭」。框架选对、格式导对、张量约束卡对、量化策略选对——四件事都做对了,后面部署就顺了。

我个人建议:第一次做项目时,先用Keras导出.h5,走通全流程。等熟悉了,再尝试TFLite量化或ONNX跨平台。别一上来就搞QAT,那玩意儿是锦上添花,不是雪中送炭。

最后提醒一句:我曾经因为输入张量的shape写错一个维度,在Cube.AI里卡了整整两天。导出前,用model.summary()和print(input_shape)反复确认三遍。这个习惯救过我很多次。