从Inception到Xception(含网络模型pytorch代码解析)

最近相关项目用到了网络模型inceptionv3及xception,并取得了不错的效果,于是相关知识整理如下(来源于网络),并附自己对于相应网络模型的pytorch代码解析。

总共包含4个部分:inception、xception、各自代码核心结构及流程解析。

1.inception

参考:https://baijiahao.baidu.com/s?id=1601882944953788623&wfr=spider&for=pc
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. Xception:

参考:https://blog.csdn.net/MOU_IT/article/details/84945512
在这里插入图片描述
两个维度:跨通道、跨空间
在这里插入图片描述
深度可分离卷积:先逐通道处理,即处理每一个空间,然后是通道的融合。
11处理通道间融合,33或5*5处理每一个空间的融合。

在Inception v3的基础上,把Inception模块替换为深度可分离卷积,然后结合ResNet的跳跃连接,提出了Xception。
在这里插入图片描述
Inception 最初提出的版本,其核心思想就是使用多尺寸卷积核去观察输入数据。举个例子,我们看某个景象由于远近不同,同一个物体的大小也会有所不同,那么不同尺度的卷积核观察的特征就会有这样的效果。

于是我们的网络就变胖了,增加了网络的宽度,同时也提高了对于不同尺度的适应程度。

Pointwise Conv
但是我们的网络变胖了的同时,计算量也变大了,所以我们就要想办法减少参数量来减少计算量,于是在 Inception v1 中的最终版本加上了 1x1 卷积核。使用 1x1 卷积核对输入的特征图进行降维处理,这样就会极大地减少参数量,从而减少计算。

举个例子,输入数据的维度是 256 维,经过 1x1 卷积之后,我们输出的维度是 64 维,参数量是原来的 1/4 。这就是 Pointwise Convolution,俗称叫做 1x1 卷积,简写为 PW,主要用于数据降维,减少参数量。也有使用 PW 做升维的,在 MobileNet v2 中就使用 PW 将 3 个特征图变成 6 个特征图,丰富输入数据的特征。

就算有了 PW ,由于 5x5 和 7x7 卷积核直接计算参数量还是非常大,训练时间还是比较长,我们还要再优化。于是就想出了 使用多个小卷积核替代大卷积核 的方法,这就是 Inception v3。
使用两个 3x3 卷积核来代替 5x5 卷积,效果上差不多,但参数量减少很多,达到了优化的目的。不仅参数量少,层数也多了,深度也变深了。
除了规整的的正方形,我们还有分解版本的 3x3 = 3x1 + 1x3,这个效果在深度较深的情况下比规整的卷积核更好。

我们假设输入 256 维,输出 512 维,计算一下参数量:
5x5 卷积核
256∗5∗5∗512=3276800256∗5∗5∗512=3276800
两个 3x3 卷积核
256∗3∗3∗256+256∗3∗3∗512=1769472

输出维度,就是表示此层所用到的卷积核的个数,所有的卷积核都要作用在每一个输入维度上,所以是25655*512.

Bottleneck
我们发现就算用了上面的结构和方法,我们的参数量还是很大,于是乎我们结合上面的方法创造出了 Bottleneck 的结构降低参数量。Bottleneck 三步走是先 PW 对数据进行降维,再进行常规卷积核的卷积,最后 PW 对数据进行升维。我们举个例子,方便我们了解:
在这里插入图片描述
根据上图,我们来做个对比计算,假设输入 feature map 的维度为 256 维,要求输出维度也是 256 维。有以下两种操作:

直接使用 3x3 的卷积核。256 维的输入直接经过一个 3×3×256 的卷积层,输出一个 256 维的 feature map ,那么参数量为:256×3×3×256 = 589,824 。

先经过 1x1 的卷积核,再经过 3x3 卷积核,最后经过一个 1x1 卷积核。 256 维的输入先经过一个 1×1×64 的卷积层,再经过一个 3x3x64 的卷积层,最后经过 1x1x256 的卷积层,则总参数量为:256×1×1×64 + 64×3×3×64 + 64×1×1×256 = 69,632 。

经过两种方式的对比,我们可以很明显的看到后者的参数量远小于前者的。Bottleneck 的核心思想还是利用多个小卷积核替代一个大卷积核,利用 1x1 卷积核替代大的卷积核的一部分工作。

Xception大致的步骤是这样的

分别按不同通道进行一次卷积(生成 输入通道数 张 Feature Maps)- DW

再将这些 Feature Maps 一起进行第二次卷积 - PW

多个不同尺寸的卷积核,提高对不同尺度特征的适应能力。
PW 卷积,降维或升维的同时,提高网络的表达能力。

多个小尺寸卷积核替代大卷积核,加深网络的同时减少参数量。

精巧的 Bottleneck 结构,大大减少网络参数量。

精巧的 Depthwise Separable Conv 设计,再度减少参数量。

3.Pytorch中inceptionv3的网络代码结构:

在这里插入图片描述
第一层为基本卷积结构,第二层和第四层为inception结构,第三层为辅组分类器;
在这里插入图片描述
提取特征及最后的逻辑处理,注意这里如何使用辅组分类器,需要返回2个结果,分别是分类器及辅组分类器的结果。辅组分类器就是一个简单的二层卷积分类器,在后期主要是为了防止梯度消失。
在这里插入图片描述
基本的卷积结构,除了包含卷积,还包含批标准化和激活函数relu。
在这里插入图片描述
基本的inception模块如上,有33堆叠的,也有77分解为17堆叠71的,最后是一个最大池化层,最后将三个结果cat在一起进行返回。

4.Xception

如下的提取特征层及最后的逻辑输出层;
Conv1及conv2为基本的卷积层;中间包括12层的block层,最后的conv3及conv4即为其引出的核心:可分离卷积层。
在这里插入图片描述
在这里插入图片描述
先来看一下堆叠的block模块:
在这里插入图片描述
由基本的rep堆叠操作来处理,以及是否处理跳过连接skip,skip就是一层卷积:
在这里插入图片描述
最后两者直接相加即可;来看一下堆叠的rep操作:
在这里插入图片描述
同样是一些可分离卷积的堆叠;最后来看一下可分离卷积:
在这里插入图片描述
可分离卷积包含两个部分,一个是基本的33卷积(传入的kernel_size=3),这是相当于同一通道上空间上的卷积,通过Conv2d中的groups=in_channels来控制逐通道卷积;另一个就是pointwise为11卷积,这里是否执行维度的升降,主要是靠输入输出的channel决定,相当于是不同通道维度上的处理。
在这里插入图片描述
这里对于网络的解析,也只是一个基本的流程及其核心结构处理的梳理及理解,更详细完整的代码参加原pytorch代码。

  • 8
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
Xception是一种深度卷积神经网络模型,它在ImageNet数据集上取得了很好的性能。下面是一个简单的Xception模型PyTorch代码示例: ```python import torch import torch.nn as nn import torch.nn.functional as F class SeparableConv2d(nn.Module): def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, bias=False): super(SeparableConv2d, self).__init__() self.depthwise = nn.Conv2d(in_channels, in_channels, kernel_size, stride, padding, dilation, groups=in_channels, bias=bias) self.pointwise = nn.Conv2d(in_channels, out_channels, 1, 1, 0, 1, 1, bias=bias) def forward(self, x): x = self.depthwise(x) x = self.pointwise(x) return x class Block(nn.Module): def __init__(self, in_channels, out_channels, reps, stride=1, start_with_relu=True, grow_first=True): super(Block, self).__init__() if out_channels != in_channels or stride != 1: self.skip = nn.Conv2d(in_channels, out_channels, 1, stride=stride, bias=False) self.skipbn = nn.BatchNorm2d(out_channels) else: self.skip = None self.relu = nn.ReLU(inplace=True) rep = [] filters = in_channels if grow_first: rep.append(self.relu) rep.append(SeparableConv2d(in_channels, out_channels, 3, stride=1, padding=1, bias=False)) rep.append(nn.BatchNorm2d(out_channels)) filters = out_channels for i in range(reps - 1): rep.append(self.relu) rep.append(SeparableConv2d(filters, filters, 3, stride=1, padding=1, bias=False)) rep.append(nn.BatchNorm2d(filters)) if not grow_first: rep.append(self.relu) rep.append(SeparableConv2d(in_channels, out_channels, 3, stride=1, padding=1, bias=False)) rep.append(nn.BatchNorm2d(out_channels)) if stride != 1: rep.append(self.relu) rep.append(SeparableConv2d(out_channels, out_channels, 3, stride=2, padding=1, bias=False)) rep.append(nn.BatchNorm2d(out_channels)) self.rep = nn.Sequential(*rep) def forward(self, inp): x = self.rep(inp) if self.skip is not None: skip = self.skip(inp) skip = self.skipbn(skip) else: skip = inp x += skip return x class Xception(nn.Module): def __init__(self, num_classes=1000): super(Xception, self).__init__() self.conv1 = nn.Conv2d(3, 32, 3, 2, 0, bias=False) self.bn1 = nn.BatchNorm2d(32) self.relu = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(32, 64, 3, bias=False) self.bn2 = nn.BatchNorm2d(64) self.block1 = Block(64, 128, 2, 2, start_with_relu=False, grow_first=True) self.block2 = Block(128, 256, 2, 2, start_with_relu=True, grow_first=True) self.block3 = Block(256, 728, 2, 2, start_with_relu=True, grow_first=True) self.block4 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) self.block5 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) self.block6 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) self.block7 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) self.block8 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) self.block9 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) self.block10 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) self.block11 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) self.block12 = Block(728, 1024, 2, 2, start_with_relu=True, grow_first=False) self.conv3 = SeparableConv2d(1024, 1536, 3, stride=1, padding=1) self.bn3 = nn.BatchNorm2d(1536) self.conv4 = SeparableConv2d(1536, 2048, 3, stride=1, padding=1) self.bn4 = nn.BatchNorm2d(2048) self.fc = nn.Linear(2048, num_classes) def forward(self, x): x = self.conv1(x) x = self.bn1(x) x = self.relu(x) x = self.conv2(x) x = self.bn2(x) x = self.relu(x) x = self.block1(x) x = self.block2(x) x = self.block3(x) x = self.block4(x) x = self.block5(x) x = self.block6(x) x = self.block7(x) x = self.block8(x) x = self.block9(x) x = self.block10(x) x = self.block11(x) x = self.block12(x) x = self.conv3(x) x = self.bn3(x) x = self.relu(x) x = self.conv4(x) x = self.bn4(x) x = self.relu(x) x = F.adaptive_avg_pool2d(x, (1, 1)) x = torch.flatten(x, 1) x = self.fc(x) return x model = Xception() ``` 这段代码定义了一个Xception模型,包括了各个模块和层的定义。你可以根据自己的需求进行修改和使用。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值