CNN入门——Tensorflow2实现MNIST手写数据识别

  项目组经过我和大师兄一个月的Coding,目前基本开发进度告一段落。可以沉下心来搞一搞机器学习相关的东西了。实际上,一开始进项目组的时候就想尝试采用机器学习(更具体的说是深度学习)的方式来做点不一样的事情,而不是纯数学的东西。当然,数学有数学的优势,机器学习有机器学习的优势。

  扯得有点远,回到主题,这是最近学习CNN入门的时候练手用的,也应该算是我的第一个机器学习程序吧。记得做出来的时候,还是有点小小的激动,毕竟人生中第一次也不多,哈哈哈 。中间也踩了不少坑,总结一下,整理成文,供后来者参考。

CNN是什么

  卷积神经网络(英语:convolutional neural network,缩写:CNN)是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的周围单元,对于大型图像处理有出色表现。

  卷积神经网络由一个或多个卷积层和顶端的全连通层(对应经典的神经网络)组成,同时也包括关联权重和池化层(pooling layer)。这一结构使得卷积神经网络能够利用输入数据的二维结构。与其他深度学习结构相比,卷积神经网络在图像和语音识别方面能够给出更好的结果。这一模型也可以使用反向传播算法进行训练。相比较其他深度、前馈神经网络,卷积神经网络需要考量的参数更少,使之成为一种颇具吸引力的深度学习结构。

  卷积神经网络的灵感来自于动物视觉皮层组织的神经连接方式。单个神经元只对有限区域内的刺激作出反应,不同神经元的感知区域相互重叠从而覆盖整个视野。

  以上内容来自于维基百科 ,如果第一次接触的话,肯定是看的一脸懵。有机会我开个坑,尝试用大白话讲一讲。这里有个gif图,形象的展现了卷积的过程:
卷积GIF

MNIST数据集

  MNIST数据集是一个手写数字识别数据集,常用于机器学习和深度学习的训练与测试。这个数据集包含了大量的手写数字图像,包括0到9的数字。这些图像都是28x28像素的灰度图像,每个图像都对应着一个标签,表示图像中所包含的数字。

  MNIST手写数据集👉:官网 。官网中数据集一共分成了四个部分:
lp3fg513.png
  官网上提供了数据集的下载,主要包括四个文件:

文件下载文件用途
train-images-idx3-ubyte.gz训练集图像
train-labels-idx1-ubyte.gz训练集标签
t10k-images-idx3-ubyte.gz测试集图像
t10k-labels-idx1-ubyte.gz测试集标签

  在上述文件中,训练集一共包含了 60,000 张图像和标签,而测试集一共包含了 10,000 张图像和标签。测试集中前5000个来自最初NIST项目的训练集.,后5000个来自最初NIST项目的测试集。前5000个比后5000个要规整,这是因为前5000个数据来自于美国人口普查局的员工,而后5000个来自于大学生。

  这里有一版整合了四个部分的数据集,推荐用这个,我已经上传至网盘,可以直接下载,我的代码也是基于这个整合后的数据集,下载解压即可✌️: 蓝奏云

加载数据集

  这里默认已经准备好所有环境,推荐使用AnacondaPyCharm,新手友好。
  加载数据集有两种方式,第一种是直接无需下载MNIST数据集,因为在Tensorflow中自带了,不过任然需要下载,且下载地址在海外,有可能会出现下载失败的情况,所以不推荐。代码如下,如果没有下载会自动下载:

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

  下载的数据集路径位于:C:\Users\{用户名}\.keras。目前我还没有找到指定下载路径的方法,默认都放在了C盘下。

  第二种方式是手动加载,把下载好的数据集文件放在项目目录下,编写加载数据集代码:

# 加载Mnist数据集
def load_mnist():
    with np.load('mnist.npz', allow_pickle=True) as data:
        return (data['x_train'], data['y_train']), (data['x_test'], data['y_test'])

这里说明一下各个变量:

  • x_train:训练集手写数据图像
  • y_train:训练集手写数据图像对应的标签
  • x_test:测试集手写数据图像
  • y_test:测试集手写数据图像对应的标签

我们可以通过下面函数的打印出形状:

# 主函数
def main():
    mnist = load_mnist()
    print_shape(mnist)
# 打印形状
def print_shape(data):
    x_train, x_label = data[0]
    y_train, y_label = data[1]
    print(x_train.shape)
    print(x_label.shape)
    print(y_train.shape)
    print(y_label.shape)

如果你和我一样,有这样的输出,那么就可以继续进行下一步了。
lp3gb2v9.png

预览数据集

  上一步我们以及实现加载数据集,这里我们要看看这个数据集里的内容的真面貌。比如预览前25个图像,这里用到了matplotlib绘图库,如果没有安装,记得安装一下。

import matplotlib.pyplot as plt
# 主函数
def main():
    mnist = load_mnist()
    view_image(mnist[0])
# 预览前25个图像
def view_image(data):
    x_train, y_train = data
    fig, ax = plt.subplots(nrows=5, ncols=5, sharex='all', sharey='all')
    ax = ax.flatten()
    for i in range(25):
        img = x_train[i].reshape(28, 28)
        ax[i].set_title(y_train[i])
        ax[i].imshow(img, cmap='Greys', interpolation='nearest')
    ax[0].set_xticks([])
    ax[0].set_yticks([])
    plt.tight_layout()
    plt.show()

预览结果:
lp3gl3g5.png
可以看到某些数字好像写的我自己都不认识,但是以标签为准,这也是机器学习的魅力。

训练数据集并保存

  这一步就是最核心的部分了。编写训练部分的代码,然后将训练好的模型保存下来,这样以后直接加载这个模型就可以了,不用再次训练。
  由于MNIST数据集比较简单,我这里没有用到CNN卷积神经网络的卷积层和池化层,只进行了全连接层搭建。激活函数选择了relu,优化器选择Adam:

# 训练并保存为SaveModel
def train_model(mnist):
    x_train, y_train = mnist[0]
    x_test, y_test = mnist[1]
    # 归一化
    x_train = x_train / 255.0
    x_test = x_test / 255.0
    model = keras.Sequential([
        layers.Flatten(input_shape=(28, 28)),
        layers.Dense(128, activation='relu'),
        layers.Dense(10)
    ])
    model.summary()
    model.compile(optimizer=keras.optimizers.Adam(),
                  loss=losses.SparseCategoricalCrossentropy(from_logits=True),
                  metrics=['accuracy'])
    model.fit(x_train, y_train, epochs=10)
    model.save('success')
    test_loss, test_acc = model.evaluate(x_test, y_test, verbose=2)
    print('\nTest accuracy:', test_acc)
    print('Test loss', test_loss)

  等待10轮训练完成,可以看到loss在不断降低,accuracy在不断提高。最终可以达到97.7%的准确率
lp3gyrn9.png

  注意:这里都是通过CPU进行训练的,没有用到GPU,Tensorflow 2.10是Windows本地支持 GPU 的最后一个版本。从2.11开始,你就需要在WSL2上安装Tensorflow,或者使用TensorFlow-DirectML-Plugin
  我用的是2.15版本,不过好在这个数据集并不大,CPU已经完全够用了,整个过程不到20秒就训练结束。训练完成后,会把模型保存在项目文件夹下的success文件夹。

加载模型并可视化验证结果

  训练完毕后,我们可以加载保存好的模型,并进行验证。我这里就直接从测试集中随机选取10张图像来进行验证。有能力的小伙伴完全可以自己手写好数字,压缩成28*28的图像并用模型验证。

# 加载模型并可视化验证结果
def load_model_check(mnist):
    matplotlib.rc("font", family='YouYuan')
    x_test1, y_test = mnist[1]
    # 归一化
    x_test = x_test1 / 255.0
    # 加载模型
    model = keras.models.load_model('success')
    model.summary()
    plt.figure()
    for i in range(10):
        num = np.random.randint(1, 10000)
        plt.subplot(2, 5, i + 1)
        plt.axis('off')
        plt.imshow(x_test1[num], cmap='gray')
        demo = tf.reshape(x_test[num], (1, 28, 28))
        y_pred = np.argmax(model.predict(demo))
        plt.title('标签值:' + str(y_test[num]) + '\n预测值:' + str(y_pred))
    plt.show()

  注意,matplotlib库显示中文有可能会出现方框,记得指定一下字体(代码第二行),我这里就使用幼圆了。
lp3hcgxx.png
  可以看到预测结果还是很不错的,也给出了预测的时间,基本上都是在20ms范围内。

总结

  到此,Tensorflow2实现MNIST手写数据识别就结束了。有的人肯定会问我为啥不用pytorch,因为Tensorflow最大的优势是能够把模型迁移到各种位置,嵌入式、web都可以,而且还能够压缩模型,详情可看:https://tensorflow.google.cn/lite?hl=zh-cn

打赏
评论区
头像
    头像
    Hay
      

    来学习

    头像
    LvHrn
      

    最近刚好在学习这个

    头像

    不愧是研究生

    头像
    wresource
      

    组里面都是用pytorch写的,说是大部分学术论文都是用这个写的,方便学习

文章目录