学习《TensorFlow实战Google深度学习框架》(十)Inception-v3模型

6.4.2 Inception-v3模型

Inception-v3结构是一种和LeNet-5结构完全不同的卷积神经网络结构。在LeNet-5模型中,不同卷积层通过串联的方式连接在一起,而Inception-v模型中的Inception结构是将不同的卷积层通过并联的方式结合在一起。

Inception提出同时使用所有不同尺寸的卷积核,然后再将得到的矩阵拼接起来。
如图所示
学习《TensorFlow实战Google深度学习框架》(十)Inception-v3模型
Inceptiong模块会首先使用不同尺寸的卷积核处理输入矩阵。

图中,最上方的矩阵为使用了边长为1的卷积核的卷积层前向传播结果。

不同的矩阵代表了Inception模块中的一条计算路径。

虽然卷积核尺寸不同,但如果每次卷积都使用全0填充且步长为1,那么前向传播得到的结果矩阵的长和宽都与输入矩阵一致。

这样经过不同卷积核处理的结果矩阵可以拼接成一个更深的矩阵。

参考论文Rethinking the Inception Architecture for Computer Vision
下图给出了Inception-v3的模型架构图
学习《TensorFlow实战Google深度学习框架》(十)Inception-v3模型
Inception-v3模型总共有46层,由11个Inception模块组成。图中方框标注出来的结构就是一个Inception模块。在Inception-V3模型中有96个卷积层,如果将6.4.1的程序直接搬过来,那么一个卷积层就需要5行代码,于是总共需要480行代码来实现所有的卷积层,这样使得代码的可读性非常差。为了更好的实现类似Inception-v3模型这样的复杂卷积神经网络,在下面将先介绍TensorFlow-Slim工具来更加简洁地实现一个卷积层。以下代码对比了直接使用TensorFlow实现一个卷积层和使用TensorFlow-Slim实现同样结构的神经网络的代码量。

# 直接使用TensorFlow原始API实现卷积层
with tf.variable_scope(scope_name):
	weights = tf.get_variable("weight",...)
	biases = tf.get_variable("bias",...)
	conv = tf.nn.conv2d(...)
	relu = tf.nn.relu(tf.nn.bias_add(conv,biases))

# 使用TensorFlow-Slim实现卷积层。slim.conv2d有三个参数是必填的。第一个参数为输入节点矩阵,第二个参数是当前卷积核的个数,第三个参数是卷积核的尺寸。可选的参数有军机和移动的步长,是否使用全0填充,激活函数的选择以及变量的命名空间等。
net = slim.conv2d(input, 32, [3, 3])

TensorFlow-Slim是对TensorFlow原生API的二次封装

以下代码实现了上图方框中的Inception模块

# 加载slim库
slim = tf.contrib.slim

# slim.arg_scope函数可以用于设置默认的参数取值。slim_arg_scope函数的第一个参数是一个函数列表,
# 在这个列表中的函数将使用默认的参数取值。比如通过下面的定义,调用slim.conv2d(net, 320, [1, 1])
# 函数时会自动加上stride=1和padding='SAME'的参数。通过这种方式可以进一步减少冗余代码
with slim.arg_scope([slim.conv2d, slim.max_poole2d, slim.avg_pool2d],
                    strides=1, padding='VALID'):
    # 此处省略了Inception-V3模型其他的网络结构而直接实现最后的Inception结构。结社输入图片经过之
    # 前的神经网络前向传播结果保存在变量net中。
    net = 上一层输出节点矩阵
    # 为一个Inception模块声明一个统一的变量命名空间
    with tf.variable_scope('Mixed_7c'):
        # 给Inception模块中每一条路径声明一个命名空间
        with tf.variable_scope('Branch_0'):
            # 实现一个有320个边长为1的卷积核的卷积层
            branch_0 = slim.conv2d(net, 320, [1, 1], scope='Conv2d_0a_1x1')

        # Inception模块中第二条路径。这条计算路径上的结构本身也是一个Inception结构
        with tf.variable_scope('Branch_1'):
            branch_1 = slim.conv2d(net, 384, [1, 1],scope='Conv2d_0a_1x1')
            # tf.concat函数可以将多个矩阵拼接起来。tf.concat函数的第一个参数指定了拼接的维度,这里给出的“3”
            # 代表了矩阵是在这个深度这个维度上进行拼接。图1展示了在深度上拼接矩阵的方式。
            branch_1 = tf.concat(3, [
                # 如图2所示,此处2层卷积层的输入都是branch_1而不是net
                slim.conv2d(branch_1, 384, [1, 3], scope='Conv2d_0b_1x3'),
                slim.conv2d(branch_1, 384, [3, 1], scope='Conv2d_0c_3x1')
            ])
        # Inception模块中第三条路径。此计算路径也是一个Inception结构
        with tf.variable_scope('Branch_2'):
            branch_2 = slim.conv2d(net, 448, [1, 1], scope='Conv2d_0a_1x1')
            branch_2 = slim.conv2d(branch_2, 384, [3, 3], scope='Conv2d_0b_3x3')
            branch_2 = tf.concat(3, [
                slim.conv2d(branch_2, 384, [1, 3], scope='Conv2d_0c_1x3'),
                slim.conv2d(branch_2, 384, [3, 1], scope='Conv2d_0c_3x1')
            ])
            
        # Inception模块中第四条路径。
        with tf.variable_scope('Branch_3'):
            branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
            branch_3 = slim.conv2d(branch_3, 192, [1, 1], scope='Conv2d_0b_1x1')
            
        # 当前Inception模块的最后输出是由上面4个计算结果拼接得到的
        net = tf.concat(3, [branch_0, branch_1, branch_2, branch_3])
上一篇:【DB笔试面试389】在Oracle中,什么是绑定变量窥探?


下一篇:136. 只出现一次的数字 389. 找不同 位运算