19章卷积神经网络
本章介绍现阶段神经网络中非常火的模型——卷积神经网络,它在计算机视觉中有着非常不错的效果。不仅如此,卷积神经网络在非图像数据中也有着不错的表现,各项任务都有用武之地,可谓在机器学习领域遍地开花。那么什么是卷积呢?网络的核心就在于此,本章将带大家一步步揭开卷积神经网络的奥秘。
19.1卷积操作原理
卷积神经网络也是神经网络的一种,本质上来说都是对数据进行特征提取,只不过在图像数据中效果更好,整体的网络模型架构都是一样的,参数迭代更新也是类似,所以难度就在于卷积上,只需把它弄懂即可。
19.1.1卷积神经网络应用
卷积神经网络既然这么火爆,肯定能完成一些实际任务,先来看一下它都能做什么。
图19-1是经典的图像分类任务,但是神经网络也能完成这个任务,那么,为什么说卷积神经网络在计算机视觉领域更胜一筹呢?想想之前遇到的问题,神经网络的矩阵计算方式所需参数过于庞大,一方面使得迭代速度很慢,另一方面过拟合问题比较严重,而卷积神经网络便可以更好地处理这个问题。
图19-1 图像分类任务
图19-2是检测任务的示例,不仅需要找到物体的位置,还要区分物体属于哪个类别,也就是回归和分类任务结合在一起。现阶段物体检测任务随处可见,当下比较火的无人驾驶也需要各种检测任务。
图19-2 检测任务
大家早就对人脸识别不陌生了,以前去机场,安检员都是拿着身份证来回比对,查看是不是冒牌的,现在直接对准摄像头,就会看到你的脸被框起来进行识别。
图像检索与推荐如图19-3所示,各大购物APP都有这样一个功能——拍照搜索,有时候我们看到一件心仪的商品,但是却不知道它的名称,直接上传一张照片,同款就都出来了。
图19-3 检索与推荐
卷积网络的应用实在太广泛了,例如,医学上进行细胞分析、办公上进行拍照取字、摄像头进行各种识别任务,这些早已融入大家的生活当中(见图19-4)。
图19-4 卷积网络广泛的应用
简单介绍卷积神经网络的应用后,再来探索一下其工作原理,卷积神经网络作为深度学习中的杰出代表肯定会让大家不虚此行。
19.1.2卷积操作流程
接下来就要深入网络细节中,看看卷积究竟做了什么,首先观察一下卷积网络和传统神经网络的不同之处,如图19-5所示。
图19-5 卷积网络与传统神经网络的不同
传统的神经网络是一个平面,而卷积网络是一个三维立体的。不难发现,卷积神经网络中多了一个概念——深度。例如图像输入数据h×w×c,其中颜色通道c就是输入的深度。
在使用TensorFlow做神经网络的时候,首先将图像数据拉成像素点组成的特征,这样做相当于输入一行特征数据而非一个原始图像数据,而卷积中要操作的对象是一个整体,所以需要定义深度。
如果大家没有听过卷积这个词,把它想象成一种特征提取的方法就好,最终的目的还是要得到计算机更容易读懂的特征。下面解释一下卷积过程。
假设已有输入数据(32×32×3),如图19-6所示,此时想提取图像中的特征,以前是对每个像素点都进行变换处理,看起来是独立对待每一个像素点特征。但是图像中的像素点是有一定连续关系的。如果能把图像按照区域进行划分,再对各个区域进行特征提取应当更合理。
图19-6 卷积操作
图19-6中假设把原始数据平均分成多个小块,接下来就要对每一小块进行特征提取,也可以说是从每一小部分中找出关键特征代表。
如何进行体征提取呢?这里需要借助一个帮手,暂时叫它filter,它需要做的就是从其中每一小块区域选出一个特征值。假设filter的大小是5×5×3,表示它要对输入的每个5×5的小区域都进行特征提取,并且要在3个颜色通道(RGB)上都进行特征提取再组合起来。
通过助手filter进行特征提取后,就得到图19-7所示的结果,看起来像两块板子,它们就是特征图,表示特征提取的结果,为什么是两个呢?这里在使用filter进行特征提取的时候,不仅可以用一种特征提取方法,也就是filter可以有多个,例如在不同的纹理、线条的层面上(只是举例,其实就是不同的权重参数)。
图19-7 特征提取
例如,在同样的小块区域中,可以通过不同的方法来选择不同层次的特征,最终所有区域特征再组合成一个整体。先不用管28×28×2的特征图大小是怎么来的,最后会向大家介绍计算公式,现在先来理解它。
在一次特征提取的过程中,如果使用6种不同的filter,那么肯定会得到6张特征图,再把它们堆叠在一起,就得到了h×w×6的特征输出结果。这其实就是一次卷积操作,如图19-8所示。
图19-8 特征图
刚才从流程上解释了卷积操作的目的,那么是不是只能对输入数据执行卷积操作呢?并不是这样,我们得到的特征图是28×28×6,感觉它与输入数据的格式差不多。此时第3个维度上,颜色通道数变成特征图个数,所以对特征图依旧可以进行卷积操作,相当于在提取出的特征上再进一步提取。
图19-9所示为卷积神经网络对输入图像数据进行特征提取,使用3个卷积提取特征,最终得到的结果就是特征图。
图19-9 卷积特征提取
在基本神经网络中,用多个隐藏层进行特征提取,卷积神经网络也是如此,只不过用的是卷积层。
19.1.3卷积计算方法
卷积的概念和作用已经很清晰,那么如何执行卷积计算操作呢?下面要深入其计算细节中。
假设输入数据的大小为7×7×3(图像长度为7;宽度为7;颜色通道为RGB),如图19-10所示。此例中选择的filter大小是3×3×3(参数需要自己设计,卷积核长度为3;宽度为3;分别对应输入的3颜色通道),看起来输入数据的颜色通道数和filter一样,都是3个,这点是卷积操作中必须成立的,因为需要对应计算,如果不一致,那就完全不能计算。
图19-10 卷积计算
大家可以将输入数据中的数值当作图像中的像素点,但是filter(卷积核)中的数值是什么意思呢?它与神经网络中的权重参数的概念一样,表示对特征进行变换的方法。初始值可以是随机初始化的,然后通过反向传播不断进行更新,最终要求解的就是filter中每个元素的值。
filter(卷积核)就是卷积神经网络中权重参数,最终特征提取的结果主要由它来决定,所以目标就是优化得到最合适的特征提取方式,相当于不断更新其数值。
特征值计算方法比较简单,每一个对应区域与filter(卷积核)计算内积即可,最终得到的是一个结果值,表示该区域的特征值,如图19-11所示。但是,还需要考虑一点,图像中的区域并不是一个平面,而是一个带有颜色通道的三维数据,所以还需把所有颜色通道的结果分别计算,最终求和即可。
在图19-10的第一块区域中,各个颜色通道的计算结果累加在一起为0+2+0=2,这样就可以计算得出卷积核中权重参数作用在输入数据上的结果,但是,不要忘记还有一个偏置项需要加起来:2+1=3,这样就得到在原始数据中第一个3×3×3小区域的特征代表,把它写到右侧的特征图第一个位置上。
图19-11 特征值计算方法
算完第一个区域,下一个计算区域应当在哪里呢?区域的选择与滑动窗口差不多,需要指定滑动的步长,以依次选择特征提取的区域位置。
滑动两个单元格(步长也是卷积中的参数,需要自己设置),区域选择到中间位置,接下来还是相同的内积计算方法,得到的特征值为-5,同样写到特征图中相应位置,如图19-12所示。
图19-12 分别计算特征值
继续滑动窗口,直到计算完最后一个位置的特征值,这样就得到一个特征图(3×3×1),如图19-13所示。通常一次卷积操作都希望能够得到更多的特征,所以一个特征图肯定不够用,这里实际上选择两个filter,也就是有两组权重参数进行特征提取,由于Filter W1和Filter W2中的权重参数值各不相同,所以得到的特征图肯定也不同,相当于用多种方式得到不同的特征表示,再把它们堆叠在一起,就完成全部卷积操作。
图19-13 特征图组成
19.1.4卷积涉及参数
卷积操作比传统神经网络的计算复杂,在设计网络结构过程中,需要给定更多的控制参数,并且全部需要大家完成设计,所以必须掌握每一个参数的意义。
(1)卷积核(filter)。卷积操作中最关键的就是卷积核,它决定最终特征提取的效果,需要设计其大小与初始化方法。大小即长和宽,对应输入的每一块区域。保持数据大小不变,如果选择较大的卷积核,则会导致最终得到的特征比较少,相当于在很粗糙的一大部分区域中找特征代表,而没有深入细节。所以,现阶段在设计卷积核时,基本都是使用较小的长和宽,目的是得到更细致(数量更多)的特征。
一般来说,一种特征提取方法能够得到一个特征图,通常每次卷积操作都会使用多种提取方法,也就是多来几个卷积核,只要它们的权重值不一样,得到的结果也不同,一般256、512都是常见的特征图个数。对参数初始化来说,它与传统神经网络差不多,最常见的还是使用随机高斯初始化。
图19-14表示使用两个卷积核进行特征提取,最后得到的就是2张特征图,关于具体的数值(例如4×4),需要介绍完所有参数之后,再给出计算公式。
图19-14 卷积核
(2) 步长(stride)。在选择特征提取区域时,需要指定每次滑动单元格的大小,也就是步长。如果步长比较小,意味着要慢慢地尽可能多地选择特征提取区域,这样得到的特征图信息也会比较丰富,如图19-15所示。
图19-15 步长为1的卷积
如果步长较大,选中的区域就会比较少,得到的特征图也会比较小,如图19-16所示。
对比可以发现,步长会影响最终特征图的规模。在图像任务中,如果属于非特殊处理任务(文本数据中的步长可表示为一次考虑多少上下文信息),最好选择小一点的步长,虽然计算多了一些,但是可利用的特征丰富了,更有利于计算机完成识别任务。
图19-16 步长为2的卷积
现阶段大家看到的网络模型步长基本上都为1,这可以当作是科学家们公认的结果,也就是可以参考的经验值。
(3)边界填充(pad)。首先考虑一个问题:在卷积不断滑动的过程中,每一个像素点的利用情况是同样的吗?边界上的像素点可能只被滑动一次,也就是只能参与一次计算,相当于对特征图的贡献比较小。而那些非边界上的点,可能被多次滑动,相当于在计算不同位置特征的时候被利用多次,对整体结果的贡献就比较大。
这似乎有些不公平,因为拿到输入数据或者特征图时,并没有规定边界上的信息不重要,但是卷积操作却没有平等对待它们,如何进行改进呢?只需要让实际的边界点不再处于边界位置即可,此时通过边界填充添加一圈数据点就可以解决上述问题。此时原本边界上的点就成为非边界点,显得更公平。
边界填充如图19-17所示,仔细观察一下输入数据,有一点比较特别,就是边界上所有的数值都为0,表示这个像素点没有实际的信息,这就是卷积中的边界填充(pad)。
图19-17 边界填充
此时实际图像的输入为5×5,由于加了一圈0,所以变成7×7,为什么填充的都是0呢?如果要对图像大小进行变换,可用的方法其实有很多,这里选择0值进行填充,其目的是为了结果的可靠性,因为毕竟是填充出来的数据,如果参与到计算中,对结果有比较大的影响,那岂不是更不合理?所以一般都用0值进行填充。
(4)特征图规格计算。当执行完卷积操作后会得到特征图,那么如何计算特征图的大小呢?只要给定上述参数,就能直接进行计算:
其中,W1、H1分别表示输入的宽度、长度;W2、H2分别表示输出特征图的宽度、长度;F表示卷积核长和宽的大小;S表示滑动窗口的步长;P表示边界填充(加几圈0)。
如果输入数据是32×32×3的图像,用10个5×5×3的filter进行卷积操作,指定步长为1,边界填充为2,最终输入的规模为 (32-5+2×2)/1+1=32,所以输出规模为32×32×10,经过卷积操作后,也可以保持特征图长度、宽度不变。
在神经网络中,曾举例计算全连接方式所需的权重参数,一般为千万级别,实在过于庞大。卷积神经网络中,不仅特征提取方式与传统神经网络不同,参数的级别也差几个数量级。
卷积操作中,使用参数共享原则,在每一次迭代时,对所有区域使用相同的卷积核计算特征。可以把卷积这种特征提取方式看成是与位置无关的,这其中隐含的原理是:图像中一部分统计特性与其他部分是一样的。这意味着在这一部分学习的特征也能用在另一部分上,所以,对于这个图像上的所有位置,都能使用相同的卷积核进行特征计算。
大家肯定会想,如果用不同的卷积核提取不同区域的特征应当更合理,但是这样一来,计算的开销就实在太大,还得综合考虑。
如图19-18所示,左图中未使用共享原则,使得每一个区域的卷积核都不同,其结果会使得参数过于庞大,右图中虽然区域很多,但每一个卷积核都是固定的,所需权重参数就少多了。
例如,数据依旧是32×32×3的图像,继续用10个5×5×3的filter进行卷积操作,所需的权重参数有多少个呢?
5×5×3=75,表示每一个卷积核只需要75个参数,此时有10个不同的卷积核,就需要10×75=750个卷积核参数,不要忘记还有b参数,每个卷积核都有一个对应的偏置参数,最终只需要750+10=760个权重参数,就可以完成一个卷积操作。
图19-18 卷积参数共享
观察可以发现,卷积涉及的参数与输入图像大小并无直接关系,这可解决了大问题,可以快速高效地完成图像处理任务。
19.1.5池化层
池化层也是卷积神经网络中非常重要的组成部分,先来看看它对特征做了什么。
假设把输入(224×224×64)当作某次卷积后的特征图结果,池化层基本都是放到卷积层之后使用的,很少直接对原始图像进行池化操作,所以一般输入的都是特征图。经过池化操作之后,给人直观的感觉就是特征图缩水了,高度和宽度都只有原来的一半,体积变成原来的1/4,但是特征图个数保持不变,如图19-19所示。
图19-19 池化操作
并不是所有池化操作都会使得特征图长度、宽度变为原来的一半,需根据指定的步长与区域大小进行计算,但是通常“缩水”成一半的池化最常使用。
池化层的作用就是要对特征图进行压缩,因为卷积后得到太多特征图,能全部利用肯定最好,但是计算量和涉及的权重参数随之增多,不得不采取池化方法进行特征压缩。常用池化方法有最大池化和平均池化。
图19-20是最大池化的示例。最大池化的原理很简单,首先在输入特征图中选择各个区域,然后“计算”其特征值,这里并没有像卷积层那样有实际的权重参数进行计算,而是直接选择最大的数值即可,例如在图19-20的左上角[1,1,5,6]区域中,经过最大池化操作得到的特征值就是6,其余区域也是同理。
图19-20 最大池化
平均池化的基本原理也是一样,只不过在计算过程中,要计算区域的平均值,而不是直接选择最大值,经过平均池化操作,[1,0,3,4]区域得到特征值就是2。
在池化操作中,依然需要给定计算参数,通常需要指定滑动窗口的步长(stride)和选择区域的大小(例如2×2,只有大小,没有参数)。
最大池化的感觉是做法相对独特一些,只是把最大的特征值拿出来,其他完全不管,而平均池化看起来更温柔一些,会综合考虑所有的特征值。那么,是不是平均池化效果更好呢?并不是这样,现阶段使用的基本都是最大池化,感觉它与自然界中的优胜劣汰法则差不多,只会把最合适的保留下来,在神经网络中也是如此,需要最突出的特征。
池化层的操作非常简单,因为并不涉及实际的参数计算,通常都是接在卷积层后面。卷积操作会得到较多的特征图,让特征更丰富,池化操作会压缩特征图大小,利用最有价值的特征。
19.2经典网络架构
完成了卷积层与池化层之后,就要来看一看其整体效果,可能此时大家已经考虑了一个问题,就是卷积网络中可以调节的参数还有很多,网络结构肯定千变万化,那么,做实验时,是不是需要把所有可能都考虑进去呢?通常并不需要做这些基础实验,用前人实验好的经典网络结构是最省时省力的。所谓经典就是在各项竞赛和实际任务中,总结出来比较实用而且通用性很强的网络结构。
19.2.1卷积神经网络整体架构
在了解经典之前,还要知道基本的卷积神经网络模型,这里给大家先来分析一下。
图19-21是一个完整的卷积神经网络,首先对输入数据进行特征提取,然后完成分类任务。通常卷积操作后,都会对其结果加入非线性变换,例如使用ReLU函数。池化操作与卷积操作是搭配来的,可以发现卷积神经网络中经常伴随着一些规律出现,例如2次卷积后进行1次池化操作。最终还需将网络得到的特征图结果使用全连接层进行整合,并完成分类任务,最后一步的前提是,要把特征图转换成特征向量,因为卷积网络得到的特征图是一个三维的、立体的,而全连接层是使用权重参数矩阵计算的,也就是全连接层的输入必须是特征向量,需要转换一下,在后续代码实战中,也会看到转换操作。
图19-21 卷积神经网络整体架构
卷积神经网络的核心就是得到的特征图,如图19-22所示,特征图的大小和个数始终在发生变化,通常卷积操作要得到更多的特征图来满足任务需求,而池化操作要进行压缩来降低特征图规模(池化时特征图个数不变)。最后再使用全连接层总结好全部特征,在这之前还需对特征图进行转换操作,可以当作是一个把长方体的特征拉长成一维特征的过程。
图19-22 卷积神经网络特征图变化
19.2.2AlexNet网络
AlexNet可以说是深度学习的开篇之作,如图19-23所示。在2012年的ImageNet图像分类竞赛中,用卷积神经网络击败传统机器学习算法获得冠军,也使得越来越多的人加入到深度学习的研究中。
图19-23 AlexNet网络
AlexNet网络结构从现在的角度来看还有很多问题,整体网络结构是8层,其中卷积层5个,全连接层3个。当计算层数的时候,只考虑带有参数的层,也就是卷积层和全连接层,其中池化层由于没有涉及参数计算,就不把它算作层数里面。从结构中可以看到,3个全连接层全部放到最后,相当于把所有卷积得到的特征组合起来再执行后续的分类或回归任务。
网络结构中,卷积核的选择都偏大,例如第一层11×11的卷积核,并且步长为4,感觉就像是大刀阔斧地提取特征,这样提取的特征肯定不够细致,还有很多信息没有被利用,所以相信大家也能直观感受到ALEXNET的缺点。总之,就是网络层数太少,提取不够细腻,当时这么做的出发点估计还是硬件设备计算性能所限。
当大家在做实际任务时,如果不考虑时间效率,还是需要使网络结构更庞大一些,AlexNet只做了解即可,实际中效果还有待提高。
19.2.3VGG网络
由于VGG网络层数比较多,可以直接通过表格的形式看它的组成。它是2014年的代表作,其使用价值至今还在延续,所以很值得学习。VGG有好几种版本,下面看其最经典的结构(也就是图19-24框住的部分)。首先其网络层数有16层,是ALEXNET的2倍,作者曾经做过对比实验:相同的数据集分别用ALEXNET和VGG来建模分类任务,保持学习率等其他参数不变,VGG的效果要比ALEXNET高出十几个百分点,但是相对训练时间也要长很多。
图19-24 VGG网络
网络层数越多,训练时间也会越长,通常这些经典网络的输入大小都是224×224×3,如果AlexNet需要8小时完成训练,VGG大概需要2天。
VGG网络有一个特性,所有卷积层的卷积核大小都是3×3,可以用较小的卷积核来提取特征,并且加入更多的卷积层。这样做有什么好处呢?还需要解释一个知识点—感受野,它表示特征图能代表原始图像的大小,也就是特征图能感受到原始输入多大的区域。
选择3×3的卷积核来执行卷积操作,经过两次卷积后,选择最后特征图中的一个点,如图19-25所示。现在要求它的感受野,也就是它能看到原始输出多大的区域,倒着来推,它能看到第一个特征图3×3的区域(因为卷积核都是3×3的),而第一个特征图3×3的区域能看到原始输入5×5的区域,此时就说当前的感受野是5×5。通常都是希望感受野越大越好,这样每一个特征图上的点利用原始数据的信息就更多。
图19-25 感受野
同理,如果堆叠3个3×3的卷积层,并且保持滑动窗口步长为1,其感受野就是7×7,这与一个使用7×7卷积核的结果相同,那么,为什么非要堆叠3个小卷积呢?假设输入大小都是h×w×c,并且都使用C个卷积核(得到C个特征图),可以计算一下其各自所需参数。
使用1个7×7卷积核所需参数:
C×(7×7×C)=49C2
使用3个3×3卷积核所需参数:
3×C×(3×3×C)=27C2
很明显,堆叠小的卷积核所需的参数更少,并且卷积过程越多,特征提取也会越细致,加入的非线性变换也随之增多,而且不会增大权重参数个数,这就是VGG网络的基本出发点,用小的卷积核来完成体特征提取操作。
观察其网络结构还可以发现,基本上经过maxpool(最大池化)之后的卷积操作都要使特征图翻倍,这是由于池化操作已经对特征图进行压缩,得到的信息量相对有所下降,所以需要通过卷积操作来弥补,最直接的方法就是让特征图个数翻倍。
后续的操作还是用3个全连接层把之前卷积得到的特征再组合起来,可以说VGG是现代深度网络模型的代表,用更深的网络结构来完成任务,虽然训练速度会慢一些,但是整体效果会有很大提升。如果大家拿到一个实际任务,还不知如何下手,可以先用VGG试试,它相当于一套通用解决方案,不仅能用在图像分类任务上,也可以用于回归任务。
19.2.4ResNet网络
通过之前的对比,大家发现深度网络的效果更好,那么为什么不让网络再深一点呢?100层、1000层可不可以呢?理论上是可行的,但是先来看看之前遇到的问题。
如图19-26所示,如果沿用VGG的思想将网络继续堆叠得到的效果并不好,深层的网络(如56层)无论是在训练集还是在测试集上的效果都不理想,那么所谓的深度学习是不是到此为止呢?在解决问题的过程中,又一神作诞生了——深度残差网络。
图19-26 深层网络遇到的问题
其基本思想就是,因网络层数继续增多,导致结果下降,其原因肯定是网络中有些层学习得不好。但是,在继续堆叠过程中,可能有些层学习得还不错,还可以被利用。
图19-27为ResNet网络叠加方法。如果这样设计网络结构,相当于输入x(可以当作特征图)在进行卷积操作的时候分两条路走:一条路中,x什么都不做,直接拿过来得到当前输出结果;另一条路中,x需要通过两次卷积操作,以得到其特征图结果。再把这两次的结果加到一起,这就相当于让网络自己判断哪一种方式更好,如果通过卷积操作后,效果反而下降,那就直接用原始的输入x;如果效果提升,就把卷积后的结果加进来。
图19-27 ResNet网络叠加方法
这就解决了之前提出的问题,深度网络模型可能会导致整体效果还不如之前浅层的。按照残差网络的设计,继续堆叠网络层数并不会使得效果下降,最差也是跟之前一样。
如图19-28所示,通过对比可以发现,残差网络在整体设计中沿用了图19-27所示的方法,使得网络继续堆叠下去。这里只是简单介绍了一下其基本原理,如果大家想详细了解其细节和实验效果,最好的方式是阅读其原始论文,非常有学习价值。
图19-28 ResNet网络整体架构
如图19-29所示,图19-29(a)就是用类似VGG的方法堆叠更深层网络模型,层数越多,效果反而越差。图19-29(b)是ResNet,它完美地解决了深度网络所遇到的问题。
图19-29 Resnet效果对比
图19-30列出了在ImageNet图像分类比赛中各种网络模型的效果,最早的时候是用浅层网络进行实验,后续逐步改进,每一年都有杰出的代表产生(见图19-31)。
图19-30 经典网络效果对比
不仅在图像分类任务中,在检测任务中也是如此,究其根本还是特征提取得更好,所以现阶段残差网络已经成为一套通用的基本解决方案。
图19-31 日新月异的改革
19.3TensorFlow实战卷积神经网络
讲解完卷积与池化的原理之后,还要用TensorFlow实际把任务做出来。依旧是Mnist数据集,只不过这回用卷积神经网络来进行分类任务,不同之处是输入数据的处理:
1 import tensorflow as tf
2 import random
3 import numpy as np
4 import matplotlib.pyplot as plt
5 import datetime
6 %matplotlib inline
7
8 from tensorflow.examples.tutorials.mnist import input_data
9 mnist = input_data.read_data_sets("data/", one_hot=True)
10
11 tf.reset_default_graph()
12 sess = tf.InteractiveSession()
13 x = tf.placeholder("float", shape = [None, 28,28,1]) #shape in CNNs is always None x height x width x color channels
14 y_ = tf.placeholder("float", shape = [None, 10]) #shape is always None x number of classes
在卷积神经网络中,所有数据格式就都是四维的,先向大家解释一下输入中每一个维度表示的含义[batchsize,h,w,c]:batchsize表示一次迭代的样本数量,h表示图像的长度,w表示图像的宽度,c表示颜色通道(或者特征图个数)。需要注意h和w的顺序,不同深度学习框架先后顺序可能并不一样。在placeholder()中对输入数据也需要进行明确的定义,标签与之前一样。
接下来就是权重参数初始化,由于是卷积操作,所以它与之前全连接的定义方式完全不同:
1 W_conv1 = tf.Variable(tf.truncated_normal([5, 5, 1, 32], stddev=0.1))#shape is filter x filter x input channels x output channels
2 b_conv1 = tf.Variable(tf.constant(.1, shape = [32])) #shape of the bias just has to match output channels of the filter
从上述代码可以看出,还是随机进行初始化操作,w表示卷积核,b表示偏置。其中需要指定的就是卷积核的大小,同样也是四维的。[5,5,1,32]表示使用卷积核的大小是5×5,前面连接的输入颜色通道是1(如果是特征图,就是特征图个数),使用卷积核的个数是32,就是通过这次卷积操作后,得到32个特征图。卷积层中需要设置的参数稍微有点多,需要注意卷积核的深度(这个例子中就是这个1),一定要与前面的输入深度一致(Mnist是黑白图,颜色通道为1)。
对于偏置参数来说,方法还是相同的,只需看最终结果。卷积中设置了32个卷积核,那么,肯定会得到32个特征图,偏置参数相当于要对每一个特征图上的数值进行微调,所以其个数为32。
1 h_conv1 = tf.nn.conv2d(input=x, filter=W_conv1, strides=[1, 1, 1, 1], padding='SAME') + b_conv1
2 h_conv1 = tf.nn.relu(h_conv1)
3 h_pool1 = tf.nn.max_pool(h_conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
这就是卷积的计算流程,tensorflow中已经实现卷积操作,直接使用conv2d函数即可,需要传入当前的输入数据、卷积核参数、步长以及padding项。
步长同样也是四维的,第一个维度中,1表示在batchsize上滑动,通常情况下都是1,表示一个一个样本轮着来。第二和第三个1表示在图像上的长度和宽度上的滑动都是每次一个单位,可以看做一个小整体[1,1],长度和宽度的滑动一般都是一致的,如果是[2,2],表示移动两个单位。最后一个1表示在颜色通道或者特征图上移动,基本也是1。通常情况下,步长参数在图像任务中只需按照网络的设计改动中间数值,如果应用到其他领域,就需要具体分析。
Padding中可以设置是否加入填充,这里指定成SAME,表示需要加入padding项。在池化层中,还需要指定ksize,也就是一次选择的区域大小,与卷积核参数类似,只不过这里没有参数计算。[1,2,2,1]与步长的参数含义一致,分别表示在batchsize,h,w,c上的区域选择,通常batchsize和通道(特征图)上都为1,只需要改变中间的[2,2]来控制池化层结果,这里选择ksize和stride都为2,相当于长和宽各压缩到原来的一半。
第一层确定后,后续的卷积和池化操作也相同,继续进行叠加即可,其实,在网络结构中,通常都是按照相同的方式进行叠加,所以可以先定义好组合函数,这样就方便多了:
1 def conv2d(x, W):
2 return tf.nn.conv2d(input=x, filter=W, strides=[1, 1, 1, 1], padding='SAME')
3
4 def max_pool_2x2(x):
5 return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
继续做第二个卷积层:
1 #Second Conv and Pool Layers
2 W_conv2 = tf.Variable(tf.truncated_normal([5, 5, 32, 64], stddev=0.1))
3 b_conv2 = tf.Variable(tf.constant(.1, shape = [64]))
4 h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
5 h_pool2 = max_pool_2x2(h_conv2)
对于Mnist数据集来说,用两个卷积层就差不多,下面就是用全连接层来组合已经提取出的特征:
1 #First Fully Connected Layer
2 W_fc1 = tf.Variable(tf.truncated_normal([7 * 7 * 64, 1024], stddev=0.1))
3 b_fc1 = tf.Variable(tf.constant(.1, shape = [1024]))
4 h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
5 h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
这里需要定义好全连接层的权重参数:[7×7×64,1024],全连接参数与卷积参数有些不同,此时需要一个二维的矩阵参数。第二个维度1024表示要把卷积提取特征图转换成1024维的特征。第一个维度需要自己计算,也就是当前输入特征图的大小,Mnist数据集本身输入28×28×1,给定上述参数后,经过卷积后的大小保持不变,池化操作后,长度和宽度都变为原来的一半,代码中选择两个池化操作,所以最终的特征图大小为28×1/2×1/2=7。特征图个数是由最后一次卷积操作决定的,也就是64。这样就把7×7×64这个参数计算出来。
在全连接操作前,需要reshape一下特征图,也就是将一个特征图压扁或拉长成为一个特征。最后进行矩阵乘法运算,就完成全连接层要做的工作。
1 #Dropout Layer
2 keep_prob = tf.placeholder("float")
3 h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
讲解神经网络时,曾特别强调过拟合问题,此时也可以加进dropout项,基本都是在全连接层加入该操作。传入的参数是一个比例,表示希望保存神经元的百分比,例如50%。
1 #Second Fully Connected Layer
2 W_fc2 = tf.Variable(tf.truncated_normal([1024, 10], stddev=0.1))
3 b_fc2 = tf.Variable(tf.constant(.1, shape = [10]))
WARNING:tensorflow:From <ipython-input-9-5ff7d49325fd>:15: calling dropout (from tensorflow.python.ops.nn_ops) with keep_prob is deprecated and
will be removed in a future version.
Instructions for updating:
Please use `rate` instead of `keep_prob`. Rate should be set to `rate = 1 - keep_prob`.
现在的1024维特征可不是最终想要的结果,当前任务是要做一个十分类的手写字体识别,所以第二个全连接层的目的就是把特征转换成最终的结果。大家可能会想,只设置最终输出10个结果,能和它所属各个类别的概率对应上吗?没错,神经网络就是这么神奇,它要做的就是让结果和标签尽可能一致,按照标签设置的结果,返回的就是各个类别的概率值,其中的奥秘就在于如何定义损失函数。
1 #Final Layer
2 y = tf.matmul(h_fc1_drop, W_fc2) + b_fc2
3
4 crossEntropyLoss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y_, logits = y))
5 trainStep = tf.train.AdamOptimizer().minimize(crossEntropyLoss)
6 correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
7 accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
WARNING:tensorflow:From <ipython-input-10-ba3a22c42ec1>:1: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated
and will be removed in a future version.
Instructions for updating:
Future major versions of TensorFlow will allow gradients to flow
into the labels input on backprop by default.
See `tf.nn.softmax_cross_entropy_with_logits_v2`.
同样是设置损失函数以及优化器,这里使用AdamOptimizer()优化器,相当于在学习的过程中让学习率逐渐减少,符合实际要求,而且将计算准确率当作衡量标准。
1 sess.run(tf.global_variables_initializer())
2
3 batchSize = 50
4 for i in range(1000):
5 batch = mnist.train.next_batch(batchSize)
6 trainingInputs = batch[0].reshape([batchSize,28,28,1])
7 trainingLabels = batch[1]
8 if i%100 == 0:
9 trainAccuracy = accuracy.eval(session=sess, feed_dict={x:trainingInputs, y_: trainingLabels, keep_prob: 1.0})
10 print ("step %d, training accuracy %g"%(i, trainAccuracy))
11 trainStep.run(session=sess, feed_dict={x: trainingInputs, y_: trainingLabels, keep_prob: 0.5})
step 0, training accuracy 0.12
step 100, training accuracy 0.96
step 200, training accuracy 0.98
step 300, training accuracy 0.92
step 400, training accuracy 1
step 500, training accuracy 0.94
step 600, training accuracy 0.96
step 700, training accuracy 0.98
step 800, training accuracy 1
step 900, training accuracy 1
之前使用神经网络的时候,需要1000次迭代,效果才能达到90%以上,加入卷积操作之后,准备率的提升是飞快的,差不多100次,就能满足需求。
本章小结:
图19-32就是卷积神经网络的基本结构,先将卷积层和池化层搭配起来进行特征提取,最后再用全连接操作把特征整合到一起,其核心就是卷积层操作以及其中涉及的参数。在图像处理中,卷积网络模型使用更少的参数,识别效果却更好,大大促进了计算机视觉领域的发展,深度学习作为当下最热门的领域,进步也是飞快的,每年都会有杰出的网络代表产生,学习的任务永远都会持续下去。
图19-32 卷积网络特征提取
第19章完。
《Python数据分析与机器学习实战-唐宇迪》读书笔记第18章--TensorFlow实战
《Python数据分析与机器学习实战-唐宇迪》读书笔记第20章--神经网络项目实战——影评情感分析
该书资源下载,请至异步社区:https://www.epubit.com
来源:oschina
链接:https://my.oschina.net/u/4386848/blog/3235325