手写数字识别系统

1.主要内容与技术路线

1.1.卷积神经网络

        近年来,深度学习的概念非常火热。深度学习的概念最早由Hinton等人在2006年提出。基于深度置信网络(DBN),提出非监督贪心逐层训练算法,为解决深层结构相关的优化难题带来希望,随后提出多层自动编码器深层结构。此外Lecun等人提出的卷积神经网络(Convolutional Neural Networks / CNNs / ConvNets)是第一个真正多层结构学习算法,它利用空间相对关系减少参数数目以提高训练性能。Alex在2012年提出的AlexNet网络结构模型引爆了神经网络的应用热潮,并赢得了2012届图像识别大赛的冠军,使得CNN成为在图像分类上的核心算法模型。

1.2.深度学习框架

        随着深度学习研究的热潮持续高涨,各种开源深度学习框架也层出不穷,其中包括TensorFlow、Caffe2、Keras、CNTK、Pytorch、MXNet、Leaf、Theano、DeepLearning4等等。Google、Microsoft、Facebook等巨头都参与了这场深度学习框架大战。目前,由谷歌推出的TensorFlow和Facebook推出的Pytorch、Caffe2较受欢迎。下面将简单介绍TensorFlow框架的特点。

        TensorFlow是相对高阶的机器学习库,用户可以方便地用它设计神经网络结构,而不必为了追求高效率的实现亲自写C++或CUDA代码。它和Theano一样都支持自动求导,用户不需要再通过反向传播求解梯度。其核心代码和Caffe一样是用C++编写的,使用C++简化了线上部署的复杂度,并让手机这种内存和CPU资源都紧张的设备可以运行复杂模型(Python则会比较消耗资源,并且执行效率不高)。除了核心代码的C++接口,TensorFlow还有官方的Python、Go和Java接口,是通过SWIG(Simplified Wrapper and Interface Generator)实现的,这样用户就可以在一个硬件配置较好的机器中用Python进行实验,并在资源比较紧张的嵌入式环境或需要低延迟的环境中用C++部署模型。

        TensorFlow的另外一个重要特点是它灵活的移植性,可以将同一份代码几乎不经过修改就轻松地部署到有任意数量CPU或GPU的PC、服务器或者移动设备上。用户能够将训练好的模型方便地部署到多种硬件、操作系统平台上,支持Intel和AMD的CPU,通过CUDA支持NVIDIA的GPU,支持Linux和Mac、Windows,也能够基于ARM架构编译和优化,在移动设备(Android和iOS)上表现得很好。TensorFlow还有功能强大的可视化组件TensorBoard,能可视化网络结构和训练过程,对于观察复杂的网络结构和监控长时间、大规模的训练很有帮助。TensorFlow针对生产环境高度优化,它产品级的高质量代码和设计都可以保证在生产环境中稳定运行,同时TensorFlow广泛地被工业界使用,产生了良性循环,成为了深度学习领域的事实标准。

1.3.MNIST 数据集

        除了神经网络理论体系的发展和深度学习框架的推进,数据集的完善同样促进了人工智能领域的发展。MNIST(Mixed National Institute of Standards and Technology database)是一个计算机视觉数据集,它包含70000张手写数字的灰度图片,其中每一张图片包含 28 X 28 个像素点。可以用一个数字数组来表示这张图片。

图1 MNIST数据格式

        每一张图片都有对应的标签,也就是图片对应的数字,例如上面这张图片的标签就是 1。数据集被分成两部分:60000 行的训练数据集(mnist.train)和10000行的测试数据集(mnist.test)。

        其中:60000 行的训练集分拆为 55000 行的训练集和 5000 行的验证集。60000行的训练数据集是一个形状为 [60000, 784] 的张量,第一个维度数字用来索引图片,第二个维度数字用来索引每张图片中的像素点。在此张量里的每一个元素,都表示某张图片里的某个像素的强度值,值介于 0  1 之间。

        60000 行的训练数据集标签是介于 0  9 的数字,用来描述给定图片里表示的数字。称为 "one-hot vectors"。一个 one-hot 向量除了某一位的数字是 1 以外其余各维度数字都是 0。所以在此教程中,数字 n 将表示成一个只有在第 n 维度(从 0 开始)数字为 1  10 维向量。比如,标签 0 将表示成 ( [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] )。因此,其标签是一个 [60000, 10] 的数字矩阵。如图2a)、(b)所示。

 (a)

(b)

图2 MNIST训练集格式

        下面将借助于TensorFlow框架,在Mnist数据集的基础上,通过卷积神经网络,进行手写数字识别的仿真测试。

2.相关技术简介

2.1.python语言

        Python 是一门简单而又强大的编程语言。对于那些在编程方面有困难的人来说,Python 的出现Python 的诸多特点使它可以作为人工智能领域的脚本语言,这些特点包括以下几方面。

        (1)简单且易学。相对于其他高度结构化的编程语言(CH+或 Visual Basic)而言,Python 更容易被掌握。它的语法简单,编程者将有更多的时间来解决实际问题,而不需要在学习 Python 语音上耗费太多精力。

        (2)免费且开源。Python是一款免费并且开源的软件。用户可以自由地分发该软件的副本,查看和修改源代码,或者将其中一部分代码用在其他免费的程序里。Python 语言如此好用的一个重要原因在于它有一个十分活跃的用户社区,社区里的成员都积极地参与Python 的开发和维护。正是由于 Python 是开源的,所以 Python 才被部署在人工智能的TensorFlow 工具中。

        (3)跨平台。Python 支持包括 Windows、Mac、Linux 在内的各种平台。不同平台上的Python程序只需要做极小的改动甚至不改动,就能在其他平台上正常运行。Python 的用户之所以如此庞大,其中一个重要原因就是它跨平台的特性。

        (4)解释性。许多程序语言(例如C++或 Visual Basic)需要将程序源文件转换成计算机可以理解的二进制代码。这就需要有适用于各种程序语言的编译器。而 Python 是一种解释性语言,它不需要编译就可以直接运行。这一特点使 Python使用起来更加简单,并具有更强的移植性。

        (5)面向对象。Python是一门面向对象的编程语言。面向对象的程序不再是功能的堆砌,而是由一系列相互作用的对象构建起来的。很多现代编程语言都支持面向对象的编程。从这个角度看,将Python 作力 TensorFlow的脚本语言是一个不错的选择。

2.2.基于Adam的优化算法

        Adam优化算法的理论核心是自适应估计梯度的一阶矩和二阶矩,然后利用这些估计值来更新模型的参数。以下是Adam算法的主要计算步骤和理论基础:

2.2.1.理论核心

1.一阶矩估计(均值):Adam算法使用一阶矩估计来跟踪梯度的运行平均值,这有助于确定参数的估计方向。

2.二阶矩估计(未平方的方差):算法还使用二阶矩估计来跟踪梯度平方的运行平均值,这有助于调整学习率的幅度。

3.自适应学习率:每个参数的自适应学习率由其特定的一阶和二阶矩估计值决定,从而允许算法针对不同的参数调整更新步长。

2.2.2.计算推导过程

        初始化:在训练β1和β2开始之前,初始化一阶矩和二阶矩为零向量,学习率

为η,衰减率通常分别设为0.9和0.999,ε是一个很小的数值,通常设为1e-8,以避免分母为零。

参数更新,对于每一步t,执行以下操作:

计算当前参数θ的梯度

更新一阶矩估计:

m_t = β1 * m_(t-1) + (1 - β1) * g_t                       (2.1)

更新二阶矩估计:

v_t = β2 * v_(t-1) + (1 - β2) * (g_t * g_t)               (2.2)

自适应学习率计算公式:

r_t = m_t /(sqrt(v_t) + ε)                                       (2.3)      

修正后的一阶矩估计:

m_t = m_t /(1 -β1^t)                                             (2.4)

修正后的二阶矩估计:

v_t = v_t /(1 -β2^t)                                               (2.5)

参数更新公式:

θ_(t+1) = θ_t - η * (m_t /(sqrt(v_t) + ε))               (2.6)

        Adam优化器的推导过程主要基于指数加权移动平均的思想,通过不断更新一阶和二阶矩估计,使得算法能够自适应地调整学习率。一阶矩估计提供了梯度的动量项,有助于加速收敛;二阶矩估计则提供了对梯度波动的调整,有助于在不同参数上实现更加精细的学习率控制。

2.2.3.特点和优势

        自适应性:每个参数都有自己的学习率,根据其历史梯度信息自适应调整。

        内存效率:只需要存储一阶和二阶矩估计向量,不需要额外的内存开销。

        计算效率:更新规则简单,易于实现和并行化。

        适用于大规模数据和参数:由于其自适应性,Adam在处理大规模问题时表现良好。

        Adam优化器因其高效性和自适应性,在深度学习领域得到了广泛应用,特别是在计算机视觉和自然语言处理等任务中。然而,它也有一些局限性,比如在某些情况下可能会遇到收敛速度慢或陷入局部最小值的问题,这需要在实际应用中根据具体情况进行调整和优化。

2.3.卷积神经网络

        卷积神经网络(Convolutional Neural Network, CNN)是一种深度学习架构,特别适用于处理具有网格结构的数据,如图像(2D网格)和视频(3D网格)。CNN的核心特点在于其卷积层,这些层能够自动学习数据中的局部特征,并且具有参数共享和空间不变性的特点。下面是对CNN的深度技术介绍:

2.3.1.卷积层(Convolutional Layer)

        卷积操作:卷积层通过滤波器(或称为卷积核)对输入数据进行卷积操作,以提取局部特征。每个滤波器负责从输入数据中提取一种特定的特征,如边缘、角点或某些纹理。

        参数共享:卷积层中的滤波器在整个输入数据上滑动,但滤波器的参数在整个网络中是共享的,这大大减少了模型的参数数量。

        用表示输入图像的第i行第j列的像素。用表示过滤器的第m行第n列的值,表示权值偏置,用表示特征图的第i行第j列的值,激活函数选取ReLU函数,则卷积操作可以由下面的公式(2.1)计算:

            (2.1)

特征值的计算由公式:

                 (2.2)

        决定。式(2.2)中表示输出特征图的宽,W_{1}表示输入图像的宽,F_{}表示过滤器的宽,P是填充零的圈数,S是步幅。长和宽等价,所以图像的长也可以用上式(2.2)计算。

        以上就是卷积层卷积和的计算,体现了卷积神经网络的局部连接和权值共享特性,通过卷积操作,参数的数量大幅降低。

卷积层计算过程

2.3.2.激活函数

        非线性引入:在卷积层之后通常会跟一个激活函数,如ReLU(Rectified Linear Unit),它可以帮助引入非线性,使网络能够学习更复杂的特征表示。

2.3.3. 池化层(Pooling Layer)

        降维:池化层用于降低特征图的空间维度,减少参数数量和计算量,同时使特征检测更加鲁棒。

        最大池化:最常见的池化操作是最大池化,它通过选择覆盖区域的最大值来实现。

定义池化窗口:首先定义一个池化窗口(pooling window),通常是一个正方形或矩形,例如2x2或3x3。

        滑动窗口:将池化窗口在输入的特征图(feature map)上按照一定的步长(stride)滑动。步长决定了窗口移动的距离,常见的步长为1或2。

        选择最大值:在池化窗口覆盖的区域内,选择该区域内的最大值作为输出。

        生成新的特征图:重复上述过程,直到覆盖整个输入特征图,生成一个新的、尺寸更小的特征图。

具体步骤

        假设输入特征图的大小为 H×W,池化窗口大小为 k×k,步长为s,则最大池化的计算过程可以详细描述为:

        初始化:创建一个大小为的输出特征图。

        遍历输入特征图:对于输入特征图的每个位置(i,j),计算对应的输出特征图位置 (m,n)其中:n=\frac{j}{s} m=\frac{i}{s}

        确定池化区域:确定以 (i,j) 为中心的 k×k 池化区域。

        计算最大值:在确定的池化区域内,找到最大的元素值:

        其中,i 和 j 分别在 k×k 窗口内遍历。

        填充输出特征图:将计算得到的最大值填充到输出特征图的对应位置(m,n)。

池化过程示例

2.3.4. 全连接层(Fully Connected Layer)

        分类器:在多个卷积和池化层之后,CNN通常包含一个或多个全连接层,用于将学习到的特征映射到最终的输出类别上。

        密集连接:每个神经元都与前一层的所有神经元相连,这使得网络能够在特征上进行更复杂的组合。

       

        如图,假设左边的x1,X2,x3就是我们展平后得到的向量,那么我们用

x*w_{11}+x_{2}*w_{21}+x_{3}*w_{31}=b_{1}

同理,b2也是这么算出来的。这个计算过程可以表示 矩阵运算

 

        那么这个运算中,只要我们增加w矩阵的列数,就可以得到不同的结果数量。比如w设置为3x3的,那就会得到1x3的结果。所以呢,全连接层输出一列向量,最终得到的结果数量是我们可以定义的。

3.手写数字识别系统的分析与设计

3.1.系统需求分析

3.1.1 系统功能分析

        手写数字识别系统是一种利用计算机视觉和机器学习技术,将人类手写数字图像转化为计算机可识别的数字格式的系统。该系统广泛应用于科学研究、金融、医疗保健、教育等领域,为用户提供方便快捷的数字输入方式。有着数据收集与预处理,还可以特征提取,往往都是从预处理后的图像里提取关键特征,如边缘、轮廓、形状等。还有分类与识别就是通过一系列算法对提取完的特征进行分类和识别。以及结果输出将识别以数字形式输出以供我们用户或系统使用等功能。

        手写数字识别系统面向的对象主要是那些需要高效、准确地识别手写数字的场景或用户群体。如科研人员,他们需要处理和分析大量手写数字数据,在就金融工作者和医疗人员、教育工作者和学生等,该系统在金融方面可以处理手写支票,签名认证,在医疗里还可以读取病历和处方里的信息,其次教育领域我们当今学生用的智慧屏,在线考试系统等。

        由于该系统准确性高、速度快、易用性、扩展性这些方面都在发展和应用场景都在扩大,手写数字识别系统应易于使用,无需进行复杂的设置即可上手。还通过需求可设置识别系统、用户界面、性能优化和监控、数据安全和隐私保护,可设置成支持多种手写风格、字体大小和背景条件识别,多种输入方式(如扫描、拍照等),以适应不同用户的需求。让系统在优化同时仍可高负载保持稳定运行提供实时监控和日志记录同时提高安全性通过数据加密和访问控制功能,防止数据泄露和滥用。

3.2.系统流程设计

3.2.1.数据准备

         加载数据集:通过调用datasets.mnist.load_data(),我们轻松获取了MNIST手写数字数据集,它包含了60000个训练样本和10000个测试样本。这些样本都是28x28像素的灰度图像,每个图像关联一个标签(0-9的手写数字)。

        归一化处理:为了提高模型训练的效率和收敛性,我们将图像数据的像素值从0-255的范围归一化到0-1之间,即

        `train_images = train_images.astype('float32') / 255`和`test_images = test_ images.astype('float32')  / 255`。这个步骤保证了输入模型的数据具有标准正态分布特性,有利于模型参数的更新和学习。

        调整数据形状:我们重新调整了图像数据的形状,使其符合模型输入要求。对于CNN模型,输入数据需要是4维张量,所以需要将原数据从二维数组转变为四维数组,即(samples, height, width, channels)。在这里,通道数为1,表示灰度图像。

3.2.2.模型构建:

        序贯模型搭建:使用models.Sequential()构建了一个序贯模型,它允许我们堆叠不同的层。这种模型的优点是简洁、易于理解,并且能够快速定义。

        卷积层与池化层:模型中包含两个卷积层,每个卷积层使用了32个和64个卷积核,核的大小为3x3,激活函数为ReLU。卷积层用于提取图像中的局部特征。每层卷积后都跟随了一个最大池化层,池化窗口为2x2,这有助于降低特征图的空间尺寸,减少计算量并防止过拟合。

        连接全连接层:在卷积和池化层之后,我们使用Flatten层将二维的特征图展平为一维向量。接着,添加两个全连接层(也称为密集层),第一个全连接层包含128个神经元,用于高层特征的非线性变换,第二个全连接层有10个神经元对应于10个类别的输出,并使用softmax激活函数输出分类概率。

3.2.3.模型编译:

        设置编译参数:模型在编译时需要指定优化器、损失函数和评估指标。这里使用的优化器是Adam,一种自适应学习率的优化方法。损失函数选用了sparse_categorical_crossentropy,适合多分类问题,特别是当标签为整数时。评估指标设置accuracy,即分类准确率。

3.2.4.模型训练:

        训练过程:使用model.fit()方法对模型进行训练,传入训练数据、标签以及验证数据。训练过程中,模型的参数将不断更新以最小化损失函数。训练周期设为10个epochs,意味着整个训练数据集将被完整地遍历10次。

3.2.5.模型预测:

        预测与展示:一旦模型训练完成,我们使用它来预测测试集中的第一张图像,并通过plt.imshow()显示该图像。然后,我们打印出模型对该图像的预测结果。

4.手写数字识别系统的实现

4.1.库的准备与数据加载

        首先导入必要的库,包括TensorFlow及其Keras API,用于构建和训练神经网络,以及Matplotlib用于数据可视化。然后,它加载了MNIST数据集,这是一个广泛用于手写数字识别任务的公共数据集。

        接着加载数据集,train_images 和 test_images 是图像数据,它们的形状分别是 (60000, 28, 28) 和 (10000, 28, 28)。这表示训练集有60,000张图像,测试集有10,000张图像,每张图像是28像素宽和28像素高。train_labels 和 test_labels 是对应的标签数据,它们的形状分别是 (60000,)和(10000,)。这意味着训练集和测试集的标签分别是60,000个和10,000个,每个标签是一个整数,表示图像中手写数字的类别(0到9)。如图4.1所示,

图4.1数据集形状

然后是像素归一化:代码中将图像数据的像素值从范围 [0, 255] 归一化到 [0, 1]。这是神经网络训练中常见的预处理步骤,有助于加快收敛速度并提高模型性能。最后是数据集形状输出:打印出的数据形状信息确认了数据集的维度,这对于理解数据结构和后续处理非常重要。

4.2.数据可视化

数据可视化是深度学习项目中一个重要的环节,它帮助我们直观地了解数据集的特征和分布。在手写数字识别系统中,可视化主要关注于展示MNIST数据集中手写数字的图像。

在本模块中,我们选择了数据集中的前20个样本进行展示。每个样本是一个28x28像素的灰度图像,代表一个手写数字。为了更清晰地展示这些图像,我们使用了Matplotlib库来创建一个包含20个子图的图形界面。

每个子图对应一个手写数字图像,我们通过以下步骤进行展示:

创建一个图形对象,设置其大小为20宽、10高,以确保有足够的空间展示所有子图。

使用plt.subplot函数将整个图形窗口划分为2行10列的网格,并在每个网格中绘制一个子图,plt.subplot(2,10,i+1)将确保每个图像按照顺序排列在正确的位置上。

对于每个子图,我们隐藏了坐标轴和网格线,以便更清晰地展示图像本身。

使用plt.imshow函数将每个手写数字图像显示出来,使用plt.cm.binary颜色映射,这样可以更清楚地看到数字的轮廓。

在每个子图的下方,使用plt.xlabel设置标签,显示对应的数字类别,以便于识别。

最后,我们使用plt.show()函数展示整个图形,并使用plt.savefig保存为图像文件mnist1.png。使用matplotlib绘制训练集中前20个图像,如图4.2所示。

图4.2数据可视化

通过这种可视化方式,我们可以快速地对数据集有一个直观的认识,检查数据的质量,确认图像的归一化效果,以及是否存在异常值或需要清洗的数据点。

在数据预处理阶段,我们首先对MNIST数据集中的图像进行了重塑,以符合卷积神经网络的输入格式,即(批量大小,高度,宽度,通道数)。由于MNIST图像是灰度的,我们添加了一个通道维度,使得图像的形状从(60000, 28, 28)变为(60000, 28, 28, 1),测试集也进行了相同的操作,如图4.3所示

图4.3 二次归一化

4.3.模型构建

在模型构建部分,定义了一个Sequential模型,是一个线性堆叠的层次模型。人工神经网络包括:输入层、隐藏层、输入层,隐藏包含以下层:

卷积层 (Conv2D):通过卷积操作对输入图像进行降维和特权抽取,共进行了3次,每次使用32个大小为3x3的卷积核。

激活函数:relu被用作激活函数,它有助于引入非线性,增强模型的表达能力。activation参数将激活函数设置为relu函数作为激励函数可以增强判定函数和整个神经网络的非线性特性,相比于某些其他激活函数,relu对于梯度的传播能够更好地缓解梯度消失问题,有助于训练深层神经网络,所以relu函数更受青睐,将神经网络的训练速度提升数倍

池化层:共进行了2次,每次使用2x2的池化窗口,用于降低特征维度,减少参数数量,也可以叫做“压缩”。

全连接层:进行了2次,第一次全连接层有32个神经元,使用ReLU激活函数;第二次全连接层即输出层,有10个神经元,每个神经元对应一个类别(从0到9的数字)。

使用TensorFlow和Keras构建的卷积神经网络(CNN)模型的结构。模型设计用于处理MNIST数据集,该数据集包含28x28像素的手写数字图像。模型层级结构如图4.4所示

图4.4 层次结构

4.4.模型编译与训练

在模型编译与训练中,首先,我们使用model.summary函数来获取模型的概要信息。这个函数将打印出模型中每一层的名称、输出形状以及参数数量,帮助我们快速了解模型的结构和复杂度。

之后,我们选择了adam优化器,这是一种基于梯度下降的优化算法,它是一种自适应学习率的优化算法,广泛用于训练深度学习模型。

损失函数使用了SparseCategoricalCrossentropy,该函数适用于多类分类问题,其中from_logits=True参数表示模型输出未经softmax转换的原始预测值,其中标签是整数形式。评估指标选择了accuracy,即模型预测正确类别的比例。

随后,使用训练数据对模型进行了训练,设置了15个训练周期数(epoch),并在每个epoch后使用测试集对模型进行了验证。

训练完成后,我们使用模型对测试集进行预测。我们先使用plt.imshow函数展示测试集中的一张图像。然后,通过model.predict函数对测试集的所有图像进行预测,并打印第一张图像的预测结果。结果如图4.5所示,根据图可以看出12.8是最大的,即预测结果应该是1,如图4.6所示

图4.5 预测结果

图4.6 可视化预测结果

4.5.图像处理

图像处是机器学习和深度学习项目中的重要步骤,图像处理确保输入数据的格式和范围与模型的预期输入相匹配,例如调整图像大小、归一化像素值等。通过图像预处理,可以提高模型对图像的识别率。归一化像素值有助于模型更快地收敛,并且可以提高模型的泛化能力。

图像处理的目的是将用户手写的数字图像转换为模型能够识别的格式。以下是图像处理的步骤:

图像读取:使用图像处理库读取图像文件。

灰度转换:将彩色图像转换为灰度图像,因为MNIST数据集是灰度的。

大小调整:调整图像大小至28x28像素,以匹配MNIST数据集的图像尺寸。

归一化:将像素值从[0, 255]归一化到[0, 1]。

维度调整:扩展图像维度以匹配模型的输入格式,即从(28, 28)变为(1, 28, 28, 1)。如图4.7所示

图4.7 维度调整

4.6.测试及结果

在图像处理的时候,已经将图像转化为可以识别的格式,只需将手写的名字改为image3.png,放在如图4.8所示的位置即可,之后便可以进行执行,如图4.9所示是模型在训练过程中的部分性能。

图4.8 image3图片位置

图4.9 训练过程

随着训练的进行,可以看到模型的准确率逐渐提高,损失逐渐降低,如图4.10所示是损失值。这表明模型在训练集和验证集上的性能都在改善。模型在训练集上表现良好,同时在验证集上也能保持较高的准确率,以避免过拟合。如图4.11所示,测试结果为1。

图4.10 损失值

图4.11 测试结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值