IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

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

    Veen Zhao发表于 2023-11-18 11:27:00
    love 0

      项目组经过我和大师兄一个月的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个来自于大学生。

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

    加载数据集

      这里默认已经准备好所有环境,推荐使用Anaconda和PyCharm,新手友好。
      加载数据集有两种方式,第一种是直接无需下载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。



沪ICP备19023445号-2号
友情链接