4、模型训练与导出:使用Keras训练一个简单的正弦波预测模型
在TFLite Micro的Hello World示例中,核心任务是让微控制器(MCU)根据输入的时间步预测正弦波的下一个值。这一看似简单的任务,实际上完整地串联了从数据生成、模型设计、训练评估到格式转换的深度学习全流程。本章将聚焦于如何在PC端使用Keras完成这一流程,并最终导出为适合嵌入式部署的TensorFlow Lite格式(.tflite)。我们将深入探讨每一个环节的工程细节,确保你不仅知道“怎么做”,更理解“为什么这么做”。
4.1 数据生成:构造高质量的正弦波样本集
核心原理
正弦波预测本质上是时间序列回归问题。我们需要将连续的正弦波信号离散化为一系列采样点,然后构造“输入-输出”对:给定当前时刻的值 \( x_t \),预测下一个时刻的值 \( x_{t+1} \)。在Hello World案例中,为了降低MCU的计算负担,通常采用单步预测策略,即输入一个浮点数,输出一个浮点数。数据质量直接决定了模型的上限,因此生成数据时需关注采样密度、数值范围和样本多样性。
操作流程
- 确定采样参数:设定采样频率(如1000个点/周期)和周期数(如5个完整周期),生成原始正弦波序列。
- 构造数据集:将序列转换为特征矩阵 \( X \) 和标签向量 \( Y \)。对于第 \( i \) 个样本,\( X[i] = sin(i \times \Delta t) \),\( Y[i] = sin((i+1) \times \Delta t) \)。
- 数据归一化:将数据缩放到 [-1, 1] 区间(正弦波天然满足此条件),这有助于加速模型收敛。
- 划分训练/验证集:按 80/20 比例随机划分,确保验证集覆盖不同相位区域。
import numpy as np
# 生成正弦波数据
sampling_rate = 1000 # 每周期采样点数
num_periods = 5
x = np.linspace(0, 2 * np.pi * num_periods, sampling_rate * num_periods)
y = np.sin(x)
# 构造输入输出对
X = y[:-1].reshape(-1, 1) # 当前值
Y = y[1:].reshape(-1, 1) # 下一个值
# 划分数据集
split_idx = int(len(X) * 0.8)
X_train, X_val = X[:split_idx], X[split_idx:]
Y_train, Y_val = Y[:split_idx], Y[split_idx:]
4.2 模型构建:设计轻量级Keras网络
核心原理
MCU的Flash和RAM资源极其有限(通常Flash < 1MB,RAM < 300KB),因此模型必须足够精简。Hello World案例采用三层全连接网络:输入层(1个神经元)→ 隐藏层(16个神经元,ReLU激活)→ 输出层(1个神经元,线性激活)。总参数量约 1×16 + 16 + 16×1 + 1 = 49 个权重和偏置,加上优化器状态,内存占用极小。选择ReLU而非tanh,是因为ReLU计算简单且能有效缓解梯度消失。
模型结构表
| 层类型 | 输出维度 | 参数量 | 激活函数 |
|---|---|---|---|
| Dense (输入层) | 16 | 1×16+16=32 | ReLU |
| Dense (隐藏层) | 16 | 16×16+16=272 | ReLU |
| Dense (输出层) | 1 | 16×1+1=17 | Linear |
代码实现
import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
model = Sequential([
Dense(16, activation='relu', input_shape=(1,)),
Dense(16, activation='relu'),
Dense(1) # 线性激活,用于回归
])
model.compile(optimizer='adam', loss='mse', metrics=['mae'])
model.summary()
4.3 训练与评估:监控收敛与过拟合
核心原理
训练过程通过反向传播算法最小化均方误差(MSE)。Adam优化器自适应调整学习率,适合此小规模回归任务。关键监控指标包括训练损失(MSE)和验证损失。若验证损失远高于训练损失,则表明模型过拟合。由于数据量较小(约5000个样本),通常训练500-1000个epoch即可收敛。
训练配置与结果分析
history = model.fit(
X_train, Y_train,
validation_data=(X_val, Y_val),
epochs=500,
batch_size=32,
verbose=1
)
# 评估模型
val_loss, val_mae = model.evaluate(X_val, Y_val)
print(f"验证集 MSE: {val_loss:.6f}, MAE: {val_mae:.6f}")
典型训练结果:经过500个epoch后,训练MSE降至约 1e-5,验证MSE约 1.2e-5,MAE约 0.003。这表明模型能够以约0.3%的误差预测正弦波的下一个值,满足嵌入式演示需求。
4.4 导出为TensorFlow Lite格式
核心原理
TFLite是专为移动和嵌入式设备优化的模型格式。导出过程将Keras的H5格式模型转换为FlatBuffer序列化的.tflite文件。关键步骤包括:模型转换(使用TFLiteConverter)、可选的量化(将float32权重转为int8,减小体积并加速推理)。对于Hello World案例,默认导出float32格式即可,因为模型本身仅约1KB。
导出流程
- 保存Keras模型:
model.save('sine_model.h5') - 转换模型:使用
tf.lite.TFLiteConverter.from_keras_model() - 设置优化选项:默认无优化,或指定
optimizations=[tf.lite.Optimize.DEFAULT]进行量化。 - 生成.tflite文件:
converter.convert()并写入文件。
# 转换模型
converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()
# 保存为文件
with open('sine_model.tflite', 'wb') as f:
f.write(tflite_model)
print(f"模型大小: {len(tflite_model)} bytes")
量化对比表
| 格式 | 权重精度 | 模型大小 | 推理精度损失 | 适用场景 |
|---|---|---|---|---|
| float32 | 32位浮点 | ~1.2KB | 无 | 原型验证、资源充足的MCU |
| int8量化 | 8位整数 | ~0.3KB | 约0.5% | 低功耗、小内存MCU |
tf.lite.TFLiteConverter.from_saved_model(),除非你已保存为SavedModel格式。Keras的H5格式需使用from_keras_model()。另外,量化后务必在PC端用TFLite解释器验证输出,确保精度损失在可接受范围内。4.5 总结复盘:从训练到部署的完整链路
本章完成了正弦波预测模型的完整训练与导出流程。核心要点可归纳为:数据决定上限,模型决定下限,导出决定可行性。在数据生成阶段,我们构造了干净的回归数据集;模型设计遵循“小而精”原则,仅用49个参数就实现了高精度预测;训练过程通过验证集监控泛化能力;最终导出为1KB左右的.tflite文件,为后续在MCU上的部署奠定了基础。
tf.float32类型。这提醒我们,数据类型一致性是跨平台部署的隐形陷阱。下一阶段,我们将把生成的.tflite模型转换为C++字节数组,并集成到TFLite Micro的推理引擎中,实现在MCU上的实时正弦波预测。届时,你将看到模型从“数字”到“物理信号”的完整蜕变。