综述
InceptionV2的核心思想来自Google的《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》[1]和《Rethinking the Inception Architecture for Computer Vision》[2]这两篇论文。它根据第一篇论文加入了BN层。根据第二篇论文用一系列更小的卷积核(3x3)替代了原来的大卷积核(5x5,7x7)[3]。
Batch Normalization
第一篇论文中提出了Internal Covariate Shift这个问题,文章中说,在训练神经网络的过程中,因为前一层的参数变化而导致每层的输入分布都在不断变化(the distribution of each layer’s inputs changes during training, as the parameters of the previous layers change.)。这使得我们需要更低的学习率和更小心地进行参数初始化,导致我们难以充分构建一个具有饱满地非线性结构的模型,而这个现象就被称作Internal Covariate Shift。
为了解决这个问题,Google提出了Batch Normalization(批规范化)[4]。即在每次SGD时,通过mini-batch来对相应的activation做归一化操作,使得结果(输出信号各个维度)的均值为0,方差为1,其具体做法如图1第三步(normalize).在Normalization完成后,Google的研究员仍对数值稳定性不放心,又加入了两个参数gamma和beta,进行了scale and shift,如图1第四步。注意到,如果我们令gamma等于之前求得的标准差,beta等于之前求得的均值,则这个变换就又将数据还原回去了。两个参数与每层的W和b一样,是需要迭代求解的[5]。
而这两个参数gamma和beta的迭代求解过程,在论文中也给了出来,也是在反向传播的过程中算损失函数对gamma和beta两个参数的导数,还要求损失函数对Wx+b中的x的导数,以便使误差继续向后传播。其具体过程如图2所示,使用了链式法则。
图2
最后,文章给出了训练一个BN网络的方法,如图3.在训练的最后一个epoch时,要对这一epoch所有的训练样本的均值和标准差进行统计,这样在一张测试图片进来时,使用训练样本中的标准差的期望和均值的期望对测试数据进行归一化,注意这里标准差使用的期望是其无偏估计,如图3第10步所示。
图3
实际上,在tensorflow的源码里,inceptionV1也已经使用了Batch Normalization,只是给了一个参数用来选择是否使用,而从inceptionV2开始去掉了这个参数,都使用BN算法了。
更小的卷积核
大尺寸的卷积核可以带来更大的感受野,但也意味着更多的参数,比如5x5卷积核参数是3x3卷积核的25/9=2.78倍。为此,作者提出可以用2个连续的3x3卷积层(stride=1)组成的小网络来代替单个的5x5卷积层,(保持感受野范围的同时又减少了参数量)[6],并且可以避免表达瓶颈,加深非线性表达能力,如图4.
同时,作者提出了两个问题并给出了回答:
- 这种替代会造成表达能力的下降吗? 后面有大量实验可以表明不会造成表达缺失;
-
3x3卷积之后还要再加激活吗? 作者也做了对比试验,表明添加非线性激活会提高性能。
这样,新的inception结构如图5所示。
其代码实现如下所示:
with tf.variable_scope(end_point):
with tf.variable_scope('Branch_0'):
branch_0 = slim.conv2d(net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
with tf.variable_scope('Branch_1'):
branch_1 = slim.conv2d(
net, depth(64), [1, 1],
weights_initializer=trunc_normal(0.09),
scope='Conv2d_0a_1x1')
branch_1 = slim.conv2d(branch_1, depth(64), [3, 3],
scope='Conv2d_0b_3x3')
with tf.variable_scope('Branch_2'):
branch_2 = slim.conv2d(
net, depth(64), [1, 1],
weights_initializer=trunc_normal(0.09),
scope='Conv2d_0a_1x1')
branch_2 = slim.conv2d(branch_2, depth(96), [3, 3],
scope='Conv2d_0b_3x3')
branch_2 = slim.conv2d(branch_2, depth(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, depth(32), [1, 1],
weights_initializer=trunc_normal(0.1),
scope='Conv2d_0b_1x1')
net = tf.concat(3, [branch_0, branch_1, branch_2, branch_3])
end_points[end_point] = net
总结
在我看来,inceptionV2更像一个过渡,它是Google的工程师们为了最大程度挖掘inception这个idea而进行的改良,它使用的Batch Normalization是对inceptionV1的一个补充,而用小的卷积核去替代大的卷积核这一点,在inceptionV3中发扬光大,实际上,《Rethinking the Inception Architecture for Computer Vision》这篇论文正是tensorflow源码中所写的incptionV3的核心论文,而这篇论文中把提出的新的网络结构称作inceptionV2(而代码实现却叫inceptionV3)。这大概也是Google的工程师和科学家们的一个小失误吧,不过从这里也可以看出inceptionV2也是对新思想的一个尝试。
Reference
[1]Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift
[2]Rethinking the Inception Architecture for Computer Vision
[3]googleNet Inception v1 - v4 papers 发展历程(CSDN)
[4]深度学习中 Batch Normalization为什么效果好?(知乎)
[5]“Batch Normalization Accelerating Deep Network Training by Reducing Internal Covariate Shift”阅读笔记与实现
[6]Inception in CNN(CSDN)
网友评论