Xception学习笔记

深度可分离卷积

传统卷积是卷积核的channel = 输入feature map的channel数。传统卷积这样做可以提取到图像数据的空间信息和channel信息。如果想同时提取空间和通道信息且减小参数量,可以怎么做?这个时候就到深度可分离卷积出场了!

在这里插入图片描述

深度可分离卷积包括两个步骤:

1、 Depthwise Separable Convolutions:卷积核的数量与输入feature map的channel数一致 ,但每一个卷积核的channel都为1。各个卷积核只处理feature map二维的空间信息,而不处理跨通道的信息即单通道的卷积。如下图:
在这里插入图片描述

2、Pointwise Convolution:

前面的深度可分离卷积这样做只能提取到二维的空间信息,会丢失跨通道维度上的信息。这个时候可以再利用1x1的卷积核对Depthwise Separable Convolutions堆叠得到的feature map进行通道信息的处理,即卷积核的空间维度大小为1,channel维度与输入feature map的维度一致,专门提取跨channel维度的特征信息!这样就把这两种特征信息解耦了。如下图:

在这里插入图片描述

这样最终得到的feature map大小就可以达到传统卷积一致。

这种分步的卷积有什么好处呢?

最重要的一点是大大减少了参数量!

举个栗子:

假设输入的feature map的大小为 5x5x3,经过3x3的卷积层得到的feature map为3x3x128(忽略bias)

传统卷积参数量:3x3x3x128=3456

深度可分离卷积参数量:3x3x3 + 1x1x3x128 = 411

3456 / 3456 约等于8.4。这参数量差了8倍!

所以,这就是轻量化网络中广泛使用 Depthwise Separable Convolutions的原因!

SeparableConv Block

Xception的网络架构是由Inception_v3中的第一个Inception模块简化改进得来的。

如下图:
在这里插入图片描述

首先改pool层为1x1的卷积,去掉第三层。改为一层1x1卷积和一层3x3的卷积,进一步简化把第一层的1x1卷积改为一个,再简化,把1x1卷积的输出的feature map按通道均分喂给第二层3x3的卷积,各个branch的3x3卷积处理分配到channel数的feature map。进行极致的化简就是,把channel切分为1,用多个branch单独处理一个channel的feature map如下图:

在这里插入图片描述

参考B站up:同济子豪兄

网络整体架构

在这里插入图片描述

整体大模块分为三部分:Entry flow、Middle flow、Exit flow,且引入了残差连接。

Pytorch代码实现

Xception轻量化的关键就是定义深度可分离卷积,

沦文中是先进行pointwise convolution 再进行deepwise convolution

而**先进行pointwise convolution再进行deepwise convolution的方式更适用于需要更好特征提取能力的任务,而先进行deepwise convolution再进行pointwise convolution的方式更适用于需要减少参数数量和计算量的任务。**所以,这里实现就使用先进行deepwise convolution再进行pointwise convolution的方式!

SeparableConv

class SeperableConv2d(nn.Module):
    def __init__(self,in_channel,out_channel,bias=True):
        super(SeperableConv2d, self).__init__()
        #深度可分离卷积
        self.dconv = nn.Conv2d(in_channel,in_channel,3,1,1,dilation=1,
                               groups=in_channel,bias=bias)
        self.pconv = nn.Conv2d(in_channel,out_channel,1,bias=bias)

    def forward(self,x):
        x = self.dconv(x)
        x = self.pconv(x)
        return x

ResidualConnection

残差连接

class ResidualConnection(nn.Module):
    def __init__(self,in_channel,out_channel):
        super(ResidualConnection, self).__init__()
        self.conv1x1 = nn.Conv2d(in_channel,out_channel,1,stride=2)
        self.bn = nn.BatchNorm2d(out_channel)

    def forward(self,x):
        x = self.conv1x1(x)
        x = self.bn(x)
        return x

OK,这里实现了小模块。接下来就是三大主要的模块。

Entry flow

注意:这里的第一个seperableconv之前是没有进行relu的

class entryflow_PoolBlock(nn.Module):
    def __init__(self,in_channel,out_channel,relu1=True):
        super(entryflow_PoolBlock, self).__init__()
        self.relu1 = relu1
        self.residual = ResidualConnection(in_channel, out_channel)
        if relu1:
            self.relu1 = nn.ReLU(inplace=True)
        self.sepconv_b1 = SeperableConv2d(in_channel,out_channel)
        self.bn1 = nn.BatchNorm2d(out_channel)

        self.relu2 = nn.ReLU(out_channel)
        self.sepconv_b2 = SeperableConv2d(out_channel,out_channel)
        self.bn2 = nn.BatchNorm2d(out_channel)

        self.maxpool = nn.MaxPool2d(3,stride=2,padding=1)
    def forward(self,x):
        identity = self.residual(x)
        # 第1个Separable Conv前面没有ReLU
        if self.relu1:
            x = self.relu1(x)
        x = self.sepconv_b1(x)
        x = self.bn1(x)

        x = self.relu2(x)
        x = self.sepconv_b2(x)
        x = self.bn2(x)
        x = self.maxpool(x)
        x = x + identity
        return x

Middle flow

Middle flow比较简单,输入的空间、channel维度都不变

class middelfow_PoolBlock(nn.Module):
    def __init__(self,inchannel):
        super(middelfow_PoolBlock, self).__init__()
        self.pipeline = nn.Sequential(
            nn.ReLU(inplace=True),
            SeperableConv2d(inchannel,inchannel,bias=False),
            nn.BatchNorm2d(inchannel)
        )
    def forward(self,x):
        x = self.pipeline(x)
        x = self.pipeline(x)
        x = self.pipeline(x)
        return x

Exit flow

class exitflow_PoolBlock(nn.Module):
    def __init__(self,in_channel,out_channel):
        super(exitflow_PoolBlock, self).__init__()
        self.identity = ResidualConnection(in_channel,out_channel)

        self.relu1 = nn.ReLU(inplace=True)
        self.sepconv1 = SeperableConv2d(in_channel,in_channel,bias=False)
        self.bn1 = nn.BatchNorm2d(in_channel)

        self.relu2 = nn.ReLU(inplace=True)
        self.sepconv2 = SeperableConv2d(in_channel,out_channel,bias=False)
        self.bn2 = nn.BatchNorm2d(out_channel)

        self.maxpool = nn.MaxPool2d(3,2,1)
    def forward(self,x):
        identity = self.identity(x)
        x = self.relu1(x)
        x = self.sepconv1(x)
        x = self.bn1(x)
        x = self.relu2(x)
        x = self.sepconv2(x)
        x = self.bn2(x)
        x = self.maxpool(x)
        x = x + identity
        return x

加上最后的几层输出写在主网络的定义,这样就差不多了

主网络Xception

class Xception(nn.Module):
    def __init__(self,num_classes=5):
        super(Xception, self).__init__()
        #entry flow
        self.ef_conv1 = nn.Sequential(
            nn.Conv2d(3,32,3,2,1,bias=False),
            nn.BatchNorm2d(32),
            nn.ReLU(inplace=True)
        )
        self.ef_conv2 = nn.Sequential(
            nn.Conv2d(32,64,3,1,1,bias=False),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True)
        )
        self.entryflow_b1 = entryflow_PoolBlock(64,128,relu1=False)
        self.entryflow_b2 = entryflow_PoolBlock(128,256)
        self.entryflow_b3 = entryflow_PoolBlock(256,728)
        #middle flow
        self.middle_flow_8 = nn.ModuleList([middelfow_PoolBlock(inchannel=728)] * 8)
        #exit flow
        self.exitflow_conv1 = exitflow_PoolBlock(in_channel=728,out_channel=1024)
        self.exitflow_conv2 = nn.Sequential(
            SeperableConv2d(in_channel=1024,out_channel=1536,bias=False),
            nn.ReLU(inplace=True),
            SeperableConv2d(in_channel=1536,out_channel=2048,bias=False),
            nn.ReLU(inplace=True)
        )
        self.avgpool = nn.AdaptiveAvgPool2d(1)
        self.flatten = nn.Flatten()

        self.fc = nn.Sequential(
            nn.Linear(2048,num_classes),
            nn.ReLU(inplace=True),
            nn.Softmax(dim=1)
        )
    def forward(self,x):
        x = self.ef_conv1(x)
        x = self.ef_conv2(x)
        x = self.entryflow_b1(x)
        x = self.entryflow_b2(x)
        x = self.entryflow_b3(x)
        # Middle Flow
        for block in self.middle_flow_8:
            x = block(x)
        x = self.exitflow_conv1(x)
        x = self.exitflow_conv2(x)
        x = self.avgpool(x)
        x = self.flatten(x)
        x = self.fc(x)
        return x

总结

Xception结构的成功揭示了降低参数量也可以使得模型学习到不错的特征,揭示了深度可分离卷积的强大!Xception通过对空间信息和通道信息的充分解耦实现参数量的减少,且效果不会大幅减低!

x)
x = self.exitflow_conv2(x)
x = self.avgpool(x)
x = self.flatten(x)
x = self.fc(x)
return x




# 总结

Xception结构的成功揭示了降低参数量也可以使得模型学习到不错的特征,揭示了深度可分离卷积的强大!Xception通过对空间信息和通道信息的充分解耦实现参数量的减少,且效果不会大幅减低!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学AI不秃头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值