23.1 计算机视觉实战:MNIST 手写数字识别(CNN)
TensorFlow MNIST CNN 实战:手写数字识别完整教程
本章节详细讲解使用TensorFlow构建卷积神经网络(CNN)进行MNIST手写数字识别的完整流程,包括数据加载、预处理、模型构建、训练调优和评估预测,适合初学者快速入门计算机视觉实战。
计算机视觉实战:MNIST 手写数字识别(CNN)
欢迎来到TensorFlow中文学习手册的实战章节!本章将带领大家从零开始,使用卷积神经网络(CNN)解决经典的MNIST手写数字识别问题。这是一个入门级的计算机视觉项目,适合新人快速上手深度学习。我们将覆盖项目需求、数据加载、预处理、模型构建、训练调优和评估预测的完整流程。
项目概述
MNIST(Modified National Institute of Standards and Technology)数据集是计算机视觉领域的一个标准数据集,包含70,000张28x28像素的手写数字灰度图像(60,000张用于训练,10,000张用于测试)。我们的目标是构建一个CNN模型,准确识别这些数字(0-9)。通过这个项目,您将掌握TensorFlow的基本操作和CNN的应用。
数据加载与预处理
首先,我们需要加载和处理数据。TensorFlow的tf.keras.datasets.mnist模块提供了便捷的数据加载功能。
数据加载
使用tf.keras.datasets.mnist.load_data()函数加载数据,它会自动下载并分割为训练集和测试集。
import tensorflow as tf
# 加载MNIST数据集
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
# 查看数据形状
print("训练集图像形状:", x_train.shape) # (60000, 28, 28)
print("训练集标签形状:", y_train.shape) # (60000,)
print("测试集图像形状:", x_test.shape) # (10000, 28, 28)
print("测试集标签形状:", y_test.shape) # (10000,)
数据预处理
为了提升模型性能,我们需要对数据进行预处理。
- 归一化(Normalization):将图像像素值从0-255缩放到0-1之间,这有助于模型更快收敛。
- 独热编码(One-Hot Encoding):将标签转换为独热向量格式,适用于多分类任务。
# 归一化:将像素值缩放到0-1
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0
# 独热编码:将标签转换为独热向量
y_train = tf.keras.utils.to_categorical(y_train, 10) # 10个类别(0-9)
y_test = tf.keras.utils.to_categorical(y_test, 10)
# 查看预处理后的形状
y_train形状现在变为: (60000, 10)
此外,我们还需要调整输入数据的维度,以匹配CNN的输入要求。
# 调整输入维度:添加通道维度(对于灰度图像,通道数为1)
x_train = x_train.reshape(-1, 28, 28, 1)
x_test = x_test.reshape(-1, 28, 28, 1)
print("调整后的训练集形状:", x_train.shape) # (60000, 28, 28, 1)
CNN 模型构建
接下来,我们构建一个简单的CNN模型。CNN擅长处理图像数据,通过卷积层提取特征,池化层降维,最后使用全连接层进行分类。
我们使用tf.keras.Sequential模型,依次添加:
- Conv2D层:卷积层,使用ReLU激活函数提取特征。
- MaxPooling2D层:池化层,减少计算量和防止过拟合。
- Flatten层:将多维输入展平,以便连接全连接层。
- Dense层:全连接层,最终输出10个类别的概率。
model = tf.keras.Sequential([
# 第一个卷积层:32个3x3卷积核,输入形状(28,28,1),使用ReLU激活
tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(28,28,1)),
# 第一个池化层:2x2最大池化
tf.keras.layers.MaxPooling2D((2,2)),
# 第二个卷积层:64个3x3卷积核
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
# 第二个池化层
tf.keras.layers.MaxPooling2D((2,2)),
# 展平层
tf.keras.layers.Flatten(),
# 全连接层:128个神经元
tf.keras.layers.Dense(128, activation='relu'),
# 输出层:10个神经元(对应0-9数字),使用Softmax激活函数输出概率
tf.keras.layers.Dense(10, activation='softmax')
])
# 编译模型:使用分类交叉熵作为损失函数,Adam优化器,准确率作为评估指标
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
# 查看模型摘要
model.summary()
模型训练与调优
训练模型时,我们需要考虑调优技术来提升性能和防止过拟合。
添加调优技术
- Dropout:在训练过程中随机丢弃部分神经元,减少过拟合。我们可以在全连接层后添加Dropout层。
- 早停(Early Stopping):监控验证集的性能,如果性能不再提升,则提前停止训练。
- 学习率调度(Learning Rate Scheduling):动态调整学习率,例如使用学习率衰减。
我们将模型稍作修改以加入Dropout,并使用回调函数实现早停和学习率调度。
# 修改模型,在全连接层后添加Dropout
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(28,28,1)),
tf.keras.layers.MaxPooling2D((2,2)),
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D((2,2)),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dropout(0.5), # 添加Dropout,丢弃率为50%
tf.keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
# 定义回调函数
from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau
# 早停:监控验证集准确率,如果3个epoch没有提升则停止
early_stopping = EarlyStopping(monitor='val_accuracy', patience=3, verbose=1)
# 学习率调度:当验证集损失停止改善时,将学习率减少一半
lr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=2, verbose=1)
callbacks = [early_stopping, lr_scheduler]
# 训练模型
history = model.fit(x_train, y_train,
epochs=20, # 设置最大训练轮数
batch_size=32, # 批量大小
validation_split=0.2, # 从训练集划分20%作为验证集
callbacks=callbacks,
verbose=1)
模型评估与预测
训练完成后,我们需要评估模型性能并进行预测。
评估模型
使用测试集评估模型的准确率。
# 评估模型
test_loss, test_accuracy = model.evaluate(x_test, y_test, verbose=0)
print("测试集损失:", test_loss)
print("测试集准确率:", test_accuracy)
预测与混淆矩阵可视化
进行预测并可视化混淆矩阵,以深入了解模型在不同类别上的表现。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
import seaborn as sns
# 预测测试集
predictions = model.predict(x_test)
predicted_classes = np.argmax(predictions, axis=1) # 转换为类别索引
# 获取真实标签(从独热编码转换回)
y_true = np.argmax(y_test, axis=1)
# 计算混淆矩阵
cm = confusion_matrix(y_true, predicted_classes)
# 可视化混淆矩阵
plt.figure(figsize=(10, 8))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=range(10), yticklabels=range(10))
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.title('混淆矩阵可视化')
plt.show()
随机测试图像预测示例
我们还可以随机抽取测试图像进行预测展示。
# 随机选择一些测试图像
import random
num_samples = 5
sample_indices = random.sample(range(len(x_test)), num_samples)
for i in sample_indices:
img = x_test[i].reshape(28, 28) # 展平为图像显示
plt.imshow(img, cmap='gray')
plt.title(f"预测: {predicted_classes[i]}, 真实: {y_true[i]}")
plt.axis('off')
plt.show()
总结
本章详细介绍了使用TensorFlow构建CNN进行MNIST手写数字识别的完整实战过程。您学会了如何加载数据、进行预处理、构建CNN模型、添加调优技术以及评估和预测。通过实践这个项目,您应该对TensorFlow和CNN有了更深入的理解。建议尝试调整模型参数或添加更多层来进一步提升准确率。在接下来的章节中,我们将探索更复杂的计算机视觉任务,如CIFAR-10图像分类。