4. TensorFlow模型训练基础(一):环境搭建与手写数字识别

好,咱们正式开始动手了。这一章的目标很明确——把TensorFlow环境搭起来,然后用一个最简单的全连接网络,把MNIST手写数字识别跑通。说白了,就是先让模型“动起来”,后面再谈怎么优化、怎么移植到STM32上。

我个人习惯把环境搭建放在第一步。很多初学者一上来就写代码,结果跑不通,最后发现是Python版本不对或者CUDA没装好。嗯,咱们先把地基打牢。

4.1 TensorFlow环境搭建

TensorFlow支持CPU和GPU两种模式。如果你只是做模型验证和轻量训练,CPU版完全够用。但如果你要训练稍大一点的网络,我建议还是用GPU版,速度快很多。

我的建议: 第一次搭建,先用CPU版跑通流程。等后面做图像分类、目标检测时再切GPU。这样能避免一开始就被驱动问题劝退。

4.1.1 安装步骤(Windows/Linux通用)

  1. 安装Python:推荐3.8~3.10版本。我遇到过3.11装TF报错的情况,所以别追新。
  2. 创建虚拟环境(强烈建议):
    python -m venv tf_env
    tf_env\Scripts\activate  # Windows
    source tf_env/bin/activate  # Linux
  3. 安装TensorFlow:
    pip install tensorflow==2.10.0
  4. 验证安装:
    python -c "import tensorflow as tf; print(tf.__version__)"
我曾经踩过的坑: 直接 pip install tensorflow 装到最新版,结果跟CUDA版本不匹配,训练时直接报错。后来我养成了习惯——先查TF官方文档的“Build from source”页面,确认CUDA和cuDNN的对应版本再装。

4.1.2 环境验证清单

组件 推荐版本 验证命令
Python 3.8 ~ 3.10 python --version
TensorFlow 2.10.0 tf.__version__
Keras 2.10.0(随TF自带) tf.keras.__version__
NumPy 1.24.3 np.__version__

4.2 Keras Sequential API入门

Keras的Sequential API,说白了就是“搭积木”。你一层一层往上加,模型就搭好了。我个人觉得这是最适合入门的方式——代码直观,逻辑清晰。

你想想看,一个神经网络无非就是:输入层 → 隐藏层 → 输出层。Sequential API让你用.add()方法一层层堆叠,跟写清单一样简单。

4.2.1 核心概念

  • 层(Layer):Dense(全连接层)、Conv2D(卷积层)、Flatten(展平层)等。
  • 激活函数:relu、softmax、sigmoid。
  • 损失函数:分类用categorical_crossentropy或sparse_categorical_crossentropy。
  • 优化器:adam、sgd。
关键区别: 如果你的标签是one-hot编码(如[0,0,1,0,0]),用categorical_crossentropy;如果标签是整数(如2),用sparse_categorical_crossentropy。选错了会报错,我刚开始就犯过这个错。

4.3 全连接网络实现手写数字识别(MNIST)

MNIST数据集是深度学习的“Hello World”。28×28像素的手写数字图片,0到9共10个类别。咱们用全连接网络来识别它。

4.3.1 数据加载与预处理

import tensorflow as tf
from tensorflow.keras.datasets import mnist

# 加载数据
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 归一化:把0-255的像素值缩放到0-1之间
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0

# 展平:把28x28的二维图像变成一维向量(784个像素)
x_train = x_train.reshape(-1, 784)
x_test = x_test.reshape(-1, 784)

# 标签转one-hot编码
y_train = tf.keras.utils.to_categorical(y_train, 10)
y_test = tf.keras.utils.to_categorical(y_test, 10)
为什么归一化? 我在项目中遇到过,如果不归一化,网络训练时梯度会震荡,收敛很慢。归一化后,所有输入都在0~1之间,训练稳定多了。

4.3.2 构建模型

model = tf.keras.Sequential([
    tf.keras.layers.Dense(128, activation='relu', input_shape=(784,)),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(10, activation='softmax')
])

# 编译模型
model.compile(
    optimizer='adam',
    loss='categorical_crossentropy',
    metrics=['accuracy']
)

# 查看模型结构
model.summary()

这个网络结构很简单:输入784个像素 → 128个神经元(ReLU) → 64个神经元(ReLU) → 10个输出(Softmax)。你想想看,这其实就是在做“特征提取+分类”。

4.3.3 模型训练

history = model.fit(
    x_train, y_train,
    batch_size=32,
    epochs=10,
    validation_split=0.2,
    verbose=1
)

训练时你会看到loss逐渐下降,accuracy逐渐上升。一般来说,10个epoch后测试集准确率能到97%以上。嗯,这个网络虽然简单,但效果还不错。

4.4 模型保存与加载(.h5格式)

训练好的模型要保存下来,后面才能转成STM32能用的格式。我个人习惯保存成.h5格式,兼容性好,Keras原生支持。

4.4.1 保存模型

# 保存整个模型(结构+权重+优化器状态)
model.save('mnist_fc_model.h5')

# 或者只保存权重(更轻量)
model.save_weights('mnist_fc_weights.h5')
注意: 如果你只保存权重,加载时必须先重建一模一样的模型结构,再加载权重。否则会报错。我建议初学者直接保存整个模型,省心。

4.4.2 加载模型

from tensorflow.keras.models import load_model

# 加载完整模型
loaded_model = load_model('mnist_fc_model.h5')

# 验证加载是否正确
loss, accuracy = loaded_model.evaluate(x_test, y_test)
print(f'Test accuracy: {accuracy:.4f}')

4.5 本章知识体系

下面这张图把本章的核心流程串起来了。从环境搭建到模型保存,每一步都是后面移植的基础。

TensorFlow模型训练基础流程 TensorFlow模型训练基础流程 环境搭建 Python + TF + 虚拟环境 数据准备 MNIST加载 + 归一化 模型构建 Sequential + Dense层 模型编译 优化器 + 损失函数 模型训练 fit() + epoch + batch 模型保存 .h5格式保存 💡 关键说明 • 环境搭建是基础,建议用虚拟环境隔离项目依赖 • 数据归一化能加速收敛,是训练前的标准操作 • .h5格式保存完整模型,后续可直接加载和转换 完整流程:环境 → 数据 → 模型 → 编译 → 训练 → 保存 每个环节都影响最终模型质量,建议按步骤逐一验证

4.6 完整代码示例

下面是一份可以直接运行的完整代码。你复制到Python文件里,跑一遍,就能看到训练过程和保存的模型文件。

import tensorflow as tf
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.utils import to_categorical

# 1. 加载数据
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 2. 预处理
x_train = x_train.reshape(-1, 784).astype('float32') / 255.0
x_test = x_test.reshape(-1, 784).astype('float32') / 255.0
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

# 3. 构建模型
model = Sequential([
    Dense(128, activation='relu', input_shape=(784,)),
    Dense(64, activation='relu'),
    Dense(10, activation='softmax')
])

# 4. 编译
model.compile(optimizer='adam',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# 5. 训练
model.fit(x_train, y_train,
          batch_size=32,
          epochs=10,
          validation_split=0.2,
          verbose=1)

# 6. 评估
loss, acc = model.evaluate(x_test, y_test, verbose=0)
print(f'Test accuracy: {acc:.4f}')

# 7. 保存
model.save('mnist_fc_model.h5')
print('Model saved as mnist_fc_model.h5')
跑完代码后: 你会得到一个.h5文件,大小大概几百KB。这个文件就是咱们后面要移植到STM32上的“种子”。下一阶段,我们会把它转换成C代码,然后烧到板子上跑。

嗯,这一章的内容就到这里。环境搭好了,模型跑通了,文件也保存了。后面的事情,就是怎么把这个模型塞进STM32里——那才是咱们这门课的核心。不过别急,先把基础打牢,后面自然水到渠成。