前言
CNN(卷积神经网络)在图片识别领域有着广泛的应用,其中一个经典案例是使用MNIST数据集进行手写数字识别。
MNIST数据集构成如下:
- 训练集:由来自250个不同人手写的数字构成,其中一半是高中学生的手写体,另一半来自人口普查局(the Census Bureau)的工作人员。训练集通常用于训练CNN模型,以学习如何识别手写数字。
- 测试集:测试集也是由同样比例的手写数字数据构成,用于评估训练好的CNN模型的性能。
文件说明
这里基于TensorFlow框架构建CNN模型来识别手写数字图片。
CNN模型构建
通过datasets库加载数据集,对加载的数据集进行归一化处理,并将数据集中的一小部分可视化。
由于MNIST数据集中的图像是灰度图像,每个像素的取值范围为0到255,其中0表示黑色,255表示白色。通过将每个像素值除以255,可以将其缩放到0到1之间的范围。这样做的目的是确保输入数据的数值范围一致,使模型更容易学习和处理。
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
import matplotlib.pyplot as plt
# 加载MNIST数据集并归一化图像数据
(train_images, train_labels), (test_images, test_labels) = datasets.mnist.load_data()
train_images = train_images / 255.0
test_images = test_images / 255.0
# 可视化数据集中的一些示例图像
plt.figure(figsize=(10,10))
for i in range(25):
plt.subplot(5, 5, i+1)
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(train_images[i], cmap=plt.cm.binary)
plt.xlabel(train_labels[i])
plt.show()
可以看到数据集加载如下:
接下来使用models.Sequential()容器构建CNN模型。该模型的结构如下:
- 第一层是一个卷积层(Conv2D),具有32个 3x3 的过滤器,激活函数为 ReLU。输入形状为 (28, 28, 1)。
- 第二层是一个最大池化层(MaxPooling2D),使用 2x2 的窗口进行池化。
- 第三层是另一个卷积层(Conv2D),具有64个 3x3 的过滤器,激活函数为 ReLU。
- 第四层是另一个最大池化层(MaxPooling2D),使用 2x2 的窗口进行池化。
- 第五层是一个展平层(Flatten),将输出展平为一个1维向量。
- 第六层是一个全连接层(Dense),具有64个神经元,激活函数为 ReLU。
- 第七层是一个输出层(Dense),具有10个神经元,对应于10个数字类别,没有使用激活函数。
这个网络结构采用了交替的卷积层和池化层,以提取图像特征并逐渐减小特征图的大小。然后通过展平操作将特征图转换为1维向量,并通过全连接层进行分类。最后一层是一个具有10个神经元的输出层,用于预测输入图像的类别。
代码如下:
# 构建CNN模型
model = models.Sequential()
# 添加第一个卷积层(32个3x3的过滤器)
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
# 添加第一个Max Pooling层(2x2窗口)
model.add(layers.MaxPooling2D((2, 2)))
# 添加第二个卷积层(64个3x3的过滤器)
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
# 添加第二个Max Pooling层(2x2窗口)
model.add(layers.MaxPooling2D((2, 2)))
# 将输出展平为1维向量
model.add(layers.Flatten())
# 添加全连接层(64个神经元)
model.add(layers.Dense(64, activation='relu'))
# 添加输出层(10个神经元,对应10个数字类别)
model.add(layers.Dense(10))
通过model.summary()输出模型摘要信息如下:
接下来编译模型并进行训练,训练代码和训练过程如下:
# 编译模型并训练
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
history = model.fit(train_images[..., tf.newaxis], train_labels, epochs=10,
validation_data=(test_images[..., tf.newaxis], test_labels))
训练器默认使用 Adam 优化器,除此之外,我们还可以使用别的优化算法进行优化,如下:
# 导入优化器
from tensorflow.keras.optimizers import SGD
# 创建一个自定义优化器
optimizer = SGD(learning_rate=0.01, momentum=0.9)
# 编译模型并指定损失函数和优化器
model.compile(optimizer=optimizer,
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
从训练过程中其实可以看到,准确率可能会有降低的可能性。在测试集上评估模型的性能如下:
# 评估模型在测试集上的性能
test_loss, test_acc = model.evaluate(test_images[..., tf.newaxis], test_labels, verbose=2)
print('Test accuracy:', test_acc)