文章目录
- 卷积核分解
- 第一步分解,对称分解
- 第二步分解,非对称分解
- 在Inception中的改造
- 一般模型的参数节省量
- 可能导致的问题
- 针对两个辅助分类起的改造
- 特征图尺寸缩减
- Model Regularization via Label Smoothing——LSR
- 问题描述,也就是LSR解决什么问题
- 解决办法
- 网络结构
- Inception V2 & Inception V3
前一篇的Batch Normalization只是Inception V2,V3的一个前菜。真正提出Inception V2,V3的概念的是在论文Rethinking the Inception Architecture for Computer Vision中,这篇论文中综合了Batch Normalization的概念,然后提出了自己的一些改进。通过不同的组合,形成了Inception V2,V3的概念。
我们这一篇就记录了一下这篇论文提出了改进,然后记录一下什么是V2,V3与V2相比又改了点什么东西。
这篇论文主要的论点是围绕着降低计算开销和内存开销来展开的,因为是要为移动设备做准备。
卷积核分解
第一步分解,对称分解
对称分解是相对于下一步的非对称分解而言的,就是将一个n * n的卷积核分解成若干个m * m的卷积核, m < n。
论文中的标题是Factorizing Convolutions with Large Filter Size。或者简单的说就是把一个大的卷积核分解成几个小的卷积核。
这样做的好处是总体参数减少了,自然训练和推理的开销都会减少。
文中举的例子是一个5 * 5的卷积核可以分解成两个3 * 3的卷积核。
如图:
那么,参数的个数就从 5 * 5 = 25 缩减到 2 * 3 * 3 = 18。
第二步分解,非对称分解
上一步的小卷积核都是正方形的,这次使用的逻辑是将一个n * n的卷积核分解成一个1 * n的卷积核和一个 n * 1的卷积核。
如图:
在Inception中的改造
原始的Inception结构:
经过第一步改造为:
再经过第二步改造为:
如果是上面的3的话,这里的n就等于3。当然在整个GoogleLeNet中,这个n不一定是等于3。
一般模型的参数节省量
所以在文章中,对一般的网络做这个改造的参数节省量做了一个评估。
一般在CNN网络中,featrue map都是越来越是高层特征,所以尺寸是缩小的,在论文中提出了公式:
n
=
α
m
n=\alpha m
n=αm
- n应该是前一层的输出尺寸(相对于卷积层,或者一个Inception单位)
- m为后一层的输入尺寸(同上)
所以一个改造前的卷积层的参数数量为:
m
∗
w
∗
w
∗
n
=
w
2
∗
m
∗
α
m
=
α
w
2
m
2
m * w * w * n = w^2 * m * \alpha m = \alpha w^2 m^2
m∗w∗w∗n=w2∗m∗αm=αw2m2,w为卷积核的尺寸。
如果把这个
w
∗
w
w * w
w∗w的拆成了两个卷积核,那么前一层的输出和后一层的输入的尺寸相同,在数学上这个原来的端到端参数
α
\alpha
α在每一层中可以计算为
α
\sqrt{\alpha}
α
所以计算的公式就变成为:
w
∗
w
∗
α
m
∗
α
+
w
∗
w
∗
α
∗
m
w * w * \alpha m * \sqrt{\alpha} + w * w * \sqrt{\alpha} * m
w∗w∗αm∗α+w∗w∗α∗m
简化后:
w
2
α
(
α
+
1
)
m
2
w^2\sqrt{\alpha}(\alpha+1)m^2
w2α(α+1)m2
如果尺寸不变,也就是 α = 1 \alpha = 1 α=1的时候,前一层是5,后一层是3的情况下,节省的参数量就是28%。
可能导致的问题
- 是否会减少模型的表达能力(Does this replacement result in any loss of expressiveness)
- 还有个问题就是第一层卷积后面,比如第一个3 * 3后面跟什么激活函数,是线性的Sigmond还是非线性的ReLU(If our main goal is to factorize the linear part of the computation, would it not suggest to keep linear activations in the first layer?)
论文中做了一些实验,得到的结果为:
结果是使用ReLU要好一些,而且可以配合Batch Normalization一起使用。
针对两个辅助分类起的改造
论文中提到,虽然说在v1中设计了两个辅助分类器,两个辅助分类起位于网络的较前的层,在训练过程中可以更有效的利用梯度(前一篇提到了https://blog.csdn.net/pcgamer/article/details/131914842?spm=1001.2014.3001.5502)。
但是这篇文章是认为,这两个分类起没毛用。具体是为啥没说,估计是用数据做了验证吧,原话是:
Interestingly, we found that
auxiliary classifiers did not result in improved convergence early in the training: the training progression of network with and without side head looks virtually identical before both models reach high accuracy。Near the end of training, the network with the auxiliary branches starts to overtake the accuracy of the network without any auxiliary branch and reaches a slightly higher plateau.
Also used two side-heads at different stages in the
network. The removal of the lower auxiliary branch did not have any adverse effect on the final quality of the network。
这篇论文中提到,在训练过程的前期,这两个分类器没有起到什么作用,只是在快结束的时候可以提升一点准确率
另外提到的是,如果在这两个分类器前面增加一个 batch-normalization层或者dropout层更有用。
在后面提到的v2网络中的BN网络就是增加了这个Batch Normalization层。
特征图尺寸缩减
论文中的标题是Efficient Grid Size Reduction。这块简单解释下,论文中在这一小节中的描述是:Traditionally, convolutional networks used some pooling operation to decrease the grid size of the feature maps. In order to avoid a representational bottleneck, before applying maximum or average pooling the activation dimension of the network filters is expanded。
这一小段描述的是,在一般的CNN网络中,为了不损失特征,保持网络的表达能力,但是又要不断通过更小尺寸的卷积核一步一步提取高维特征,一般在卷积完之后,filter的宽度会变宽。观察一般的CNN网络,从input层开始,卷积一层之后,featrue map尺寸变小,但是channels变多,我的理解是高维特征尺寸小,但是为了不损失,需要多提取一些高维特征,也就是较多的通道数(channels)
一般的网络中,下一层featrue map的尺寸是上一层的一半(宽高都是),然后通道数翻倍。也就是文中的: starting a
d
×
d
d×d
d×d grid with k filters, if we would like to arrive at a
d
2
×
d
2
d2 × d2
d2×d2 grid with 2k filters, we first need to compute a stride-1 convolution with 2k filters and then apply an additional pooling step。
那么操作次数是
2
d
2
k
2
2d^2k^2
2d2k2。
一般来说,是在卷积之后跟一个pooling层。那么最简单的简化方法就是把这两个层换一下,因为pooling之后,grid或者说featrue map的尺寸就只有原来的四分之一了。
那么操作数就变成了
2
(
d
2
)
2
k
2
2 (\frac{d}{2})^2k^2
2(2d)2k2了。
(尺度的宽高减半,层数翻倍)
但是这有一个重要的问题是,池化层会有很多的损失,造成网络的表达能力下降,文中称作representational bottlenecks。
所以文中提出了另外一种结构:
文中的描述为:We can use two parallel stride 2
blocks: P and C. P is a pooling layer (either average or maximum pooling) the activation, both of them are stride 2 the filter banks。
简单来说,就是把卷积操作中的stride = 1变成了 stride = 2。这样会减少不少的计算量,步长大了嘛。
Model Regularization via Label Smoothing——LSR
问题描述,也就是LSR解决什么问题
这个改进就是基于交叉熵的损失函数上做的一个改造:
基于交叉熵的损失函数是(之前的文章描述过
https://blog.csdn.net/pcgamer/article/details/131713549?spm=1001.2014.3001.5501):
不过之前的文章中的p和q与论文中的恰好是反过来的,这里以论文为准。
H ( p , q ) = − ∑ k = 1 K l o g ( p ( x ) ) q ( x ) H(p,q) = -\sum_{k=1}^K{log(p(x))q(x)} H(p,q)=−k=1∑Klog(p(x))q(x)
然后在论文里,把
q
(
x
)
q(x)
q(x)这个标签的数据分布描述成一个狄拉克分布,也就是一个激活函数,因为在K个列表中只有一个1,记做
δ
k
,
y
\delta{k,y}
δk,y,后续
q
(
x
)
q(x)
q(x)就用
δ
k
,
y
\delta_{k,y}
δk,y代替。
这里存在一个问题是,在计算交叉熵和反向传播的时候,网络根据损失函数,会去拟合label的分布
q
(
x
)
q(x)
q(x)。
比如一个三分类,标签数据是(0, 1, 0),而预测出来的结果是(0.4, 0.5, 0.1)。
那么此时的损失函数计算为:
l
=
−
(
0
∗
l
o
g
(
0.4
)
+
1
∗
l
o
g
(
0.5
)
+
0
∗
l
o
g
(
0.1
)
)
≈
0.3
l=-(0 * log(0.4) + 1 * log(0.5) + 0 * log(0.1)) \approx 0.3
l=−(0∗log(0.4)+1∗log(0.5)+0∗log(0.1))≈0.3
而如果预测结果是(0.1, 0.8, 0.1)的话,损失值为:
l
=
−
(
0
∗
l
o
g
(
0.1
)
+
1
∗
l
o
g
(
0.8
)
+
0
∗
l
o
g
(
0.1
)
)
≈
0.1
l=-(0 * log(0.1) + 1 * log(0.8) + 0 * log(0.1)) \approx 0.1
l=−(0∗log(0.1)+1∗log(0.8)+0∗log(0.1))≈0.1
也就是越接近,损失越小(好像是废话,网络不就是要干这个么)。其实就是要提出一个过拟合的方法,论文中的描述是:
the model becomes too confident about its predictions。模型对预测过于自信。。。。
解决办法
论文中提出的解决办法就是增加一个超参数
ϵ
\epsilon
ϵ,然后再引入另外一个随机变量分布
u
u
u,这个
ϵ
\epsilon
ϵ就用于分配两个分布的权重。
也就是把上面计算损失函数的
q
(
x
)
q(x)
q(x)用
q
′
(
x
)
q^{'}(x)
q′(x)来代替:
q
′
(
x
)
=
(
1
−
ϵ
)
δ
k
,
y
+
ϵ
u
(
k
)
q^{'}(x)=(1-\epsilon)\delta_{k,y} + \epsilon u(k)
q′(x)=(1−ϵ)δk,y+ϵu(k)
让label的分布从一个纯粹的
δ
k
,
y
\delta_{k,y}
δk,y分布变一点点,这个引入的分布通常是一个平均分布,也就是
u
(
k
)
=
1
K
u(k) = \frac{1}{K}
u(k)=K1。上式就变成:
q
′
(
x
)
=
(
1
−
ϵ
)
δ
k
,
y
+
ϵ
K
q^{'}(x)=(1-\epsilon)\delta_{k,y} + \frac{\epsilon}{K}
q′(x)=(1−ϵ)δk,y+Kϵ
然后损失函数就变成:
H
(
q
′
,
p
)
=
−
∑
k
=
1
K
l
o
g
p
(
k
)
q
′
(
k
)
=
(
1
−
ϵ
)
H
(
q
,
p
)
+
ϵ
H
(
u
,
p
)
H(q^{'}, p)=-\sum_{k=1}^Klogp(k)q^{'}(k)=(1-\epsilon )H(q, p) + \epsilon H(u,p)
H(q′,p)=−k=1∑Klogp(k)q′(k)=(1−ϵ)H(q,p)+ϵH(u,p)
论文中的
K
K
K和
ϵ
\epsilon
ϵ取值为:In our ImageNet experiments with K = 1000 classes,
we used u(k) = 1/1000 and
ϵ
\epsilon
ϵ = 0.1.
这个方法为准确度贡献了 0.2 0.2% 0.2。
另外,论文中提到的这个动作是logit这个位置,这个位置位于softmax激活层之前。
logit的概念(借用一下):
网络结构
V2的组成如下表:
- V1中的最初的7 * 7的卷积层变成了3个 3 * 3的卷积层。
- 三个Inception部分
- 第一个部分采用的是图1-对称分解的方式组成的Inception模块,连续3个Inception模块。
- 第二部分采用的是图2-不对称分解组成的Inception模块,连续5个。在论文中关于这块描述是:In practice, we have found that employing this factorization does not work well on early layers, but it gives very good results on medium grid-sizes (On m×m feature maps, where m ranges between 12 and 20). On that level, very good results can be achieved by using 1 × 7 convolutions followed by 7 × 1 convolutions。
- 第三部分采用了一种新的方式:如图。
总共2个模块。
论文中对这种连接方式的描述是:This architecture is used on the coarsest (8 × 8) grids to promote high dimensional representations。We are using this solution only on the coarsest grid, since that is the place where producing high dimensional sparserepresentation is the most critical as the ratio of local processing
(by 1 × 1 convolutions) is increased compared to the spatial aggregation.。
讲的是这种构造只用于高层特征的featrue map中。也就是卷积了很多次的特征图中。在V2结构中,直接是用在网络的最后部分。
和图二相比,相当于就是把1 * n和n * 1的这两个动作由串行变成了并行,然后把并行卷积出来的featrue map进行连接,通道数相当于就翻倍了。应该是要利用不同尺寸的卷积进行不同尺寸特征的提取。
1 * 3和3 * 1卷积出来的尺寸不一样的问题,就通过在上一层的featrue map上做padding来补充(用的是0-padding的方法),来保证卷积出来的尺寸维持在8 * 8。
padding只针对宽的那个边进行,短的那边不需要填充。体现到代码上就是:
nn.Conv2d(8, 8, kernel_size=(1,3), padding=(0, 1))
nn.Conv2d(8, 8, kernel_size=(3,1), padding=(1, 0))
另外,论文中提到了输入大小的问题,做了 299 * 299(stride=2+m-pooling) / 151 * 151(stride=1+m-pooling) / 79 * 79(stride=1)的比较,认为对于低分辨率的图像,还是使用相对较大的感受野(就是这个输入我理解就是要去裁剪或者缩放原图。),效果要更好,同样的逻辑也可以用到R-CNN的目标检测模型中。
Inception V2 & Inception V3
论文在Experiment章节中,把上述提出的改进点,一个一个加进去来测试结果,结论如图:
也就是说Inception v2是在v1的基础上(激活层前)上加了BN层。然后再慢慢加上LSR,加上7 * 7的分解,辅助分类器加上BN等操作。
最后一行就是Inception v3版本。
最后的V3版本效果最好。