【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

目录

前言

这是入门CV的第三篇,尽量做到通俗易懂,该篇是目前写的内容最多的文章,体会了什么叫创作不易,干货满满哈,慢慢看,主要是消化.希望文章某部分对你有帮助,本文也是自己一点一点理解后写的,内容相对较多,难免某些地方可能出现小错误,如有错误,欢迎评论交流。
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

一、GoogLeNet背景介绍

GoogLeNet是谷歌(Google)研究出来的深度网络结构,这里注意是GoogLeNet,中间是大写L,这是这个团队发现刚好两个字母双关,并以此致敬LeNet网络,故这样取名。谈GooLeNet就不得不说说VGGNet,2014年,GoogLeNet和VGG是当年ImageNet挑战赛(ILSVRC14)的前两名,GoogLeNet第一、VGG获得了第二。这两个模型结构的共同特点是模型层次更深,一个22层(Inception v1),一个19层(VGG-19)。VGG继承了LeNet以及AlexNet的一些框架结构,即在AlexNet基础上进行了较大的改进,使性能在当时有显著提高。而GoogLeNet则做了更加大胆的尝试,即提出了新的网络结构Inception,这也是GoogLeNet的核心,它的出现可以说影响巨大。虽然GoogLeNet深度有22层,比VGG还深一点,但是GoogLeNet参数为500万个,比AlexNet与VGG都少很多,仅仅是VGG参数的1/3,是AlexNet参数的1/12,这样一比较,GoogLeNet的性能是更加优越的,因为参数的规模是和计算量有直接关系的,因此GoogLeNet斩获了2014年ImageNet比赛冠军。

我们针对上述描述的GoogLeNet,产生一个问题,GoogLeNet是如何提升性能的呢?
答:我们可以看看AlexNet与VGGNet提升性能的方法,两个网络提升性能的方法是增加网络的层数和通道数量(通道宽度)。但参数越多,随之而来的问题是网络容易产生过拟合,计算量显著增大,并且网络会随着后面深度增加,会产生梯度弥散或梯度爆炸现象。所以不能一味的增加深度,需要减少参数,需要更高效利用计算资源,这样可以让网络提取到更多特征信息。为了达到上面这个目的,GoogLeNet提出了Inception网络结构,Inception到目前为止有5个版本Inception v1-v4以及Inception-ResNet,Inception出现之前,大多性能提升都是基于提升网络深度与宽度,但会导致参数增加,计算量增大等负面作用。针对上述,有没有办法能即通过深度增加提升的同时,参数又得到减少。我们知道网络模型结构参数90%左右都是全连接层产生的,那么就要从全连接考虑,怎么减少全连接产生的参数,所以该团队想到了将全连接变成稀疏连接,稀疏连接体现的是CNN中核心思想之一局部感知,只与自己有关的神经元连接,而不用全连接,这样可以大大减少参数。但是全连接变稀疏连接后,虽然参数减少,但是计算量并没有得到提升,计算时间并没有减少。于是该团队就开始研究搭建参数少,性能高的Inception网络,下面我们来介绍这个网络,从Inception v1版本到v4版本。

二、Inception网络结构

2.1 Inception v1

论文地址:Going deeper with convolutions
该Inception结构将CNN中常用的卷积(1x1,3x3,5x5)、 池化(3x3) 操作堆叠在一起,如下图
注:通常写GoogLeNet默认就是指v1版本
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
我们先看上图左边(a)图的原始Inception模块,我们通常称为Inception v0,该网络卷积层能够提取输入的每一个细节信息,同时5×5的卷积核能够覆盖大部分输入,进行一个池化操作,减少空间大小,这样就可以起到压缩图像矩阵、减少参数的作用。但是原始版本1x1,3x3,5x5三个卷积都在同一层上做,导致计算量太大,造成了特征图的维度很大,维度即特征图feature map的通道数。为了避免这种情况,该团队在3x3前、5x5前、max pooling后分别加上了1x1的卷积核,此处1x1卷积起到降维效果,如右边(b)图为改进后的Inception模块,由多个Inception模块组成的网络,就是Inception v1网络结构

1×1卷积核的作用(重点):
(1) 降维与升维

1.1 降维

降维可以减少卷积核参数的规模
如:输入的feature map是28×28×192
1×1卷积通道为64
3×3卷积通道为128
5×5卷积通道为32
左图(a)卷积核参数:192 × (1×1×64) +192 × (3×3×128) + 192 × (5×5×32) = 387072
右图(b)对3×3和5×5卷积层前分别加入了通道数为96和16的1×1卷积层,这样卷积核参数就变成了:
192 × (1×1×64) +(192×1×1×96+ 96 × 3×3×128)+(192×1×1×16+16×5×5×32)= 157184

maxpooling层后加入1×1卷积层后也可以降低输出的特征feature map数量(通道数)
左图feature map数量:64 + 128 + 32 + 192(pooling后feature map不变) = 416 (如果每个模块都这样,网络的输出会越来越大)
右图feature map数量:64 + 128 + 32 + 32(pooling后面加了通道为32的1×1卷积) = 256

1.2 升维
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
假设输入的是(3,3,3)的feature map,后接一个1×1×3的卷积核,卷积后会得到一个(3,3,3)的feature map,一共有64个1×1×3的卷积核,得到64个(3,3,3)的feature map,然后我们对同一位置的参数进行线性相加得到64个3×3的网格,最后组成3×3×64的feature map,这样通过1×1的卷积核,将特征图从3×3×3变为3×3×64,,达到升维效果,并且用1×1卷积核升维可以用最少的参数拓宽网络维度。

重点tips:滤波器(卷积核)的数量决定了卷积后 featuremap 的通道数;
  卷积过程中,输入层有多少个通道,滤波器就要有多少个通道;
  所以下一层的卷积核的通道会和上一层输出的通道一致

升维与降维总结:二者用1×1卷积核进行升维与降维都可以相比传统做法减少很多参数

(2)跨通道信息交互(通道的变换)
例子:使用1×1卷积核,实现降维和升维的操作其实就是通道(channel)间信息的线性组合变化,若3×3,64channels的卷积核后面添加一个1×1,28channels的卷积核,就变成了3×3,28channels的卷积核,原来的64个channels就可以理解为跨通道线性组合变成了28channels,这就是通道间的信息交互,本来平行关系下的通道通过线性组合进行了通道变换,也就是信息交互。

(3)增加非线性特性
1*1卷积核,可以在保持feature map尺度不变的的前提下,利用后接的非线性激活函数,大幅增加非线性特性,把网络做的很深,如升维中的图,64个1×1×3卷积核,每一个后面都可以加激活函数,增加非线性特特征。

Inception v1网络结构
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
按上图过程分析:
先宏观了解下层数,有这么概念就行,GoogLeNet v1网络共有22层,结构就是9个Inception模块组成的,每个Inception又有两层,Inception就有18层,加上开头的3个卷积层和输出前的FC层,总共22层(输入与输出层不算)

1、首先输入图像矩阵(224,224,3),经过第一个convolution,第一个convolution结构如下,有一层【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

有64个7×7大小kernel,且padding=(7-1)/2=3,stride=2,由公式
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
(224+2×3-7)/2+1=223/2+1=112,所以卷积后大小为112×112,channels=64,因为有64个7×7卷积核
112×112×64
tips:我们可以稍微记两个常用卷积核的padding,1×1和3×3,分别是(1-1)/2=0与(3-1)/2=1,即padding=0【same】和padding=1【vaild】。

2、经过最大池化操作,压缩减少参数,3x3的卷积核,滑动步长stride=2,padding=(3-1)/2=1,等于1表示paddng=same,使输入维度和输出维度一致,保持边界信息,公式计算(112 - 3+1)/2+1=56,即56x56x64

3、第二个convolution结构如下,有两层【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
192个1x1×64的卷积核,stride为1,padding为0,这里先用1×1卷积核进行升维,输出56×56×192
192个3×3×192的卷积核,stride为1,padding为1,输出(56+2×1-3)/1+1=56,即56×56×192

4、最大池化,3×3卷积核,stride=2,padding=(3-1)/2=1,输出(56+2-3)/2+1=28,长宽减半,输出28×28×192
tips:最大池化,stride设为2一般都是长宽减半进行压缩操作,卷积时长宽不变,改变通道,所以卷积的stride为1

5、Inception 3a,两层
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
上图就是最开始原始图改进后的Inception结构,如下为参数
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

Inception 3a操作
观察图,有四个箭头流,我们逐个分析,注意的是此时对每个箭头都是上一层的输出28×28×192作为该层输入。
我们从左往右分析,reduce表示降维:
第一个:64个1x1×192的卷积核(padding为0,步长stride为1,SAME),1×1卷积核起到降维的作用,输出28x28x64
第二个:96个1x1×192的卷积核,该1×1卷积核作为3x3卷积核之前的降维,先变成28x28x96,然后进行ReLU计算,再进行128个3x3的卷积(padding为1,步长为1,SAME),输出28x28x128
第三个:16个1x1×192的卷积核,作为5x5卷积核之前的降维,将输入的28×28×192变成28x28x16,进行ReLU计算后,再进行32个5x5的卷积(padding为2,步长为1,SAME),输出28x28x32
第四个:maxPool层,使用3x3的卷积核(padding为1,步长为1,SAME),输出28x28x192,然后进行32个1x1的卷积,进行降维,输出28x28x32
最后四个进行concat堆叠拼接,即将通道数相加,64+128+32+32=256,所以堆叠后输出为28x28x256
tips:1×1×192卷积核中192可写可不写,因为不写也会默认和上一层的输入的维度一致
6、Inception 3b
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
这个与上面3a操作基本一致,参数不同而已
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

第一个:128个1x1的卷积核,然后接ReLU,输出28x28x128
第二个:128个1x1的卷积核,作为3x3卷积核之前的降维,变成28x28x128,进行ReLU,再进行192个3x3的卷积(padding为1),输出28x28x192
第三个:32个1x1的卷积核,作为5x5卷积核之前的降维,变成28x28x32,进行ReLU计算后,再进行96个5x5的卷积(padding为2),输出28x28x96
第四个:maxPool层,使用3x3的核(padding为1),输出28x28x256,然后进行64个1x1的卷积,输出28x28x64
  将四个结果进行堆叠拼接,即128+192+96+64=480,最终输出输出为28x28x480
7、Inception 4a【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

第一个:192个1x1的卷积核,然后RuLU,输出14x14x192
第二个:96个1x1的卷积核,作为3x3卷积核之前的降维,变成14x14x96,进行ReLU,再进行208个3x3的卷积(padding为1),输出14x14x208
第三个:16个1x1的卷积核,作为5x5卷积核之前的降维,变成14x14x32,进行ReLU计算后,再进行48个5x5的卷积(padding为2),输出14x14x48
第四个:maxPool层,使用3x3的核(padding为1,步长1,SAME),14x14x48,然后进行64个1x1的卷积,输出14x14x64。
将四个结果进行堆叠连接,通道数相加,即192+208+48+64=512,最终输出输出为14x14x512

8、Inception 4b
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
这里与前面不一样的地方是最后一条线,有个辅助分类器softmax0,GoogLeNet共有三个辅助分类器softmax0,1,2

为什么在网络中间就用softmax进行输出?
答:为了避免因为网络较深导致的梯度消失现象,网络额外增加了2个辅助的softmax用于向前传导梯度。因为除了最后一层的输出结果,中间节点的分类效果也可能是不错的,所以GoogLeNet将中间的某一层作为输出,并以一个较小的权重加入到最终分类结果中,这样相当于做了模型融合,同时给网络增加了反向传播的梯度信号,也提供了额外的正则化,对于整个网络的训练很有裨益。而在实际测试的时候,这两个额外的softmax会被去掉,会使用最后最深的那个。其实这种训练方式可以看作将几个不同深度的子网络合并到一块进行训练,由于网络的卷积核权值共享,梯度由权值计算,因此计算的梯度可以累加,这样最终的梯度便不会很小。
总结就是:辅助分类器可以避免梯度消失,网络中间层提前拿出类进行回归分类,主要目的是为了更有效地回传梯度,因为网络越深,梯度反向传播能够通过所有层的能力就会降低,并提供正则化,一句话就是缓解梯度消失,加速收敛

中间两个辅助分类器softmax0与softmax1的结构相同,包括以下组件:
一个尺寸为5x5、步长为3的平均池化层。
一个尺寸为1x1、输出通道数为128 的卷积层。
两个具有1024 个单元的全连接层。
一个Dropout 层。
一个使用softmax 作为输出层。

Inception 4b其他四个与前面一样
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
第一步、160个1x1的卷积核,然后ReLU,输出14x14x160
第二步、112个1x1的卷积核,作为3x3卷积核之前的降维,变成14x14x112,进行ReLU,再进行224个3x3的卷积(padding为1),输出14x14x224
第三步、24个1x1的卷积核,作为5x5卷积核之前的降维,变成14x14x24,进行ReLU计算后,再进行64个5x5的卷积(padding为2),输出14x14x64
第四步、maxPool层,使用3x3的核(padding为1),然后进行64个1x1的卷积,输出14x14x64。
将四个结果拼接concat,即160+64+224+64=512,最终输出输出为14x14x512

下面Ineption 4c-5b的变化就直接看参数表把,计算方法都是一样的。注意中间池化,stride=2,所以长宽减半,avgpool用了7×7的卷积核,stride=1,padding=0,(7-7)/1+1=1,所以大小变为1×1,不过也很好理解,直接用同样大小卷积核就可以输出为1×1×1024
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
最后softmax1输出层前只有一个全连接层,还有一个用全局平均池化层GAP(即将图片尺寸变为1*1)来取代FC来融合学到的深度特征,因为整个网络的大部分参数都是在全连接层,所以取代一个全连接就可以减少一部分参数。

整体架构图
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
GoogLeNet v1错误率6.67%

keras实现Inception v1,代码如下:

def Inception(x,nb_filter):
    branch1x1 = Conv2D(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None)

    branch3x3 = Conv2D(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None)
    branch3x3 = Conv2D(branch3x3,nb_filter,(3,3), padding='same',strides=(1,1),name=None)

    branch5x5 = Conv2D(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None)
    branch5x5 = Conv2D(branch5x5,nb_filter,(5,5), padding='same',strides=(1,1),name=None)

    branchpool = MaxPooling2D(pool_size=(3,3),strides=(1,1),padding='same')(x)
    branchpool = Conv2D(branchpool,nb_filter,(1,1),padding='same',strides=(1,1),name=None)

    x = concatenate([branch1x1,branch3x3,branch5x5,branchpool],axis=3)

    return x

def GoogLeNet():
    input = Input(shape=(224,224,3))
    # padding = 'same',填充为(步长-1)/2,还可以用ZeroPadding2D((3,3))
    x = Conv2D(inpt,64,(7,7),strides=(2,2),padding='same')
    x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)
    x = Conv2D(x,192,(3,3),strides=(1,1),padding='same')
    x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)
    x = Inception(x,64)#256
    x = Inception(x,120)#480
    x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)
    x = Inception(x,128)#512
    x = Inception(x,128)
    x = Inception(x,128)
    x = Inception(x,132)#528
    x = Inception(x,208)#832
    x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)
    x = Inception(x,208)
    x = Inception(x,256)#1024
    x = AveragePooling2D(pool_size=(7,7),strides=(7,7),padding='same')(x)
    x = Dropout(0.4)(x)
    x = Dense(1000,activation='relu')(x)
    x = Dense(1000,activation='softmax')(x)
    model = Model(inpt,x,name='inception')
    return model

2.2 Inception v2

文章内容:提出Batch Normalization (批标准化)。
论文地址:Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift
可以说从v1发布后,后面文章都是对v1进行修改,进一步提升性能。
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

上图是文章的摘要,众所周知,一篇论文首先最重要的就是读摘要,因为摘要是对全文的概括。从该文摘要中,我们可以看出本文提出了Batch Normalization批标准化,并说明了它可以用作正则化,可减少14倍训练步骤,错误率到了4.9%,而第一个版本是6.67%,这个算法很6,我们以后经常用的BN就是这里v2提出的。

最后结果:
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

注:Inception v2还没有学习VGGNet,借鉴VGGNet进行改进的文章是Inception v3。

一、初识Batch Normalization

下面我们来分析分析该文提出的Batch Normalization(简称BN):
如摘要中,作者训练过程中遇到一个问题,叫做"internal covariate shift",简写ICS,内部协变量偏移,这个问题是由于在训练过程中,隐藏层网络参数变化所引起的。我们可以分析一下,就是在神经网络模型的训练过程中,每一轮训练后,参数都会产生变化,如权重偏置的参数更新,由于每轮参数变化,故在训练中每次隐藏层的输出是不一样的,因为前一层的输出作为后一层的输入,后一层输出数据分布也就会发生变化,我们把网络中间层在训练过程中,数据分布的改变叫做"内部协变量偏移",我们发现ICS就是分布不稳定,会发生变化意思。BN就是为了解决这一问题而产生的,也就是让参数变化变稳定。

BN的来源
之前的研究表明如果在图像处理中对输入图像进行白化操作的话,那么神经网络会较快收敛。而图像是深度神经网络的输入层,做白化能加快收敛,那么其实对于深度网络来说,其中某个隐藏层的输出是下一层的输入,那么作者想能不能把每一层都做个类似白化的操作,因为后一层的输入都是前一层的输出,每一层都可以视作有一个输入。这就是BN思想的来源,不过白化计算量还是比较大,BN算法参考了白化思想,近似白化预处理。
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
后面我们也将用这个公式,对某一个层网络的输入数据做一个归一化处理。E(xk)指的是每一批训练数据神经元x(k)的平均值,分母就是每一批数据神经元x(k)激活度的一个标准差

BN做法
将数据的分布都标准化到均值为0,方差为1的标准正态分布上去(这样做标准化可以解决ICS问题,让数据分布稳定),在网络的每一层输入的时候,又插入了一个BN层(就像激活函数层、卷积层、全连接层、池化层一样,BN也属于网络的一层),使得分布的稳定性都提高了,故而整体减小了网络的内部协变量偏移ICS,即把不稳定变为稳定,因为稳定的话,网络模型就能更好的学习数据中的规律,因为神经网络学习过程本质就是为了学习数据分布,一旦每批训练数据的分布各不相同(batch 梯度下降),那么网络就要在每次迭代都去学习适应不同的分布,那么这样将会大大降低网络的训练速度。
链接:归一化和标准化的区别

BN的思想介绍:
深层神经网络在做非线性变换前的激活输入值x(就是那个y=wx+b,x是激活输入值)随着网络深度加深或者在训练过程中,其分布逐渐发生偏移或者变动(ICS),之所以训练收敛慢,一般是整体分布逐渐往非线性函数的取值区间的上下限两端靠近(如sigmoid两端慢慢变平缓),所以这导致反向传播时低层神经网络的梯度消失,这是训练深层神经网络收敛越来越慢的本质原因,而BN就是通过一定的规范化手段,把每层神经网络任意神经元这个输入值的分布强行拉回到均值为0,方差为1的标准正态分布,其实就是把越来越偏的分布强制拉回比较标准的分布,这样使得激活输入值x落在非线性函数对输入比较敏感的区域,这个敏感区域就是梯度比较大的区域,如比较靠近中心原点区域,这样输入的小变化就会导致损失函数有较大的变化,这样可以让梯度变大,避免梯度消失问题产生,而且梯度变大意味着学习收敛速度快,能大大加快训练速度。并且我们不用去理会过拟合中Dropout以及学习率参数的选择问题,采用BN算法后,你可以移除这两项了参数,或者可以选择更小的L2正则约束参数了,因为BN具有提高网络泛化能力的特性。

二、BN算法实现
按照上述描述,这样看来BN不就是在中间层的每一层加一个BN标准化,将数据分布拉回均值为0,方差为1的标准正太分布,使得梯度还可以吗?还是挺简单的呀。但事实没有这么简单,因为如果是仅仅使用上面的标准化公式,对网络某一层A的输出数据做标准化,然后送入网络下一层B,这样是会影响到本层网络A所学习到的特征的。比如如果一个数据分布,本来就在相对靠两侧的位置,你强行拉回靠近Sigmoid激活函数中间想使它梯度变化还不错,这样就相当于我这一层网络所学习到的特征分布就被搞坏了,结构都被破化了。于是BN提出了变换重构这一概念,引入了可学习参数γ、β,这下面这个函数很关键。

【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

由上面一系列过程推导,其中m指的是mini-batch size,均值u、标准差σ
作者引入了这个可学习重构参数γ、β,让我们的网络可以学习恢复出原始网络所要学习的特征分布。这样结构就不会改变了。
代码实现:

	m = K.mean(X, axis=-1, keepdims=True)	# 计算均值
    std = K.std(X, axis=-1, keepdims=True)	# 计算标准差
    X_normed = (X - m) / (std + self.epsilon)	# 标准化
    out = self.gamma * X_normed + self.beta		# 重构变换

根据文献说,BN可以应用于一个神经网络的任何神经元上。文献主要是把BN变换置于网络激活函数层的前面,如图。

三、测试过程
以上是训练过程,测试过程如下
网络一旦训练完毕,参数都是固定的,这个时候即使是每批训练样本进入网络,那么BN层计算的均值u、和标准差σ都是固定不变的。我们可以采用这些数值来作为测试样本所需要的均值、标准差,均值和标注差计算如下:
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
E(x)指的是每一批训练数据神经元x的平均值,Val[x]即为一批数据神经元的标准差
测试阶段:BN的使用公式如下【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
据文献说,BN可以应用于一个神经网络的任何神经元上。文献主要是把BN置于网络激活函数层的前面。下面第一个是论文没有用BN的激活函数,第二个是用了BN的,由于偏置参数b经过BN层后其实是没有用的,最后也会被均值归一化,当然BN层后还有个β参数作为偏置项,所以b这个参数就可以不用了
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
四、Batch Normalization在CNN中的使用
既然BN是对单个神经元的运算,那么在CNN中卷积层上要怎么搞?假如某一层卷积层有6个特征图,每个特征图的大小是100×100,即(100,100,6),这样就相当于这一层网络有6×100×100个神经元,如果采用BN,要处理6×100×100个神经元,就会有6×100×100个参数γ、β,这样产生的参数岂不是太多,BN到目前又怎么会如此好用呢?其实卷积层上的BN使用,其实也是使用了类似权值共享的策略,把一整张特征图当做一个神经元进行处理,即对于每个特征图都只有一对可学习参数:γ、β。

如下为Inception v2主体代码,主要加了Batch Normalization进行标准化

#coding=utf-8
from keras.models import Model
from keras.layers import Input,Dense,Dropout,BatchNormalization,Conv2D,MaxPooling2D,AveragePooling2D,concatenate
from keras.layers.convolutional import Conv2D,MaxPooling2D,AveragePooling2D
import numpy as np
seed = 7
np.random.seed(seed)
 
# 卷积层和标准层组成的block
def Conv2d_BN(x, nb_filter,kernel_size, padding='same',strides=(1,1),name=None):
    if name is not None:
        bn_name = name + '_bn'
        conv_name = name + '_conv'
    else:
        bn_name = None
        conv_name = None
 
    x = Conv2D(nb_filter,kernel_size,padding=padding,strides=strides,activation='relu',name=conv_name)(x)
    x = BatchNormalization(axis=3,name=bn_name)(x)
    return x
 
# Inception Block
def Inception(x,nb_filter):
    branch1x1 = Conv2d_BN(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None)
 
    branch3x3 = Conv2d_BN(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None)
    branch3x3 = Conv2d_BN(branch3x3,nb_filter,(3,3), padding='same',strides=(1,1),name=None)
 
    branch5x5 = Conv2d_BN(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None)
    branch5x5 = Conv2d_BN(branch5x5,nb_filter,(5,5), padding='same',strides=(1,1),name=None)
 
    branchpool = MaxPooling2D(pool_size=(3,3),strides=(1,1),padding='same')(x)
    branchpool = Conv2d_BN(branchpool,nb_filter,(1,1),padding='same',strides=(1,1),name=None)
 
    x = concatenate([branch1x1,branch3x3,branch5x5,branchpool],axis=3)
 
    return x

def GoogLeNet():
	input = Input(shape=(224,224,3))
	x = Conv2d_BN(inpt,64,(7,7),strides=(2,2),padding='same')
	x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)
	x = Conv2d_BN(x,192,(3,3),strides=(1,1),padding='same')
	x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)
	x = Inception(x,64)#256
	x = Inception(x,120)#480
	x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)
	x = Inception(x,128)#512
	x = Inception(x,128)
	x = Inception(x,128)
	x = Inception(x,132)#528
	x = Inception(x,208)#832
	x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)
	x = Inception(x,208)
	x = Inception(x,256)#1024
	x = AveragePooling2D(pool_size=(7,7),strides=(7,7),padding='same')(x)
	x = Dropout(0.4)(x)
	x = Dense(1000,activation='relu')(x)
	x = Dense(1000,activation='softmax')(x)
	model = Model(inpt,x,name='inception')
	return model
	

2.3 Inception v3

v3论文:Rethinking the Inception Architecture for Computer Vision
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
关键词:分解卷积aggressive正则
我们从摘要中可以看到这两个关键词是本篇论文提出的关键点,我们来分析分析。

一、分解卷积
这个词看起来新,其实对于读过VGG文章的朋友就不陌生了,如果你还没读过非常建议你去读。文章链接:深入解析VGGNet网络 ,在小卷积核部分会讲到,整个过程推导计算及原理都讲了,这里就不过多再详细解释,直接上VGGNet中结论。
结论:
1、2个3x3的卷积堆叠获取到的感受野大小相当于一个5x5的卷积。
2、3个3x3的卷积堆叠获取到的感受野大小相当于一个7x7的卷积。

为什么用小卷积核比用大卷积核好?
1、多个小卷积核的堆叠比单一大卷积核带来了精度提升,并且感受野没有下降,计算量还减少(最重要)
2、多个小卷积核的堆叠相比较大卷积核的非线性函数会更多,更多的卷积核的使用可使决策函数更加具有辨别能力
3、卷积层的参数减少,如下图conv sum,两个3x3卷积和一个5x5卷积的参数量的比为(9+9)/5×5=18/25,分解减少了28%=7/25的参数,我们可以用下图计算验证一下,2592×2+7200×0.28=7200
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
借鉴了VGG这个分解方法后,GoogLeNet还继续做了改进。

二、空间卷积分解为不对称卷积
上面说了大的卷积核可以分解为多个小卷积核,那么是否能继续再分解呢。于是作者通过卷积的非对称分解可以将3x3卷积分解为1x3和3x1卷积。在输入输出卷积核数目一定的时候,卷积的非对称分解可以将计算量减少33%。如下图
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

文章还提到,如果将3×3卷积变成2个2×2也可以减少计算量,但是只减少11%,没有非对称分解好。
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
分解后每个Inception结构都改成这样的结构,这样虽然网络深度会增加,但是计算量和参数都会减少。作者说尽管我们的网络深达42层,但我们的计算成本仅比GoogLeNet高约2.5倍,并且仍然比VGGNet高效得多。

三、特征图的尺寸的高效减小
我们先做个文章中的假设,假设我们需要把k通道的d×d特征图,如果我们要得到一个2k通道的d/2×d/2特征图,那么我们首先要去做一个stride=1,通道数为2k的卷积,得到d×d×2k特征图,然后再用一个stride=2的池化,得到d/2×d/2×2k特征图,但是如果直接这么做的话,效果并不好,如下图。
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
作者说有两个方法(已知的)解决假设的问题,一个是左边的先池化,再卷积,但这样会带来一个具有代表性的瓶颈,这个瓶颈没直接说,我觉得应该是性能问题,无非就是计算量与参数。右边先卷积,再池化计算量会更大,增加3倍。所以它自己提出一个高性能的减小特征图大小的方法,如下图
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

上图就是作者提出的高效方法,就是卷积和池化并行操作,并且两个步长都为2,这样直接都变为17,然后再拼接,也实现了假设,而且解决了以前方法的问题。

四、Label Smoothing模型正则
上面大多和分解相关,现在开始讲另一个重要的理论—标签平滑正则化。

什么是标签平滑正则化(Label Smoothing Regularization, LSR)方法?
答:在深度学习样本训练的过程中,我们采用one-hot标签去进行计算交叉熵损失时,只考虑到训练样本中正确的标签位置(one-hot标签为1的位置)的损失,而忽略了错误标签位置(one-hot标签为0的位置)的损失。这样一来,模型可以在训练集上拟合的很好,但由于其他错误标签位置的损失没有计算,导致预测的时候,预测错误的概率增大。为了解决这一问题,标签平滑的正则化方法便应运而生。

论文中全都是公式,感觉挺复杂,我就在网上搜了下,发现一篇讲的很好的推荐给大家,我觉得讲的很好理解,它举例子就很好懂:深度学习中的标签平滑正则化

最后看下结果:
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

2.4 Inception v4,Inception ResNet

论文:Inception-v4, Inception-ResNet and the Impact of Residual Connections on Learning

讲到最后一个v4了,我们来回顾一下Inception结构发展史,Inception结构最初由GoogLeNet引入,即GoogLeNet=Inception v1;之后引入了BatchNormalization,叫做Inception v2;随后引入分解卷积和标签平滑正则,叫做Inception v3。Inception v4与Inception ResNet是该文章一起提出的。两个网络结合最后性能达到3.08%,现在我们来看看吧。注意Inception v4和Inception ResNet是两种结构噢,ResNet表示残差网络。

2.4.1 Inception v4

先看看Inception v4的网络结构
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
用于设计处理35×35网格的Inception-A,基本的Inception v2/v3模块,使用两个3x3卷积代替5x5卷积,并且使用average pooling,如下
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
用于设计处理17×17网格的Inception-B,模块使用1xn和nx1卷积代替nxn卷积,同样使用average pooling,如下
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
用于设计处理8×8网格的Inception-B,模块上将3x3卷积用1x3卷积和3x1卷积,同样使用average pooling,如下
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

设计用于使网格从35×35减少到17×17的Reduction-A,用最大池化
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
总之:Inception v4在结构上没有较大改变,去掉不必要的模块,只是结构看起来更加简洁统一,相当于就是把v3弄得更工整了,并没有提出创新出的新方法。

2.4.2 Inception-ResNet

这是该篇文章的第二个网络结构,细分为两种结构:Inception-Resnet-v1和Inception-Resnet-v2。Inception和ResNet混合,于是就出现多种Inception-ResNet结构,这里我们需要解释下,残差结构模块提出如下,残差网络在该篇文章性能提升发挥的作用很大。
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
为什么残差模块能不断提高网络性能?
答:我们知道网络越深的时候,提取到的不同层次的信息会越多,但随着网络层数的增加,网络很大程度可能出现梯度消失和梯度爆炸情况,loss不减反增,传统对应的解决方案则是数据的初始化,批标准化(batch normlization)和正则化,但是这样虽然解决了梯度传播的问题,深度加深了,可以训练几十层的网络,却带来了另外的问题,就是网络性能的退化问题,深度加深了,错误率却上升了。因此为解决随着深度加深,性能不退化,残差网络就出现了,残差用来设计解决退化问题,其同时也解决了梯度问题,更使得网络的性能也提升了,用了残差结构的网络深度可以到达几百上千层。

残差网络的特点是容易优化,并且能够通过增加相当的深度来提高准确率。其内部的残差块使用了跳跃连接,跳过中间一部分层,链接到后面层,缓解了在深度神经网络中增加深度带来的梯度消失问题。

关于ResNet残差网络文章还没读,等以后读了再写一篇ResNet原理详解的文章,大家先有个概念吧。

(1)Inception-Resnet-v1
Inception-ResNet V1 计算量接近Inception V3,Inception-ResNet-v1因为是在Inception-v3上加入ResNet
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
模块细致结构如下
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
(2)Inception-ResNet-v2
Inception-ResNet v2 计算量接近Inception v4,Inception-ResNet-v2因为是在Inception-v4上加入ResNet
注意:Inception-ResNet v1与Inception-ResNet v2 网络结构相同,只是里面stem,Inception-renet-A,Inception-renet-B等模块不同
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现
实验结果
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

网络结构都是之前讲过的的,主要是加了个残差结构,起到跳跃连接的作用。

以上就是GoogLeNet全部啦,终于写完了,我也体会了什么叫做创作不易啊。欢迎点赞,评论交流,希望对你有帮助。
【深度学习原理第7篇】深入解析GoogLeNet v1-v4 + keras实现

上一篇:GoogLeNet Incepetion V1


下一篇:深度学习论文阅读笔记(5)-GoogLeNet