47、使用MNIST数据集进行多分类

使用MNIST数据集进行多分类

1. 深度学习硬件支持

在深度学习中,有多种硬件可以提升模型性能:
- NVIDIA GPUs :许多NVIDIA GPU与TensorFlow兼容,进而也与Keras兼容,能够增强深度学习模型的性能。
- Google TPUs :Google开发了TPU(张量处理单元),用于其Cloud TPU服务,单个TPU Pod可提供高达11.5 petaflops的性能,且能源效率高,适合像Google这样拥有大规模计算集群的公司。

2. 卷积神经网络与MNIST数据集

MNIST手写数字数据集常用于探索卷积神经网络(CNN)的深度学习。与Scikit - learn的Digits数据集相比,MNIST数据集有70,000个标记的数字图像样本(60,000个用于训练,10,000个用于测试),每个样本是28x28像素的灰度图像。

传统机器学习模型输出的是数字图像的预测类别(0 - 9的整数),而CNN模型将进行概率分类,为每个数字图像输出一个包含10个概率的数组,概率最高的类别即为预测值。

3. Keras和深度学习的可重复性

在深度学习中,由于库对浮点计算操作进行大量并行处理,每次操作执行顺序可能不同,导致结果存在差异。要在Keras中获得可重复的结果,需要结合环境设置和代码设置,具体可参考Keras FAQ:https://keras.io/getting - started/faq/#how - can - i - obtain - reproducible - results - using - keras - during - development

4. 基本Keras神经网络组件

一个Keras神经网络由以下组件组成:
- 网络(模型) :一系列包含神经元的层,数据通过输入层进入网络,经过隐藏层学习,最后由输出层产生预测。层数越多,网络越深,即深度学习。
- 损失函数 :衡量网络对目标值的预测效果,损失值越低,预测越好。
- 优化器 :尝试最小化损失函数的值,调整网络以做出更好的预测。

5. 启动JupyterLab

假设已经激活了tf_env Anaconda环境,并从ch15示例文件夹启动了JupyterLab。可以打开MNIST_CNN.ipynb文件执行代码,也可以创建新的笔记本输入代码。若模型性能不佳,可以通过JupyterLab的Kernel菜单中的“Restart Kernel and Clear All Outputs…”重置笔记本并清除输出,然后重新执行代码。

6. 加载MNIST数据集

使用以下代码导入tensorflow.keras.datasets.mnist模块并加载数据集:

from tensorflow.keras.datasets import mnist
(X_train, y_train), (X_test, y_test) = mnist.load_data()

调用 load_data 函数会将MNIST数据下载到系统中,函数返回一个包含训练集和测试集的元组。

7. 数据探索

查看训练集图像(X_train)、训练集标签(y_train)、测试集图像(X_test)和测试集标签(y_test)的维度:

print(X_train.shape)  # (60000, 28, 28)
print(y_train.shape)  # (60000,)
print(X_test.shape)   # (10000, 28, 28)
print(y_test.shape)   # (10000,)

可以看出MNIST图像的分辨率高于Scikit - learn的Digits数据集。

可视化数字

使用以下代码可视化24个随机选择的MNIST训练集图像:

%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(font_scale=2)
import numpy as np
index = np.random.choice(np.arange(len(X_train)), 24, replace=False)
figure, axes = plt.subplots(nrows=4, ncols=6, figsize=(16, 9))
for item in zip(axes.ravel(), X_train[index], y_train[index]):
    axes, image, target = item
    axes.imshow(image, cmap=plt.cm.gray_r)
    axes.set_xticks([])
    axes.set_yticks([])
    axes.set_title(target)
plt.tight_layout()

从可视化结果可以看出手写数字识别具有挑战性,因为不同人的书写风格差异很大。

8. 数据准备
重塑图像数据

Keras CNN要求输入的NumPy数组每个样本的形状为 (width, height, channels) ,对于MNIST数据集,每个样本的形状应为 (28, 28, 1)

X_train = X_train.reshape((60000, 28, 28, 1))
X_test = X_test.reshape((10000, 28, 28, 1))
归一化图像数据

将图像数据的像素值转换为0.0 - 1.0的范围:

X_train = X_train.astype('float32') / 255
X_test = X_test.astype('float32') / 255
独热编码

将标签从整数转换为分类数据,使用 tensorflow.keras.utils 模块的 to_categorical 函数:

from tensorflow.keras.utils import to_categorical
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
9. 创建神经网络

使用 Sequential 模型创建一个CNN:

from tensorflow.keras.models import Sequential
cnn = Sequential()
添加层到网络
  • 卷积层 :使用 Conv2D 层,通过像素间的关系学习有用的特征。
from tensorflow.keras.layers import Conv2D, Dense, Flatten, MaxPooling2D
cnn.add(Conv2D(filters=64, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1)))

该层的输入样本为28x28x1,输出为26x26x64,特征图的维度显著增加。

  • 池化层 :使用 MaxPooling2D 层减少过拟合和计算时间,压缩特征。
cnn.add(MaxPooling2D(pool_size=(2, 2)))

该层将上一层的输出从26x26x64减少到13x13x64。

  • 添加另一个卷积层和池化层
cnn.add(Conv2D(filters=128, kernel_size=(3, 3), activation='relu'))
cnn.add(MaxPooling2D(pool_size=(2, 2)))

第二个卷积层的输出为11x11x128,池化层的输出为5x5x128。

  • 扁平化结果 :使用 Flatten 层将三维输出转换为一维。
cnn.add(Flatten())

输出为1x3200。

  • 添加全连接层 :使用 Dense 层学习特征之间的关系。
cnn.add(Dense(units=128, activation='relu'))
  • 添加输出层 :使用 Dense 层和 softmax 激活函数输出分类概率。
cnn.add(Dense(units=10, activation='softmax'))
10. 打印模型摘要
cnn.summary()

模型摘要显示了各层的输出形状和参数数量,该网络需要学习近500,000个参数。

11. 可视化模型结构
from tensorflow.keras.utils import plot_model
from IPython.display import Image
plot_model(cnn, to_file='convnet.png', show_shapes=True, show_layer_names=True)
Image(filename='convnet.png')

以下是创建CNN的流程图:

graph TD;
    A[加载MNIST数据集] --> B[数据探索];
    B --> C[数据准备];
    C --> D[创建Sequential模型];
    D --> E[添加卷积层];
    E --> F[添加池化层];
    F --> G[添加另一个卷积层和池化层];
    G --> H[扁平化结果];
    H --> I[添加全连接层];
    I --> J[添加输出层];
    J --> K[打印模型摘要];
    K --> L[可视化模型结构];

以下是数据准备步骤的表格:
| 步骤 | 操作 | 代码示例 |
| ---- | ---- | ---- |
| 重塑图像数据 | 将图像数据重塑为 (width, height, channels) 形状 | X_train = X_train.reshape((60000, 28, 28, 1))
X_test = X_test.reshape((10000, 28, 28, 1)) |
| 归一化图像数据 | 将像素值转换为0.0 - 1.0的范围 | X_train = X_train.astype('float32') / 255
X_test = X_test.astype('float32') / 255 |
| 独热编码 | 将标签从整数转换为分类数据 | from tensorflow.keras.utils import to_categorical
y_train = to_categorical(y_train)
y_test = to_categorical(y_test) |

使用MNIST数据集进行多分类

12. 卷积层原理深入解析

卷积层是CNN的核心组成部分,它通过卷积操作学习图像中的特征。卷积操作使用一个小的矩阵(内核)在输入图像上滑动,对每个位置的像素进行加权求和,从而提取局部特征。

以一个6x6的图像和3x3的内核为例,内核就像一个“滑动窗口”,从图像的左上角开始,每次向右移动一个像素,直到到达图像的右边缘,然后向下移动一个像素,继续从左到右滑动,直到覆盖整个图像。

在这个过程中,内核与图像的每个局部区域进行卷积运算,产生一个新的特征图。每个特征图代表了图像中不同的特征,如边缘、线条等。

在我们的CNN模型中,第一个卷积层使用了64个3x3的内核,因此会产生64个特征图。每个特征图的大小为26x26,这是因为在卷积过程中,图像的边缘部分无法被内核完全覆盖,所以特征图的尺寸会减小。

13. 池化层作用及原理

池化层通常紧跟在卷积层之后,用于减少特征图的维度,从而降低计算量和过拟合的风险。最常见的池化方法是最大池化(Max Pooling)。

最大池化操作使用一个固定大小的窗口(通常是2x2)在特征图上滑动,每次取窗口内的最大值作为输出。这样可以保留特征图中的主要特征,同时减少数据量。

例如,对于一个6x6的特征图,使用2x2的最大池化窗口,窗口每次移动2个像素,最终会得到一个3x3的输出特征图。通过这种方式,特征图的维度被压缩了75%。

在我们的模型中,第一个池化层将卷积层输出的26x26x64的特征图压缩为13x13x64,第二个池化层将11x11x128的特征图压缩为5x5x128。

14. 全连接层的作用

全连接层(Dense层)用于将前面卷积层和池化层学习到的特征进行整合,并进行分类。在我们的模型中,经过Flatten层将三维的特征图转换为一维向量后,输入到全连接层。

第一个全连接层有128个神经元,它会学习特征向量之间的复杂关系,从而提取更高级的特征。最后一个全连接层有10个神经元,使用softmax激活函数输出每个类别的概率,从而实现对数字的分类。

15. 模型训练与评估

在完成模型的构建后,我们需要对模型进行训练和评估。以下是训练和评估模型的基本步骤:

编译模型

在训练模型之前,需要对模型进行编译,指定损失函数、优化器和评估指标。

cnn.compile(optimizer='adam',
            loss='categorical_crossentropy',
            metrics=['accuracy'])

这里使用了 adam 优化器, categorical_crossentropy 作为损失函数, accuracy 作为评估指标。

训练模型

使用训练数据对模型进行训练,指定训练的轮数(epochs)和批次大小(batch_size)。

history = cnn.fit(X_train, y_train,
                  epochs=5,
                  batch_size=64,
                  validation_data=(X_test, y_test))

在训练过程中,模型会根据损失函数的反馈不断调整参数,以提高预测的准确性。

评估模型

使用测试数据对训练好的模型进行评估,得到模型在测试集上的损失和准确率。

test_loss, test_acc = cnn.evaluate(X_test, y_test)
print(f"Test accuracy: {test_acc}")
16. 模型训练过程可视化

为了更好地理解模型的训练过程,我们可以将训练过程中的损失和准确率进行可视化。

import matplotlib.pyplot as plt

# 绘制训练和验证损失曲线
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()

# 绘制训练和验证准确率曲线
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()

通过可视化训练过程,我们可以观察到模型的收敛情况,判断是否存在过拟合或欠拟合的问题。

17. 防止过拟合的方法总结

过拟合是深度学习中常见的问题,当模型在训练数据上表现良好,但在测试数据上表现不佳时,就可能出现了过拟合。以下是一些防止过拟合的方法:

  • 减少训练轮数 :过多的训练轮数可能导致模型过度学习训练数据中的噪声,从而降低泛化能力。可以通过观察训练和验证损失曲线,选择合适的训练轮数。
  • 数据增强 :通过对训练数据进行随机变换,如旋转、翻转、缩放等,增加数据的多样性,从而提高模型的泛化能力。
  • Dropout :在训练过程中,随机忽略一部分神经元,防止神经元之间的过度依赖,从而减少过拟合的风险。
  • 正则化 :使用L1或L2正则化方法,对模型的参数进行约束,防止参数过大,从而减少过拟合。
18. 模型预测示例

训练好的模型可以用于对新的图像进行预测。以下是一个简单的预测示例:

import numpy as np

# 随机选择一个测试样本
index = np.random.randint(0, len(X_test))
test_image = X_test[index]
test_label = y_test[index]

# 对测试样本进行预测
predictions = cnn.predict(np.expand_dims(test_image, axis=0))
predicted_digit = np.argmax(predictions)

# 显示测试图像和预测结果
plt.imshow(test_image.reshape(28, 28), cmap='gray_r')
plt.title(f"Predicted: {predicted_digit}, Actual: {np.argmax(test_label)}")
plt.show()
19. 总结与展望

通过以上步骤,我们成功地构建了一个基于CNN的手写数字识别模型,并对其进行了训练和评估。在训练过程中,我们学习了卷积层、池化层、全连接层的原理和作用,以及如何防止过拟合。

CNN在图像识别领域取得了巨大的成功,但它也存在一些局限性,如对数据的需求量大、计算资源要求高、可解释性差等。未来的研究方向包括开发更高效的模型结构、提高模型的可解释性、将CNN应用于更多的领域等。

以下是模型训练和评估的流程图:

graph TD;
    A[编译模型] --> B[训练模型];
    B --> C[评估模型];
    C --> D[可视化训练过程];
    D --> E[模型预测];

以下是防止过拟合方法的表格:
| 方法 | 原理 |
| ---- | ---- |
| 减少训练轮数 | 避免模型过度学习训练数据中的噪声 |
| 数据增强 | 增加数据的多样性,提高模型的泛化能力 |
| Dropout | 随机忽略一部分神经元,防止神经元之间的过度依赖 |
| 正则化 | 对模型的参数进行约束,防止参数过大 |

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值