4、TensorFlow入门与模型构建:安装TensorFlow 2.x,用Keras Sequential API搭建一个简单的全连接网络
好,咱们进入正题。
上一章我们把环境搭好了,现在该动手写代码了。说实话,很多初学者一上来就想着搞复杂的CNN、RNN,结果连个全连接网络都写不利索。我个人习惯是,先跑通一个最简单的模型,再慢慢往上加东西。你想想看,地基都没打牢,盖什么高楼?
4.1 安装TensorFlow 2.x
安装这事,说简单也简单,说坑也多。我建议直接用pip安装,干净利落。
# 创建一个干净的虚拟环境(如果你还没做)
conda create -n tf2_env python=3.8
conda activate tf2_env
# 安装TensorFlow 2.x
pip install tensorflow==2.10.0
# 验证安装
python -c "import tensorflow as tf; print(tf.__version__)"
嗯,这里要注意。如果你用的是Apple Silicon芯片的Mac,建议装tensorflow-macos版本。我在项目里踩过这个坑,直接装普通版,跑起来慢得让人抓狂。
4.2 认识Keras Sequential API
Keras的Sequential API,说白了就是「搭积木」。你一层一层往上堆,模型就出来了。为什么我喜欢用这个?因为直观。你想想看,全连接网络不就是输入层、隐藏层、输出层串在一起吗?Sequential正好对应这个思路。
它的核心用法就三步:
- 创建模型对象:
model = tf.keras.Sequential() - 往里面加层:
model.add(...) - 编译模型:
model.compile(...)
就这么简单。我在项目中做过一个手势识别的小demo,就是用Sequential搭的,前后不到20行代码。
4.3 搭建一个全连接网络
好,咱们动手写一个。这个网络用来做手写数字识别(MNIST数据集),虽然老掉牙了,但作为入门例子再合适不过。
import tensorflow as tf
from tensorflow.keras import layers, models
# 1. 创建Sequential模型
model = models.Sequential()
# 2. 添加层
# 输入层:把28x28的图片展平成一维向量
model.add(layers.Flatten(input_shape=(28, 28)))
# 隐藏层1:128个神经元,激活函数用ReLU
model.add(layers.Dense(128, activation='relu'))
# 隐藏层2:64个神经元
model.add(layers.Dense(64, activation='relu'))
# 输出层:10个神经元(对应0-9十个数字),用softmax输出概率
model.add(layers.Dense(10, activation='softmax'))
# 3. 编译模型
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# 4. 查看模型结构
model.summary()
💡 关键点解释:
Flatten:把二维图像拉成一维。28x28=784个像素点,变成784个输入特征。Dense:全连接层。每个神经元和上一层的所有神经元都连接。activation='relu':ReLU激活函数,说白了就是「负数变0,正数不变」。它能有效解决梯度消失问题。softmax:把输出变成概率,所有输出加起来等于1。
4.4 模型参数的计算
你可能会问:这个模型到底有多少参数?咱们算一下。
| 层 | 输入维度 | 输出维度 | 参数数量 |
|---|---|---|---|
| Flatten | (28, 28) | 784 | 0 |
| Dense_1 | 784 | 128 | 784×128 + 128 = 100,480 |
| Dense_2 | 128 | 64 | 128×64 + 64 = 8,256 |
| Dense_3 | 64 | 10 | 64×10 + 10 = 650 |
| 总计 | 109,386 |
你看,一个简单的网络就有10万多个参数。这就是深度学习的「暴力美学」——用大量参数去拟合数据。不过,参数多不代表模型好。我在项目里见过有人堆了上千万参数,结果过拟合得一塌糊涂。
4.5 训练与评估
模型搭好了,得让它学东西。咱们用MNIST数据集来训练。
# 加载MNIST数据集
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 数据归一化:把像素值从0-255缩放到0-1之间
x_train, x_test = x_train / 255.0, x_test / 255.0
# 训练模型
history = model.fit(x_train, y_train,
epochs=10,
batch_size=32,
validation_split=0.2)
# 评估模型
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=2)
print(f'\n测试集准确率: {test_acc:.4f}')
🔧 实用技巧:
epochs:训练轮数。我一般先设10轮看看效果,不够再加。batch_size:每次喂多少数据。32是个常用值,显存不够就改小点。validation_split:从训练集里切20%做验证。这样能实时看模型有没有过拟合。
跑完10轮,准确率大概在97%左右。嗯,对于这么简单的网络,这个结果已经不错了。你想想看,一个只有10万参数的小网络,就能识别手写数字,是不是挺神奇的?
4.6 保存与加载模型
训练完了,模型得保存下来。不然每次用都要重新训练,那多麻烦。
# 保存整个模型(包括结构和权重)
model.save('mnist_model.h5')
# 加载模型
loaded_model = tf.keras.models.load_model('mnist_model.h5')
# 用加载的模型做预测
predictions = loaded_model.predict(x_test[:5])
这里有个坑,我提醒一下。保存模型时,如果你用了自定义层或者自定义损失函数,加载的时候要指定custom_objects参数。不然会报错。我曾经因为这个折腾了一下午。
4.7 小结与思考
这一章咱们干了三件事:装环境、搭网络、跑训练。看起来简单,但这是所有深度学习的基础。你想想看,后面要学的CNN、RNN,本质上都是在Sequential的基础上加一些特殊层。
最后留个思考题:如果我把隐藏层的神经元数量改成256和128,参数数量会变成多少?训练时间会变长多少?准确率能提升吗?
下一章,咱们会深入聊聊数据预处理。别小看这一步,我在项目里见过太多人模型写得很漂亮,结果数据没处理好,训练出来的模型根本不能用。嗯,到时候咱们细说。