TensorFlow CNN 完整教程
本教程将涵盖以下内容:

- CNN 核心概念回顾:简要介绍卷积、池化等关键层的作用。
- 环境准备:安装必要的库。
- 数据准备:使用经典的 MNIST 手写数字数据集。
- 构建 CNN 模型:使用 Keras 高级 API 搭建网络。
- 编译与训练模型:选择优化器、损失函数,并进行训练。
- 模型评估:在测试集上评估模型性能。
- 模型预测:使用训练好的模型进行新图像的预测。
- 进阶技巧与总结:介绍提升模型性能的方法。
CNN 核心概念回顾
在写代码之前,我们快速理解一下 CNN 的几个核心组件:
- 卷积层:这是 CNN 的核心,它使用一组卷积核 或称滤波器 在输入图像上滑动,以提取局部特征(如边缘、角点、纹理等),每个滤波器负责检测一种特定的特征。
Conv2D:在 TensorFlow 中,tf.keras.layers.Conv2D用于创建二维卷积层。
- 激活函数:通常在卷积层之后使用,如 ReLU (Rectified Linear Unit),它为网络引入非线性,使其能够学习更复杂的模式。
ReLU:公式为f(x) = max(0, x),简单且有效。
- 池化层:通常跟在卷积层之后,用于下采样,即减小数据的空间尺寸(宽度和高度),这有助于减少计算量,并使模型对特征的微小位移不那么敏感。
MaxPooling2D:最常见的池化方式,在指定区域内取最大值。
- 展平层:在将特征图输入到全连接层之前,需要将其从二维(或三维)张量转换为一维向量。
Flatten:将(height, width, channels)的张量展平为(height * width * channels,)的向量。
- 全连接层:也称为密集层,是传统神经网络中的层,它将前面提取的所有高级特征进行整合,并用于最终的分类决策。
Dense:在 TensorFlow 中,tf.keras.layers.Dense用于创建全连接层。
- Dropout 层:一种正则化技术,在训练过程中随机“丢弃”一部分神经元,以防止模型过拟合。
环境准备
确保你已经安装了 TensorFlow,如果没有,可以使用 pip 安装:
pip install tensorflow
数据准备
我们将使用 MNIST 数据集,它包含 60,000 张 28x28 像素的手写数字训练图像和 10,000 张测试图像,TensorFlow 已经内置了这个数据集,加载非常方便。
import tensorflow as tf
# 加载 MNIST 数据集
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 数据预处理
# CNN 需要一个“通道”维度,对于灰度图像,通道数为1
# 将原始图像数据从 (num_samples, 28, 28) 转换为 (num_samples, 28, 28, 1)
x_train = x_train.reshape((x_train.shape[0], 28, 28, 1)).astype('float32') / 255.0
x_test = x_test.reshape((x_test.shape[0], 28, 28, 1)).astype('float32') / 255.0
# 将标签进行 One-Hot 编码
# 标签 '5' 会被转换为 [0,0,0,0,0,1,0,0,0,0]
num_classes = 10
y_train = tf.keras.utils.to_categorical(y_train, num_classes)
y_test = tf.keras.utils.to_categorical(y_test, num_classes)
print("训练数据形状:", x_train.shape)
print("训练标签形状:", y_train.shape)
print("测试数据形状:", x_test.shape)
print("测试标签形状:", y_test.shape)
解释:

reshape(..., 1):为图像数据增加一个颜色通道维度,虽然 MNIST 是灰度图,但 CNN 的标准输入格式是(height, width, channels)。/ 255.0:将像素值从[0, 255]归一化到[0, 1],这有助于模型更快、更稳定地收敛。to_categorical:将整数标签转换为 One-Hot 向量,这是使用categorical_crossentropy损失函数所必需的。
构建 CNN 模型
我们将使用 Keras 的 Sequential API,它允许我们像搭积木一样一层一层地堆叠网络层。
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout # 定义模型 model = Sequential() # 第一个卷积块 # 32个滤波器,每个大小为3x3,激活函数为ReLU # input_shape 必须指定在第一层 model.add(Conv2D(filters=32, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1))) model.add(MaxPooling2D(pool_size=(2, 2))) # 第二个卷积块 # 64个滤波器,每个大小为3x3 model.add(Conv2D(filters=64, kernel_size=(3, 3), activation='relu')) model.add(MaxPooling2D(pool_size=(2, 2))) # 展平层,将卷积特征图转换为向量 model.add(Flatten()) # 添加一个全连接层,包含128个神经元 model.add(Dense(units=128, activation='relu')) # 添加 Dropout 层,丢弃 50% 的神经元以防止过拟合 model.add(Dropout(0.5)) # 输出层,必须包含10个神经元(对应10个类别) # 使用 softmax 激活函数,输出每个类别的概率 model.add(Dense(units=num_classes, activation='softmax')) # 打印模型结构 model.summary()
解释:
Conv2D(filters=32, ...): 我们使用 32 个不同的滤波器来提取特征。MaxPooling2D(pool_size=(2, 2)): 将特征图的尺寸减半(从 26x26 变为 13x13)。Flatten(): 将MaxPooling2D后的输出(5x5x64)展平为一个 1600 的一维向量。Dense(128, ...): 一个包含 128 个神经元的传统全连接层。Dropout(0.5): 在训练时,随机将 50% 的输入神经元置零,防止模型对某些训练样本产生过度依赖。Dense(10, activation='softmax'): 输出层。softmax函数确保所有输出值的总和为 1,可以解释为概率分布。
编译与训练模型
在训练之前,我们需要配置模型的优化器、损失函数和评估指标。
# 编译模型
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
# 训练模型
# batch_size: 每次梯度更新使用的样本数
# epochs: 训练轮次,即遍历整个训练数据集的次数
# validation_data: 在每个 epoch 结束后,在测试集上评估模型性能
history = model.fit(x_train, y_train,
batch_size=128,
epochs=10,
validation_data=(x_test, y_test))
解释:

optimizer='adam':Adam 是一种非常流行且高效的优化器,能自适应地调整学习率。loss='categorical_crossentropy':对于多分类问题,这是标准的损失函数,它衡量模型预测的概率分布与真实标签分布之间的差距。metrics=['accuracy']:我们关心模型的分类准确率。
模型评估
训练完成后,我们可以在测试集上评估模型的最终性能。
# 在测试集上评估模型
score = model.evaluate(x_test, y_test, verbose=0)
print('测试集上的损失值:', score[0])
print('测试集上的准确率:', score[1])
对于这个简单的模型,你应该能得到 98% 左右的准确率。
模型预测
我们可以用训练好的模型来预测一张新的手写数字图片。
import numpy as np
import matplotlib.pyplot as plt
# 选择测试集中的第一张图片
image_to_predict = x_test[0]
true_label = np.argmax(y_test[0]) # 获取真实标签
# CNN 模型期望一个批次的输入,所以需要增加一个维度
# 从 (28, 28, 1) 变为 (1, 28, 28, 1)
image_to_predict = np.expand_dims(image_to_predict, axis=0)
# 进行预测
predictions = model.predict(image_to_predict)
predicted_label = np.argmax(predictions[0]) # 获取预测的标签
# 显示结果
plt.imshow(x_test[0].reshape(28, 28), cmap='gray')f"真实标签: {true_label}, 预测标签: {predicted_label}")
plt.axis('off')
plt.show()
print(f"预测的概率分布: {predictions[0]}")
print(f"预测的数字是: {predicted_label}")
进阶技巧与总结
如何提升模型性能?
-
数据增强:如果你的数据集很小,可以通过旋转、平移、缩放、翻转等方式人工生成新的训练样本,这可以有效防止过拟合。
from tensorflow.keras.preprocessing.image import ImageDataGenerator datagen = ImageDataGenerator( rotation_range=10, zoom_range=0.1, width_shift_range=0.1, height_shift_range=0.1 ) # 在 model.fit 中使用 datagen.flow() 而不是直接传入数据 -
调整网络结构:
- 增加或减少卷积层的数量。
- 增加
Conv2D中的filters数量(从 32 增加到 64 或 128)。 - 尝试不同的
kernel_size(如 5x5)。 - 在全连接层前增加更多的
Dense层。
-
超参数调优:
- 调整
batch_size(如 64, 256)。 - 增加
epochs数量(但要配合EarlyStopping防止过拟合)。 - 尝试不同的优化器(如
RMSprop,SGD)和学习率。
- 调整
-
使用预训练模型:对于更复杂的任务(如 ImageNet 上的分类),可以使用在大型数据集上预训练好的模型(如 VGG16, ResNet, MobileNet),并进行迁移学习,这能极大地提升在小数据集上的性能。
通过这个教程,你已经学会了:
- 如何使用 TensorFlow/Keras 搭建一个完整的 CNN 模型。
- 如何处理图像数据(归一化、增加通道维度、One-Hot 编码)。
- 如何编译、训练和评估一个深度学习模型。
- 如何使用模型进行实际的预测。
CNN 是现代计算机视觉的基石,掌握它是通往更高级领域(如目标检测、图像分割、生成对抗网络)的关键一步,希望这份教程对你有帮助!
