TensorFlow 中文手册

23.1 计算机视觉实战:MNIST 手写数字识别(CNN)

TensorFlow MNIST CNN 实战:手写数字识别完整教程

TensorFlow 中文手册

本章节详细讲解使用TensorFlow构建卷积神经网络(CNN)进行MNIST手写数字识别的完整流程,包括数据加载、预处理、模型构建、训练调优和评估预测,适合初学者快速入门计算机视觉实战。

推荐工具
PyCharm专业版开发必备

功能强大的Python IDE,提供智能代码补全、代码分析、调试和测试工具,提高Python开发效率。特别适合处理列表等数据结构的开发工作。

了解更多

计算机视觉实战: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,)

数据预处理

为了提升模型性能,我们需要对数据进行预处理。

  1. 归一化(Normalization):将图像像素值从0-255缩放到0-1之间,这有助于模型更快收敛。
  2. 独热编码(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()

模型训练与调优

训练模型时,我们需要考虑调优技术来提升性能和防止过拟合。

添加调优技术

  1. Dropout:在训练过程中随机丢弃部分神经元,减少过拟合。我们可以在全连接层后添加Dropout层。
  2. 早停(Early Stopping):监控验证集的性能,如果性能不再提升,则提前停止训练。
  3. 学习率调度(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图像分类。

开发工具推荐
Python开发者工具包

包含虚拟环境管理、代码格式化、依赖管理、测试框架等Python开发全流程工具,提高开发效率。特别适合处理复杂数据结构和算法。

获取工具包