经典论文阅读(4)-InceptionV2

经典论文阅读(4)-InceptionV2及TensorFlow实现

0. 前言

  InceptionV2是Google团队在InceptionV1的基础上做的一系列的改进,最终将ImageNet分类任务上,将错误率降低到了4.9%。在这篇文章中主要有两个改进:

  1. 引入了Batch-Normalization(批量标准化);
  2. 将原Inception模块中的 5 ∗ 5 5*5 55卷积层用两层的 3 ∗ 3 3*3 33卷积来代替。

论文:Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shif

1. 使用mini-batch进行批量标准化

  文章对于解决这个问题提出了两个必要的简化:

  1. 通过独立标准化每一个神经元的输入,避免与每层的输入与输出混在一起。【这意味着BN操作在本层的神经元激活之前】
  2. 在进行标准化的时候,均值和方差从mini-batch中产生。

基于mini-batch的标准化可以写成如下的形式:

其中,B是来自上一层m个样本的输出,将这m个输入转化为均值为0,方差为1的标准正太分布。然后再通过一个线性变换转化为y。这里有两点需要说明:
  1. $\varepsilon $是一个常数,加入到mini-batch normalization中用于维持数值稳定,避免除数为0的情况;
  2. 线性变换中, γ 、 β \gamma、\beta γβ都是需要学习的未知量。

通过上面的变换我们不难发现, x ^ i \hat x_i x^i只是出现在batch-normalization中的一个变量,最终还是会被恢复成y,但是它的出现很关键,这个操作在论文中的解释是用于修正输入的均值和方差;让隐藏层之间有稍微不同的分布,也增加了非线性效果。

于是:

输入 x i x_i xi的分布的均值和方差为: μ B \mu_B μB σ B \sigma_B σB

进行标准化之后 x ^ i \hat x_i x^i的分布均值和方差为:0和1;

进行线性变换之后y的分布均值和方差为: β \beta β γ \gamma γ

为什么将的分布归一化为均值0标准差为1后又要用和进行分布变换?

因为当均值和标准差为0和1后,给每一层一个自主学习的能力,通过自我学习调整每一层数据的均值和方差和跟参数w一样进行梯度下降更新,而由于input的x减去均值会抵消掉bias的效果,所以我们可以不用处理bias的更新了。如果 γ \gamma γ是方差, β \beta β是均值,那么相当于没有做BN的操作。在训练的过程中和控制了数据集的均值和方差,使得数据与上一层的联系降低,每一层能够比较独立的训练。

2. mini-BatchNormalization的好处

  1. 对每层都做了标准化处理,可以避免梯度爆炸和梯度消失的问题。主要见减小了梯度与参数初始化的相关性,从而可以差用较大的学习率进行训练(一般是InceptionV1的30倍)。提高了训练速度;
  2. BN有部分正则化的效果,因此可以减小对于Dropout的依赖;同时也允许使用一些饱和非线性激活函数如Sigmoid,因为BN可以将输入的分布拉回到这些饱和非线性的线性区域中。

3. 训练BN和使用BN进行推理(inference)

上一部分是训练,黄色部分是推理。在推理的时候,我们需要知道每一层的均值和方差,这里采用的是记录每一个mini-batch的计算出来的均值和方差,使用这些均值和方差估计整体的均值和方差,这里就是算法中第10步中,所描述的内容。

最后一步,将x恢复成y写成了两个部分,与 y = γ x ^ i + β y=\gamma \hat x_i + \beta y=γx^i+β一样,这样写是因为 γ V a r [ x ] + ε \frac{\gamma}{\sqrt{Var[x]+\varepsilon }} Var[x]+ε γ γ E [ x ] V a r [ x ] + ε \frac{\gamma E[x]}{\sqrt{Var[x]+\varepsilon }} Var[x]+ε γE[x]可以在训练过程中存储下来,这样可以减少运算量。

4. 文章中的另一改进-将Inception模块中的5X5卷积用两层3X3卷积来代替

改进Inception模块后,在网络中去掉了全连接层。网络结构如下所示:

文中提到的几个加速BN网络训练的方法

  1. 增大学习率;
  2. 移除Dropout;
  3. 减小损失函数中的L2正则化项5倍;
  4. 加速学习率的衰减;
  5. 移除LRN;
  6. 更加彻底的打乱训练数据;
  7. 减少光度失真。

5. InceptionV2的tensorflow实现

Inception模块做了部分改动,增加量BN:

import tflearn
from tflearn.layers.core import input_data, dropout, fully_connected
from tflearn.layers.conv import conv_2d, max_pool_2d, avg_pool_2d, batch_normalization
from tflearn.layers.estimator import regression
import tflearn.datasets.oxflower17 as oxflower17
import tensorflow as tf

def loadData():
    # 获取Oxford Flower17训练集
    X, Y = oxflower17.load_data(dirname='17flowers/', one_hot=True, resize_pics=(227, 227))
    return X, Y

def Inception_model(network, para):
    '''
    定义Inception模块
    :param network:  网络参数
    :param para:     相关参数
    :return:
    '''

    for i in range(1, para['maxlayer']+1):
        stride = 1
        if para[i][3][0] == 'max_pool': # max_pool
            stride = 2
        # branch1 : 1*1
        branch1 = conv_2d(network, para[i][0], 1, strides=stride, activation='relu')
        batch_normalization(branch1)

        # branch2: 1*1 -> 3*3
        branch2 = conv_2d(network, para[i][1][0], 1, strides=1, activation='relu')
        branch2 = conv_2d(branch2, para[i][1][1], 3, strides=stride, activation='relu')
        batch_normalization(branch2)
        # branch3: 1*1 -> 3*3 -> 3*3
        branch3 = conv_2d(network, para[i][2][0], 1, strides=1, activation='relu')
        branch3 = conv_2d(branch3, para[i][2][1], 3, strides=1, activation='relu')
        branch3 = conv_2d(branch3, para[i][2][1], 3, strides=stride, activation='relu')
        batch_normalization(branch3)
        # branch4: max_pool -> 1*1
        if para[i][3][0] == 'max_pool': # max_pool
            branch4 = max_pool_2d(network, 3, strides=2)
        else: # avg_pool
            branch4 = avg_pool_2d(network, 3, strides=1)
            branch4 = conv_2d(branch4, para[i][3][1], 1, strides=1, activation='relu')

        batch_normalization(branch4)

        # 按通道数合并, 第三个维度
        network = tf.concat([branch1, branch2, branch3, branch4], 3)

    return network

搭建InceptionV2网络结构:

def InceptionV2():
    # 定义输入层
    network = input_data(shape=[None, 227, 227, 3])

    # 定义第一层卷积层
    network = conv_2d(network, 64, 7, strides=2, activation='relu')
    batch_normalization(network)
    network = max_pool_2d(network, 3, strides=2)

    # 定义第二层卷积层
    network = conv_2d(network, 64, 1, strides=1)
    network = conv_2d(network, 192, 3, strides=1)
    batch_normalization(network)
    network = max_pool_2d(network, 3, strides=2)

    # 定义Inception3a, 3b, 3c
    para = {1: [64, [64, 64], [64, 96], ['avg_pool', 32]],  # [1*1, [1*1, 3*3], [1*1, 3*3], pool-1*1]
            2: [64, [64, 96], [64, 96], ['avg_pool', 64]],
            3: [0, [128, 160], [64, 96], ['max_pool', 'pass_through']],
            'maxlayer': 3,
            }

    network = Inception_model(network, para)

    # 定义Inception4a, 4b, 4c, 4d, 4e
    para = {1: [224, [64, 96], [96, 128], ['avg_pool', 128]],  # [1*1, [1*1, 3*3], [1*1, 3*3], pool-1*1]
            2: [192, [96, 128], [96, 128], ['avg_pool', 128]],
            3: [160, [128, 160], [128, 160], ['avg_pool', 128]],
            4: [96, [128, 192], [192, 256], ['avg_pool', 128]],
            5: [0, [128, 192], [192, 256], ['max_pool', 'pass_through']],
            'maxlayer': 5,
            }
    network = Inception_model(network, para)

    # 定义Inception5a, 5b
    para = {1: [352, [192, 320], [160, 224], ['avg_pool', 128]],  # [1*1, [1*1, 3*3], [1*1, 3*3], pool-1*1]
            2: [352, [192, 320], [192, 224], ['avg_pool', 128]],
            'maxlayer': 2,
            }
    network = Inception_model(network, para)
    batch_normalization(network)

    # 池化层
    network = avg_pool_2d(network, 7, strides=1)

    # 再添加一层全连接层
    # NOTE: 在原论文中,到上面的池化层就结束了,因为此时的输出就是1024个。这里使用
    #       的是Oxfordflower17数据集,所以还需要更改一下输出
    network = fully_connected(network, 17, activation='softmax')

    return network

定义主函数进行训练即可:

def main():
    # 加载数据
    X, Y = loadData()

    # 获取参数
    network = InceptionV2()
    network = regression(network, optimizer='momentum', learning_rate=0.03) # 默认为0.001,增大30倍

    print('-'*15, 'Start training... ', '-'*15)

    # Start training
    model = tflearn.DNN(network, checkpoint_path='model_InceptionV2/', max_checkpoints=5, tensorboard_verbose=0, tensorboard_dir='logs/')
    model.fit(X, Y, n_epoch=1, validation_set=0.1, shuffle=True, show_metric=True, batch_size=32, snapshot_step=200, snapshot_epoch=False, run_id='InceptionV2_oxflowers17')
    model.save('E:/python_project/CNN/07-CNN/model_file/InceptionV2')

if __name__ == '__main__':
    main()

整体的效果感觉并没有InceptionV1快,可能是其中的一些trick我还没加入到代码中,有待后续研究~

参考资料

【深度学习系列】用PaddlePaddle和Tensorflow实现GoogLeNet InceptionV2/V3/V4

经典分类CNN模型系列其五:Inception v2与Inception v3

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值