2. TensorFlow模型训练基础:Python环境配置、Keras构建简单全连接网络、模型训练与保存、模型量化原理(FP32转INT8)

好,咱们正式开始动手了。

上一章我们聊了TFLite Micro的整体架构,你可能觉得有点虚。这一章,咱们来点实在的——把模型跑起来。不过别急,在微控制器上跑模型之前,得先在电脑上把模型训练好。这就像盖房子,你得先有设计图,才能施工。

2.1 Python环境配置:别让环境卡住你

我个人习惯用Anaconda来管理Python环境。为什么?因为项目多了,依赖冲突真的让人头大。我曾经在一个项目里,因为TensorFlow和OpenCV的版本不兼容,折腾了整整一个下午。从那以后,我就老老实实用虚拟环境了。

配置步骤其实很简单,你跟着我来:

  1. 安装Anaconda(或者Miniconda,更轻量)
  2. 创建虚拟环境conda create -n tflite_micro python=3.9
  3. 激活环境conda activate tflite_micro
  4. 安装TensorFlowpip install tensorflow==2.13.0
小提示:版本别追新。TFLite Micro对TensorFlow 2.13的支持最稳定。我试过2.15,有些算子转换会报错。嗯,这里要注意,稳定比新功能重要。

装完之后,验证一下:

import tensorflow as tf
print(tf.__version__)  # 应该输出 2.13.0

看到版本号,环境就搞定了。是不是很简单?

2.2 Keras构建简单全连接网络:从零开始搭积木

Keras是TensorFlow的高级API。说白了,就是帮你把神经网络搭积木一样拼起来。你想想看,如果让你手写反向传播和梯度下降,那得多痛苦?Keras把这些都封装好了。

咱们要构建一个全连接网络(Fully Connected Network),也叫多层感知机(MLP)。它是最基础的神经网络结构,也是TFLite Micro上最常用的结构之一。

看代码:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten

model = Sequential([
    Flatten(input_shape=(28, 28)),  # 输入层:将28x28的图像展平
    Dense(128, activation='relu'),  # 隐藏层:128个神经元,ReLU激活
    Dense(64, activation='relu'),   # 隐藏层:64个神经元
    Dense(10, activation='softmax') # 输出层:10个类别,Softmax归一化
])

这里我解释一下:

  • Flatten:把二维图像变成一维向量。比如28x28的图片,展平后就是784个像素点。
  • Dense:全连接层。每个神经元都和上一层的所有神经元相连。
  • ReLU:激活函数,说白了就是让网络有非线性能力。没有它,网络再深也只是线性变换。
  • Softmax:输出层专用,把输出变成概率分布,所有类别的概率加起来等于1。
重点:这个网络结构虽然简单,但它是TFLite Micro的基石。我在项目中做过手势识别、关键词唤醒,底层都是这种全连接网络。别小看它。

2.3 模型训练与保存:让模型学会“思考”

网络搭好了,接下来就是训练。训练的过程,说白了就是让模型不断调整内部的权重,使得预测结果越来越准。

先准备数据。我们用MNIST手写数字数据集,这是深度学习的“Hello World”:

from tensorflow.keras.datasets import mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0  # 归一化到0~1

为什么要归一化?因为像素值范围是0~255,直接输入会让梯度爆炸。我刚开始学的时候没注意这个,训练出来的模型准确率只有10%,跟瞎猜一样。嗯,这个坑我踩过。

然后编译模型:

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
  • optimizer:优化器,Adam是默认首选,自适应学习率,省心。
  • loss:损失函数,分类问题用交叉熵。
  • metrics:评估指标,我们关注准确率。

开始训练:

history = model.fit(x_train, y_train, epochs=5, validation_data=(x_test, y_test))

训练完成后,保存模型:

model.save('mnist_model.h5')  # 保存为HDF5格式
注意:这里保存的是完整的Keras模型,包含网络结构和权重。但TFLite Micro不直接支持.h5文件,我们需要转换成.tflite格式。别急,下一章会讲。

2.4 模型量化原理:FP32转INT8,瘦身不减智

好,模型训练好了,准确率也还行。但问题来了——微控制器的RAM和Flash都很小,动不动就几十KB到几百KB。一个FP32的模型,参数都是32位浮点数,占空间大,计算也慢。

怎么办?量化。

量化的核心思想很简单:用更少的比特数来表示权重和激活值。最常见的是从FP32(32位浮点数)转到INT8(8位整数)。

你想想看,32位变成8位,模型大小直接缩小到原来的1/4。而且整数运算比浮点运算快得多,在MCU上简直是天壤之别。

量化原理可以用一个公式概括:

q = round(r / scale) + zero_point
  • r:原始浮点值
  • q:量化后的整数值
  • scale:缩放因子,决定每个量化步长代表多少浮点数
  • zero_point:零点偏移,让浮点0对应一个整数

举个例子:假设浮点范围是[-1.0, 1.0],要量化到INT8(范围-128~127)。那么scale = (1.0 - (-1.0)) / (127 - (-128)) ≈ 0.0078。浮点0.5量化后就是 round(0.5 / 0.0078) + 0 ≈ 64。

量化有两种方式:

量化类型 说明 适用场景
训练后量化(Post-training Quantization) 模型训练完后,直接转换权重 快速部署,精度损失小
量化感知训练(Quantization-aware Training) 训练过程中模拟量化效果 精度要求高,模型较大
我的经验:对于大多数MCU项目,训练后量化就足够了。我曾经在STM32上部署一个手势识别模型,FP32版本准确率97.2%,量化到INT8后还有96.8%,几乎没损失。但模型大小从1.2MB降到了300KB,推理速度提升了3倍。

在TensorFlow中做量化很简单,几行代码搞定:

import tensorflow as tf

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]  # 或者用默认的INT8
tflite_model = converter.convert()

with open('model_quantized.tflite', 'wb') as f:
    f.write(tflite_model)
注意:如果要用INT8量化,通常需要提供代表性数据集(representative dataset),让转换器知道输入数据的分布。否则可能会用默认范围,导致精度下降。我曾经因为这个疏忽,模型量化后准确率直接掉到50%,排查了半天才发现是数据集没给。

好了,这一章的内容就到这里。你学会了Python环境配置、用Keras搭了一个全连接网络、训练并保存了模型,还理解了量化的基本原理。下一章,我们会把模型转换成TFLite格式,然后部署到微控制器上。到时候,模型就能在真正的硬件上跑起来了。

有什么问题,欢迎随时交流。