Tensorflow实现Google Inception Net V3

InceptionNet系列网络不断进化,从InceptionV1到V4,通过优化InceptionModule结构,引入BN和Factorization into small convolutions等技术,大幅提升模型效率与准确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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用多个分支提取不同抽象程度的高阶特征的思路很有效,可以丰富网络的表达能力。
内容概要:本文档主要展示了C语言中关于字符串处理、指针操作以及动态内存分配的相关代码示例。首先介绍了如何实现键值对(“key=value”)字符串的解析,包括去除多余空格和根据键获取对应值的功能,并提供了相应的测试用例。接着演示了从给定字符串中分离出奇偶位置字符的方法,并将结果分别存储到两个不同的缓冲区中。此外,还探讨了常量(const)修饰符在变量和指针中的应用规则,解释了不同类型指针的区别及其使用场景。最后,详细讲解了如何动态分配二维字符数组,并实现了对这类数组的排序与释放操作。 适合人群:具有C语言基础的程序员或计算机科学相关专业的学生,尤其是那些希望深入理解字符串处理、指针操作以及动态内存管理机制的学习者。 使用场景及目标:①掌握如何高效地解析键值对字符串并去除其中的空白字符;②学会编写能够正确处理奇偶索引字符的函数;③理解const修饰符的作用范围及其对程序逻辑的影响;④熟悉动态分配二维字符数组的技术,并能对其进行有效的排序和清理。 阅读建议:由于本资源涉及较多底层概念和技术细节,建议读者先复习C语言基础知识,特别是指针和内存管理部分。在学习过程中,可以尝试动手编写类似的代码片段,以便更好地理解和掌握文中所介绍的各种技巧。同时,注意观察代码注释,它们对于理解复杂逻辑非常有帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值