写在前面
googlenet是由google团队在2014年在ilsvrc时提出的,并获得了多项冠军。googlenet v1[1]版本发表在CVPR2015上。由Hebbian principle 和多尺度Gabor滤波器提取特征[2]提出了inception模块。训练时通过辅助损失,模型达到了22层超过了vgg,但参数只有它的1/12,主要是减少在全连接层。
GoogleNet v1
Inception 通过多尺度卷积得到了更丰富的特征,同时在提出inception模块后,进行了改进加入了1*1卷积进行降维,减少参数和计算。
naive 版本的inception ,3*3/5*5卷积计算时参数大,池化结果参数多。所以,改进版本inception通过1*1卷积进行降维:1.3*3/5*5卷积计算前进行降维,减少计算量 2.对3*3 池化后参数进行降维 减少传递到下一层的参数量。
模型结构
模型上分为三个阶段,四个部分组成
前期
输入数据进行快速降维 ,因为多尺度卷积会增加计算量等于同一层计算多次,所以要把原图降参数。
中期
9个连续的inception 模块多尺度提取特征。辅助损失模块辅助分类进行loss反向传播,防止梯度消失。
后期
inception 模块输出的结果 -》avgpooling-》fc(分类结果)
为什么只有一层全连接层,在vgg中有三层
全连接层主要是用来做分类的,也就是将之前提取到的分布式特征映射到标签空间,vgg前面的特征提取的泛化性不是很强,相比于googlenet和resnet来说,后两者网络的深度够深,泛化性足够好,特征对于位置的依赖性不明显,vgg则需要通过多层全连接来处理非线性的问题。
GoogleNet v2
googlenet v2 [3]结构上与v1版本区别不大。主要工作引入了BN,推动了深度学习训练的发展。
为什么要引入BN?
数据的饱和阶段(数据偏向0或者1)会引起梯度消失,它不仅仅发生在数据输入阶段,它也发生在数据的训练阶段(隐藏层),什么是批标准化 (Batch Normalization)。
BN算法简述
减少均值,除以标准差,乘gamma,加beta。均值和标准差是通过指数滑动平均得来的。
1.mini - batch上计算均值
2.mini-batch上计算标准差
3.减均值除以标准差( = ,是一个极小的参数防止分母为0)
4.线性变换,乘加,实现缩放和平移(此阶段为反向工程,其中,是超参数神经网络自己学习,根据效果自动调整平移大小)
BN的效果及代码
此处引用Batch Normalization 批标准化,这里模拟全连接层与接入BN层后数据的对比,后两行是前两行经过tanh激活层后的参数变换。
相对于v1版本的改进:
1.激活函数前加入BN
2.5*5卷积替换为2个3*3卷积(vgg中提出)
3.第一个Inception 模块增加一个Inception结构
4.增加多个“5*5”卷积核
5.尺寸变化采用stride = 2 的卷积
6.增加9层(10-1层)到31层(10表示inception数量)
实验:
1.实验一:速度
参数设置:初始学习率=0.0015
图中 x5表示学习率=0.0015*5=0.0075
1.加BN更快:BN-Baseline比Inception快一倍
2.可用大学习率:BN-x5 比Inception 快14倍
3.加BN精度更高:BN-x30比x5慢,但精度更高
4.Sigmoid时,加BN精度更高
2.实验二:
GoogleNet v3
提出4个网络模型设计准则
- 非对称卷积
在vgg中为了减少大卷积参数,用多个3*3卷积替换5*5/7*7卷积达到减少参数的效果。在本文中提出非对称卷积让卷积替换贯彻到底。
- 辅助分类层
- 辅助分类层再初期起不到任何作用,再后期才能提升网络性能,所以移除第一个辅助分类层,精度不影响。
- 辅助分类层可以辅助低层提取特征(v1的观点)是不正确的
- 辅助分类层对模型起到正则的作用(v1的观点)是正确的
- googlenet-v3在17*17特征图结束接入辅助分类层
- 池化策略
池化操作,比如35*35*320进行直接池化(比如2*2 max pooling),那么就为 17*17*320,那么就会损失一部分信息造成信息表征瓶颈。传统用池化后进行卷积还原,或者先卷积再池化,那么会造成计算量太大,能不能跳过池化操作。于是提出了再卷积时候步长设置为2替代池化。
- 标签平滑(label smoothing)
Lable Smoothing是分类问题中错误标注的一种解决方法。
对于分类问题,特别是多分类问题,常常把向量转换成one-hot-vector(独热向量:比如分类结果为[0,0,0,0,0,0,0,1,0,0,0],如果概率为1,其他均为0就称为one-hot)
one-hot带来的问题:
对于损失函数,我们需要用预测概率去拟合真实概率,而拟合one-hot的真实概率函数会带来两个问题:
1)无法保证模型的泛化能力,容易造成过拟合;
2) 全概率和0概率鼓励所属类别和其他类别之间的差距尽可能加大,而由梯度有界可知,这种情况很难适应。会造成模型过于相信预测的类别。
标签平滑通过公式,将最大概率值匀给其他label,达到防止过拟合的效果。
交叉熵:
标签平滑:
,这里的为均值概率分布所以,。
改编后的交叉熵:
结构上改进
针对v1:
1.采用3个3*3卷积替换1个7*7卷积,并且在第一个卷积就采用stride=2来降低分辨率
2.第二个3个3*3卷积,在第2个卷积才下降分辨率
3.第一个block增加一个inception-module,第一个inception-module只是将5*5卷积替换成为2个3*3卷积
4.第二个block,处理17*17特征图,采用非对称卷积
5.第三个block,处理8*8特征图,提出扩展的卷积
6.最后输出2048个神经元
针对V2:
1.采用RMSProp优化方法(原本SGD)
2.采用Label Smoothing 正则化方法
3.采用非对称卷积提取17*17特征图
4.采用带BN的辅助分类层
Performance on Lower Resolution Input
低分辨率图片输入后,提高识别率的策略:
1.将第一个卷积层stride=2改为stride=1,用于151*151的图像
2.将第一个卷积层stride=2改为stride=1,移除第一个池化层,用于79*79的图像
修改思路:修改网络模型头部stride和池化,来处理低分辨率图片,可尽可能的保留原网络模型结构,不损失网络精度。
代码
由于googlenet代码是一代代迭代和升级的,所以在这里举例市场上比较常用的googlenet v3的代码
- 主干
class Inception3(nn.Module):
def __init__(self, num_classes=1000, aux_logits=True, transform_input=False):
super(Inception3, self).__init__()
self.aux_logits = aux_logits
self.transform_input = transform_input
self.Conv2d_1a_3x3 = BasicConv2d(3, 32, kernel_size=3, stride=2)
self.Conv2d_2a_3x3 = BasicConv2d(32, 32, kernel_size=3)
self.Conv2d_2b_3x3 = BasicConv2d(32, 64, kernel_size=3, padding=1)
self.Conv2d_3b_1x1 = BasicConv2d(64, 80, kernel_size=1)
self.Conv2d_4a_3x3 = BasicConv2d(80, 192, kernel_size=3)
self.Mixed_5b = InceptionA(192, pool_features=32)
self.Mixed_5c = InceptionA(256, pool_features=64)
self.Mixed_5d = InceptionA(288, pool_features=64)
self.Mixed_6a = InceptionB(288)
self.Mixed_6b = InceptionC(768, channels_7x7=128)
self.Mixed_6c = InceptionC(768, channels_7x7=160)
self.Mixed_6d = InceptionC(768, channels_7x7=160)
self.Mixed_6e = InceptionC(768, channels_7x7=192)
if aux_logits:
self.AuxLogits = InceptionAux(768, num_classes)
self.Mixed_7a = InceptionD(768)
self.Mixed_7b = InceptionE(1280)
self.Mixed_7c = InceptionE(2048)
self.fc = nn.Linear(2048, num_classes)
for m in self.modules():
if isinstance(m, nn.Conv2d) or isinstance(m, nn.Linear):
import scipy.stats as stats
stddev = m.stddev if hasattr(m, 'stddev') else 0.1
X = stats.truncnorm(-2, 2, scale=stddev)
values = torch.Tensor(X.rvs(m.weight.numel()))
values = values.view(m.weight.size())
m.weight.data.copy_(values)
elif isinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
def forward(self, x):
if self.transform_input:
x_ch0 = torch.unsqueeze(x[:, 0], 1) * (0.229 / 0.5) + (0.485 - 0.5) / 0.5
x_ch1 = torch.unsqueeze(x[:, 1], 1) * (0.224 / 0.5) + (0.456 - 0.5) / 0.5
x_ch2 = torch.unsqueeze(x[:, 2], 1) * (0.225 / 0.5) + (0.406 - 0.5) / 0.5
x = torch.cat((x_ch0, x_ch1, x_ch2), 1)
# N x 3 x 299 x 299
x = self.Conv2d_1a_3x3(x)
# N x 32 x 149 x 149
x = self.Conv2d_2a_3x3(x)
# N x 32 x 147 x 147
x = self.Conv2d_2b_3x3(x)
# N x 64 x 147 x 147
x = F.max_pool2d(x, kernel_size=3, stride=2)
# N x 64 x 73 x 73
x = self.Conv2d_3b_1x1(x)
# N x 80 x 73 x 73
x = self.Conv2d_4a_3x3(x)
# N x 192 x 71 x 71
x = F.max_pool2d(x, kernel_size=3, stride=2)
# N x 192 x 35 x 35
x = self.Mixed_5b(x)
# N x 256 x 35 x 35
x = self.Mixed_5c(x)
# N x 288 x 35 x 35
x = self.Mixed_5d(x)
# N x 288 x 35 x 35
x = self.Mixed_6a(x)
# N x 768 x 17 x 17
x = self.Mixed_6b(x)
# N x 768 x 17 x 17
x = self.Mixed_6c(x)
# N x 768 x 17 x 17
x = self.Mixed_6d(x)
# N x 768 x 17 x 17
x = self.Mixed_6e(x)
# N x 768 x 17 x 17
if self.training and self.aux_logits:
aux = self.AuxLogits(x)
# N x 768 x 17 x 17
x = self.Mixed_7a(x)
# N x 1280 x 8 x 8
x = self.Mixed_7b(x)
# N x 2048 x 8 x 8
x = self.Mixed_7c(x)
# N x 2048 x 8 x 8
# Adaptive average pooling
x = F.adaptive_avg_pool2d(x, (1, 1))
# N x 2048 x 1 x 1
x = F.dropout(x, training=self.training)
# N x 2048 x 1 x 1
x = x.view(x.size(0), -1)
# N x 2048
x = self.fc(x)
# N x 1000 (num_classes)
if self.training and self.aux_logits:
return x, aux
return x
GoogleNet V4
googlenet v4[5]有六大模块,同时把残差网络的思想引用过来。六大模块分为Stem主干,inceptionA/B/C对每个inception都进行了独特的设计(所以v4版本很复杂),reductionA/B降维度,avg pooling,droup out(0.8),softmax。
stem
inception
reduction
主要功能是降低维度
残差设计
引入resnet思想,收敛更快。
Reference:
[1] Szegedy C, Liu W, Jia Y, et al. "Going deeper with convolutions"[J]. arXiv preprint arXiv:1409.4842, 2014. googlenet v1
[2] T Serre,L Wolf,S Bileschi,M Riesenhuber,T Poggio "Robust Object Recognition with Cortex-Like Mechanisms" .2007
[3] S Ioffe,C Szegedy “Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift” .2015 v2
[4] C Szegedy,V Vanhoucke,S Ioffe,J Shlens,Z Wojna "Rethinking the Inception Architecture for Computer Vision" .2015 v3
[5] C Szegedy,S Ioffe,V Vanhoucke,A Alemi "Inception-v4, Inception-ResNet and the Impact of Residual Connections on Learning" . 2016 v4