第四周深度学习小结

MobileNets学习

MobileNetV1

网络亮点

MobileNet是由谷歌提出的用于移动端设备的网络架构,其在大幅减少参数个数的情况下可以做到尽量不影响准确度

网络结构

MobileNetV1中最为重要的部分是Depthwise Separable Convolution,其可以做到极大较少参数量
对于普通卷积而言,如下图所示,如果在步长为1,输出维度和输入相同时,对于每一个卷积核而言都有 K i ∈ N D K ∗ D K ∗ M K_i\in N^{D_K*D_K*M} KiNDKDKM,其中 i ∈ ( 0 , N ) i\in(0,N) i(0,N),在步长为1的情况下,卷积核每次移动一格,这样得到最终的计算代价为
D K ∗ D K ∗ M ∗ N ∗ D F ∗ D F D_K*D_K*M*N*D_F*D_F DKDKMNDFDF
普通卷积
接下来将传统卷积变换的一步变成两步,分别为Depthwise和PointWise,在Depthwise的情况下,对于每一个卷积核而言 K i ∈ D K ∗ D K ∗ 1 K_i\in D_K*D_K*1 KiDKDK1,其中 i ∈ ( 0 , M ) i\in(0,M) i(0,M),之后直接拼接,于是这个步骤中计算代价为 D K ∗ D K ∗ 1 ∗ M ∗ D F ∗ D F D_K*D_K*1*M*D_F*D_F DKDK1MDFDF,而后面的pointwise中,其计算代价为 1 ∗ 1 ∗ M ∗ N ∗ D F ∗ D F 1*1*M*N*D_F*D_F 11MNDFDF,故总计算带代价为 D K ∗ D K ∗ M ∗ D F ∗ D F + M ∗ N ∗ D F ∗ D F D_K*D_K*M*D_F*D_F+M*N*D_F*D_F DKDKMDFDF+MNDFDF,与传统卷积相比,比率为 D K ∗ D K ∗ M ∗ D F ∗ D F + M ∗ N ∗ D F ∗ D F D K ∗ D K ∗ M ∗ N ∗ D F ∗ D F = 1 N + 1 D K 2 \frac{D_K*D_K*M*D_F*D_F+M*N*D_F*D_F}{D_K*D_K*M*N*D_F*D_F}=\frac{1}{N}+\frac{1}{D_{K}^{2}} DKDKMNDFDFDKDKMDFDF+MNDFDF=N1+DK21
DSC卷积
最后总的卷积替换可以用原论文中的图片表示
卷积比照
在具体使用中,网络结构对比如下
在这里插入图片描述
与上文一样,将传统 3 ∗ 3 3*3 33卷积分为两个部分,其中BN层和ReLU层并不改变channel。

MobileNetV2

网络结构

MobileNetV2借用resnet结构对MobileNetV1进行改造,其中最为重要的部分为Linear Bottlenecks。对于输入图片,我们也许只需要较少的channel便可以存储其中有作用的部分(manifolds of interest),但在这种情况下使用ReLU函数便不可避免地丢失一些信息,但如果我们一开始就有很多channel,那么这其中的一些信息仍然可以在保存下来的channel中存储下去。正如原论文所展示的两点结论:

  1. If the manifold of interest remains non-zero volume after ReLU transformation, it corresponds to a linear transformation
  2. ReLU is capable of preserving complete information about the input manifold, but only if the input manifold lies in a low-dimensional subspace of the input space.
    于是可以设计出一种结构,在原MobileNetV1的基础上,首先对原输入进行pointwise卷积进行升维,之后进行depthwise卷积,最后再进行pointwise降维至想要的维度,这里如果在输入和输出维度、尺度相同的情况下可以使用resnet中的residual结构,将输入和输出相加。具体结构如下
    在这里插入图片描述

MobileNetV3

网络结构

在block中,最重要的部分是对MobileNetV2中的depthwise卷积和pointwise降维卷积之间加入SENet注意力模块,关于SENet注意力模块具体见下面的SENets。
SENet

其他部分便是对一些结构的精减,具体设计如下:
精减结构
除此以外MobileNetV3还重新设计了激活函数,如下图
activation function
swish
h-swish

SENets学习

SENet结构

SENet
在具体操作中,主要有两个部分–squeeze和excitation
squeeze部分主要是对图片每一个channel都采用pooling计算,得到一个一维tensor,例如maxpooling具体操作如下
sequence
在sequeeze之后得到一维tensor,之后便是对tensor进行excitation操作,具体操作如下:
excitation
excitation操作便是对一维tensor进行全连接,以此学习参数得到不同channel的一个比例,该数值可以看成对channel的一个重要性排序,再将比例和原图片每个channel的pixel相乘。

SENet代码

class BasicBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1):
        super(BasicBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channels)

        # shortcut的输出维度和输出不一致时,用1*1的卷积来匹配维度
        self.shortcut = nn.Sequential()
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(out_channels))

        # 在 excitation 的两个全连接
        self.fc1 = nn.Conv2d(out_channels, out_channels//16, kernel_size=1) 
        self.fc2 = nn.Conv2d(out_channels//16, out_channels, kernel_size=1)

    #定义网络结构
    def forward(self, x):
        #feature map进行两次卷积得到压缩
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))

        # Squeeze 操作:global average pooling
        w = F.avg_pool2d(out, out.size(2))
        
        # Excitation 操作: fc(压缩到16分之一)--Relu--fc(激到之前维度)--Sigmoid(保证输出为 0 至 1 之间)
        w = F.relu(self.fc1(w))
        w = F.sigmoid(self.fc2(w))

        # 重标定操作: 将卷积后的feature map与 w 相乘
        out = out * w 
        # 加上浅层特征图
        out += self.shortcut(x)
        #R elu激活
        out = F.relu(out)
        return out
class SENet(nn.Module):
    def __init__(self):
        super(SENet, self).__init__()
        #最终分类的种类数
        self.num_classes = 10
        #输入深度为64
        self.in_channels = 64

        #先使用64*3*3的卷积核
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        #卷积层的设置,BasicBlock
        #2,2,2,2为每个卷积层需要的block块数
        self.layer1 = self._make_layer(BasicBlock,  64, 2, stride=1)
        self.layer2 = self._make_layer(BasicBlock, 128, 2, stride=2)
        self.layer3 = self._make_layer(BasicBlock, 256, 2, stride=2)
        self.layer4 = self._make_layer(BasicBlock, 512, 2, stride=2)
        #全连接
        self.linear = nn.Linear(512, self.num_classes)

    #实现每一层卷积
    #blocks为大layer中的残差块数
    #定义每一个layer有几个残差块,resnet18是2,2,2,2
    def _make_layer(self, block, out_channels, blocks, stride):
        strides = [stride] + [1]*(blocks-1)
        layers = []
        for stride in strides:
            layers.append(block(self.in_channels, out_channels, stride))
            self.in_channels = out_channels
        return nn.Sequential(*layers)

    #定义网络结构
    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = self.layer4(out)
        out = F.avg_pool2d(out, 4)
        out = out.view(out.size(0), -1)
        out = self.linear(out)
        return out

HybridSN代码学习

论文学习

这里同时使用了3D卷积和2D卷积,正如原论文所说,高光谱图片是一类立体数据,其同时拥有光谱维度,如果只有2D卷积则不能从光谱维度去提取好的区分特征,而深的3D卷积计算量过大,并且只有3D卷积似乎在分类共谱带相似纹理的类别时效果并不理想。故这里同时使用3D卷积和2D卷积,如下图所示。
在这里插入图片描述

网络结构补充

class_num=16

class HybridSN(nn.Module):
  def __init__(self):
    super(HybridSN,self).__init__()
    self.conv1=nn.Conv3d(1,8,kernel_size=(7,3,3))
    self.bn1=nn.BatchNorm3d(8)
    self.conv2=nn.Conv3d(8,16,kernel_size=(5,3,3))
    self.bn2=nn.BatchNorm3d(16)
    self.conv3=nn.Conv3d(16,32,kernel_size=(3,3,3))
    self.bn3=nn.BatchNorm3d(32)
    self.conv4=nn.Conv2d(576,64,kernel_size=(3,3))
    self.bn4=nn.BatchNorm2d(64)
    self.fc1=nn.Linear(18496,256)
    self.fc2=nn.Linear(256,128)
    self.fc3=nn.Linear(128,class_num)
    self.dropout=nn.Dropout(p=0.4)
  def forward(self,x):
    x=F.relu(self.bn1(self.conv1(x)))
    x=F.relu(self.bn2(self.conv2(x)))
    x=F.relu(self.bn3(self.conv3(x)))
    x=x.reshape(x.shape[0],-1,19,19)
    x=F.relu(self.bn4(self.conv4(x)))
    x=x.reshape(x.shape[0],-1)
    x=F.relu(self.dropout(self.fc1(x)))
    x=F.relu(self.dropout(self.fc2(x)))
    x=self.fc3(x)
    return x

网络结果

accuracy
Classification Image
第二次测试
在这里插入图片描述
可以看到两次测试结果并不完全相同

问题回答

  1. 每次分类结果不同原因?
    在网络中使用了dropout方法,所以在网络训练中每次会丢弃一写结点,这会导致每次结果不同
  2. 改进方法
    在网络中可以加入各种网络模块,例如可以尝试加入group convolution甚至depthwise convolution等,也可以加入本次学习中的SENet模块,对网络模块进行改变
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值