深度学习基础系列:GoogleNet

深度学习基础系列:GoogleNet

GoogleNet简介
2014年,GoogLeNet和VGG是当年ImageNet挑战赛(ILSVRC14)的双雄,GoogLeNet获得了第一名、VGG获得了第二名,这两类模型结构的共同特点是层次更深了。VGG继承了LeNet以及AlexNet的一些框架结构,而GoogLeNet则做了更加大胆的网络结构尝试,虽然深度只有22层,但大小却比AlexNet和VGG小很多,GoogleNet参数为500万个,AlexNet参数个数是GoogleNet的12倍,VGGNet参数又是AlexNet的3倍,因此在内存或计算资源有限时,GoogleNet是比较好的选择;从模型结果来看,GoogLeNet的性能却更加优越。

GoogleNet系列网络的概览

  • InceptionV1,通过把不同尺寸的卷积核如1x1,3x3,5x5进行堆叠增加了网络对不同尺度的适应性。并且通过在3x3的网络,5x5的网络后加入1x1使得网络的计算复杂度降低,而且提高网络的非线性的程度,基于更强的表征能力。
  • InceptionV2,加入了BatchNormalization层,减少了Internal Variance Shift。使得每一程的输出的分布都满足指定的高斯分布,可以防止训练集与测试集之间分布的不匹配,还能加快网络收敛速度,防止过拟合。
  • InceptionV3,在InceptionV3中google将分解的思想用到了极致,把二维卷积核(NxN)拆成两个方向上的一维卷积核(Nx1,1xN)。这样做不仅仅加快了网络的运算速度,而且由于增加网络的层数,使得网络的非线性增加,提高网络的表征能力。
  • InceptionV4,尝试着把Inception的结构与Resnet的结构进行结合,并设计了一个更深更加优秀的网络InceptionV4。

InceptionV1模块介绍
要想理解Googlenet的结构,第一步必须先知道Inception的结构,因为它是由多个Inception的结构组合而成的。如下图Fig.2所示,(a)表示朴素的版本的inception v1示意图,(b)表示降维版本的Inception v1示意图。
深度学习基础系列:GoogleNet
Inception的主要思想基于——一个卷积网络里面的局部稀疏最优结构往往可以由简单可复用的密集组合来近似或者替代。就像(a)里面,1x1,3x3,5x5的卷积层,与3x3的池化层的组合一个inception。这样做的几点说明:

  • 不同尺寸的卷积核可以提取不同尺度的信息。
  • 采用1x1,3x3,5x5可以方便对齐,padding分别为0,1,2就可以对齐。
  • 由于池化层在CNN网络里面的成功运用,也把池化层当做组合的一部分。
  • 由于Googlenet是好几个Inception模块的堆叠,而且往往越后面的Inception模块提取的是更加高级抽象的特征,而由于高级抽象的特征的时域联系会降低。(在这里加上一点个人理解,当提取的特征比较简单,比如边缘,轮廓的时候,往往只需要提取某个像素附近的几个像素就行了,这时卷积核比较小,没有问题。但是当提取的特征变得复杂的时候,比如提取的是人的鼻子,耳朵的时候,需要的可能就是某个像素旁边几十或者几百个像素了。当然我说的这些像素指的是特征图里面的像素。)因此为了获取这些高级信息,我们在后面的Inception模块里面需要增大3x3,5x5这些大卷积核的比例。

但是这么做,问题又来了,如果提高大卷积核的比例,那么这会意味着计算复杂度的飙升。为此,google的工程师们又提出(b)的这个Inception结构

Inception的架构
下面的Table.1给出了Googlenet是怎么由Inception模块和一些传统的卷积层与池化层构成的。比较Inception(3a)和Inception(5b),我们可以看到大卷积核的滤波器的个数的比例已经提高了。最后需要注意两点,该网络的使用了avg pool来替代第一层全连接层,大大降低了参数的个数。后面在avg pool后面加入全连接层则是为了方便微调的操作。

深度学习基础系列:GoogleNet
GoogLeNet的Keras实现:

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

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,(1,1), 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():
    inpt = Input(shape=(224,224,3))
    #padding = 'same',填充为(步长-1)/2,还可以用ZeroPadding2D((3,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
上一篇:c# – SmartDevice CAB“不是有效的Windows CE安装文件”


下一篇:Python开发环境搭建