第2章:TFLite模型基础
好,咱们正式开始动手了。这一章我带你走一遍从训练模型到转换成TFLite格式的完整流程。说白了,就是搞清楚你的模型是怎么从电脑里的"数字玩具"变成能在单片机上跑的"小精灵"的。
2.1 TensorFlow模型训练基础
先聊点基础的。TensorFlow这玩意儿,我刚开始接触的时候也觉得挺唬人。但用久了你会发现,核心就三件事:定义模型、编译模型、训练模型。
我个人习惯把训练过程想象成教小孩认数字。你给他看一堆数字图片,告诉他"这是1,这是2",他慢慢就学会了。模型训练也是这个道理——给它数据,告诉它正确答案,它自己调整内部参数。
核心三要素:
- 数据:得有足够多、足够好的样本
- 模型:神经网络结构,比如全连接层、卷积层
- 损失函数:衡量模型猜得准不准的尺子
嗯,这里要注意一点:训练集和测试集一定要分开。我曾经犯过这个错,把同一份数据又训练又测试,结果准确率99%,一上真实数据直接崩了。说白了就是模型"背答案"了,根本没学会。
2.2 Keras构建简单模型
Keras是TensorFlow的高级API,用起来特别顺手。我建议新手都从这里入手。你想想看,用几行代码就能搭出一个神经网络,多爽。
咱们来搭一个最简单的全连接网络,用于手写数字识别(MNIST数据集):
import tensorflow as tf
from tensorflow import keras
# 加载数据
mnist = keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 数据预处理
x_train = x_train.reshape(-1, 784).astype('float32') / 255.0
x_test = x_test.reshape(-1, 784).astype('float32') / 255.0
# 构建模型
model = keras.Sequential([
keras.layers.Dense(128, activation='relu', input_shape=(784,)),
keras.layers.Dense(64, activation='relu'),
keras.layers.Dense(10, activation='softmax')
])
# 编译模型
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# 训练模型
model.fit(x_train, y_train, epochs=5, batch_size=32, validation_split=0.1)
这段代码我解释一下:
- Sequential:顺序模型,一层接一层
- Dense:全连接层,128和64是神经元数量
- relu:激活函数,说白了就是给网络加点"非线性"
- softmax:输出层用的,把结果变成概率
我的小经验:激活函数别乱选。隐藏层用relu基本不会错,输出层看任务——分类用softmax,回归不用激活函数。我在项目里见过有人输出层用了relu,结果模型死活不收敛,折腾了两天才发现是这问题。
2.3 模型保存为SavedModel与H5格式
模型训练好了,得保存下来。TensorFlow支持两种主流格式:SavedModel和H5。
SavedModel格式:这是TensorFlow自家的标准格式,包含模型结构、权重、训练配置等。我建议在部署场景下优先用这个。
# 保存为SavedModel格式
model.save('my_mnist_model', save_format='tf')
# 保存为H5格式
model.save('my_mnist_model.h5')
H5格式:Keras的经典格式,就是一个文件,方便传输。但要注意,它不保存优化器状态,所以如果你想接着训练,得重新编译。
| 格式 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
| SavedModel | 完整保存,可部署 | 文件夹形式,文件多 | 生产部署 |
| H5 | 单个文件,方便分享 | 不保存优化器状态 | 模型交换、存档 |
注意:保存模型时,确保输入形状是固定的。我在项目中遇到过,模型保存时输入形状是(None, 784),但转换TFLite时要求固定形状,结果报错。解决办法是在保存前用model.build((None, 784))明确指定。
2.4 模型转换为FlatBuffer(tflite格式)
终于到重点了。TFLite Micro用的是FlatBuffer格式,说白了就是把模型"压缩"成一种特别紧凑的二进制格式,让单片机也能读懂。
转换过程分两步:
- 加载模型:从SavedModel或H5加载
- 转换并量化:用TFLiteConverter转换,可选量化
import tensorflow as tf
# 加载SavedModel模型
model = tf.saved_model.load('my_mnist_model')
# 创建转换器
converter = tf.lite.TFLiteConverter.from_saved_model('my_mnist_model')
# 可选:量化(减少模型大小)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
# 转换模型
tflite_model = converter.convert()
# 保存为.tflite文件
with open('model.tflite', 'wb') as f:
f.write(tflite_model)
print(f"模型大小:{len(tflite_model)} 字节")
这段代码里,converter.optimizations = [tf.lite.Optimize.DEFAULT]这行是关键。它告诉转换器做量化——把32位浮点数变成8位整数。效果怎么样?我实测过,模型大小能缩小4倍,速度提升2-3倍,准确率只下降不到1%。
量化前后的对比(以我的MNIST模型为例):
- 原始模型:约2.3MB
- 量化后:约580KB
- 准确率:从98.2%降到97.8%
- 推理速度:快了约2.5倍
为什么会这样?因为单片机没有浮点运算单元(FPU),处理浮点数特别慢。量化成整数后,直接用整数运算,快得多。
嗯,这里要提醒一句:量化不是万能的。如果你的模型对精度特别敏感(比如医疗诊断),量化后准确率下降可能不可接受。我建议先做小批量测试,看看效果再决定。
2.5 避坑指南
最后,分享几个我踩过的坑:
- 输入形状不匹配:训练时用的(28,28),转换时要求(1,28,28,1),多了一个batch维度和通道维度。解决办法:在转换前用
input_shape参数指定。 - 不支持的操作:有些TensorFlow操作TFLite不支持,比如某些自定义层。我建议尽量用标准操作,少用花哨的。
- 量化后精度暴跌:如果量化后准确率从95%掉到60%,说明模型对量化太敏感。可以试试
tf.lite.Optimize.OPTIMIZE_FOR_SIZE,或者只量化权重不量化激活。
好了,这一章的内容就到这儿。你学会了怎么用Keras搭模型、保存模型、转换成TFLite格式。下一章咱们就真刀真枪地部署到开发板上跑起来。
课后练习:用本章的代码,训练一个识别猫狗图片的模型(用CIFAR-10数据集),然后转换成TFLite格式。试试看量化前后模型大小和准确率的变化。