第4章:TensorFlow模型设计与训练

好,咱们进入正题。这一章要聊的是怎么在Google Colab里搭一个简单的MLP模型,然后训练、评估,最后转成.tflite格式。说白了,就是让我们的AI模型能在STM32上跑起来。

4.1 为什么选Google Colab?

我个人习惯用Colab做模型训练。原因很简单——免费GPU。你想想看,自己配一台带GPU的电脑,少说也得大几千。Colab直接给你白嫖,何乐而不为?

我在项目中遇到过不少次,本地训练模型跑到一半,电脑直接卡死。换了Colab之后,再也没这烦恼了。而且它跟Google Drive无缝对接,模型文件直接存云端,方便得很。

4.2 搭建简单MLP模型

MLP,全称多层感知机。说白了就是一堆神经元堆叠起来。咱们这次要做的,是一个三层的MLP。

模型结构:

  • 输入层:根据传感器数据维度定
  • 隐藏层:64个神经元,ReLU激活
  • 输出层:根据分类数量定,Softmax激活

代码长这样:

import tensorflow as tf
from tensorflow import keras

model = keras.Sequential([
    keras.layers.Dense(64, activation='relu', input_shape=(input_dim,)),
    keras.layers.Dense(32, activation='relu'),
    keras.layers.Dense(num_classes, activation='softmax')
])

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

嗯,这里要注意。input_dim和num_classes要根据你的实际数据来定。比如你用了加速度计、陀螺仪、磁力计三个传感器,每个输出3轴数据,那input_dim就是9。

4.3 训练数据准备

数据准备这一步,我吃过不少亏。曾经有一次,我偷懒没做数据归一化,结果模型训练了三天,准确率死活上不去。后来才发现,数据范围差太多,模型根本学不动。

避坑指南:传感器数据一定要做归一化!我曾经因为没做归一化,模型收敛慢得像蜗牛爬。

数据准备的步骤:

  1. 从STM32采集原始数据,存成CSV文件
  2. 上传到Google Drive
  3. 在Colab里挂载Drive,读取数据
  4. 做归一化处理
  5. 划分训练集和测试集

代码示例:

from google.colab import drive
drive.mount('/content/drive')

import pandas as pd
import numpy as np

# 读取数据
data = pd.read_csv('/content/drive/MyDrive/sensor_data.csv')

# 归一化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X = scaler.fit_transform(data.iloc[:, :-1])
y = data.iloc[:, -1].values

# 划分数据集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

4.4 模型训练与评估

训练模型这一步,说白了就是让模型自己学习数据里的规律。我个人习惯设置100个epoch,配合早停机制。

# 训练模型
history = model.fit(
    X_train, y_train,
    epochs=100,
    validation_split=0.2,
    callbacks=[keras.callbacks.EarlyStopping(patience=10)]
)

# 评估模型
test_loss, test_acc = model.evaluate(X_test, y_test)
print(f'测试集准确率: {test_acc:.4f}')

你想想看,为什么要加早停?因为训练时间长了,模型可能会过拟合。早停就是当验证集准确率不再提升时,自动停止训练。我在项目中试过,不加早停,模型准确率反而会下降。

小技巧:训练时记得把history保存下来,后面可以画损失曲线和准确率曲线,直观看到训练效果。

4.5 模型量化与转换为.tflite

这一步最关键。STM32的资源有限,不能直接跑完整的TensorFlow模型。我们需要把模型量化,转成.tflite格式。

为什么要量化?说白了就是把模型里的浮点数变成整数。比如原来用float32,现在用int8。这样模型体积能缩小4倍,推理速度也能提升不少。

量化效果对比:

指标 量化前 量化后
模型大小 约500KB 约125KB
推理时间 约50ms 约15ms
准确率 95% 94.5%

转换代码:

# 转换为TFLite模型
converter = tf.lite.TFLiteConverter.from_keras_model(model)

# 启用量化
converter.optimizations = [tf.lite.Optimize.DEFAULT]

# 设置代表数据集进行校准
def representative_dataset():
    for i in range(100):
        yield [X_train[i:i+1].astype(np.float32)]

converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8

tflite_model = converter.convert()

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

嗯,这里要注意。代表数据集这一步不能省。它用来校准量化参数,直接影响量化后的模型准确率。我曾经偷懒没做校准,结果量化后的模型准确率从95%掉到了60%,简直惨不忍睹。

4.6 验证量化模型

转换完之后,一定要验证一下。看看量化后的模型还能不能正常工作。

# 加载量化模型
interpreter = tf.lite.Interpreter(model_path='model_quantized.tflite')
interpreter.allocate_tensors()

# 获取输入输出信息
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()

# 推理测试
input_data = X_test[0].astype(np.int8)
interpreter.set_tensor(input_details[0]['index'], [input_data])
interpreter.invoke()
output_data = interpreter.get_tensor(output_details[0]['index'])

print(f'预测结果: {np.argmax(output_data)}')
print(f'真实标签: {y_test[0]}')

注意:量化模型的输入输出都是int8类型,推理前要确保数据格式正确。我见过有人忘了转换数据类型,结果推理结果全是错的。

4.7 小结

这一章我们走完了从模型设计到量化的完整流程。说白了就是三步:搭模型、训练、量化。每一步都有坑,但踩过之后你就记住了。

下一章,我们会把量化好的.tflite模型部署到STM32上。到时候你就知道,原来AI离嵌入式这么近。

个人建议:刚开始做的时候,先用简单的二分类任务练手。等流程跑通了,再上多分类。这样遇到问题好排查。

好了,这一章就到这儿。有什么问题,欢迎交流。