五、GoogleNet V3
论文导读
论文主题:
重新思考计算机视觉中的Inception结构
- 研究背景(回顾):
1、GoogLeNet-V1主要采用了多尺度卷积核、1x1卷积操作、辅助损失函数;
2、GoogLeNet-V2在V1的基础上加了BN层,使用小卷积核堆叠替换大卷积核;
- 研究意义:
1、总结模型设计准则,为卷积神经网络模型设计提供参考;
2、提出3个技巧,结合Inception,奠定Inception系列最常用模型——Inception-V3;
论文概要
- 4个网络结构设计准则
1、尽量避免信息瓶颈,通常发生在池化层,即特征图减小,信息量减少;
2、采用高维的表示方法能够更容易处理网络的局部信息;
3、大的卷积核可以分解为数个小卷积核,且不会降低网络能力;
4、把握好深度和宽的的平衡;
- 卷积分解
1、大卷集合分解成小卷积核堆叠
2、分解非对称卷积,1个n x n卷积可以分解成1 x n卷积和n x 1卷积堆叠
注意:非对称卷积在后半段使用效果才好,特别是特征图分辨率在12-20之间,文章在17x17的时候使用非对称卷积分解;
- 辅助分类层
V1中提出辅助分类层,用于缓解梯度消失,提升低层的特征提取能力;
新的结论:
1、辅助分类层在初期不起作用,在后期才能提升网络的性能;
2、辅助分类层可辅助低层提取特征是不正确的;
3、辅助分类层对模型起到正则的作用;
4、V3在17x17特征图结束后接入辅助分类层;
- 高效特征图下降策略
左图表示传统的池化方法,会损失特征图的信息,右图表示先将特征图增大再进行池化的过程,存在问题是计算亮过大;
解决办法:用卷积得到一半的特征图,池化得到一半的特征图,再进行拼接
注意:该Inception-module用于35x35下降到17x17和17x17下降到8x8;
- 标签平滑
传统的One-hot编码存在问题——过度自信,导致过拟合
操作:把One-hot中概率为1的那一项进行衰减,避免过度自信,衰减那部分confience平均分到每个类别中
公式推导:
- Inception-V3的主要改进点:
1、采用RMSProp优化方法;
2、采用标签平滑正则化方法;
3、采用非对称卷积提取17x17特征图;
4、采用BN的辅助分类层;
- 低分辨率图像分类策略
思路:修改模型头部stride和池化,来处理低分辨率图片,尽可能的保留原网络模型结构,不损失网络精度
论文总结
关键点:
1、非对称卷积分解:减少参数计算,为卷积结构设计提供新思路;
2、高效特征图下降策略:利用stride=2的卷积与池化,避免信息表征瓶颈;
3、标签平滑:避免网络过度自信,减轻过拟合;
启发点:
1、CNN的分类是CNN视觉任务的基础:在分类上表现好的CNN,通常在其他视觉任务中表现也好
2、GoogLe的很多论文的最优解均是通过大量实验得出,一般玩家难以复现;
3、非对称卷积分解在分辨率为12-20的特征图上效果好,且用1x7和7x1进行特征提取;
4、在网络训练初期,辅助分类层的加入并没有加快网络收敛,在训练后期,才加快网络的收敛;
5、移除两个辅助分类层的第一个,并不影响网络性能;
6、标签平滑参数设置,让非标签的概率保持在10-4左右;
论文代码
Inception-V3中有Inception六个模块,A-E以及一个辅助分类层的Aux模块;
if inception_blocks is None:
inception_blocks = [
BasicConv2d, InceptionA, InceptionB, InceptionC,
InceptionD, InceptionE, InceptionAux
]
1、去掉权重初始化,提高网络构建的速度(这是Inception-V3可优化的一点)
2、Inception-A:主要思想是用两个3x3卷积替换5x5卷积,实现有一些不同;
3、Inception-B:主要思想是上文提到的高效的特征图分辨率下降策略;
4、Inception-C、D:主要思想是非对称卷积,采用1x7和7x1的卷积核;
5、Inception-E:主要思想是拓展卷积,使用了1x3和3x1卷积核;
6、Inception-Aux:主要思想是辅助分类层,接在17x17特征图之后;
7、标签平滑后损失函数定义
- PyTorch中的CrossEntropyLoss其实包含了Softmax和交叉熵损失
- Softmax:先将每个数转换成ex的形式,在进行归一化操作;
- 交叉熵损失函数的计算:
H(q,p)代表交叉熵的计算,H(u,p)代表取log(pi)的平均值
实现代码:
class LabelSmoothingCrossEntropy(nn.Module):
def __init__(self, eps=0.001):
super(LabelSmoothingCrossEntropy, self).__init__()
self.eps = eps
def forward(self, x, target):
# CE(q, p) = - sigma(q_i * log(p_i))
log_probs = torch.nn.functional.log_softmax(x, dim=-1) # 实现log(p_i),先进行softmax再取对数
# H(q, p)
H_qp = -log_probs.gather(index=target.unsqueeze(1), dim=-1) # 只需要q_i == 1的地方,此时已经得到CE
print("index:" , target.unsqueeze(1), target.unsqueeze(1).shape,)
print("log_probs: ", log_probs, log_probs.shape)
print("H_qp: ", H_qp, H_qp.shape)
H_qp = H_qp.squeeze(1)
# H(u, p)
H_uq = -log_probs.mean() # 由于u是均匀分布,等价于求均值
loss = (1-self.eps) * H_qp + self.eps * H_uq
return loss.mean()
criterion = LabelSmoothingCrossEntropy(eps=0.001)
loss_ls = criterion(output, label)
print("CrossEntropy:{}".format(loss))
print("LableSmoothingCrossEntropy:{}".format(loss_ls))