Tensorflow实现Google Inception Net V3


Google Inception Net是一个大家族,包括Inception V1、Inception V2、Inception V3和Inception V4,而且都取得了不错的效果。

Inception V1

Inception V1有22层深,相比于AlexNet的8层和VGGNet的19层都要深,但是计算量却小了很多,只有AlexNet参数量的1/12,却可以达到优胜于AlexNet的准确率。其降低参数量的目的有两点:

  1. 参数越多规模越庞大,需要提供模型学习的数据量就越大
  2. 参数越多,耗费的计算资源越大

Inception V1参数量少但效果好有以下原因:

  1. 模型层数能力更深,表达能力更强
  2. 去除了最后的全连接层,用全局平均池化层(将图片尺寸变为1x1)
  3. Inception V1使用了精心设计的Inception Module提高了参数的利用率关于Inception V1的设计借鉴了NIN的思想,就如同大网络中的一个小网络,通过反复堆叠形成一个大网路。一般来说卷积层要提升表达能力,主要依靠增加输出通道数,但副作用是计算量大和过拟合,因为每一个输出通道对应一个滤波器,同一个滤波器共享参数,只能提取一类特征,因此一个输出通道只能做一种特征处理。而NIN的MLPConv拥有更强大的能力,允许在输出通道之间组合信息,其基本等效于普通卷积层再连接1x1的卷积层和ReLU激活函数。
Inception Module

Inception Module的基本结构包括四个分支:

  • 第一个分支,对输入进行1x1的卷积,它可以夸通道组织信息,提高网络的表达能力,同时可以对输出通道升维和降维操作,降低计算量成本。
  • 第二个分支,先使用了1x1卷积,然后连接3x3卷积,相当于两次特征变换。
  • 第三个分支,先是1x1的卷积,然后连接5x5的卷积。
  • 第四个分支,先使用3x3最大池化层后直接使用1x1卷积。

最终将这四个分支通过一个聚合操作合并(在输出通道数这个维度上聚合)。
Inception Module可以让网络的深度和宽度高效率地扩充,提升准确率却不至于过拟合。人脑神经元的连接是稀疏的,Inception Net的主要目标就是找到最优的稀疏结构单元(Inception Module)。

Hebbian原理表明一起发射的神经元会连接在一起,所以应该把相关性高的一簇神经元节点连接在一起,在普通的数据集中,可能需要对神经元节点聚类,但是图片数据中临近区域的数据相关性,可以直接通过卷积操作连接在一起。所以1x1的的卷积就可以很自然地把这些相关性很高的、在同一个空间位置但在不同通道的特征连接在一起。

在整个网络中,使用了多个堆叠的Inception Module,我们希望靠后的Inception Module可以捕捉更高阶的抽象特征,因此越靠后的Inception Module的卷积的空间集中度应该逐渐降低,这样可以捕获更大面积的特征,因此越靠后,Inception Module中的3x3和5x5卷积核的占比应该更多。

Inception V2

Inception V2学习了VGGNet,使用两个3x3的卷积核代替5x5的卷积核,还提出了BN(Batch Normalization),是一种非常有效的正则化方法。其思想是BN用于神经网络某层时,会对每一个mini-batch数据的内部进行标准化处理,使输出规范化到N(0, 1)的正态分布,减少了内部神经元分布的改变。

传统的深度神经网络再训练时,每一层的输出都在变化,导致训练变得困难,只能用一个很小的学习速率解决问题,而使用了BN之后,学习速率可以增大很多倍。

Inception V3

Inception V3引入了Factoriztion into small convolutions的思想,将一个较大的二维卷积拆成两个较小的一维卷积,比如将3x3卷积拆成1x3和3x1的两个卷积,即节约了参数又加速运算减轻了过拟合,同时增加了一层非线性扩展模型表达能力。

Inception V3优化了Inception Module的结构,在Inception Molule中使用了分支,还在分支中使用了分支,也就是Network In Network in Network

Inception V4

相比于Inception V3,Inception V4主要结合了ResNet,这个接下来学习了再总结。


Inception V3的结构比较复杂,所以使用了tf.contrib.slim辅助设计这个网络,通过使用contrib.slim中的一些功能和组件可以大大减少设计的代码量。
以下是每一段的输出尺寸:
InceptionV3/InceptionV3/Mixed_5b/concat [32, 35, 35, 256]
InceptionV3/InceptionV3/Mixed_5c/concat [32, 35, 35, 288]
InceptionV3/InceptionV3/Mixed_5d/concat [32, 35, 35, 288]
InceptionV3/InceptionV3/Mixed_6a/concat [32, 17, 17, 768]
InceptionV3/InceptionV3/Mixed_6b/concat [32, 17, 17, 768]
InceptionV3/InceptionV3/Mixed_6c/concat [32, 17, 17, 768]
InceptionV3/InceptionV3/Mixed_6d/concat [32, 17, 17, 768]
InceptionV3/InceptionV3/Mixed_6e/concat [32, 17, 17, 768]
InceptionV3/InceptionV3/Mixed_7a/concat [32, 8, 8, 1280]
InceptionV3/InceptionV3/Mixed_7b/concat [32, 8, 8, 2048]
InceptionV3/InceptionV3/Mixed_7c/concat [32, 8, 8, 2048]
InceptionV3/AuxLogits/SpatialSqueeze [32, 1000]
InceptionV3/Logits/SpatialSqueeze [32, 1000]


由于代码比较长,这里只放了第一个Inception模块的实现,具体实现现点这里

      with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d],
                            stride=1, padding='SAME'):
            # 第一个Inception模块的第一个Inception Model
            with tf.variable_scope('Mixed_5b'):
                with tf.variable_scope('Branch_0'):
                    branch_0 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
                with tf.variable_scope('Branch_1'):
                    branch_1 = slim.conv2d(net, 48, [1, 1], scope='Conv2d_0a_1x1')
                    branch_1 = slim.conv2d(branch_1, 64, [5, 5], scope='Conv2d_0b_5x5')
                with tf.variable_scope('Branch_2'):
                    branch_2 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
                    branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0b_3x3')
                    branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0c_3x3')
                with tf.variable_scope('Branch_3'):
                    branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
                    branch_3 = slim.conv2d(branch_3, 32, [1, 1], scope='Conv2d_0b_1x1')
                net = tf.concat([branch_0, branch_1, branch_2, branch_3], 3)
                print_activations(net)

            # 第一个Inception模块的第二个Inception Model
            with tf.variable_scope('Mixed_5c'):
                with tf.variable_scope('Branch_0'):
                    branch_0 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
                with tf.variable_scope('Branch_1'):
                    branch_1 = slim.conv2d(net, 48, [1, 1], scope='Conv2d_0b_1x1')
                    branch_1 = slim.conv2d(branch_1, 64, [5, 5], scope='Conv2d_0b_5x5')
                with tf.variable_scope('Branch_2'):
                    branch_2 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
                    branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0b_3x3')
                    branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0c_3x3')
                with tf.variable_scope('Branch_3'):
                    branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
                    branch_3 = slim.conv2d(branch_3, 64, [1, 1], scope='Conv2d_0b_1x1')
                net = tf.concat([branch_0, branch_1, branch_2, branch_3], 3)
                print_activations(net)

            # 第一个Inception模块的第三个Inception Model
            with tf.variable_scope('Mixed_5d'):
                with tf.variable_scope('Branch_0'):
                    branch_0 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
                with tf.variable_scope('Branch_1'):
                    branch_1 = slim.conv2d(net, 48, [1, 1], scope='Conv2d_0b_1x1')
                    branch_1 = slim.conv2d(branch_1, 64, [5, 5], scope='Conv2d_0b_5x5')
                with tf.variable_scope('Branch_2'):
                    branch_2 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
                    branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0b_3x3')
                    branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0c_3x3')
                with tf.variable_scope('Branch_3'):
                    branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
                    branch_3 = slim.conv2d(branch_3, 64, [1, 1], scope='Conv2d_0b_1x1')
                net = tf.concat([branch_0, branch_1, branch_2, branch_3], 3)
                print_activations(net)

总结
  1. Factorization into small convolutions很有效,可以降低参数量、减轻过拟合,增加网络非线性的表达能力。
  2. 卷积网络从输入到输出,应该让图片尺寸逐减小,输出通道数逐渐增加,即让空间结构简化,将空间信息转化为高阶抽象的特征信息。
  3. Inception Module用多个分支提取不同抽象程度的高阶特征的思路很有效,可以丰富网络的表达能力。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容