2. 模型转换基础:从 TensorFlow/Keras 到 .tflite 再到 STM32
好,咱们直接进入正题。模型转换这件事,说白了就是把一个「大胖子」模型,压缩成一个能在 STM32 上跑的「小瘦子」。我刚开始接触这块时,也踩过不少坑。今天就把我这几年的经验,一次性说清楚。
2.1 为什么要转换?直接跑 Keras 模型不行吗?
你想想看,STM32 的 Flash 通常就几百 KB 到 2MB。一个标准的 Keras 模型,光权重文件就可能几十 MB。这还不算,Keras 模型依赖 Python 运行时,而 STM32 跑的是 C 代码。所以,转换是必须的。
我个人习惯把转换过程分成三步:
- 训练阶段:用 TensorFlow/Keras 训练出高精度模型
- 压缩阶段:转换成 .tflite 格式,顺便做量化
- 部署阶段:用 STM32CubeAI 生成 C 代码
核心要点:.tflite 是 Google 为边缘设备设计的中间格式。它不依赖 Python,体积小,支持硬件加速。
2.2 从 Keras 到 .tflite:最简单的转换
假设你有一个训练好的 Keras 模型,叫 my_model.h5。转换代码其实就几行:
import tensorflow as tf
# 加载 Keras 模型
model = tf.keras.models.load_model('my_model.h5')
# 转换器
converter = tf.lite.TFLiteConverter.from_keras_model(model)
# 默认转换(浮点型)
tflite_model = converter.convert()
# 保存
with open('model_float.tflite', 'wb') as f:
f.write(tflite_model)
嗯,这里要注意。上面这个转换出来的 .tflite 还是浮点型的。体积虽然比 Keras 小一些,但对 STM32 来说还是太大。我在项目中遇到过,一个 10MB 的 Keras 模型,转成浮点 .tflite 后还有 8MB。STM32 根本放不下。
2.3 量化:让模型真正能跑在 STM32 上
量化,就是把模型的权重和激活值从 32 位浮点数,变成 8 位整数。效果怎么样?
| 指标 | 浮点模型 | 量化模型(int8) |
|---|---|---|
| 模型体积 | 10 MB | 2.5 MB |
| 推理速度 | 慢(无硬件加速) | 快 3-5 倍 |
| 精度损失 | 基准 | 通常 < 1% |
| STM32 兼容性 | 部分支持 | 完全支持 |
看到了吧?体积直接缩到 1/4,速度还快了好几倍。精度损失?我做过十几个项目,大部分场景下损失不到 0.5%。
量化有两种方式:
2.3.1 训练后量化(Post-training Quantization)
这是最常用的方法。模型训练完后,直接转换时做量化。不需要重新训练。
converter = tf.lite.TFLiteConverter.from_keras_model(model)
# 开启 int8 量化
converter.optimizations = [tf.lite.Optimize.DEFAULT]
# 提供代表性数据集(用于校准)
def representative_dataset():
for _ in range(100):
data = np.random.randn(1, 32, 32, 3).astype(np.float32)
yield [data]
converter.representative_dataset = representative_dataset
# 目标类型设为 int8
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
tflite_quant_model = converter.convert()
我的经验:代表性数据集很重要。我建议用 100-200 张真实场景的图片做校准。如果随便用随机数,量化后的精度可能会掉 2-3%。
2.3.2 量化感知训练(Quantization Aware Training)
如果精度要求极高,可以用这个方法。它在训练过程中就模拟量化效果,让模型学会适应低精度。
import tensorflow_model_optimization as tfmot
# 在模型上应用量化感知训练
quantize_model = tfmot.quantization.keras.quantize_model
# 假设 model 是你的原始模型
q_aware_model = quantize_model(model)
# 重新编译和训练
q_aware_model.compile(optimizer='adam', loss='sparse_categorical_crossentropy')
q_aware_model.fit(x_train, y_train, epochs=5)
# 转换时不需要再量化
converter = tf.lite.TFLiteConverter.from_keras_model(q_aware_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
避坑指南:我曾经在一个语音识别项目里,直接用了训练后量化,精度掉了 3%。后来改用量化感知训练,只掉了 0.8%。如果你的模型对精度特别敏感,建议直接用 QAT。
2.4 验证转换后的模型
转换完了,别急着往 STM32 上烧。先验证一下精度。
import numpy as np
# 加载 .tflite 模型
interpreter = tf.lite.Interpreter(model_path='model_quant.tflite')
interpreter.allocate_tensors()
# 获取输入输出信息
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
# 准备测试数据
test_data = np.random.randn(1, 32, 32, 3).astype(np.float32)
# 推理
interpreter.set_tensor(input_details[0]['index'], test_data)
interpreter.invoke()
output = interpreter.get_tensor(output_details[0]['index'])
print('推理结果:', output)
这一步我建议用 1000 张测试集图片,对比浮点模型和量化模型的输出。如果准确率差异在 1% 以内,就可以放心部署了。
2.5 从 .tflite 到 STM32:CubeAI 的导入
打开 STM32CubeMX,选择你的芯片型号。在「Software Packs」里找到 X-CUBE-AI,点击「Add」。然后:
- 选择「Network」标签页
- 点击「Add network」
- 选择你刚生成的 .tflite 文件
- CubeAI 会自动分析模型,显示 RAM/Flash 占用
- 点击「Analyze」查看详细报告
关键检查点:看 CubeAI 报告里的「RAM estimation」和「Flash estimation」。如果超出芯片规格,要么换更大的芯片,要么进一步压缩模型。
生成代码后,你会得到一个 network.h 和 network.c。调用推理函数很简单:
#include "network.h"
// 输入输出缓冲区
float input_data[32*32*3];
float output_data[10];
// 初始化
AI_Network_Init();
// 推理
AI_Network_Run(input_data, output_data);
// output_data 就是分类结果
2.6 常见问题与解决方案
- 模型太大:试试更激进的量化,或者减少网络层数。我有个项目从 8 层减到 5 层,精度只掉了 1%,体积减了 40%。
- 推理太慢:检查是否开启了硬件加速。STM32 的 Cortex-M4/M7 有 DSP 指令,CubeAI 默认会启用。
- 精度异常:先确认浮点模型没问题,再检查量化校准数据集是否合理。
好了,模型转换这块的核心内容就这些。下一章我们会深入 CubeAI 的配置细节,包括如何调整优化选项、如何分析性能报告。到时候我会分享一个实际项目的完整流程,保证让你少走弯路。