使用Keras进行深度学习:(二)CNN讲解及实践

本文是全系列中第2 / 7篇:Keras 从入门到精通

笔者:Ray网络

现今最主流的处理图像数据的技术当属深度神经网络了,尤为是卷积神经网络CNN尤其出名。本文将经过讲解CNN的介绍以及使用keras搭建CNN经常使用模型LeNet-5实现对MNist数据集分类,从而使得读者更好的理解CNN。app

1.CNN的介绍机器学习

CNN是一种自动化提取特征的机器学习模型。首先咱们介绍CNN所用到一些基本结构单元:函数

1.1卷积层:在卷积层中,有一个重要的概念:权值共享。咱们经过卷积核与输入进行卷积运算。经过下图能够理解如何进行卷积运算。卷积核从左到右对输入进行扫描,每次滑动1格(步长为1),下图为滑动一次后,卷积核每一个元素和输入中绿色框相应位置的元素相乘后累加,获得输出中绿色框中的0。通常会使用多个卷积核对输入数据进行卷积,获得多个特征图。学习

图1:卷积运算测试

1.2激活层:对卷积层的输出进行一个非线性映射,由于卷积计算是一种线性计算。常见的激活函数有relu、tanh、sigmoid等,通常使用relu。字体

1.2.1引入非线性激活函数的缘由:编码

若是不引入非线性映射的话,不管有多少层神经网络,输出都是输入的线性组合,这与一层隐藏层的效果至关。.net

1.2.2通常使用relu的缘由:3d

在反向传播计算梯度中,使用relu求导明显会比tanh和sigmoid简单,能够减小计算量。

同时,使用tanh和sigmoid,当层数较多时容易致使梯度消失,由于tanh和sigmoid的导数均小于1(可参考激活函数的导数公式),当咱们神经网络有多层的时候,每层都要乘以这个小于1的导数,就有可能接近于0,这就是所谓的梯度消失。而使用relu求导,若输出不为0时,导数均为1,能够有效避免梯度消失问题。

另外,relu还会将小于0的映射为0,使得网络较为稀疏,减小神经元之间的依赖,避免过拟合。

图2:从左到右依次为sigmoid、tanh、relu激活函数

池化层:池化的目的就是减小特征图的维度,减小数据的运算量。池化层是在卷积层以后,对卷积的输出,进行池化运算。池化运算,通常有两种MaxPooling和MeanPooling。选取一个池化窗口(通常为2*2),而后从左往右进行扫描,步长通常为2。以下图MaxPooling操做,选取池化窗口中最大值做为该位置的输出。如:左边绿色方框中四个特征值中,选取最大的6做为输出相应位置的特征值。而MeanPooling则是对于池化窗口中的特征值求平均。

全链接层:主要是对特征进行从新的拟合,减小特征信息的丢失。经过卷积池化操做后获得的是多个特征矩阵,而全链接层的输入为向量,因此在进行全链接层以前,要将多个特征矩阵“压平”为一个向量。

 

对于CNN的卷积、池化操做,其实不少文章都会详细的介绍,但卷积和池化的意义是什么,不少文章都没有明确给出解释。可能会有人认为卷积和池化能够很大程度的减小权重参数,但只是由于这个缘由吗?显然不是的,接下来将讲解CNN是如何实现有效的分类从而理解卷积和池化的意义。

用深度学习解决图像识别问题,从直观上讲是一个从细节到抽象的过程。所谓细节,就是指输入图像的每一个像素点,甚至像素点构成的边也能够理解为是细节。假设咱们大脑接收到一张动物图,大脑最早反应的是该图的点和边。而后由点和边抽象成各类形状,好比三角形或者圆形等,而后再抽象成耳朵和脸等特征。最后由这些特征决定该图属于哪一种动物。深度学习识别图像也是一样的道理。这里关键的就是抽象。何为抽象呢?抽象就是把图像中的各类零散的特征经过某种方式汇总起来,造成新的特征。而利用这些新的特征可更好区分图像类别。如刚才这个例子,点和边就是零散的特征,经过将边进行汇总咱们就获得了三角形或圆形等新的特征,同理,将三角形这个特征和一些其余零散的特征汇总成耳朵这个新特征。显而易见,耳朵这个新特征会比三角形特征更利于识别图像。

深度学习正是经过卷积操做实现从细节到抽象的过程。由于卷积的目的就是为了从输入图像中提取特征,并保留像素间的空间关系。何以理解这句话?咱们输入的图像其实就是一些纹理,此时,能够将卷积核的参数也理解为纹理,咱们目的是使得卷积核的纹理和图像相应位置的纹理尽量一致。当把图像数据和卷积核的数值放在高维空间中,纹理等价于向量,卷积操做等价于向量的相乘,相乘的结果越大,说明两个向量方向越近,也即卷积核的纹理就更贴近于图像的纹理。所以,卷积后的新图像在具备卷积核纹理的区域信号会更强,其余区域则会较弱。这样,就能够实现从细节(像素点)抽象成更好区分的新特征(纹理)。每一层的卷积都会获得比上一次卷积更易区分的新特征。

而池化目的主要就是为了减小权重参数,但为何能够以Maxpooling或者MeanPooling表明这个区域的特征呢?这样不会有可能损失了一些重要特征吗?这是由于图像数据在连续区域具备相关性,通常局部区域的像素值差异不大。好比眼睛的局部区域的像素点的值差异并不大,故咱们使用Maxpooling或者MeanPooling并不会损失不少特征。

 

2项目实例

2.1模型介绍

有了上文对CNN的讲解后,读者对CNN应该有了必定的理解,接下来咱们将基于此搭建CNN常见模型LeNet-5模型,并对Mnist数据集进行预测。

图3:LeNet-5模型

从上图LeNet-5模型中,能够了解到该模型由如下结构组成:

第一层:卷积层

这一层的输入的原始的图像像素,该模型接受的图像为32321,6个5*5卷积核,步长为1,不使用全0填充。因此这层输出的尺寸为32-5+1=28,深度为6。

第二层:池化层

该层的输入为第一层的输出,是一个28286的节点矩阵。本层采用的过滤器大小为22,长和宽的步长均为2,因此本层的输出矩阵大小为1414*6。

第三层:卷积层

本层的输入矩阵大小为14146,16个55卷积核,一样不使用全0填充,步长为1,则本层的输出为1010*16。

第四层:池化层

该层使用22的过滤器,步长为2,故本层的输出矩阵为55*16。

第五层:全链接层

如上文所说,在全链接层以前,须要将5516的矩阵“压扁”为一个向量。本层的输出节点个数为120。

第六层:全链接层

该层输出节点个数为84。

第七层:全链接层

最后一层,输出节点个数为10,样本的标签个数。

2.2代码实现

2.2.1数据导入及处理

Mnist数据集为手写字体,训练集有60000张图片,测试集中有10000张图片,标签为0-9。因为Mnist数据集为IDX文件格式,是一种用来存储向量与多维度矩阵的文件格式,不能直接读取。有两种方式能够进行读取。第一种是Keras.datasets库中有mnist数据集,直接调用便可,可是因为须要Keras指定地址下载数据集,速度较慢,最好先下载;第二种是使用struct库函数解析数据集,比较麻烦,可是也能够试试。

图4:导入Mnist数据集

对于mnist数据集只是作了一些简单的预处理,将输入数据的数据类型转换为float32,并进行归一化。对标签进行独热编码,由于最后输出节点个数为10,而标签只有1维。

图5:数据预处理

2.2.2LeNet-5模型的搭建

图6: Keras搭建LeNet-5模型

2.2.3训练模型

图7:训练模型

2.2.4 评估模型

图8:评估模型

最终在测试集的准确率能够达到99.7%。

经过一个简单项目的实现,既能够帮助咱们进一步了解CNN,又能够熟悉Keras应用。最终模型还能够保存到本地,便于下次使用。

图9:保存和读取模型

3.迁移学习

迁移学习就是把已训练好的模型参数迁移到新模型来帮助新模型训练。考虑到大部分数据或任务存在相关性的,因此经过迁移学习咱们能够将已经学到的模型参数经过某种方式来分享给模型从而加快训练模型。

keras.applications库中有许多已经训练好的模型,咱们能够对已有的模型进行一些修改获得咱们想要的模型,从而提升模型搭建和训练的效率。另外,当咱们的数据不足的时候,使用迁移学习思想也是一个很好的想法。在下图,将简单的经过迁移学习实现VGG16。可是因为VGG16模型要求输入为RGB图像,因此须要使用opencv模块对图像进行处理。

图10:经过迁移学习高效搭建vgg16模型

经过上图,能够看出经过迁移学习咱们能够省去搭建多个卷积和池化层,而且能够省去训练参数的时间,vgg16有3364万个网络权重,若是所有从新训练将须要一段较长的时间。是否从新训练网络权重参数,要取决于咱们要所用的数据集的分布与原模型所使用的数据集的分布是否具备相关性。由于模型训练是让模型学习数据的分布,若是不具备相关性,已有的网络权重并不适合于咱们的数据集。