图像分类算法GoogLeNet论文解读(Inception v1、v2、v3、v4)

[v1] Going Deeper with Convolutions, 6.67% test error, http://arxiv.org/abs/1409.4842
[v2] Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift, 4.8% test error, https://arxiv.org/abs/1502.03167
[v3] Rethinking the Inception Architecture for Computer Vision, 3.5% test error, https://arxiv.org/abs/1512.00567
[v4] Inception-v4, Inception-ResNet and the Impact of Residual Connections on Learning, 3.08% test error, https://arxiv.org/abs/1602.07261

 

一、Going Deeper with Convolutions

Inception v1提出了Inception结构

1.1 摘要

作者在文中提出了一种新的叫做Inception的结构,取得了ILSVRC-2014的分类和检测任务的冠军。作者设计的网络具有很大的depth和width,但是参数数量却仅为AlexNet的1/12。

1.2 简介

近年来,CNN的发展不仅取决于数据集、计算能力,还取决于提出了新的算法和网络结构。作者提出的模型不仅可以用于学术研究,并且由于计算量较小,可以应用于实际业务。受NIN网络的影响,提出了Inception结构,显著提升了图像分类和检测的性能。

从LeNet-5开始,CNN有一套标准化的处理流程,即堆积卷积层(following by max-polling layer),后面再接数个全连接层,最后使用softmax loss。在mnist、cifar和ImageNet数据集上都取得了很好的效果,近期CNN的发展趋势是增大网络的depth和width,用dropout预防过拟合。作者借鉴了NIN网络中1*1卷积核的使用,起到了在不影响网络效果的情况下,降低了参数数量,增强了网络的depth和width。对标了R-CNN,对potential proposal估计和分类两个模块都进行了性能提升,因此提升了目标检测的性能。

1.3 为什么这么设计Inception结构

最简单的CNN性能提升办法是增大网络的depth和width,这里的width指的是每一层的神经元数量,这种方式要两个缺点:首先是毕竟大规模训练集获取的代价很大,增加网络参数数量会增加过拟合的风险;其次,增加参数数量增加了计算量。将全连接层和卷积层换成更稀疏的连接方式可以降低参数数量。

1.4 Inception结构

作者也是根据理论假设了Inception结构,对输入数据进行1 * 1,3 * 3,5 * 5的卷积,将各自的卷积结构连接构成下一层的输入。同时由于pooling操作的必要性,进行了并行的pooling操作,结果也合并到3个卷积的输出中构成整体的输出。这就是naive版本的Inception结构。

上述实现方式的问题就是计算量大,即使当前Inception结构的输入数据的channel数大小适中,进行一个大规模卷积核的5 * 5的卷积操作的计算量依然很大,并且池化操作的输出channel和输入相同,这样进行合并操作之后,输出的channel数肯定会增加,这样逐层增加会显著增加计算量,降低模型的使用效率。

对于计算量大的问题,需要对inception结构进行降维操作,办法就是在3 *3 和5 * 5的卷积之前使用1 * 1的卷积操作,这样做一方面减少了参数,同时使用了更多的激活函数,增加了非线性表达能力。

建议低层网络依然是传统卷积操作,网络高层才使用Inception结构。这么设计结构能起到比较好的作用的直观理解是,对图像进行多尺度的处理然后聚合到一起作为下一层的输入,使得网络学习到多尺度的图像信息。

1.5 GoogleNet结构

网络共22层:
使用全局平均池化层(GAP)代替全连接层,虽然移除了全连接层,但还是在GAP后使用了dropout;
网络的中间部位还输出了一个辅助的softmax,增强低层网络的判别能力,增强反向传播的梯度的大小,提供额外的正则化能力;
训练过程中,损失值是最后层的softmax loss加0.3*中间输出的softmax loss,推理时,忽略中间的这个softmax输出。

è¿éåå¾çæè¿°

 

二、Batch Normalization

inception v2主要做的改进就是加入了batch normalization

2.1 摘要

作者将训练过程中深度网络内部子网络的输入数据的分布变化叫做internal covariate shift,通过作者提出的batch normalization方法可以减弱这些变化,使得每一层的输入数据具有相同的均值和方差,增加模型训练的收敛速度。使用BN层,可以使用更大的学习率加速训练过程;可以取得类似于dropout的防止过拟合的作用;可以使用易于饱和的非线性函数(sigmoid)。

2.2 简介

计算协方差矩阵的特征值太耗时也太耗空间,我们一般最多只用到z-score处理,即每一维度减去自身均值,再除以自身标准差,这样能使数据点在每维上具有相似的宽度,可以起到一定的增大数据分布范围,进而使更多随机分界面有意义的作用。

开始讲解算法前,先来思考一个问题:我们知道在神经网络训练开始前,都要对输入数据做一个归一化处理,那么具体为什么需要归一化呢?归一化后有什么好处呢?原因在于神经网络学习过程本质就是为了学习数据分布,一旦训练数据与测试数据的分布不同,那么网络的泛化能力也大大降低;另外一方面,一旦每批训练数据的分布各不相同(batch 梯度下降),那么网络就要在每次迭代都去学习适应不同的分布,这样将会大大降低网络的训练速度,这也正是为什么我们需要对数据都要做一个归一化预处理的原因。

对于深度网络的训练是一个复杂的过程,只要网络的前面几层发生微小的改变,那么后面几层就会被累积放大下去。一旦网络某一层的输入数据的分布发生改变,那么这一层网络就需要去适应学习这个新的数据分布,所以如果训练过程中,训练数据的分布一直在发生变化,那么将会影响网络的训练速度。

我们知道网络一旦train起来,那么参数就要发生更新,除了输入层的数据外(因为输入层数据,我们已经人为的为每个样本归一化),后面网络每一层的输入数据分布是一直在发生变化的,因为在训练的时候,前面层训练参数的更新将导致后面层输入数据分布的变化。以网络第二层为例:网络的第二层输入,是由第一层的参数和input计算得到的,而第一层的参数在整个训练过程中一直在变化,因此必然会引起后面每一层输入数据分布的改变。我们把网络中间层在训练过程中,数据分布的改变称之为:“Internal Covariate Shift”。Paper所提出的算法,就是要解决在训练过程中,中间层数据分布发生改变的情况,于是就有了Batch Normalization,这个牛逼算法的诞生。

2.3 BN的实现

由于白化的计算量大且不是处处可微的,作者提出了近似的数据处理方法,即对训练数据减均值,除方差进行归一化操作。

简单的对每一层输入数据的归一化操作会改变数据的分布情况,例如使用sigmoid作为激活函数时,简单的对输入数据的归一化会使得数据分布在sigmoid的近线性区域,所以作者添加了一对可训练的参数,对归一化之后的数据进行变换。

实现过程中,是对每一个mini-batch自身进行减均值和除方差的归一化操作。

反向传播求梯度的计算公式:

2.4 BN层的使用

BN层是应用在Wx+b之后,即在一层网络中,先计算权重和输入数据的内积,加偏置项,进行BN操作,最后进行逐元素的非线性变化得到该层的输出。原始的BN操作应该是,但是对数据进行减均值操作之后,偏移项b也就没什么作用了,并且BN操作还有一个偏移项,所以就忽略了b变成了

在卷积层中,需要BN层也具有权值共享的特性,所以就不再是对每一个与卷积核局部连接的输入数据块计算一组γ和β,而是将一个输入的feature map看作是一个整体计算一组γ和β,假设训练过程中的mini-batch大小为m,输出的feature map大小为p*q,那么现在的mini-batch大小为进行求均值和方差的计算。这样的话,学习到的参数对(γ,β)数量就是总的卷积核的个数。

2.5使用BN层的好处

使用BN层可以使用更大的学习率进行训练,传统的神经网络训练过程中,如果使用较大的学习率,会有很大的概率发生梯度爆炸的现象。BN层使得训练过程可以适应更大范围的w值,避免梯度弥散或者梯度爆炸现象的发生。神经网络中,由于梯度反向传播到当前层时会乘以后面所有层权重的乘积,当网络层数很多时,如果后面层的权重参数大多使用幅值大于1的参数会造成梯度的爆炸现象,如果大多使用幅值小于1的参数会造成梯度弥散现象。但是进行BN操作的时候,

上式中,a处于分母位置,若a大于1,对于大幅值的w,会减小对w的梯度值;若a小于1,会增大对w的梯度值。因此w的幅值大小不会影响梯度的反向传播,不会造成梯度的爆炸或者弥散,会使得整体w的更新过程比较稳定,因此也就可以使用更大的学习率加速训练过程。

使用sigmoid做激活函数时,容易发生梯度弥散现象,现在常用的relu系激活函数已解决了激活函数导致的梯度弥散现象;
而对于梯度爆炸和由较小的多层w累乘造成的梯度弥散现象,通过BN层的应用,可以得到解决。

BN层具有一定的正则化作用,由于对训练数据进行了以batch为单位的归一化操作,那么模型就更多的是关注batch的共性,而不是单个样本的个性特征。实验表明,BN有一定的正则化作用,使用BN层时可以减小dropout和正则化项的强度。

 

三、Rethinking the Inception Architecture for Computer Vision

Inception v3减少瓶颈结构、使用因子分解较大的卷积

1、解决的主要问题:

(1)减少特征的表征性瓶颈。直观上来说,当卷积不会大幅度改变输入维度时,神经网络可能会执行地更好。过多地减少维度可能会造成信息的损失,这也称为「表征性瓶颈」。

(2)使用更优秀的因子分解方法,卷积才能在计算复杂度上更加高效。

2、分解卷积(用两个3*3的卷积代替5*5的卷积

由于图像数据具有平移不变性,卷积内部也是全连接。
一般情况下,网络层数越向后,feature map的w和h越小,但depth应该越大。
相同的卷积效果下减小了参数数量,并且由于使用了更多的非线性激活函数,学习到的特征的表达能力更强。

利用平移不变性,用两个3*3卷积可以代替一个5*5卷积,如下图:

   

3、分解卷积(非对称卷积的空间因子分解

将33的卷积分解成1n和n1的卷积,进一步减少了参数。
在网络的前面层这样分解效果并不好,建议在feature map的尺寸时使用该分解方式,建议分解为1*7和7*1的形式。

分解结果如下图:

4、模块中的滤波器组被扩展(即变得更宽而不是更深)

5、引入辅助分类器,加速深度网络收敛

辅助分类器在网络训练的早期不起作用,在网络训练的末期可以帮助获取更高的准确率。

辅助分类分支起着正则化的作用,如果侧分支是批处理规范化层或dropout层,那么网络的主分类器的性能会更好。

6、避免使用瓶颈层

传统方法,卷积网络使用一些池操作以减小特征图大小。

先使用池化再进行卷积会导致瓶颈结构,违背原则1;先使用卷积再进行池化,会增加计算成本,如下图:

7、减小特征图的尺寸

inceptionv3采用一种并行的降维结构,而在扩展过滤器组的同时减小特征图大小,既减少计算量又避免了瓶颈结构。

如下图(右图是从操作角度看):

8、Inception-v2 网络整体结构如下:

其中figure5、figure6、figure7依次如下图:

    

9、基于标签平滑的模型正则化(Label Smoothing

参考地址:https://www.cnblogs.com/zyrb/p/9699168.html

(1)损失函数定义

对于每个训练样本x,它是每个标签k的可能性为(其中Zi是未标准化的logits输出):

  

它的真值对应的可能性为:,得:

定义交叉熵损失为:

最小化损失相当于最大化标签的似然,梯度变成更简单的形式:

它在-1和1之间。

(2)one-hot 带来的问题

对于损失函数,我们需要用预测概率去拟合真实概率,而拟合one-hot的真实概率函数会带来两个问题:

1) 无法保证模型的泛化能力,容易造成过拟合;

2) 全概率和0概率鼓励所属类别和其他类别之间的差距尽可能加大,而由梯度有界可知,这种情况很难adapt,会造成模型过于相信预测的类别。

(3)解决方案

为了使得模型less confident,提出以下机制:

将?(?)函数改为?(?)′。

 {原理解释}:对于以Dirac函数分布的真实标签,我们将它变成分为两部分获得(替换)

1) 第一部分:将原本Dirac分布的标签变量替换为(1 - ϵ)的Dirac函数;

2) 第二部分:以概率 ϵ ,在?(?)u(k) 中份分布的随机变量。(在文章中,作者采用先验概率也就是均布概率,而K取值为num_class = 1000)

从而交叉熵被替换为:

可以认为:Loss 函数为分别对【预测label与真实label】【预测label与先验分布】进行惩罚。

(4)优化结果

文章表示,对K = 1000,ϵ = 0.1的优化参数,实验结果有0.2%的性能提升。

10、处理低分辨率输入图像

解决办法:减小前两层网络的stride,并且移除第一个卷积层之后的池化操作,可以在低分辨率数据集上取得和高分辨率数据集上相似的准确率。

 

四、Inception-v4, Inception-ResNet and the Impact of Residual Connections on Learning

Inception-ResNet将inception 和 残差连接在一起,inception v4无残差

由于残差连接的优越性,这里考虑将inception 和 残差连接在一起。这篇文章介绍了三个网络,inception v4无残差,inceotion resnet v1/v2均加入了残差连接。

      接下来对比一下三个网络的区别:

整体架构:

                                  inception v4                                                        inception-resnet-v1/v2
      

stem 模块:

                 inception v4/ inception- resnet -v2                                  inception-resnet-v1

  

inception 模块:

inception v4: 

                                    inception A                                         inception B                                    inception C

inception resnet v1:

                        inception A                                     inception B                                             inception C

inception-resnet-v2

                        inception A                                     inception B                                             inception C

reduction 模块:

reduction A:

reduction B:

           inception v4                                                 inception resnet v1                                             inception resnet v2

 几点说明:

(1)参数量:Inception-ResNet v1 的计算成本和 Inception v3 的接近。Inception-ResNetv2 的计算成本和 Inception v4 的接近。它们有不同的 stem,正如 Inception v4 部分所展示的。

(2)inception-residual中 1x1卷积作用:为了使残差加运算可行,卷积之后的输入和输出必须有相同的维度。因此,我们在初始卷积之后使用 1x1 卷积来匹配深度(深度在卷积之后会增加)。

(3)reduction block: Inception v4 引入了专用的「缩减块」(reduction block),它被用于改变网格的宽度和高度。

(4)如果卷积核的数量超过 1000,则网络架构更深层的残差单元将导致网络崩溃。因此,为了增加稳定性,作者通过 0.1 到 0.3 的比例缩放残差激活值。

                                                                 

(5)研究发现 Inception-ResNet 模型可以在更少的 epoch 内达到更高的准确率,结合残差连接可以显著加速 Inception 的训练。inception-resnet有v1和v2两个版本,v2表现更好且更复杂。

 

五、inception_v1基本模块代码

代码参考slim,地址为:https://github.com/PanJinquan/tensorflow_models_learning/blob/master/slim/nets/inception_v1.py

1、基本组成模块代码:

也就是下图:

 

2、整体网络结构代码如下(整体网络结构图已在上文给出)

def inception_v1_base(inputs,
                      final_endpoint='Mixed_5c',
                      scope='InceptionV1'):
  """Defines the Inception V1 base architecture.
  This architecture is defined in:
    Going deeper with convolutions
    Christian Szegedy, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed,
    Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, Andrew Rabinovich.
    http://arxiv.org/pdf/1409.4842v1.pdf.
  Args:
    inputs: a tensor of size [batch_size, height, width, channels].
    final_endpoint: specifies the endpoint to construct the network up to. It
      can be one of ['Conv2d_1a_7x7', 'MaxPool_2a_3x3', 'Conv2d_2b_1x1',
      'Conv2d_2c_3x3', 'MaxPool_3a_3x3', 'Mixed_3b', 'Mixed_3c',
      'MaxPool_4a_3x3', 'Mixed_4b', 'Mixed_4c', 'Mixed_4d', 'Mixed_4e',
      'Mixed_4f', 'MaxPool_5a_2x2', 'Mixed_5b', 'Mixed_5c']
    scope: Optional variable_scope.
  Returns:
    A dictionary from components of the network to the corresponding activation.
  Raises:
    ValueError: if final_endpoint is not set to one of the predefined values.
  """
  end_points = {}
  with tf.variable_scope(scope, 'InceptionV1', [inputs]):
    with slim.arg_scope(
        [slim.conv2d, slim.fully_connected],
        weights_initializer=trunc_normal(0.01)):
      with slim.arg_scope([slim.conv2d, slim.max_pool2d],
                          stride=1, padding='SAME'):
        end_point = 'Conv2d_1a_7x7'
        net = slim.conv2d(inputs, 64, [7, 7], stride=2, scope=end_point)
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points
        end_point = 'MaxPool_2a_3x3'
        net = slim.max_pool2d(net, [3, 3], stride=2, scope=end_point)
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points
        end_point = 'Conv2d_2b_1x1'
        net = slim.conv2d(net, 64, [1, 1], scope=end_point)
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points
        end_point = 'Conv2d_2c_3x3'
        net = slim.conv2d(net, 192, [3, 3], scope=end_point)
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points
        end_point = 'MaxPool_3a_3x3'
        net = slim.max_pool2d(net, [3, 3], stride=2, scope=end_point)
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points

        end_point = 'Mixed_3b'
        with tf.variable_scope(end_point):
          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, 96, [1, 1], scope='Conv2d_0a_1x1')
            branch_1 = slim.conv2d(branch_1, 128, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_2'):
            branch_2 = slim.conv2d(net, 16, [1, 1], scope='Conv2d_0a_1x1')
            branch_2 = slim.conv2d(branch_2, 32, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_3'):
            branch_3 = slim.max_pool2d(net, [3, 3], scope='MaxPool_0a_3x3')
            branch_3 = slim.conv2d(branch_3, 32, [1, 1], scope='Conv2d_0b_1x1')
          net = tf.concat(
              axis=3, values=[branch_0, branch_1, branch_2, branch_3])
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points

        end_point = 'Mixed_3c'
        with tf.variable_scope(end_point):
          with tf.variable_scope('Branch_0'):
            branch_0 = slim.conv2d(net, 128, [1, 1], scope='Conv2d_0a_1x1')
          with tf.variable_scope('Branch_1'):
            branch_1 = slim.conv2d(net, 128, [1, 1], scope='Conv2d_0a_1x1')
            branch_1 = slim.conv2d(branch_1, 192, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_2'):
            branch_2 = slim.conv2d(net, 32, [1, 1], scope='Conv2d_0a_1x1')
            branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_3'):
            branch_3 = slim.max_pool2d(net, [3, 3], scope='MaxPool_0a_3x3')
            branch_3 = slim.conv2d(branch_3, 64, [1, 1], scope='Conv2d_0b_1x1')
          net = tf.concat(
              axis=3, values=[branch_0, branch_1, branch_2, branch_3])
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points

        end_point = 'MaxPool_4a_3x3'
        net = slim.max_pool2d(net, [3, 3], stride=2, scope=end_point)
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points

        end_point = 'Mixed_4b'
        with tf.variable_scope(end_point):
          with tf.variable_scope('Branch_0'):
            branch_0 = slim.conv2d(net, 192, [1, 1], scope='Conv2d_0a_1x1')
          with tf.variable_scope('Branch_1'):
            branch_1 = slim.conv2d(net, 96, [1, 1], scope='Conv2d_0a_1x1')
            branch_1 = slim.conv2d(branch_1, 208, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_2'):
            branch_2 = slim.conv2d(net, 16, [1, 1], scope='Conv2d_0a_1x1')
            branch_2 = slim.conv2d(branch_2, 48, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_3'):
            branch_3 = slim.max_pool2d(net, [3, 3], scope='MaxPool_0a_3x3')
            branch_3 = slim.conv2d(branch_3, 64, [1, 1], scope='Conv2d_0b_1x1')
          net = tf.concat(
              axis=3, values=[branch_0, branch_1, branch_2, branch_3])
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points

        end_point = 'Mixed_4c'
        with tf.variable_scope(end_point):
          with tf.variable_scope('Branch_0'):
            branch_0 = slim.conv2d(net, 160, [1, 1], scope='Conv2d_0a_1x1')
          with tf.variable_scope('Branch_1'):
            branch_1 = slim.conv2d(net, 112, [1, 1], scope='Conv2d_0a_1x1')
            branch_1 = slim.conv2d(branch_1, 224, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_2'):
            branch_2 = slim.conv2d(net, 24, [1, 1], scope='Conv2d_0a_1x1')
            branch_2 = slim.conv2d(branch_2, 64, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_3'):
            branch_3 = slim.max_pool2d(net, [3, 3], scope='MaxPool_0a_3x3')
            branch_3 = slim.conv2d(branch_3, 64, [1, 1], scope='Conv2d_0b_1x1')
          net = tf.concat(
              axis=3, values=[branch_0, branch_1, branch_2, branch_3])
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points

        end_point = 'Mixed_4d'
        with tf.variable_scope(end_point):
          with tf.variable_scope('Branch_0'):
            branch_0 = slim.conv2d(net, 128, [1, 1], scope='Conv2d_0a_1x1')
          with tf.variable_scope('Branch_1'):
            branch_1 = slim.conv2d(net, 128, [1, 1], scope='Conv2d_0a_1x1')
            branch_1 = slim.conv2d(branch_1, 256, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_2'):
            branch_2 = slim.conv2d(net, 24, [1, 1], scope='Conv2d_0a_1x1')
            branch_2 = slim.conv2d(branch_2, 64, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_3'):
            branch_3 = slim.max_pool2d(net, [3, 3], scope='MaxPool_0a_3x3')
            branch_3 = slim.conv2d(branch_3, 64, [1, 1], scope='Conv2d_0b_1x1')
          net = tf.concat(
              axis=3, values=[branch_0, branch_1, branch_2, branch_3])
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points

        end_point = 'Mixed_4e'
        with tf.variable_scope(end_point):
          with tf.variable_scope('Branch_0'):
            branch_0 = slim.conv2d(net, 112, [1, 1], scope='Conv2d_0a_1x1')
          with tf.variable_scope('Branch_1'):
            branch_1 = slim.conv2d(net, 144, [1, 1], scope='Conv2d_0a_1x1')
            branch_1 = slim.conv2d(branch_1, 288, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_2'):
            branch_2 = slim.conv2d(net, 32, [1, 1], scope='Conv2d_0a_1x1')
            branch_2 = slim.conv2d(branch_2, 64, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_3'):
            branch_3 = slim.max_pool2d(net, [3, 3], scope='MaxPool_0a_3x3')
            branch_3 = slim.conv2d(branch_3, 64, [1, 1], scope='Conv2d_0b_1x1')
          net = tf.concat(
              axis=3, values=[branch_0, branch_1, branch_2, branch_3])
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points

        end_point = 'Mixed_4f'
        with tf.variable_scope(end_point):
          with tf.variable_scope('Branch_0'):
            branch_0 = slim.conv2d(net, 256, [1, 1], scope='Conv2d_0a_1x1')
          with tf.variable_scope('Branch_1'):
            branch_1 = slim.conv2d(net, 160, [1, 1], scope='Conv2d_0a_1x1')
            branch_1 = slim.conv2d(branch_1, 320, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_2'):
            branch_2 = slim.conv2d(net, 32, [1, 1], scope='Conv2d_0a_1x1')
            branch_2 = slim.conv2d(branch_2, 128, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_3'):
            branch_3 = slim.max_pool2d(net, [3, 3], scope='MaxPool_0a_3x3')
            branch_3 = slim.conv2d(branch_3, 128, [1, 1], scope='Conv2d_0b_1x1')
          net = tf.concat(
              axis=3, values=[branch_0, branch_1, branch_2, branch_3])
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points

        end_point = 'MaxPool_5a_2x2'
        net = slim.max_pool2d(net, [2, 2], stride=2, scope=end_point)
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points

        end_point = 'Mixed_5b'
        with tf.variable_scope(end_point):
          with tf.variable_scope('Branch_0'):
            branch_0 = slim.conv2d(net, 256, [1, 1], scope='Conv2d_0a_1x1')
          with tf.variable_scope('Branch_1'):
            branch_1 = slim.conv2d(net, 160, [1, 1], scope='Conv2d_0a_1x1')
            branch_1 = slim.conv2d(branch_1, 320, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_2'):
            branch_2 = slim.conv2d(net, 32, [1, 1], scope='Conv2d_0a_1x1')
            branch_2 = slim.conv2d(branch_2, 128, [3, 3], scope='Conv2d_0a_3x3')
          with tf.variable_scope('Branch_3'):
            branch_3 = slim.max_pool2d(net, [3, 3], scope='MaxPool_0a_3x3')
            branch_3 = slim.conv2d(branch_3, 128, [1, 1], scope='Conv2d_0b_1x1')
          net = tf.concat(
              axis=3, values=[branch_0, branch_1, branch_2, branch_3])
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points

        end_point = 'Mixed_5c'
        with tf.variable_scope(end_point):
          with tf.variable_scope('Branch_0'):
            branch_0 = slim.conv2d(net, 384, [1, 1], scope='Conv2d_0a_1x1')
          with tf.variable_scope('Branch_1'):
            branch_1 = slim.conv2d(net, 192, [1, 1], scope='Conv2d_0a_1x1')
            branch_1 = slim.conv2d(branch_1, 384, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_2'):
            branch_2 = slim.conv2d(net, 48, [1, 1], scope='Conv2d_0a_1x1')
            branch_2 = slim.conv2d(branch_2, 128, [3, 3], scope='Conv2d_0b_3x3')
          with tf.variable_scope('Branch_3'):
            branch_3 = slim.max_pool2d(net, [3, 3], scope='MaxPool_0a_3x3')
            branch_3 = slim.conv2d(branch_3, 128, [1, 1], scope='Conv2d_0b_1x1')
          net = tf.concat(
              axis=3, values=[branch_0, branch_1, branch_2, branch_3])
        end_points[end_point] = net
        if final_endpoint == end_point: return net, end_points
    raise ValueError('Unknown final endpoint %s' % final_endpoint)

 

 

参考文章:

https://blog.csdn.net/cdknight_happy/article/details/79247280

https://blog.csdn.net/Biyoner/article/details/89848007

 

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值