起源


GoogLeNet在ImageNet挑战赛 (ILSVRC14) 拿到了冠军之后,由于其参数更少、准确率更高以及创意的网络结构获得了无数学习者的关注。

一般来说,提升网络性能最直接的方式就是增加网络的深度和宽度,有两个主要的缺点;

  1. 宽度越大,网络参数越多,网络容易过拟合,计算复杂度越大;
  2. 深度越大,容易出现梯度弥散问题(梯度越往后穿越容易消失),难以优化模型。

该团队的论文认为解决上述两个缺点的根本方法是将全连接甚至一般的卷积都转化为稀疏连接,故提出了名为Inception 的结构。

网络结构


深度共有22层,由卷积/ inception/ pooling/ softmax组成。整体结构如下图:

cnn学习率_卷积


因除Inception外,前几层都为正常的卷积层,故在这里只分析Inception结构。红框所表示的部分即为GoogLeNet所独有的Inception:

InceptionV1(Inception 3a):

cnn学习率_2d_02


Inception网络代替人工来确定卷积层中的过滤器类型,或者确定是否需要创建卷积层或池化层。

  • 1×1的卷积层,RuLU,输出28x28x64。
  • 1×1的卷积核,padding = 0,stride = 1,输出28x28x128,ReLU,再通过一个3×3的层,padding = 1,stride = 1,输出28×28×128。
  • 1×1的卷积核,padding = 0,stride = 1,输出28x28x128,ReLU,再通过一个5×5的层,padding = 2,stride = 1,输出28×28×32。
  • same类型的padding,3x3的核,padding为1,输出28x28x192,再通过一个1×1的层,输出28x28x32。

最后把得到的各个层的通道叠加,最后得到一个28×28×256 (64+128+32+32) 的输出。

Network in Network/1x1卷积:

该图卷积核表示遍历6X6个单元,用每个单元的32个数与过滤器中的相乘,ReLU,然后输出。

1×1卷积可以从根本上理解为对这32个不同的位置都应用一个全连接层,,输出结果是6×6×#filters(过滤器数量)

cnn学习率_2d_03

1x1卷积的主要目的是使维度变得可控,压缩通道数,节省计算量。

优点

  1. 在多个尺度上同时进行卷积再聚合,能提取到不同尺度的特征;
  2. 通过1x1卷积控制每一层的卷积核个数,从而使得计算复杂度可控;
  3. 模块化的结构,方便增添和修改;

综上:Inception网络本质上只是很多这些学过的模块在不同的位置重复组成的网络。


Inception V2:

简单来说就是用1xn卷积后接nx1卷积来替代nxn的卷积,在保持感受野范围的同时又减少了参数量。

GoogLeNet团队发现在网络的前期使用这种分解效果并不好,在中度大小的特征图(feature map)上使用效果才会更好(特征图大小建议在12到20之间)。


GoogLeNet in Keras


实现一:https://gist.github.com/joelouismarino/a2ede9ab3928f999575423b9887abd14

实现二:

1. def Conv2d_BN(x, nb_filter,kernel_size, padding='same',strides=(1,1),name=None): 
2. if name is not None: 
3.     bn_name = name + '_bn' 
4.     conv_name = name + '_conv' 
5. else: 
6.     bn_name = None 
7.     conv_name = None 
8. 
9.x=Conv2D(nb_filter,kernel_size,padding=padding,strides=strides,activation='relu',name=conv_name)(x) 
10. x = BatchNormalization(axis=3,name=bn_name)(x) 
11. return x 
12. 
13. def Inception(x,nb_filter): 
14. branch1x1 = Conv2d_BN(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None) 
15. 
16. branch3x3 = Conv2d_BN(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None) 
17. branch3x3 = Conv2d_BN(branch3x3,nb_filter,(3,3), padding='same',strides=(1,1),name=None) 
18. 
19. branch5x5 = Conv2d_BN(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None) 
20. branch5x5 = Conv2d_BN(branch5x5,nb_filter,(1,1), padding='same',strides=(1,1),name=None) 
21. 
22. branchpool = MaxPooling2D(pool_size=(3,3),strides=(1,1),padding='same')(x) 
23. branchpool = Conv2d_BN(branchpool,nb_filter,(1,1),padding='same',strides=(1,1),name=None) 
24. 
25. x = concatenate([branch1x1,branch3x3,branch5x5,branchpool],axis=3) 
26. 
27. return x 
28. 
29. inpt = Input(shape=(224,224,3)) 
30. #padding = 'same',填充为(步长-1)/2,还可以用ZeroPadding2D((3,3)) 
31. x = Conv2d_BN(inpt,64,(7,7),strides=(2,2),padding='same') 
32. x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x) 
33. x = Conv2d_BN(x,192,(3,3),strides=(1,1),padding='same') 
34. x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x) 
35. x = Inception(x,64)#256 
36. x = Inception(x,120)#480 
37. x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x) 
38. x = Inception(x,128)#512 
39. x = Inception(x,128) 
40. x = Inception(x,128) 
41. x = Inception(x,132)#528 
42. x = Inception(x,208)#832 
43. x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x) 
44. x = Inception(x,208) 
45. x = Inception(x,256)#1024 
46. x = AveragePooling2D(pool_size=(7,7),strides=(7,7),padding='same')(x) 
47. x = Dropout(0.4)(x) 
48. x = Dense(1000,activation='relu')(x) 
49. x = Dense(1000,activation='softmax')(x) 
50. model = Model(inpt,x,name='inception') 
51. model.compile(loss='categorical_crossentropy',optimizer='sgd',metrics=['accuracy']) 
52. model.summary()