GoogLeNet Structure

2014年的Imagenet不太寻常,GoogLeNet(我们这儿先来讨论GoogLeNet Incepetion V1)与VGG一道成为当年的冠军。当然了,他们都做到了more deeper(Alexnet只有8层),但是也有一些不同的地方,VGG貌似更为直接的继承了AlexNet的框架,但是GoogLeNet却做了更加大胆的尝试,不仅仅做到了more deeper,而且more wider,虽然层数也只有22层,但是相对于AlexNet与VGG而言,在大小上还是小了很多。下面我们来看看它是怎么go deeper and wider but more smaller的。
说点题外话,虽然GoogLeNet取名为“GoogLeNet”而非“GoogleNet”,是向LeNet致敬,但不得不承认的一点就是网络结构里很难看到LeNet的影子。:)


我们大致清楚,增加网络的深度和宽度是直接提升网络性能的两个方法,而文章中也提出:获得高质量模型最保险的做法就是增加模型的深度(层数)或者是其宽度(层核或者神经元数)。但是不管哪一种方法带来的都是参数的增加,带来的后果就是计算量的增加,而且复杂的模型也将容易过拟合难以优化
我们先来将GoogLeNet的结构可视化一把:

GoogLeNet Structure

不好意思这个有点长,但是我也没办法啊(谁叫它本身就张那样),不过请大家注意了,整个网络是由很多个红框框这样的就够重复组合而成的,现在再回去看一波,是不是对这个结构了就一目了然了O(∩_∩)O。
我们把红色方框的部分抽离出来,另外,再给它起个名字(当然只有Szegedy才有资格咯):Incepetion

Incepetion Structure

可以看到Inception里有四个并行的线路。

  1. 单个 1×1卷积。
  2. 1×1 卷积接上 3×3 卷积。通常前者的通道数少于输入通道,这样减少后者的计算量。后者加上了padding=1使得输出的长宽的输入一致(看不懂?没关系的,我在后面说明一下让你秒懂)
  3. 同2,但换成了 5×5 卷积
  4. 和1类似,但卷积前用了最大池化层

最后将这四个并行线路的结果在通道这个维度上Concat(就是矩阵拼接)在一起。

大家可能就会有疑问了,为何要在input出来的时候加个1×1卷积,1×1的kennel去卷积不是啥事儿都没做嘛?不急,听我慢慢分析。我们先来做个假设,假设这样干:

Sample Incepetion Structure

就是先把那个1×1卷积给他抹掉,那么就是直接使用1×1、3×3以及5×5的卷积核对input进行操作,但是这样仍然会带来巨大的计算量(还不明白?接着看)。我们最开始提到过,GoogLeNet取名为“GoogLeNet”而非“GoogleNet”,是向LeNet致敬,但不得不承认的一点就是网络结构里很难看到LeNet的影子,其实它是Draw lessons from NIN,具体就是采用1×1卷积核来进行降维,这样,1x1的卷积核起到了降低feature map厚度、减少计算量的作用
大家可能还是不太明白吧,没关系,咋来举个例子:
我们以3×3这个 path 为例,假设 input(也就是上层的 output)为128×100×100(channel×height×width,batch size略去,没写,事实上,batch size是不影响计算的):

  • 不使用1×1的卷积,那么经过 kennel = 3×3,channel = 256,stride=1,pad=1 的卷积之后,其 output:256×100×100,那么其参数数量128×256×3×3
  • 若是先将 output 经过 kennel = 1×1,channel = 64,的卷积经之后,再将 1×1 Conv 的 output 抛进 3×3 卷积层,还是设3×3 Conv 的kennel = 3×3,channel = 256,stride=1,pad=1。但参数数量将减少为128×64×1×1+64×256×3×3,参数将近减少了一半,而你把这个放到5×5 的那条path上面去推算一下,可能会更明显!你说腻害不腻害

现在大家明白了这个1×1的卷积层是拿来干啥的了吧
大家都 get 到这个 point 以后,我们现在再来对这个 “Incepetion模块” 进行一波总结:

  1. 采用了1×1、3×3以及5×5的不同的kennel,那么神经元将看到不同尺度的信息,最后的 concat 也就将不同尺度的features加以结合了;
  2. 1×1 卷积接上 3×3 卷积。将前者的通道数设置为少于输入通道,这样减少后者的计算量。而后者加上了padding=1使得输出的长宽与输入一致,5×5 的那个是一样的道理,padding=2 而已。输出的长宽与输入一致确保最后能 concat 在一起
  3. 文章说很多地方都表明pooling挺有效,所以Inception里面也嵌入了。至于pooling嘛,原文解释得不是很详细,只说了效果好,所以放上去了 ,大家有什么看法也可以留言交流
  4. 最后,网络使用的是average pooling,而不是全连接,结果当然要好不然人家也不这么干,而在最后它还是使用linear线性层,这么干是为了方便fine-tuning模型。

这样说大家就不会打我脸了吧(●ˇ∀ˇ●)
我们再来回归原点,讨论整个网络的思路go more deeper,go more wider:

  1. 深度,层数更深,文章采用了22层,为了避免上述提到的梯度消失问题(在反向传播更新参数时,越靠近输入梯度越小),GoogLeNet 巧妙的在不同深度处增加了两个loss来保证梯度回传消失的现象。
  2. 宽度,增加了多种核1x1,3x3,5x5,还有直接max pooling的。
    但是如果简单的将这些应用到feature map上的话,concat起来的feature map厚度将会很大,所以在googlenet中为了避免这一现象提出的inception具有如下结构,在3x3前,5x5前,max pooling后分别加上了1x1的卷积核起到了降低feature map厚度的作用
    以上我们其实都是介绍的是more wider,那么这儿再来说说如何解决more deeper的一些问题,我们看到GoogLeNet一共堆砌了多个Incepetion模块使得网络加深,因此在BP(Back Propagation )的时候梯度弥散的情况是存在的,而GoogLeNet里面的softmax0、softmax1就是来解决这个问题的具体就是在训练的时候,他们的损失误差乘以一个权值(GoogLeNet里设置为0.3)加到整体损失中。在应用的时候,这两个辅助分类器会被丢掉。GoogLeNet的实验表明,只需要一个辅助分类器就可以达到同样的效果(提升0.5%)。

补充一下,以后我还会接着介绍它的其他几个版本

下面我们就来看看大家最关心的实践吧(Mxnet+Gluon)O(∩_∩)O

首先我们定义Inception模块

from mxnet.gluon import nn
from mxnet import nd

class Inception(nn.Block):
    def __init__(self, n1_1, n2_1, n2_3, n3_1, n3_5, n4_1, **kwargs):
    # n*_* : the output of every path,  for exzample
    # n2_1 is the output of Conv 1*1 of path 2 & n2_3 is the output of Conv 3*3 of path 2
        super(Inception, self).__init__(**kwargs)
        # path 1
        self.p1_conv_1 = nn.Conv2D(n1_1, kernel_size=1,
                                   activation='relu')
        # path 2
        self.p2_conv_1 = nn.Conv2D(n2_1, kernel_size=1,
                                   activation='relu')
        self.p2_conv_3 = nn.Conv2D(n2_3, kernel_size=3, padding=1,
                                   activation='relu')
        # path 3
        self.p3_conv_1 = nn.Conv2D(n3_1, kernel_size=1,
                                   activation='relu')
        self.p3_conv_5 = nn.Conv2D(n3_5, kernel_size=5, padding=2,
                                   activation='relu')
        # path 4
        self.p4_pool_3 = nn.MaxPool2D(pool_size=3, padding=1,
                                      strides=1)
        self.p4_conv_1 = nn.Conv2D(n4_1, kernel_size=1,
                                   activation='relu')

    # define the forward propagation
    def forward(self, x):
        p1 = self.p1_conv_1(x)
        p2 = self.p2_conv_3(self.p2_conv_1(x))
        p3 = self.p3_conv_5(self.p3_conv_1(x))
        p4 = self.p4_conv_1(self.p4_pool_3(x))
        # concatenate the output of four paths together at the first dimension (channel)
        return nd.concat(p1, p2, p3, p4, dim=1)

接着,我们就可以用这个Inception模块去构造GoogLeNet了

class GoogLeNet(nn.Block):
    def __init__(self, num_classes, verbose=False, **kwargs):
        super(GoogLeNet, self).__init__(**kwargs)
        self.verbose = verbose
        # add name_scope on the outer most Sequential
        with self.name_scope():
            # block 1
            b1 = nn.Sequential()
            b1.add(
                nn.Conv2D(64, kernel_size=7, strides=2,
                          padding=3, activation='relu'),
                nn.MaxPool2D(pool_size=3, strides=2)
            )
            # block 2
            b2 = nn.Sequential()
            b2.add(
                nn.Conv2D(64, kernel_size=1),
                nn.Conv2D(192, kernel_size=3, padding=1),
                nn.MaxPool2D(pool_size=3, strides=2)
            )

            # block 3
            b3 = nn.Sequential()
            b3.add(
                Inception(64, 96, 128, 16,32, 32),
                Inception(128, 128, 192, 32, 96, 64),
                nn.MaxPool2D(pool_size=3, strides=2)
            )

            # block 4
            b4 = nn.Sequential()
            b4.add(
                Inception(192, 96, 208, 16, 48, 64),
                Inception(160, 112, 224, 24, 64, 64),
                Inception(128, 128, 256, 24, 64, 64),
                Inception(112, 144, 288, 32, 64, 64),
                Inception(256, 160, 320, 32, 128, 128),
                nn.MaxPool2D(pool_size=3, strides=2)
            )

            # block 5
            b5 = nn.Sequential()
            b5.add(
                Inception(256, 160, 320, 32, 128, 128),
                Inception(384, 192, 384, 48, 128, 128),
                nn.AvgPool2D(pool_size=2)
            )
            # block 6
            b6 = nn.Sequential()
            b6.add(
                nn.Flatten(),
                nn.Dense(num_classes)
            )
            # chain blocks together
            self.net = nn.Sequential()
            self.net.add(b1, b2, b3, b4, b5, b6)
    
    # define the forward propagation, 
    def forward(self, x):
        out = x
        for i, b in enumerate(self.net):
            out = b(out)
            if self.verbose:
                print('Block %d output: %s'%(i+1, out.shape))
        return out

这就是整个GoogLeNet的Structure了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,873评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,483评论 1 306
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,525评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,595评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,018评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,958评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,118评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,873评论 0 208
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,643评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,813评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,293评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,615评论 3 262
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,306评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,170评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,968评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,107评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,894评论 2 278

推荐阅读更多精彩内容