一、MNIST 手写数字介绍
MNIST 手写数字识别在深度学习发展历程中占据着重要地位,它不仅是学习卷积神经网络(CNN)等深度学习模型的绝佳切入点,也为众多复杂图像识别任务奠定了理论与实践基础。
1、MNIST 数据集
MNIST 数据集被誉为深度学习领域的 “Hello World”,由纽约大学柯伦实验室整理而成。它包含 60,000 张训练图像、10,000 张测试图像,每张图像均为 28×28 像素的灰度手写数字图片,涵盖数字 0 – 9 共 10 个类别。这些图像经过了归一化处理,像素值范围在 0 到 255 之间,且数字被居中放置在图像内,以减少额外干扰因素。数据集还附带对应的标签,明确标注了每张图像所代表的数字,方便模型训练和评估。
MNIST 数据集具有高度标准化和易获取性的特点,其规模适中,既能让初学者快速上手实践,又能为复杂模型提供一定的挑战性。许多新提出的深度学习算法和模型,都会先在 MNIST 数据集上进行初步验证,测试模型的基本性能和泛化能力 。
2、MNIST 经典模型构建
(一)全连接神经网络
早期,全连接神经网络(FCN)是处理 MNIST 手写数字识别的常用方法。将 28×28 的图像展平为 784 维的向量后,输入到全连接层。通过多个全连接层的层层计算,对输入特征进行不断整合和变换,最终输出 10 个神经元的结果,分别对应 0 – 9 这 10 个数字类别的预测得分,再经过 Softmax 函数将得分转换为概率,选取概率最大的类别作为预测结果。不过,全连接神经网络由于参数众多,容易出现过拟合问题,且计算效率相对较低。
(二)卷积神经网络(CNN)
随着深度学习发展,CNN 逐渐成为 MNIST 手写数字识别的主流模型。CNN 利用卷积层和池化层进行特征提取和降维,有效减少了参数数量,同时保留了图像的空间结构信息。例如,在一个简单的 CNN 模型中,通常会包含 2 – 3 个卷积层,每个卷积层使用不同数量和尺寸的卷积核提取边缘、曲线等基础特征;卷积层之后搭配池化层,降低特征图维度,减少计算量;最后通过展平层将多维特征图转换为一维向量,输入全连接层进行分类。相比全连接神经网络,CNN 在 MNIST 数据集上不仅训练速度更快,识别准确率也大幅提升,通常可以达到 99% 以上。
3、MNIST 训练与评估
在 MNIST 手写数字识别模型训练过程中,常用的损失函数是交叉熵损失函数,它能够衡量预测概率分布与真实标签分布之间的差异,引导模型不断调整参数,使预测结果更接近真实值。优化器一般选用随机梯度下降(SGD)及其变种,如 Adagrad、Adam 等,它们可以根据不同参数的梯度情况,自适应地调整学习率,加快模型的收敛速度。
模型训练完成后,需要使用测试集对其性能进行评估。除了准确率(Accuracy)这一最直观的指标外,还会计算精确率(Precision)、召回率(Recall)和 F1 值等指标,从不同角度评估模型在各个类别上的识别效果。例如,精确率反映了模型预测为某一类别的样本中,真正属于该类别的比例;召回率表示实际属于某一类别的样本中,被正确预测出来的比例。通过这些指标的综合分析,可以更全面地了解模型的优势与不足,进而进行针对性优化。
4、MNIST 拓展应用
MNIST 手写数字识别的研究成果不仅局限于数字分类本身,还为其他领域提供了宝贵经验。其背后的技术原理和模型结构,经过适当调整和优化,可以应用于其他图像识别任务,如字母识别、验证码识别等。此外,MNIST 数据集也常被用于教学和科研,帮助初学者理解深度学习的基本概念和流程,助力科研人员验证新算法和新模型的有效性。同时,围绕 MNIST 数据集开展的竞赛和研究,不断推动着图像识别技术向更高精度和更强泛化能力的方向发展 。
二、基于TensorFlow框架MNIST 手写数字识别
1、项目简介
案例名称:卷积神经网络CNN实现手写字MNIST图片识别
MNIST手写字识别是一个经典的机器学习任务,旨在通过卷积神经网络(CNN)对28×28像素的灰度手写数字图像进行分类。该数据集包含70,000张图片,分为60,000张训练图片和10,000张测试图片,每张图片代表一个0-9的手写数字。
本项目的目标是构建一个高精度的卷积神经网络模型,能够准确地识别手写数字,并且在测试集上达到较高的准确率。此外,项目还将探索如何优化模型结构、调整超参数以提高性能。

MNIST 数据集来源于 NIST(美国国家标准与技术研究院)的手写字符数据库。 它由 Yann LeCun、Corinna Cortes 和 Christopher J.C. Burges 于 1998 年整理并发布。
【1】MNIST 数据集
– 样本数量:包含 70,000 张灰度图像,分为两个部分:
— 训练集:60,000 张图像
— 测试集:10,000 张图像
– 图像尺寸:每张图像大小为 28×28 像素。
– 类别:10 个类别(0 到 9 的手写数字)。
【2】格式
– 每个图像是一个 28×28 的矩阵,像素值范围为 0 到 255,表示灰度值(0 表示黑 色,255 表示白色)。
– 标签是单个整数(0 到 9),表示图像中的数字。
【3】数据集特点
标准化:
– 图像已经经过预处理,背景被归一化为纯黑色,手写数字居中对齐。
– 这使得数据集相对简单且容易处理,适合初学者和基准测试。
多样性:
– 尽管数据集中的图像都是手写数字,但它们来自不同的书写者,具有不同的风格和笔迹,增加了数据集的多样性。
平衡性:
– 每个类别的样本数量大致相同,避免了类别不平衡的问题。
易于获取和使用:
– MNIST 数据集可以通过多种方式轻松获取,例如通过 TensorFlow、PyTorch、scikit-learn 等库直接加载。

2、用到的技术和工具
开发工具:Anaconda Jupyter Notebook
深度学习框架:TensorFlow、Keras
Python工具包:Numpy、matplotlib
3、主要功能
(1) 数据预处理
– 加载MNIST数据集。
– 对数据进行归一化处理,将像素值从0-255缩放到0-1之间。
– 将标签转换为独热编码(one-hot encoding)。
(2) 模型构建
– 构建卷积神经网络模型,包括卷积层、池化层、全连接层等。
– 使用适当的激活函数(如ReLU)和损失函数(如交叉熵)。
– 添加正则化技术(如Dropout)以防止过拟合。
(3) 模型训练
– 使用Adam优化器或其他合适的优化算法进行模型训练。
– 设置批量大小(batch size)、迭代次数(epochs)等超参数。
– 在训练过程中监控模型的损失和准确率。
(4) 模型评估
– 在测试集上评估模型的性能,计算准确率、精确率、召回率等指标。
– 可视化混淆矩阵,分析模型的分类错误情况。
(5) 模型优化
– 调整网络结构,如增加或减少卷积层、池化层的数量。
– 调整超参数,如学习率、批量大小、迭代次数等。
– 使用数据增强技术(如旋转、平移)来扩充训练数据。

4、实验结果展示
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz 11490434/11490434 ━━━━━━━━━━━━━━━━━━━━ 9s 1us/step Epoch 1/5
D:myworkAnaconda2024Libsite-packageskerassrclayersconvolutionalase_conv.py:107: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead. super().__init__(activity_regularizer=activity_regularizer, **kwargs)
750/750 ━━━━━━━━━━━━━━━━━━━━ 11s 12ms/step – accuracy: 0.8636 – loss: 0.4441 – val_accuracy: 0.9808 – val_loss: 0.0663 Epoch 2/5 750/750 ━━━━━━━━━━━━━━━━━━━━ 9s 12ms/step – accuracy: 0.9802 – loss: 0.0606 – val_accuracy: 0.9821 – val_loss: 0.0620 Epoch 3/5 750/750 ━━━━━━━━━━━━━━━━━━━━ 10s 13ms/step – accuracy: 0.9886 – loss: 0.0370 – val_accuracy: 0.9876 – val_loss: 0.0411 Epoch 4/5 750/750 ━━━━━━━━━━━━━━━━━━━━ 10s 13ms/step – accuracy: 0.9911 – loss: 0.0263 – val_accuracy: 0.9878 – val_loss: 0.0427 Epoch 5/5 750/750 ━━━━━━━━━━━━━━━━━━━━ 10s 13ms/step – accuracy: 0.9928 – loss: 0.0213 – val_accuracy: 0.9890 – val_loss: 0.0438 313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step – accuracy: 0.9856 – loss: 0.0414 Test accuracy: 0.9900000095367432 313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step

5、TensorFlow实现过程
#步骤-1:导入必要的库
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
from tensorflow.keras.utils import to_categorical
#步骤-2:加载MNIST数据集
(X_train, y_train), (X_test, y_test) = mnist.load_data()
#步骤-3:数据预处理
# 将图像数据从28×28转换为28x28x1以适应卷积层的要求
X_train = X_train.reshape((60000, 28, 28, 1))
X_test = X_test.reshape((10000, 28, 28, 1))
# 将像素值从0-255缩放到0-1之间
X_train = X_train.astype('float32') / 255
X_test = X_test.astype('float32') / 255
# 将标签转换为one-hot编码
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
#步骤-4:构建卷积神经网络模型
model = Sequential()
#步骤-5:添加第一个卷积层
# 添加第一个卷积层,输入形状为(28, 28, 1),32个滤波器,每个滤波器大小为3×3
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
# 添加最大池化层,池化窗口大小为2×2
model.add(MaxPooling2D((2, 2)))
#步骤-6:添加第二个卷积层
# 添加第二个卷积层,64个滤波器
model.add(Conv2D(64, (3, 3), activation='relu'))
# 添加另一个最大池化层
model.add(MaxPooling2D((2, 2)))
#步骤-7:添加第三个卷积层
# 添加第三个卷积层,64个滤波器
model.add(Conv2D(64, (3, 3), activation='relu'))
#步骤-8:添加展平层
# 展平层,将多维特征图转换为一维向量
# 如果觉得没有必要可以不添加池化层
model.add(Flatten())
#步骤-9:添加全连接层
# 全连接层,128个神经元
model.add(Dense(128, activation='relu'))
#步骤-10:添加输出层
# 输出层,10个神经元(对应0-9这10个类别),使用softmax激活函数
model.add(Dense(10, activation='softmax'))
#步骤-11:编译模型
# 编译模型,使用交叉熵损失函数和Adam优化器
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
#步骤-12:训练模型
# 训练模型,训练5个epoch,批量大小为64
model.fit(X_train, y_train, epochs=5, batch_size=64, validation_split=0.2)
#步骤-13:在测试集上评估模型
# 在测试集上评估模型
test_loss, test_acc = model.evaluate(X_test, y_test)
print(f'Test accuracy: {test_acc}')
#最后 可视化一些预测结果
predictions = model.predict(X_test)
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(X_test[i].reshape(28, 28), cmap=plt.cm.binary)
predicted_label = np.argmax(predictions[i])
true_label = np.argmax(y_test[i])
if predicted_label == true_label:
color = 'green'
else:
color = 'red'
plt.xlabel(f'Predicted: {predicted_label}
True: {true_label}', color=color)

6、TensorFlow程序解说
(1)TensorFlow类库
# 导入必要的库
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
from tensorflow.keras.utils import to_categorical
import numpy as np
– numpy是一个强大的科学计算库,提供了多维数组对象、各种派生对象(如掩码数组和矩阵)、以及用于数组快速操作的各种函数。
– 在深度学习中,numpy常用于处理数据、执行数学运算和生成随机数等。
import matplotlib.pyplot as plt
– matplotlib是Python中最常用的绘图库之一,pyplot是其中的一个子库,提供了类似于MATLAB的绘图接口。
– 使用matplotlib.pyplot可以方便地绘制图像、图表等可视化结果,例如展示训练过程中的损失曲线或预测结果。
from tensorflow.keras.datasets import mnist
– tensorflow.keras.datasets模块包含了多个常用的数据集,可以直接加载使用而无需手动下载和处理。
– mnist是手写数字识别的经典数据集,包含60,000张28×28像素的灰度训练图像和10,000张测试图像,标签为0到9之间的整数。
– 通过mnist.load_data()可以轻松加载MNIST数据集。
from tensorflow.keras.models import Sequential
– tensorflow.keras.models.Sequential用于创建顺序模型,即层按顺序堆叠的模型。
– 这种模型适合于简单的、线性堆叠的架构,其中每个层只有一个输入张量和一个输出张量。
– 使用Sequential模型可以方便地添加层、编译模型和进行训练。
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
– Conv2D:二维卷积层,用于对输入数据应用卷积操作。卷积操作可以提取图像中的局部特征,例如边缘、纹理等。
– MaxPooling2D:二维最大池化层,用于缩小特征图的空间尺寸,同时保留最重要的信息。它通过在每个局部区域取最大值来减少特征图的大小。
– Flatten:将多维输入(如卷积层的输出)展平为一维向量,以便连接到全连接层(Dense层)。
– Dense:全连接层,每个神经元与上一层的所有神经元连接。通常用于分类任务的最后一层,输出类别概率。
from tensorflow.keras.utils import to_categorical
– to_categorical函数用于将整数标签转换为one-hot编码形式。
– one-hot编码是一种表示类别标签的方法,其中每个类别的标签是一个长度为类别数的向量,只有对应位置为1,其余位置为0。
– 例如,标签3将被转换为[0, 0, 0, 1, 0, 0, 0, 0, 0, 0],适用于多分类任务的模型训练。
(2)加载数据集
# 加载MNIST数据集
(X_train, y_train), (X_test, y_test) = mnist.load_data()
'''
mnist.load_data() 并不是直接从网络端加载数据,而是依赖于本地缓存或预下载的数据文件。具体来说,当你第一次调用 mnist.load_data() 时,
Keras 或 TensorFlow 会检查本地是否有已经下载好的 MNIST 数据集文件。如果没有找到这些文件,它会从互联网上下载并保存到本地缓存目录中。
以后再次调用时,就会直接使用本地缓存的文件,而不再从网络下载。
在 TensorFlow/Keras 中,MNIST 数据集通常是从 [TensorFlow Datasets](https://www.tensorflow.org/datasets) 或者
[Keras Datasets](https://keras.io/api/datasets/) 下载的,默认情况下存储路径通常是用户主目录下的 .keras/datasets/ 文件夹。
(3)数据预处理
# 数据预处理
# 将图像数据从28×28转换为28x28x1以适应卷积层的要求
X_train = X_train.reshape((60000, 28, 28, 1))
X_test = X_test.reshape((10000, 28, 28, 1))
# 将像素值从0-255缩放到0-1之间
X_train = X_train.astype('float32') / 255
X_test = X_test.astype('float32') / 255
# 将标签转换为one-hot编码
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
# 加载MNIST数据集
(X_train, y_train), (X_test, y_test) = mnist.load_data()
– 这行代码使用mnist.load_data()函数从Keras库中加载MNIST数据集。
– mnist.load_data()返回两个元组:一个是训练集(X_train, y_train),另一个是测试集(X_test, y_test)。
– X_train和X_test分别包含训练图像和测试图像,y_train和y_test分别包含训练标签和测试标签。
# 数据预处理
# 将图像数据从28×28转换为28x28x1以适应卷积层的要求
X_train = X_train.reshape((60000, 28, 28, 1))
X_test = X_test.reshape((10000, 28, 28, 1))
– MNIST数据集中的每个图像是28×28像素的灰度图像,但卷积神经网络通常需要输入图像具有明确的通道数(例如RGB图像有3个通道)。对于灰度图像,
我们将其重塑为28x28x1的形式。
– X_train.reshape((60000, 28, 28, 1))将训练图像数据从形状(60000, 28, 28)重塑为(60000, 28, 28, 1),其中60000是样本数量,28×28是图像尺寸,
1表示单通道(灰度图像)。
– 同样地,X_test.reshape((10000, 28, 28, 1))将测试图像数据从形状(10000, 28, 28)重塑为(10000, 28, 28, 1)。
# 将像素值从0-255缩放到0-1之间
X_train = X_train.astype('float32') / 255
X_test = X_test.astype('float32') / 255
– 图像数据的像素值通常是0到255之间的整数。为了使模型更容易学习,通常将这些值缩放到0到1之间。
– astype('float32')将图像数据类型从默认的uint8转换为float32,以便进行除法运算。
– / 255将每个像素值除以255,从而将其缩放到[0, 1]范围。
# 将标签转换为one-hot编码
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
– MNIST数据集中的标签是0到9之间的整数,表示数字类别。
– 卷积神经网络通常需要标签以one-hot编码的形式提供,即每个类别的标签是一个长度为10的向量,其中只有一个元素为1,其余为0。
– to_categorical函数将整数标签转换为one-hot编码形式。例如,标签3将被转换为[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]。
(4)CNN建模
# 构建卷积神经网络模型
model = Sequential()
# 添加第一个卷积层,输入形状为(28, 28, 1),32个滤波器,每个滤波器大小为3×3
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
# 添加最大池化层,池化窗口大小为2×2
model.add(MaxPooling2D((2, 2)))
# 构建卷积神经网络模型
model = Sequential()
– 创建一个顺序模型 Sequential,这是一个线性堆叠的模型,其中每个层只有一个输入张量和一个输出张量。
– Sequential 模型非常适合构建简单的、线性堆叠的架构,例如典型的卷积神经网络。
# 添加第一个卷积层,输入形状为(28, 28, 1),32个滤波器,每个滤波器大小为3×3
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
– 使用 model.add() 方法向模型中添加一个新的层。
– Conv2D 是一个二维卷积层,用于对输入数据应用卷积操作,提取局部特征。
– 参数解释:
– 32:表示该层有32个滤波器(也称为卷积核),每个滤波器将生成一个特征图。
– (3, 3):表示每个滤波器的大小为3×3像素。
– activation='relu':激活函数使用ReLU(Rectified Linear Unit),即 ( f(x) = max(0, x) ),它将所有负值变为零,正值保持不变。
ReLU是常用的激活函数,因为它可以加速训练过程并避免梯度消失问题。
– input_shape=(28, 28, 1):指定输入图像的形状。对于MNIST数据集,每张图像是28×28像素的灰度图像,因此通道数为1(如果是RGB图像,
则通道数为3)。这里指定了输入图像的形状为 (28, 28, 1)。
# 添加最大池化层,池化窗口大小为2×2
model.add(MaxPooling2D((2, 2)))
– 向模型中添加一个最大池化层 MaxPooling2D,用于缩小特征图的空间尺寸,同时保留最重要的信息。
– 参数解释:
– (2, 2):表示池化窗口的大小为2×2。最大池化层将在每个2×2的区域中取最大值,从而将特征图的宽度和高度减半。
– 这种降采样操作有助于减少计算量和参数数量,同时防止过拟合。
(5)卷积层
# 添加第二个卷积层,64个滤波器
model.add(Conv2D(64, (3, 3), activation='relu'))
# 添加第二个卷积层,64个滤波器
# 在深度学习模型中,尤其是卷积神经网络(Convolutional Neural Network,CNN)里,卷积层起着关键的特征提取作用。
# 这里使用Keras框架(假设是基于Keras,因为常见的用法中 Conv2D 来自于Keras库)的 Conv2D 函数来添加第二个卷积层。
# 参数解释如下:
#【1】 第一个参数 64,表示滤波器(也常被称作卷积核)的数量为64个。每个滤波器都能够学习到输入图像(或者是上一层输出的特征图,如果不是
输入层的话)不同的特征模式。例如,有的滤波器可能会学习到图像中的边缘特征,有的可能会学习到纹理特征等,随着滤波器数量增多,能提取到
的特征也就越丰富多样。
#【2】第二个参数 (3, 3),这是指定了滤波器(卷积核)的大小。意味着每个滤波器是一个 3 行 3 列的二维矩阵,它会在输入数据(比如图像数据,
一般是由多个通道组成,例如彩色图像有红、绿、蓝三个通道)对应的通道上滑动进行卷积操作。在滑动过程中,计算滤波器与对应位置的数据元素
的点积(对应元素相乘后求和),从而生成新的特征值,这样通过在整个输入数据的空间上滑动,就得到了新的特征图。
# – activation='relu',指定了该卷积层的激活函数为ReLU(Rectified Linear Unit,修正线性单元)。激活函数的作用是给神经网络引入非线性
因素,因为如果没有激活函数,无论多少层的神经网络,其本质上都可以等价于一个线性变换模型,表达能力有限。
而ReLU函数的表达式为 f(x) = max(0, x),也就是对于输入的 x 值,当 x 大于等于 0 时,输出就是 x 本身;当 x 小于 0 时,输出为 0。
它具有计算简单、能够加快训练速度、在一定程度上缓解梯度消失问题等优点,使得模型可以学习到更复杂的非线性关系,
所以在这里被选用作为激活函数。
model.add(Conv2D(64, (3, 3), activation='relu'))
(6)池化层
# 添加另一个最大池化层
model.add(MaxPooling2D((2, 2)))
# 添加另一个最大池化层
# 在卷积神经网络架构中,池化层通常紧跟在卷积层后面,用于对卷积层输出的特征图进行下采样操作,减少数据量的同时保留重要的特征信息,
并且能够增强模型对特征位置微小变化的鲁棒性(也就是平移不变性等特性)。这里使用的是Keras框架中的 MaxPooling2D 函数来添加最大池化层。
# 参数 (2, 2) 表示池化窗口的大小,即一个 2 行 2 列的小区域。在这个最大池化操作中,会在卷积层输出的特征图上滑动这个 2×2 的窗口,
对于每个窗口内的元素(比如一个 2×2 的特征值矩阵),选取其中的最大值作为这个小区域的代表值,输出到下一层,这样就使得特征图在高度
和宽度维度上都缩小为原来的一半(假设步长也是默认与池化窗口大小一致的情况)。例如,如果原来的特征图大小是 16×16,经过这个 (2, 2)
的最大池化层后,新的特征图大小就变为 8×8 了,大大减少了后续计算的数据量,同时突出了每个小区域内最显著的特征,有助于提升模型的泛
化能力和防止过拟合等情况。
model.add(MaxPooling2D((2, 2)))
(7)展平层
# 添加第三个卷积层,64个滤波器
model.add(Conv2D(64, (3, 3), activation='relu'))
# 展平层,将多维特征图转换为一维向量
model.add(Flatten())
在深度学习中,特别是在构建卷积神经网络(CNN)时,Flatten() 层是一个非常重要的组件。它用于将多维特征图(通常是三维的,例如
(height, width, channels))转换为一维向量,以便可以将其传递给全连接层(Dense 层)。下面我将详细解释 Flatten() 层的作用和用法。
1. 为什么要使用 Flatten() 层?
在 CNN 中,卷积层和池化层通常会输出多维的特征图。这些特征图的形状通常是 (batch_size, height, width, channels),其中:
– batch_size 是批次大小,表示一次处理的样本数量。
– height 和 width 是特征图的高度和宽度。
– channels 是特征图的通道数,即卷积核的数量。
然而,当我们将这些特征图传递给全连接层(Dense 层)时,全连接层期望输入是一维的向量,而不是多维的张量。因此,我们需要一个步骤将多维
特征图展平成一维向量,这就是 Flatten() 层的作用。
2. Flatten() 层的工作原理
Flatten() 层不会改变数据的内容或顺序,它只是简单地将多维张量重新排列成一维向量。具体来说,假设你有一个形状为
(batch_size, height, width, channels) 的张量,经过 Flatten() 层后,它的形状将变为 (batch_size, height * width * channels)。
例如:
– 如果输入张量的形状是 (32, 7, 7, 64)(即批次大小为 32,特征图的高和宽都是 7,通道数为 64),那么经过 Flatten() 层后,
输出的形状将是 (32, 3136),因为 7 * 7 * 64 = 3136。
(8)全连接层
# 全连接层,128个神经元
model.add(Dense(128, activation='relu'))
model.add(Dense(128, activation='relu'))
解释:
1. model.add():
– 这个方法将一个层(layer)添加到模型中。Keras 中的模型是按顺序堆叠的层结构,每一层都会对输入数据进行某种变换。
2. Dense:
– Dense 是一种全连接层(也称为密集层或完全连接层)。在全连接层中,每个神经元都与上一层的所有神经元相连。它通常用于处理平坦化的
特征向量,并且在网络的最后几层中非常常见。
3. 128:
– 这是指该层中的神经元数量(即输出维度)。在这个例子中,有 128 个神经元。这意味着该层会输出一个长度为 128 的特征向量。每个神经元
会对输入数据进行加权求和,并通过激活函数进行非线性变换。
4. activation='relu':
– 激活函数(activation function)用于引入非线性。'relu' 表示 ReLU 激活函数,定义为 f(x) = max(0, x)。ReLU 函数有助于加速训练,
并且能够有效处理梯度消失问题。它将所有负值设为零,而正值保持不变。
背景信息:
– 全连接层的作用:
– 在卷积神经网络(CNN)中,全连接层通常位于卷积层和池化层之后。卷积层和池化层负责提取局部特征,而全连接层则负责将这些局部特征组合
成全局特征,从而进行分类或其他任务。
– 平坦化(Flattening):
– 在使用全连接层之前,通常需要将多维张量(例如来自卷积层的特征图)展平成一维向量。这可以通过 Flatten 层来实现。例如:
python
model.add(Flatten())
– 假设卷积层的输出形状是 (batch_size, height, width, channels),那么 Flatten 层会将其转换为 (batch_size, height width channels)。
– 为什么选择 128 个神经元?:
– 神经元的数量是一个超参数,可以根据具体任务的需求进行调整。128 是一个常见的选择,因为它提供了足够的表达能力,同时不会使模型过于
复杂。你可以根据实验结果和性能需求调整这个数字。
总结:
这行代码的作用是:
– 添加一个包含 128 个神经元的全连接层。
– 使用 ReLU 激活函数对每个神经元的输出进行非线性变换。
– 这一层通常位于卷积层和池化层之后,用于将提取到的特征进行进一步处理,以准备最终的分类或其他任务。
(9)输出层
# 输出层,10个神经元(对应0-9这10个类别),使用softmax激活函数
model.add(Dense(10, activation='softmax'))
# 编译模型,使用交叉熵损失函数和Adam优化器
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
解释:
1. model.compile():
– compile 方法用于配置模型的训练过程。在 Keras 中,编译是将模型的结构与优化器、损失函数和评估指标联系起来的关键步骤。
它为后续的训练做好准备。
2. optimizer='adam':
– 优化器(Optimizer):优化器决定了如何更新模型权重以最小化损失函数。Adam 是一种非常流行的优化算法,结合了 Momentum
和 RMSprop 的优点。
– Adam (Adaptive Moment Estimation):
– Adam 使用了一种自适应学习率的方法,可以有效地处理稀疏梯度和非平稳目标。
– 它通过估计一阶矩(均值)和二阶矩(方差)来动态调整每个参数的学习率。
– Adam 通常比传统的 SGD(随机梯度下降)收敛更快且更稳定。
3. loss='categorical_crossentropy':
– 损失函数(Loss Function):损失函数衡量的是模型预测值与真实标签之间的差异。选择合适的损失函数对于训练模型至关重要。
– Categorical Crossentropy:
– 这是一个多分类问题中常用的损失函数,适用于具有多个类别的分类任务。
– 假设我们有 ( n ) 个类别,每个样本的真实标签是一个 one-hot 编码向量(长度为 ( n ),只有一个位置为 1,其余为 0),
而模型的输出是一个概率分布(长度为 ( n ) 的向量,表示每个类别的概率)。
– Categorical Crossentropy 计算的是这些概率分布之间的距离。公式如下:
[
L = -sum_{i=1}^{n} y_i log(hat{y}_i)
]
其中 ( y_i ) 是真实标签,( hat{y}_i ) 是预测的概率。
4. metrics=['accuracy']:
– 评估指标(Metrics):评估指标用于在训练和验证过程中监控模型性能。Keras 支持多种评估指标,例如准确率、精确率、召回率等。
– Accuracy:
– 准确率是最常用的评估指标之一,计算的是正确分类的样本数占总样本数的比例。
– 对于多分类问题,准确率是所有类别的平均准确率。
– 在训练过程中,Keras 会根据这个指标来报告模型的表现。
背景信息:
– 为什么选择 Adam 优化器?:
– Adam 是一个自适应学习率的优化器,能够有效处理复杂的优化问题,特别是在深度神经网络中表现良好。
– 它在大多数情况下都能提供较快的收敛速度和较好的泛化性能,因此成为许多机器学习任务的默认选择。
– 为什么选择 Categorical Crossentropy 损失函数?:
– 当你有一个多分类问题时,Categorical Crossentropy 是一个自然的选择。它能很好地处理多类别标签,并且对不同类别之间的概率分布敏感。
– 如果你的问题是二分类问题,则可以选择 Binary Crossentropy。
– 为什么要使用 Accuracy 作为评估指标?:
– 准确率是最直观和最容易理解的评估指标之一,特别是对于分类问题。
– 然而,在某些不平衡数据集上,准确率可能不是最佳选择,这时你可以考虑其他指标如 F1 分数或 AUC-ROC。
总结:
这行代码的作用是:
– 配置模型的训练过程,包括选择优化器、损失函数和评估指标。
– 使用 Adam 优化器进行权重更新,确保模型能够快速且稳定地收敛。
– 使用 Categorical Crossentropy 作为损失函数,适用于多分类问题。
– 使用准确率(Accuracy)作为评估指标,以监控模型在训练和验证过程中的性能。
(10)训练模型
# 训练模型,训练5个epoch,批量大小为64
model.fit(X_train, y_train, epochs=5, batch_size=64, validation_split=0.2)
model.fit(X_train, y_train, epochs=5, batch_size=32, validation_split=0.2)
参数解释:
1. X_train 和 y_train:
– X_train: 这是训练数据集,通常是一个 NumPy 数组或 Pandas DataFrame,包含所有用于训练模型的输入特征。
– y_train: 这是训练标签,通常也是一个 NumPy 数组或 Pandas Series,包含与 X_train 对应的输出标签(即目标变量)。
2. epochs=5:
– epochs: 这是指定模型在整个训练集上进行训练的次数。每次遍历整个训练集称为一个 epoch。在这个例子中,模型将遍历训练集 5 次。
– 例如,如果 X_train 包含 1000 个样本,那么在每个 epoch 中,模型会看到这 1000 个样本一次。经过 5 个 epoch 后,模型总共会看到
这些样本 5 1000 = 5000 次。
3. batch_size=64:
– batch_size: 这是指定每次更新模型权重时使用的样本数量。在每个 epoch 内,训练数据会被分成多个批次(batches),每个批次包含
batch_size 个样本。模型会在每个批次上计算损失并更新权重。
– 在这个例子中,batch_size=32 表示每次更新模型权重时,模型会处理 64 个样本。如果 X_train 有 1000 个样本,那么在一个 epoch 中,
模型会处理大约 1000 / 32 ≈ 31.25 次(实际上是 31 次完整的批处理和 1 次剩余的批处理)。
4. validation_split=0.2:
– validation_split: 这是指定从训练数据集中划分出一部分作为验证集的比例。验证集用于评估模型在未见过的数据上的性能,从而帮助防止过拟合。
– 在这个例子中,validation_split=0.2 表示将训练数据的 20% 用作验证集。假设 X_train 有 1000 个样本,那么 800 个样本将用于训练,
200 个样本将用于验证。
总结:
– model.fit() 方法用于训练神经网络模型。
– X_train 和 y_train 分别是训练数据和对应的标签。
– epochs=5 表示模型将在训练集上训练 5 次。
– batch_size=32 表示每次更新模型权重时使用 32 个样本。
– validation_split=0.2 表示从训练集中划分出 20% 的数据作为验证集,用于监控模型的泛化能力。
(11)模型评估
# 在测试集上评估模型
test_loss, test_acc = model.evaluate(X_test, y_test)
print(f'Test accuracy: {test_acc}')
'''
test_loss, test_acc = model.evaluate(X_test, y_test)
print(f'Test accuracy: {test_acc}')
参数解释:
1. model.evaluate(X_test, y_test):
– X_test: 这是测试数据集,通常是一个 NumPy 数组或 Pandas DataFrame,包含所有用于评估模型性能的输入特征。
这些数据是模型之前未见过的数据,用于最终评估模型的泛化能力。
– y_test: 这是测试标签,通常也是一个 NumPy 数组或 Pandas Series,包含与 X_test 对应的输出标签(即目标变量)。
2. 返回值:
– model.evaluate() 返回一个包含多个指标的列表,默认情况下包括损失值(loss)和准确率(accuracy)。具体返回的
指标取决于你在编译模型时指定的指标。
– 在这个例子中,假设你编译模型时指定了 metrics=['accuracy'],那么 model.evaluate() 将返回两个值:test_loss
和 test_acc。
– test_loss: 模型在测试集上的损失值(loss),这是模型预测结果与实际标签之间的误差度量。
– test_acc: 模型在测试集上的准确率(accuracy),表示模型正确分类的比例。
3. print(f'Test accuracy: {test_acc}'):
– 这行代码使用 Python 的 f-string 格式化字符串来打印测试集上的准确率。
– f'Test accuracy: {test_acc}' 表示将 test_acc 的值插入到字符串中,形成一个格式化的输出。
总结:
– model.evaluate(X_test, y_test) 用于评估模型在测试集上的性能。
– X_test 和 y_test 分别是测试数据和对应的标签。
– model.evaluate() 返回两个值:test_loss 和 test_acc,分别表示测试集上的损失值和准确率。
– 最后一行代码将 test_acc 打印出来,显示模型在测试集上的准确率。
示例输出:
假设模型在测试集上的准确率为 0.85,那么运行这段代码后,你会看到如下输出:
Test accuracy: 0.85
(12)可视化展现
# 可视化一些预测结果
predictions = model.predict(X_test)
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(X_test[i].reshape(28, 28), cmap=plt.cm.binary)
predicted_label = np.argmax(predictions[i])
true_label = np.argmax(y_test[i])
if predicted_label == true_label:
color = 'green'
else:
color = 'red'
plt.xlabel(f'Predicted: {predicted_label}
True: {true_label}', color=color)
详细注释:
1. predictions = model.predict(X_test)
– model.predict(X_test): 使用训练好的模型对测试数据 X_test 进行预测。
– 返回值:返回一个包含所有预测结果的 NumPy 数组。对于分类问题,每个预测结果通常是各个类别的概率分布(softmax 输出)。例如,
如果是一个 10 类分类问题,predictions[i] 将是一个长度为 10 的数组,表示第 i 个样本属于每个类别的概率。
2. plt.figure(figsize=(10, 10))
– plt.figure(figsize=(10, 10)): 创建一个新的图形窗口,指定其大小为 10×10 英寸。这将用于绘制多个子图(subplot),以便同时展示多个
图像及其预测结果。
3. for i in range(25):
– for i in range(25):: 开始一个循环,迭代 25 次。这意味着我们将展示前 25 个测试样本的预测结果。
4. plt.subplot(5, 5, i + 1)
– plt.subplot(5, 5, i + 1): 在当前图形窗口中创建一个 5×5 网格的子图,并选择第 i+1 个子图作为当前绘图区域。这样可以将 25 个图像整齐
地排列在一个 5×5 的网格中。
5. plt.xticks([]), plt.yticks([]), plt.grid(False)
– plt.xticks([]) 和 plt.yticks([]): 移除 x 轴和 y 轴上的刻度标记,使图像更简洁。
– plt.grid(False): 关闭网格线,进一步简化图像显示。
6. plt.imshow(X_test[i].reshape(28, 28), cmap=plt.cm.binary)
– plt.imshow(X_test[i].reshape(28, 28), cmap=plt.cm.binary):
– X_test[i]: 获取第 i 个测试样本。
– .reshape(28, 28): 假设输入数据是 28×28 的图像,但可能以扁平化的一维数组形式存储。使用 reshape 方法将其恢复为 28×28 的二维数组。
– cmap=plt.cm.binary: 使用二值颜色映射(黑白)来显示图像。
7. predicted_label = np.argmax(predictions[i])
– np.argmax(predictions[i]): 找到 predictions[i] 中最大值的索引,即模型预测的概率最高的类别。这将给出模型对该样本的最终分类结果。
8. true_label = np.argmax(y_test[i])
– np.argmax(y_test[i]): 找到 y_test[i] 中最大值的索引,即该样本的真实标签。假设 y_test 是 one-hot 编码的标签,np.argmax 可以将
one-hot 向量转换为类别索引。
9. if predicted_label == true_label: color = 'green' else: color = 'red'
– if predicted_label == true_label:: 如果模型的预测结果与真实标签一致,则设置文本颜色为绿色(正确分类)。
– else:: 否则,设置文本颜色为红色(错误分类)。
10. plt.xlabel(f'Predicted: {predicted_label}
True: {true_label}', color=color)
– plt.xlabel(…): 在每个子图的下方添加一个 x 轴标签,显示模型的预测结果和真实标签。
– f'Predicted: {predicted_label}
True: {true_label}': 使用 f-string 格式化字符串,显示预测结果和真实标签。
– color=color: 根据前面判断的颜色(绿色或红色)设置标签文本的颜色。
总结:
这段代码的主要功能是:
1. 使用训练好的模型对测试集进行预测。
2. 创建一个 10×10 英寸的图形窗口,其中包含一个 5×5 的子图网格。
3. 对前 25 个测试样本进行可视化,显示每个样本的图像、模型的预测结果以及真实标签。
4. 使用不同颜色(绿色表示正确分类,红色表示错误分类)来突出显示模型的预测是否正确。


![[2024.10.16更新]加减乘除四则运算自动出题 - 宋马](https://pic.songma.com/blogimg/20250418/d1c4273a5ea74b6792c4090d47cfdecd.png)
















暂无评论内容