神经网络:卷积神经网络(CNN)

神经网络:卷积神经网络(CNN),第1张

神经网络 最早是由心理学家和神经学家提出的,旨在寻求开发和测试神经的计算模拟。

粗略地说, 神经网络 是一组连接的 输入/输出单元 ,其中每个连接都与一个 权 相关联。在学习阶段,通过调整权值,使得神经网络的预测准确性逐步提高。由于单元之间的连接,神经网络学习又称 连接者学习。

神经网络是以模拟人脑神经元的数学模型为基础而建立的,它由一系列神经元组成,单元之间彼此连接。从信息处理角度看,神经元可以看作是一个多输入单输出的信息处理单元,根据神经元的特性和功能,可以把神经元抽象成一个简单的数学模型。

神经网络有三个要素: 拓扑结构、连接方式、学习规则

神经网络的拓扑结构 :神经网络的单元通常按照层次排列,根据网络的层次数,可以将神经网络分为单层宴友神经网络、两层神经网络、三层神经网络等。结构简单的神经网络,在学习时收敛的速度快,但准确度低。

神经网络的层数和每层的单元数由问题的复杂程度而定。问题越复杂,神经网络的层数就越多。例如,两层神经网络常用来解决线性问题,而多层网络就可以解决多元非线性问题

神经网络的连接 :包括层次之间的连接和每一层内部的连接,连接的强度用权来表示。

根据层次之间的连接方式,分为:

1)前馈式网络:连接是单向的,上层单元的输出是下层单元的输薯祥察入,如反向传播网络,Kohonen网络

2)反馈式网络:除了单项的连接外,还把最后一层单元的输出作为第一层单元的输入,如Hopfield网络

根据连接的范围,分为:

1)全连接神经网络:每个单元和相邻层上的所有单元相连

2)局部连接网络:每个单元只和相邻层上的部分单元相连

神经网络的学习

根据学习方法分:

感知器:有监督的学习方法,训练样本的类别是已知的,并在学习的过程中指导模型的训练

认知器:无监督的学习方法,训练样本类别未知,各单元通过竞争学习。

根据学习时间分:

离线网络:学习过程和使用过程是独立的

在线网络:学习过程和使用过程是同时进行的

根据学习规则分:

相关学习网络:根据连接间的激活水平改变权系数

纠错学习网络:根据输出单元的外部反馈改变权系数

自组织学习网络:对输入进行自适应地学习

摘自《数学之美》对人工神经网络的通俗理解:

神经网络种类很多,常用的有如下四种:

1)Hopfield网络,典型的反馈网络,结构单层,有相同的单元组成

2)反向传播网络,前馈网络,结构多层,采用最小均方差的纠错学习规则,常用于语言识别和分类等问题

3)Kohonen网络:典型的自组织网络,由输入层和输出层构成,全连接

4)ART网络:自组织网络

深度神经网络:

Convolutional Neural Networks(CNN)卷积神经网络

Recurrent neural Network(RNN)循环神经网络

Deep Belief Networks(DBN)深度信念网络

深度学习是指多层神经网络上运用各种机器学习算法解决图像,文本等各种问题的算法集合。深度学习从大类上可以归入神经网络,不过在具体实现上有许多变化。

深度学习的核心是特征学习,旨在通过分层网络获取分层次的特征信息,从而解决以往需要人工设计特征的重要难题。

Machine Learning vs. Deep Learning 

神经网络(主要是感知器)经常用于 分类

神经网络的分类知识体现在网络连接上,被隐式地存储在连接的权值中。

神经网络的学习就是通过迭代算法,对权值逐步修改的优化过程,学习的目标就是通过改变权值使训练集的样本都能被正确分类。

神经网络特别适用于下列情况的分类问题:

1) 数据量比较小,缺少足够的样本建立模型

2) 数据的结构难以数茄用传统的统计方法来描述

3) 分类模型难以表示为传统的统计模型

缺点:

1) 需要很长的训练时间,因而对于有足够长训练时间的应用更合适。

2) 需要大量的参数,这些通常主要靠经验确定,如网络拓扑或“结构”。

3)  可解释性差 。该特点使得神经网络在数据挖掘的初期并不看好。

优点:

1) 分类的准确度高

2)并行分布处理能力强

3)分布存储及学习能力高

4)对噪音数据有很强的鲁棒性和容错能力

最流行的基于神经网络的分类算法是80年代提出的 后向传播算法 。后向传播算法在多路前馈神经网络上学习。 

定义网络拓扑

在开始训练之前,用户必须说明输入层的单元数、隐藏层数(如果多于一层)、每一隐藏层的单元数和输出层的单元数,以确定网络拓扑。

对训练样本中每个属性的值进行规格化将有助于加快学习过程。通常,对输入值规格化,使得它们落入0.0和1.0之间。

离散值属性可以重新编码,使得每个域值一个输入单元。例如,如果属性A的定义域为(a0,a1,a2),则可以分配三个输入单元表示A。即,我们可以用I0 ,I1 ,I2作为输入单元。每个单元初始化为0。如果A = a0,则I0置为1;如果A = a1,I1置1;如此下去。

一个输出单元可以用来表示两个类(值1代表一个类,而值0代表另一个)。如果多于两个类,则每个类使用一个输出单元。

隐藏层单元数设多少个“最好” ,没有明确的规则。

网络设计是一个实验过程,并可能影响准确性。权的初值也可能影响准确性。如果某个经过训练的网络的准确率太低,则通常需要采用不同的网络拓扑或使用不同的初始权值,重复进行训练。

后向传播算法学习过程:

迭代地处理一组训练样本,将每个样本的网络预测与实际的类标号比较。

每次迭代后,修改权值,使得网络预测和实际类之间的均方差最小。

这种修改“后向”进行。即,由输出层,经由每个隐藏层,到第一个隐藏层(因此称作后向传播)。尽管不能保证,一般地,权将最终收敛,学习过程停止。

算法终止条件:训练集中被正确分类的样本达到一定的比例,或者权系数趋近稳定。

后向传播算法分为如下几步:

1) 初始化权

网络的权通常被初始化为很小的随机数(例如,范围从-1.0到1.0,或从-0.5到0.5)。

每个单元都设有一个偏置(bias),偏置也被初始化为小随机数。

2) 向前传播输入

对于每一个样本X,重复下面两步:

向前传播输入,向后传播误差

计算各层每个单元的输入和输出。输入层:输出=输入=样本X的属性;即,对于单元j,Oj = Ij = Xj。隐藏层和输出层:输入=前一层的输出的线性组合,即,对于单元j, Ij =wij Oi + θj,输出=

3) 向后传播误差

计算各层每个单元的误差。

输出层单元j,误差:

Oj是单元j的实际输出,而Tj是j的真正输出。

隐藏层单元j,误差:

wjk是由j到下一层中单元k的连接的权,Errk是单元k的误差

更新 权 和 偏差 ,以反映传播的误差。

权由下式更新:

 其中,△wij是权wij的改变。l是学习率,通常取0和1之间的值。

 偏置由下式更新:

  其中,△θj是偏置θj的改变。

Example

人类视觉原理:

深度学习的许多研究成果,离不开对大脑认知原理的研究,尤其是视觉原理的研究。1981 年的诺贝尔医学奖,颁发给了 David Hubel(出生于加拿大的美国神经生物学家) 和Torsten Wiesel,以及Roger Sperry。前两位的主要贡献,是“发现了视觉系统的信息处理”, 可视皮层是分级的 。

人类的视觉原理如下:从原始信号摄入开始(瞳孔摄入像素Pixels),接着做初步处理(大脑皮层某些细胞发现边缘和方向),然后抽象(大脑判定,眼前的物体的形状,是圆形的),然后进一步抽象(大脑进一步判定该物体是只气球)。

对于不同的物体,人类视觉也是通过这样逐层分级,来进行认知的:

在最底层特征基本上是类似的,就是各种边缘,越往上,越能提取出此类物体的一些特征(轮子、眼睛、躯干等),到最上层,不同的高级特征最终组合成相应的图像,从而能够让人类准确的区分不同的物体。

可以很自然的想到:可以不可以模仿人类大脑的这个特点,构造多层的神经网络,较低层的识别初级的图像特征,若干底层特征组成更上一层特征,最终通过多个层级的组合,最终在顶层做出分类呢?答案是肯定的,这也是许多深度学习算法(包括CNN)的灵感来源。

卷积神经网络是一种多层神经网络,擅长处理图像特别是大图像的相关机器学习问题。卷积网络通过一系列方法,成功将数据量庞大的图像识别问题不断降维,最终使其能够被训练。

CNN最早由Yann LeCun提出并应用在手写字体识别上。LeCun提出的网络称为LeNet,其网络结构如下:

这是一个最典型的卷积网络,由 卷积层、池化层、全连接层 组成。其中卷积层与池化层配合,组成多个卷积组,逐层提取特征,最终通过若干个全连接层完成分类。

CNN通过卷积来模拟特征区分,并且通过卷积的权值共享及池化,来降低网络参数的数量级,最后通过传统神经网络完成分类等任务。

降低参数量级:如果使用传统神经网络方式,对一张图片进行分类,那么,把图片的每个像素都连接到隐藏层节点上,对于一张1000x1000像素的图片,如果有1M隐藏层单元,一共有10^12个参数,这显然是不能接受的。

但是在CNN里,可以大大减少参数个数,基于以下两个假设:

1)最底层特征都是局部性的,也就是说,用10x10这样大小的过滤器就能表示边缘等底层特征

2)图像上不同小片段,以及不同图像上的小片段的特征是类似的,也就是说,能用同样的一组分类器来描述各种各样不同的图像

基于以上两个假设,就能把第一层网络结构简化

用100个10x10的小过滤器,就能够描述整幅图片上的底层特征。

卷积运算的定义如下图所示:

如上图所示,一个5x5的图像,用一个3x3的 卷积核 :

   101

   010

   101

来对图像进行卷积 *** 作(可以理解为有一个滑动窗口,把卷积核与对应的图像像素做乘积然后求和),得到了3x3的卷积结果。

这个过程可以理解为使用一个过滤器(卷积核)来过滤图像的各个小区域,从而得到这些小区域的特征值。在实际训练过程中, 卷积核的值是在学习过程中学到的。

在具体应用中,往往有多个卷积核,可以认为, 每个卷积核代表了一种图像模式 ,如果某个图像块与此卷积核卷积出的值大,则认为此图像块十分接近于此卷积核。如果设计了6个卷积核,可以理解为这个图像上有6种底层纹理模式,也就是用6种基础模式就能描绘出一副图像。以下就是24种不同的卷积核的示例:

池化 的过程如下图所示:

可以看到,原始图片是20x20的,对其进行采样,采样窗口为10x10,最终将其采样成为一个2x2大小的特征图。

之所以这么做,是因为即使做完了卷积,图像仍然很大(因为卷积核比较小),所以为了降低数据维度,就进行采样。

即使减少了许多数据,特征的统计属性仍能够描述图像,而且由于降低了数据维度,有效地避免了过拟合。

在实际应用中,分为最大值采样(Max-Pooling)与平均值采样(Mean-Pooling)。

LeNet网络结构:

注意,上图中S2与C3的连接方式并不是全连接,而是部分连接。最后,通过全连接层C5、F6得到10个输出,对应10个数字的概率。

卷积神经网络的训练过程与传统神经网络类似,也是参照了反向传播算法

第一阶段,向前传播阶段:

a)从样本集中取一个样本(X,Yp),将X输入网络;

b)计算相应的实际输出Op

第二阶段,向后传播阶段

a)计算实际输出Op与相应的理想输出Yp的差;

b)按极小化误差的方法反向传播调整权矩阵。

在 CNN 出现之前,图像对于人工智能来说是一个难题,有2个原因:

图像需要处理的数据量太大,导致成本很高,效率很低

图像在数字化的过程中很难保留原有的特征,导致图像处理的准确率不高

下面就详细说明一下这2个问题:

图像是由像素构成的,每个像素又是由颜色构成的。

现在随随便便一张图片都是 1000×1000 像素以上的, 每个像素都有RGB 3个参数来表示颜色信息。

假如我们处理一张 1000×1000 像素的图片,我们就需要处理3百万个参数!

1000×1000×3=3,000,000

这么大量的数据处理起来是非常皮首消耗资源的,而且这只是一张不算太大的图片!

卷积神经网络 – CNN 解决的第一个问题就是「将复杂问题简化」,把大量参数降维成少量参数,再做处理。

更重要的是:我们在大部分场景下,降维并不会影响结果。比如1000像素的图片缩小成200像素,并不影响肉眼认出来图片中是一只猫还是一只狗,机器也是如此。

图片数字化的传统方式我们简化一下,就类似下图的过程:

假如有圆形是1,没有圆形是0,那么圆形的位置不同就会产生完全不同的数据表达。但是从视觉的角度来看, 图像的内容(本质)并没有发生变化,只是位置大磨发生了变化 。

所以当我们移动图像中的物体,用传统的方式的得出来的参数会差异很大!这是不符合图像处理的要求的。

而 CNN 解决了这个问题,他用类似视觉的方式保留了图像的特征,当图像做翻转,旋转或者变换位置时,它也能有效的识别出来是类似的图像。

那么卷积神经网络是如何实现的呢?在我们了解 CNN 原理之前,先来看看人类的视觉原理是什么?

深度学习的许多研究成果,离不开对大脑认知原理的研究,尤其是视觉原理的研究。

1981 年的诺贝尔医学奖,颁发给了 David Hubel(出生于加拿大的美国神经生物学家) 和TorstenWiesel,以及 Roger Sperry。前两位的主要贡献,是“ 发现了视觉系统的信息处理 ”,可视皮层是分级的。

人类的视觉原理如下:从原始信号摄入开始(瞳孔摄入像素 Pixels),接着做初步处理(大脑皮层某些细胞发现边缘和方向),然后抽象(大脑判定,眼前的物体的形状,是圆形的),然后进一步抽象(大脑进一步判定该物体是只气球)。下面是人脑进行人脸识别的一个示例:

对于不同的物体,人类视觉也是通过这样逐层分级,来进行认知的:

我们可以看到,在最底层特征基本上是类似的,就是各种边缘,越往上,越能提取出此类物体的一些特征(轮子、眼睛、躯干等),到最上层,不同的高级特征最终组合成相应的图像,从而能够让人类准确的区分不同的物体。

那么我们可以很自然的想到:可以不可以模仿人类大脑的这个特点,构造多层的神经网络,较低层的识别初级的图像特征,若干底层特征组成更上一层特征,最终通过多个层级的组合,最终在顶层做出分类呢?

答案是肯定的,这也是许多深度学习算法(包括CNN)的灵感来源。

典型的 CNN 由3个部分构成:

卷积层

池化层

全连接层

如果简单来描述的话:

卷积层负责提取图像中的局部特征;池化层用来大幅降低参数量级(降维);全连接层类似传统神经网络的部分,用来输出想要的结果。

下面的原理解释为了通俗易懂,忽略了很多技术细节,如果大家对详细的原理感兴趣,可以看这个视频《 卷积神经网络基础 》。

卷积层的运算过程如下图,用一个卷积核扫完整张图片:

这个过程我们可以理解为我们使用一个过滤器(卷积核)来过滤图像的各个小区域,从而得到这些小区域的特征值。

在具体应用中,往往有多个卷积核,可以认为,每个卷积核代表了一种图像模式,如果某个图像块与此卷积核卷积出的值滚握斗大,则认为此图像块十分接近于此卷积核。如果我们设计了6个卷积核,可以理解:我们认为这个图像上有6种底层纹理模式,也就是我们用6中基础模式就能描绘出一副图像。以下就是25种不同的卷积核的示例:

总结:卷积层的通过卷积核的过滤提取出图片中局部的特征,跟上面提到的人类视觉的特征提取类似。

池化层简单说就是下采样,他可以大大降低数据的维度。其过程如下:

上图中,我们可以看到,原始图片是20×20的,我们对其进行下采样,采样窗口为10×10,最终将其下采样成为一个2×2大小的特征图。

之所以这么做的原因,是因为即使做完了卷积,图像仍然很大(因为卷积核比较小),所以为了降低数据维度,就进行下采样。

总结:池化层相比卷积层可以更有效的降低数据维度,这么做不但可以大大减少运算量,还可以有效的避免过拟合。

这个部分就是最后一步了,经过卷积层和池化层处理过的数据输入到全连接层,得到最终想要的结果。

经过卷积层和池化层降维过的数据,全连接层才能”跑得动”,不然数据量太大,计算成本高,效率低下。

典型的 CNN 并非只是上面提到的3层结构,而是多层结构,例如 LeNet-5 的结构就如下图所示:

卷积层 – 池化层- 卷积层 – 池化层 – 卷积层 – 全连接层

在了解了 CNN 的基本原理后,我们重点说一下 CNN 的实际应用有哪些。

卷积神经网络 – CNN 很擅长处理图像。而视频是图像的叠加,所以同样擅长处理视频内容。下面给大家列一些比较成熟的应用�:

图像分类、检索

图像分类是比较基础的应用,他可以节省大量的人工成本,将图像进行有效的分类。对于一些特定领域的图片,分类的准确率可以达到 95%+,已经算是一个可用性很高的应用了。

典型场景:图像搜索…

目标定位检测

可以在图像中定位目标,并确定目标的位置及大小。

典型场景:自动驾驶、安防、医疗…

目标分割

简单理解就是一个像素级的分类。

他可以对前景和背景进行像素级的区分、再高级一点还可以识别出目标并且对目标进行分类。

典型场景:美图秀秀、视频后期加工、图像生成…

人脸识别

人脸识别已经是一个非常普及的应用了,在很多领域都有广泛的应用。

典型场景:安防、金融、生活…

骨骼识别

骨骼识别是可以识别身体的关键骨骼,以及追踪骨骼的动作。

典型场景:安防、电影、图像视频生成、游戏…

今天我们介绍了 CNN 的价值、基本原理和应用场景,简单总结如下:

CNN 的价值:

能够将大数据量的图片有效的降维成小数据量(并不影响结果)

能够保留图片的特征,类似人类的视觉原理

CNN 的基本原理:

卷积层 – 主要作用是保留图片的特征

池化层 – 主要作用是把数据降维,可以有效的避免过拟合

全连接层 – 根据不同任务输出我们想要的结果

CNN 的实际应用:

图片分类、检索

目标定位检测

目标分割

人脸识别

骨骼识别

本文首发在 easyAI - 人工智能知识库

《 一文看懂卷积神经网络-CNN(基本原理+独特价值+实际应用) 》

LeNet诞生于 1994 年,是最早的卷积神经网络之一,并且推动了深度学习领域的发展。

LeNet-5是Yann LeCun等人在多次研究后提出的最终卷积神经网络结构,主要用于手写数字识别

LeNet5的网络结构如下所示:

LeNet-5包含七层,不包括输入,每一层都包含可训练参数(权重),当时使用的输入数据是32*32像素的图像。下面逐层介绍LeNet-5的结构,并且,卷积层将用Cx表示,子采样层则被标记为Sx,全连接层被标记为Fx,其袜袭中x是层索引。

该层使用了6个卷积核,每个卷积核的大小为5×5,这样就得到了6个feature map(特征图)。

每个卷积核(5×5)与原始的输入图像(32×32)进行卷积,这样得到的feature map(特征图)大小为(32-5+1)×(32-5+1)= 28×28

卷积核与输入图像按卷积核大小逐个区域进行匹配计算,匹配后原始输入图像的尺寸将变小,因为边缘部分卷积核无法越出界,只能匹配一次,匹配计算后的尺寸变为Cr×Cc=(Ir-Kr+1)×(Ic-Kc+1),其中Cr、Cc,Ir、Ic,Kr、Kc分别表示卷积后结果图像、输入图像、卷积核的行列大小。

由于参数(权值)共享的原因,对于同个卷积核每个神经元均使用相同的参数,因此,参数个数为(5×5+1)×6= 156,其中5×5为卷积核参数,1为偏置参数

卷积后的图像大小为28×28,因此每个特征图有28×28个神经元,每个卷积核参数为(5×5+1)×6,因此,该层的连接数为(5×5+1)×6×28×28=122304

这一层主要是做池化或者特征映射(特征降维),池化单元为2×2,因此,6个特征图的大小经池化后即变为14×14。池化单元之间没有重叠,在池化区域内进行聚合统计后得到新的特征值,因此经2×2池化后,每两行两列重新算出一个特征值出来,相当于图像大小减半,因此卷积后的28×28图像经2×2池化后就变为14×14。

这一层的计算过程是:2×2 单元里的值相加,然后再乘以训练参数w,再加上一个偏置参数b(每一个特征图共享相同的w和b),然后取sigmoid值(S函数:0-1区间),作为对应的该单元的值。卷积 *** 作与池化的示意图如下:

S2层由于每个特征图都共享相同的w和b这两个参数,因此需要2×6=12个参数

下采样之后的图像大小为14×14,因此S2层的每个特征图有14×14个神经元,每个池化单元连接数为2×2+1(1为偏置量),因此,该层的连接数为(2×2+1)×14×14×6 = 5880

C3层有16个卷积核,卷积模板大小为5×5。

与C1层的分析类似,C3层的特征图大小为(14-5+1)×(14-5+1)= 10×10

需要注意的是,C3与S2并不是全连接而是部分连接,有些是C3连接到S2三层、有些四层、甚至达到6层,通过这种方式提取更多特征,连接的规则如下表所示:

例如第一列表示C3层的第0个特征图(feature map)只跟S2层的第0、1和2这三个feature maps相连接,计算过程为:用3个卷积模板分别与S2层的3个feature maps进行卷积,然后将卷积的结果相加求和,再加上一个偏置,再取sigmoid得出卷积后对应的feature map了。其它列也是类似(有些是3个卷积模板,有些是4个,有些是6个)。因此,C3层的参数数目为(5×5×3+1)×6 +(5×5×4+1)×9 +5×5×6+1 = 1516

卷积后的特征图大小为10×10,参数数量为1516,因此连接数为1516×10×10= 151600

与S2的分析类似,池化单元大小为2×2,因此,该层与C3一样共有16个特征图,每个特征图的大小为5×5。

与S2的计算类似,所需要参数个数为16×2 = 32

连接数为(2×2+1)×5×5×16 = 2000

该层有120个卷积核,每个卷积核的悉好坦大小仍为5×5,因此有120个特征图。由于S4层的大小睁桐为5×5,而该层的卷积核大小也是5×5,因此特征图大小为(5-5+1)×(5-5+1)= 1×1。这样该层就刚好变成了全连接,这只是巧合,如果原始输入的图像比较大,则该层就不是全连接了。

与前面的分析类似,本层的参数数目为120×(5×5×16+1) = 48120

由于该层的特征图大小刚好为1×1,因此连接数为48120×1×1=48120

F6层有84个单元,之所以选这个数字的原因是来自于输出层的设计,对应于一个7×12的比特图,如下图所示,-1表示白色,1表示黑色,这样每个符号的比特图的黑白色就对应于一个编码。

该层有84个特征图,特征图大小与C5一样都是1×1,与C5层全连接。

由于是全连接,参数数量为(120+1)×84=10164。跟经典神经网络一样,F6层计算输入向量和权重向量之间的点积,再加上一个偏置,然后将其传递给sigmoid函数得出结果。

由于是全连接,连接数与参数数量一样,也是10164。

Output层也是全连接层,共有10个节点,分别代表数字0到9。如果第i个节点的值为0,则表示网络识别的结果是数字i。

该层采用径向基函数(RBF)的网络连接方式,假设x是上一层的输入,y是RBF的输出,则RBF输出的计算方式是:

上式中的Wij的值由i的比特图编码确定,i从0到9,j取值从0到7×12-1。RBF输出的值越接近于0,表示当前网络输入的识别结果与字符i越接近。

由于是全连接,参数个数为84×10=840

由于是全连接,连接数与参数个数一样,也是840

from skimage import io,transform

import os

import glob

import numpy as np

import tensorflow as tf

#将所有的图片重新设置尺寸为32*32

w = 32

h = 32

c = 1

#mnist数据集中训练数据和测试数据保存地址

train_path = "E:/data/datasets/mnist/train/"

test_path = "E:/data/datasets/mnist/test/"

#读取图片及其标签函数

'''os.listdir()返回指定的文件夹包含的文件或文件夹的名字,存放于一个列表中os.path.isdir()判断某一路径是否为目录

enumerate()将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,数据下标和相应数据'''

def read_image(path):

    label_dir = [path+x for x in os.listdir(path) if os.path.isdir(path+x)]

    images = []

    labels = []

    for index,folder in enumerate(label_dir):

        for img in glob.glob(folder+'/*.png'):

            print("reading the image:%s"%img)

            image = io.imread(img)

            image = transform.resize(image,(w,h,c))

            images.append(image)

            labels.append(index)

    return np.asarray(images,dtype=np.float32),np.asarray(labels,dtype=np.int32)

#读取训练数据及测试数据           

train_data,train_label = read_image(train_path)

test_data,test_label = read_image(test_path)

#打乱训练数据及测试数据  np.arange()返回一个有终点和起点的固定步长的排列,

train_image_num = len(train_data)

train_image_index = np.arange(train_image_num) ##起始点0,结束点train_image_num,步长1,返回类型array,一维

np.random.shuffle(train_image_index)

train_data = train_data[train_image_index]

train_label = train_label[train_image_index]

test_image_num = len(test_data)

test_image_index = np.arange(test_image_num)

np.random.shuffle(test_image_index)

test_data = test_data[test_image_index]

test_label = test_label[test_image_index]

#搭建CNN 此函数可以理解为形参,用于定义过程,在执行的时候再赋具体的值,形参名X,y_

x = tf.placeholder(tf.float32,[None,w,h,c],name='x')

y_ = tf.placeholder(tf.int32,[None],name='y_')

def inference(input_tensor,train,regularizer):

    #第一层:卷积层,过滤器的尺寸为5×5,深度为6,不使用全0补充,步长为1。

    #尺寸变化:32×32×1->28×28×6

    '''参数的初始化:tf.truncated_normal_initializer()或者简写为tf.TruncatedNormal()、tf.RandomNormal() 去掉_initializer,大写首字母即可

生成截断正态分布的随机数,这个初始化方法好像在tf中用得比较多mean=0.0, stddev=1.0 正态分布

http://www.mamicode.com/info-detail-1835147.html'''

    with tf.variable_scope('layer1-conv1'):

        conv1_weights = tf.get_variable('weight',[5,5,c,6],initializer=tf.truncated_normal_initializer(stddev=0.1))

        conv1_biases = tf.get_variable('bias',[6],initializer=tf.constant_initializer(0.0))

        conv1 = tf.nn.conv2d(input_tensor,conv1_weights,strides=[1,1,1,1],padding='VALID')

        relu1 = tf.nn.relu(tf.nn.bias_add(conv1,conv1_biases))

    #第二层:池化层,过滤器的尺寸为2×2,使用全0补充,步长为2。

    #尺寸变化:28×28×6->14×14×6

    with tf.name_scope('layer2-pool1'):

        pool1 = tf.nn.max_pool(relu1,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')

    #第三层:卷积层,过滤器的尺寸为5×5,深度为16,不使用全0补充,步长为1。

    #尺寸变化:14×14×6->10×10×16

    with tf.variable_scope('layer3-conv2'):

        conv2_weights = tf.get_variable('weight',[5,5,6,16],initializer=tf.truncated_normal_initializer(stddev=0.1))

        conv2_biases = tf.get_variable('bias',[16],initializer=tf.constant_initializer(0.0))

        conv2 = tf.nn.conv2d(pool1,conv2_weights,strides=[1,1,1,1],padding='VALID')

        relu2 = tf.nn.relu(tf.nn.bias_add(conv2,conv2_biases))

    #第四层:池化层,过滤器的尺寸为2×2,使用全0补充,步长为2。

    #尺寸变化:10×10×6->5×5×16

    with tf.variable_scope('layer4-pool2'):

        pool2 = tf.nn.max_pool(relu2,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')

    #将第四层池化层的输出转化为第五层全连接层的输入格式。第四层的输出为5×5×16的矩阵,然而第五层全连接层需要的输入格式

    #为向量,所以我们需要把代表每张图片的尺寸为5×5×16的矩阵拉直成一个长度为5×5×16的向量。

    #举例说,每次训练64张图片,那么第四层池化层的输出的size为(64,5,5,16),拉直为向量,nodes=5×5×16=400,尺寸size变为(64,400)

    pool_shape = pool2.get_shape().as_list()

    nodes = pool_shape[1]*pool_shape[2]*pool_shape[3]

    reshaped = tf.reshape(pool2,[-1,nodes])

    #第五层:全连接层,nodes=5×5×16=400,400->120的全连接

    #尺寸变化:比如一组训练样本为64,那么尺寸变化为64×400->64×120

    #训练时,引入dropout,dropout在训练时会随机将部分节点的输出改为0,dropout可以避免过拟合问题。

    #这和模型越简单越不容易过拟合思想一致,和正则化限制权重的大小,使得模型不能任意拟合训练数据中的随机噪声,以此达到避免过拟合思想一致。

    #本文最后训练时没有采用dropout,dropout项传入参数设置成了False,因为训练和测试写在了一起没有分离,不过大家可以尝试。

    '''tf.matmul()这个函数是专门矩阵或者tensor乘法,而不是矩阵元素对应元素相乘

    tf.multiply()两个矩阵中对应元素各自相乘

    tf.nn.dropout(x, keep_prob):TensorFlow里面为了防止或减轻过拟合而使用的函数,它一般用在全连接层,

    x:指输入keep_prob: 设置神经元被选中的概率,使输入tensor中某些元素变为0,其它没变0的元素变为原来的1/keep_prob大小,可以想象下,比如某些元素弃用

    在初始化时keep_prob是一个占位符,keep_prob = tf.placeholder(tf.float32).

    tensorflow在run时设置keep_prob具体的值,例如keep_prob: 0.5,train的时候才是dropout起作用的时候

    keep_prob: A scalar Tensor with the same type as x. The probability that each element is kept.'''

    with tf.variable_scope('layer5-fc1'):

        fc1_weights = tf.get_variable('weight',[nodes,120],initializer=tf.truncated_normal_initializer(stddev=0.1))

        if regularizer != None:

            tf.add_to_collection('losses',regularizer(fc1_weights))

        fc1_biases = tf.get_variable('bias',[120],initializer=tf.constant_initializer(0.1))

        fc1 = tf.nn.relu(tf.matmul(reshaped,fc1_weights) + fc1_biases)

        if train:

            fc1 = tf.nn.dropout(fc1,0.5)

    #第六层:全连接层,120->84的全连接

    #尺寸变化:比如一组训练样本为64,那么尺寸变化为64×120->64×84

    '''tf.add_to_collection:把变量放入一个集合,把很多变量变成一个列表

    tf.get_collection:从一个结合中取出全部变量,是一个列表

    tf.add_n:把一个列表的东西都依次加起来'''

    with tf.variable_scope('layer6-fc2'):

        fc2_weights = tf.get_variable('weight',[120,84],initializer=tf.truncated_normal_initializer(stddev=0.1))

        if regularizer != None:

            tf.add_to_collection('losses',regularizer(fc2_weights))

        fc2_biases = tf.get_variable('bias',[84],initializer=tf.truncated_normal_initializer(stddev=0.1))

        fc2 = tf.nn.relu(tf.matmul(fc1,fc2_weights) + fc2_biases)

        if train:

            fc2 = tf.nn.dropout(fc2,0.5)

    #第七层:全连接层(近似表示),84->10的全连接

    #尺寸变化:比如一组训练样本为64,那么尺寸变化为64×84->64×10。最后,64×10的矩阵经过softmax之后就得出了64张图片分类于每种数字的概率,

    #即得到最后的分类结果。

    with tf.variable_scope('layer7-fc3'):

        fc3_weights = tf.get_variable('weight',[84,10],initializer=tf.truncated_normal_initializer(stddev=0.1))

        if regularizer != None:

            tf.add_to_collection('losses',regularizer(fc3_weights))

        fc3_biases = tf.get_variable('bias',[10],initializer=tf.truncated_normal_initializer(stddev=0.1))

        logit = tf.matmul(fc2,fc3_weights) + fc3_biases

    return logit

#正则化,交叉熵,平均交叉熵,损失函数,最小化损失函数,预测和实际equal比较,tf.equal函数会得到True或False,

#accuracy首先将tf.equal比较得到的布尔值转为float型,即True转为1.,False转为0,最后求平均值,即一组样本的正确率。

#比如:一组5个样本,tf.equal比较为[True False True False False],转化为float型为[1. 0 1. 0 0],准确率为2./5=40%。

'''规则化可以帮助防止过度配合,提高模型的适用性。(让模型无法完美匹配所有的训练项。)(使用规则来使用尽量少的变量去拟合数据)

规则化就是说给需要训练的目标函数加上一些规则(限制),让他们不要自我膨胀。

TensorFlow会将L2的正则化损失值除以2使得求导得到的结果更加简洁

如tf.contrib.layers.apply_regularization/l1_regularizer/l2_regularizer/sum_regularizer

https://blog.csdn.net/liushui94/article/details/73481112

sparse_softmax_cross_entropy_with_logits()是将softmax和cross_entropy放在一起计算

https://blog.csdn.net/ZJRN1027/article/details/80199248'''

regularizer = tf.contrib.layers.l2_regularizer(0.001)

y = inference(x,False,regularizer)

cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y,labels=y_)

cross_entropy_mean = tf.reduce_mean(cross_entropy)

loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))

train_op = tf.train.AdamOptimizer(0.001).minimize(loss)

correct_prediction = tf.equal(tf.cast(tf.argmax(y,1),tf.int32),y_)

accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

#每次获取batch_size个样本进行训练或测试

def get_batch(data,label,batch_size):

    for start_index in range(0,len(data)-batch_size+1,batch_size):

        slice_index = slice(start_index,start_index+batch_size)

        yield data[slice_index],label[slice_index]

#创建Session会话

with tf.Session() as sess:

    #初始化所有变量(权值,偏置等)

    sess.run(tf.global_variables_initializer())

    #将所有样本训练10次,每次训练中以64个为一组训练完所有样本。

    #train_num可以设置大一些。

    train_num = 10

    batch_size = 64

    for i in range(train_num):

        train_loss,train_acc,batch_num = 0, 0, 0

        for train_data_batch,train_label_batch in get_batch(train_data,train_label,batch_size):

            _,err,acc = sess.run([train_op,loss,accuracy],feed_dict={x:train_data_batch,y_:train_label_batch})

            train_loss+=errtrain_acc+=accbatch_num+=1

        print("train loss:",train_loss/batch_num)

        print("train acc:",train_acc/batch_num)

        test_loss,test_acc,batch_num = 0, 0, 0

        for test_data_batch,test_label_batch in get_batch(test_data,test_label,batch_size):

            err,acc = sess.run([loss,accuracy],feed_dict={x:test_data_batch,y_:test_label_batch})

            test_loss+=errtest_acc+=accbatch_num+=1

        print("test loss:",test_loss/batch_num)

        print("test acc:",test_acc/batch_num)


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/12406863.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-23
下一篇2023-05-23

发表评论

登录后才能评论

评论列表(0条)

    保存