- GoogLeNet 致敬 LeNet
GoogLeNet V1
- Inception块,增加网络宽度,在卷积操作时可以提取不同尺度特征
- 利用1×1卷积进行降维,减少参数量
- 参数量计算:上一层通道数 × 卷积层的长度^2 × 下一层通道数
- 采用模块化结构Stage
- 最后进行全局平均池化average pooling能让输入图像大小不用固定
- 辅助分类器,为训练提供更多梯度信息
- 将网络模块化三个阶段,每个阶段内部feature map不变,结束后下降样到下个stage,这种模式在后面经常出现,如:resnet,mobilenet,shufflenet等
GoogLeNet V2
- BatchNormalization(BN):数据归一化,对每个训练的mini-batch做归一化
- BN使得模型可以使用较大的学习率而不用特别关心诸如梯度爆炸或消失等优化问题;
- 降低了模型效果对初始权重的依赖;
- 可以加速收敛,一定程度上可以不使用Dropout这种降低收敛速度的方法,但却起到了正则化作用,提高了模型泛化性;
- 即使不使用ReLU也能缓解激活函数饱和问题;
- 能够学习到从当前层到下一层的分布缩放(scaling(方差),shift(期望))系数
- 问题
- 网络训练过程中不止学习本层数据,而且要适应上层的分布,这里相当于规定了同一的输入格式,减少适应上层分布的过程。
- 解决方法:将输入值减均值除上方差,z值化。
GoogLeNet V3
- 贡献:
- 提出通用的网络结构设计准则
- 引入卷积分解提高效率(空间可分离卷积)
- 引入高效的feature map降维
- 平滑样本标注
- 准则:
- 避免特征表示上的瓶颈,尤其在神经网络的前若干层。(慎用)
- 如果在网络的浅层35×35×320被降维到17×17×320,会丢失大量信息,后面不可逆。所以对feature map进行降维的同时会对channel进行升维。
- 特征的数目越多收敛越快(不怎么流行)
- 增加1×3和3×1激活输出,产生相互解耦的特征表示。
- 合理压缩特征维度数(通道数),来减少计算量
- 用1×1的卷积先降维,再特征提取,相邻通道信息比较像
- 网络的深度和宽度需要等比例的放大和缩小
- 没有具体给出指导,后期EfficientNet(V1,V2)才填了这个坑
- “优化”辅助分类器
- 去掉了
- 优化池化操作
- 并行做卷积和池化,然后再合并
- 优化标签
- 将one hot标签平滑一点
- (1-label_smoothing)*one_hot_labels+label_smoothing/num_classes
- label_smothing = 0.1
- num_classes = 1000
- 避免特征表示上的瓶颈,尤其在神经网络的前若干层。(慎用)
GoogLeNet V4(不流行,太复杂而且没意义)
- 以结果为导向,网络解释性不强。
- 但是融合了残差网络
- 缺点:人为赋予了模型大量的先验知识
GoogLeNet V5(很简单)
- Xception
- 1×1卷积核在通道维度上进行相乘求和,3*3卷积核在空间维度上做信息处理
- 普通卷积同时在通道和空间上进行处理,普通的卷积核是三维的
- 先做通道上处理,后做空间上处理
- 解耦精度更高,计算量更小
补充:
- 如果训练的网络结果不好,不一定是网络结构不好,有可能是超参数不好。如梯度弥散
- 模型自己学习到的知识比人为赋予的归纳偏置上限更高
- 古人诚不欺我:大道至简
GoogLeNetV5论文名称:Xception: Deep learning with depthwise separable convolutions
GoogLeNetV5论文下载链接:
https://openaccess.thecvf.com/content_cvpr_2017/papers/Chollet_Xception_Deep_Learning_CVPR_2017_paper.pdf
Xception代码
import torch.nn as nn
class SeperableConv2d(nn.Module):
def __init__(self, input_channels, output_channels, kernel_size, **kwargs):
super().__init__()
self.depthwise = nn.Conv2d(
input_channels,
input_channels,
kernel_size,
groups=input_channels,
bias=False,
**kwargs
)
self.pointwise = nn.Conv2d(input_channels, output_channels, 1, bias=False)
def forward(self, x):
x = self.depthwise(x)
x = self.pointwise(x)
return x
class EntryFlow(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(3, 32, 3, padding=1, bias=False,stride=2),
nn.BatchNorm2d(32),
nn.ReLU(inplace=True)
)
self.conv2 = nn.Sequential(
nn.Conv2d(32, 64, 3, padding=1, bias=False),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True)
)
self.conv3_residual = nn.Sequential(
SeperableConv2d(64, 128, 3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
SeperableConv2d(128, 128, 3, padding=1),
nn.BatchNorm2d(128),
nn.MaxPool2d(3, stride=2, padding=1),
)
self.conv3_shortcut = nn.Sequential(
nn.Conv2d(64, 128, 1, stride=2),
nn.BatchNorm2d(128),
)
self.conv4_residual = nn.Sequential(
nn.ReLU(inplace=True),
SeperableConv2d(128, 256, 3, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(inplace=True),
SeperableConv2d(256, 256, 3, padding=1),
nn.BatchNorm2d(256),
nn.MaxPool2d(3, stride=2, padding=1)
)
self.conv4_shortcut = nn.Sequential(
nn.Conv2d(128, 256, 1, stride=2),
nn.BatchNorm2d(256),
)
#no downsampling
self.conv5_residual = nn.Sequential(
nn.ReLU(inplace=True),
SeperableConv2d(256, 728, 3, padding=1),
nn.BatchNorm2d(728),
nn.ReLU(inplace=True),
SeperableConv2d(728, 728, 3, padding=1),
nn.BatchNorm2d(728),
nn.MaxPool2d(3, 1, padding=1)
)
#no downsampling
self.conv5_shortcut = nn.Sequential(
nn.Conv2d(256, 728, 1),
nn.BatchNorm2d(728)
)
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
residual = self.conv3_residual(x)
shortcut = self.conv3_shortcut(x)
x = residual + shortcut
residual = self.conv4_residual(x)
shortcut = self.conv4_shortcut(x)
x = residual + shortcut
residual = self.conv5_residual(x)
shortcut = self.conv5_shortcut(x)
x = residual + shortcut
return x
class MiddleFLowBlock(nn.Module):
def __init__(self):
super().__init__()
self.shortcut = nn.Sequential()
self.conv1 = nn.Sequential(
nn.ReLU(inplace=True),
SeperableConv2d(728, 728, 3, padding=1),
nn.BatchNorm2d(728)
)
self.conv2 = nn.Sequential(
nn.ReLU(inplace=True),
SeperableConv2d(728, 728, 3, padding=1),
nn.BatchNorm2d(728)
)
self.conv3 = nn.Sequential(
nn.ReLU(inplace=True),
SeperableConv2d(728, 728, 3, padding=1),
nn.BatchNorm2d(728)
)
def forward(self, x):
residual = self.conv1(x)
residual = self.conv2(residual)
residual = self.conv3(residual)
shortcut = self.shortcut(x)
return shortcut + residual
class MiddleFlow(nn.Module):
def __init__(self, block):
super().__init__()
self.middel_block = self._make_flow(block, 8)
def forward(self, x):
x = self.middel_block(x)
return x
def _make_flow(self, block, times):
flows = []
for i in range(times):
flows.append(block())
return nn.Sequential(*flows)
class ExitFLow(nn.Module):
def __init__(self):
super().__init__()
self.residual = nn.Sequential(
nn.ReLU(),
SeperableConv2d(728, 728, 3, padding=1),
nn.BatchNorm2d(728),
nn.ReLU(),
SeperableConv2d(728, 1024, 3, padding=1),
nn.BatchNorm2d(1024),
nn.MaxPool2d(3, stride=2, padding=1)
)
self.shortcut = nn.Sequential(
nn.Conv2d(728, 1024, 1, stride=2),
nn.BatchNorm2d(1024)
)
self.conv = nn.Sequential(
SeperableConv2d(1024, 1536, 3, padding=1),
nn.BatchNorm2d(1536),
nn.ReLU(inplace=True),
SeperableConv2d(1536, 2048, 3, padding=1),
nn.BatchNorm2d(2048),
nn.ReLU(inplace=True)
)
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
def forward(self, x):
shortcut = self.shortcut(x)
residual = self.residual(x)
output = shortcut + residual
output = self.conv(output)
output = self.avgpool(output)
return output
class Xception(nn.Module):
def __init__(self, block, num_classes=100):
super().__init__()
self.entry_flow = EntryFlow()
self.middel_flow = MiddleFlow(block)
self.exit_flow = ExitFLow()
self.fc = nn.Linear(2048, num_classes)
def forward(self, x):
x = self.entry_flow(x)
x = self.middel_flow(x)
x = self.exit_flow(x)
x = x.view(x.size(0), -1)
x = self.fc(x)
return x
def xception(num_classes):
return Xception(MiddleFLowBlock, num_classes=num_classes)