4. AI模型基础与格式:TensorFlow/Keras模型简介、ONNX模型格式、TFLite模型格式、模型量化概念

做嵌入式AI,说白了就是要把训练好的模型塞进单片机里跑。但这里有个关键问题——你在PC上用Python训练出来的模型,STM32根本认不出来。这就好比你把一本英文小说直接扔给只懂中文的人看,中间缺了个翻译。

所以,搞懂模型格式和转换,是咱们这行的基本功。我个人习惯把这一章看作是「模型护照办理指南」——你得知道每种格式长什么样,它们之间怎么互转,以及怎么给模型「瘦身」才能塞进MCU。

4.1 TensorFlow/Keras模型:从训练到保存

先说说最常见的起点。大部分嵌入式AI项目,模型都是在TensorFlow或Keras里训练的。Keras作为TensorFlow的高级API,用起来确实顺手。

训练完成后,模型默认保存为 .h5 格式(HDF5格式)。这个文件里包含了网络结构、权重、优化器状态等所有信息。我刚开始做的时候,直接拿这个文件去转ONNX,结果踩了不少坑。

我的习惯:训练完成后,先用 model.summary() 看一眼网络结构,确认输入输出尺寸对不对。这一步能省掉后面很多调试时间。

保存模型的典型代码长这样:

# 保存整个模型(结构+权重+优化器状态)
model.save('my_model.h5')

# 只保存权重(推荐用于部署)
model.save_weights('my_model_weights.h5')

# 保存为SavedModel格式(TensorFlow 2.x推荐)
model.save('my_model_savedmodel', save_format='tf')

这里有个坑要注意——.h5 文件里包含了优化器状态,体积会大不少。如果你只是要部署推理,用 save_weights 就够了。我曾经有一次把带优化器的模型直接转ONNX,结果转换工具报了一堆莫名其妙的错,后来才发现是优化器状态在捣乱。

4.2 ONNX模型格式:模型界的通用语言

ONNX(Open Neural Network Exchange)是什么?你可以把它理解成模型界的「普通话」。不管你是用PyTorch、TensorFlow还是其他框架训练的,都能转成ONNX格式。这样,STM32Cube AI工具就能统一识别了。

我个人觉得,ONNX最大的价值在于「解耦」。它把模型从训练框架里解放出来,让部署工具只认这一种格式就行。

把Keras模型转成ONNX,一般用 tf2onnx 工具:

# 安装工具
pip install tf2onnx

# 命令行转换
python -m tf2onnx.convert \
    --saved-model ./my_model_savedmodel \
    --output ./my_model.onnx \
    --opset 13
注意:ONNX的算子集版本(opset)很重要。STM32Cube AI目前支持opset 11~15,我建议用opset 13,兼容性最好。选太高版本,STM32可能不认;选太低版本,有些新算子又没法用。

转换完成后,我习惯用 onnxruntime 先跑一遍推理,验证结果对不对:

import onnxruntime as ort
import numpy as np

sess = ort.InferenceSession('my_model.onnx')
input_name = sess.get_inputs()[0].name
output_name = sess.get_outputs()[0].name

# 构造随机输入
dummy_input = np.random.randn(1, 32, 32, 3).astype(np.float32)
result = sess.run([output_name], {input_name: dummy_input})
print(result[0].shape)

这一步能提前发现很多问题。比如输入输出维度对不上、算子不支持等等。我曾经有一次转换后才发现模型里有个自定义层,ONNX根本不认识,折腾了两天才搞定。

4.3 TFLite模型格式:专为边缘设备而生

TFLite(TensorFlow Lite)是Google专门为移动端和嵌入式设备设计的格式。它的核心优势有两个:体积小、推理快。

TFLite模型通常以 .tflite 后缀保存。它跟ONNX的区别在于——TFLite更「专一」,只服务于TensorFlow生态;而ONNX更「通用」,什么框架都能转。

从Keras模型转TFLite很简单:

import tensorflow as tf

# 加载Keras模型
model = tf.keras.models.load_model('my_model.h5')

# 转换TFLite
converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()

# 保存
with open('my_model.tflite', 'wb') as f:
    f.write(tflite_model)

但这里有个关键点——默认转换出来的是float32精度的模型,体积还是大。真正适合STM32的,是经过量化的TFLite模型。咱们下一节细说。

格式 后缀 适用场景 体积 STM32兼容性
Keras H5 .h5 训练、调试 大(含优化器) 不直接支持
ONNX .onnx 模型交换、部署 中等 通过Cube AI支持
TFLite .tflite 移动端、嵌入式 小(可量化) 通过Cube AI支持

4.4 模型量化概念:给模型「减肥」

量化,说白了就是把模型里的float32数字,变成int8或者uint8。你想想看,一个float32占4个字节,int8只占1个字节,体积直接缩到四分之一。推理速度也能快不少,因为MCU处理整数运算比浮点快得多。

但量化不是白给的——它会损失一点精度。我见过有人一听说精度会降,就不敢用量化了。其实大部分场景下,精度损失在1%~3%以内,人眼根本看不出来。

量化的核心参数有两个:

  • 缩放因子(scale):决定浮点数和整数之间的映射关系
  • 零点(zero_point):浮点0对应哪个整数值

举个例子,假设你的权重范围是[-1.0, 1.0],要映射到int8的[-128, 127]:

scale = (1.0 - (-1.0)) / (127 - (-128)) = 2.0 / 255 ≈ 0.00784
zero_point = round(0 / scale) + 128 = 128

这样,浮点值0.5就变成了 round(0.5 / 0.00784) + 128 ≈ 192。推理时再反向换算回来。

量化方式对比:
  • 训练后量化(Post-training Quantization):最简单,训练完直接转。适合大多数场景。
  • 量化感知训练(Quantization-Aware Training, QAT):训练时就模拟量化效果,精度更高。适合对精度要求苛刻的项目。
  • 动态范围量化:权重量化成int8,激活值还是float32。折中方案。

在TFLite里做训练后量化,代码很简单:

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]  # 或者 tf.int8
tflite_quant_model = converter.convert()

但要注意——如果你要量化成int8,需要提供代表性数据集(representative dataset),让转换工具知道输入数据的分布范围。不然量化出来的模型精度可能崩掉。我吃过这个亏,第一次量化时随便给了几个随机数,结果模型推理结果全乱套了。

4.5 知识结构总览

下面这张图,把咱们这一章的核心关系梳理清楚了。你可以把它当作模型转换的「路线图」:

AI模型格式转换与量化流程 AI模型格式转换与量化流程 训练框架 TensorFlow / Keras .h5 / SavedModel ONNX 格式 TFLite 格式 量化(int8/uint8) STM32Cube AI 模型转换与部署 关键说明: • ONNX 是通用中间格式,适合跨框架模型交换 • TFLite 专为边缘设备优化,支持量化压缩 • 量化可将模型体积缩小4倍,推理速度提升2~4倍 • STM32Cube AI 同时支持 ONNX 和 TFLite 作为输入
我的建议:如果你刚开始接触STM32Cube AI,建议先用ONNX格式走通全流程。因为ONNX的调试工具链更成熟,出问题了容易定位。等流程跑通了,再尝试TFLite量化版本,追求极致性能。

嗯,这一章的内容就到这里。模型格式和量化是嵌入式AI的基石,搞懂了这些,后面部署调试才能事半功倍。记住一句话:训练是艺术,转换是技术,部署是工程。每一步都马虎不得。