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
转换完成后,我习惯用 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的基石,搞懂了这些,后面部署调试才能事半功倍。记住一句话:训练是艺术,转换是技术,部署是工程。每一步都马虎不得。