精华内容
下载资源
问答
  • 深度可分离卷积神经网络1. 深度可分离卷积网络介绍1. 1 深度可分离卷积网络与普通卷积网络1.2 普通卷积与深度可分离卷积计算量对比2. 深度可分离卷积网络实现2.1 导入相应的库2.2 数据集的加载与处理2.3 构建模型2.4...
  • 可分离卷积深度可分离卷积详解

    千次阅读 多人点赞 2020-07-17 00:55:42
    可分离卷积 再来看一下nn.Conv2d(): torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros') 输入为(N,C_in,H,W),输出为(N,C_...
    可分离卷积

    再来看一下nn.Conv2d()

    torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')
    

    输入为(N,C_in,H,W),输出为(N,C_out,H_out,W_out).
    dilation空洞卷积来控制卷积膨胀间隔;
    groups分组卷积来控制输入和输出的连接方式,in_channels和out_channels都要被groups整除。当groups设置不同时,可以区分出分组卷积或深度可分离卷积:

    1. groups=1时,表示普通卷积层
    2. groups<in_channels时,表示普通的分组卷积
    3. groups=in_channels时,表示深度可分离卷积,每个通道都有一组自己的滤波器,大小为:

    可分离卷积

    可分离卷积包括空间可分离卷积(Spatially Separable Convolutions)和深度可分离卷积(depthwise separable convolution)。

    假设feature的size为[channel, height , width]

    • 空间也就是指:[height, width]这两维度组成的。
    • 深度也就是指:channel这一维度。

    1. 空间可分离卷积

    通俗的说,空间可分离卷积就是将nxn的卷积分成1xn和nx1两步计算。

    • 普通的3x3卷积在一个5x5的feature map上的计算方式如下图,每个位置需要9此惩罚,一共9个位置,整个操作要81次做乘法:
    * 同样的状况在空间可分离卷积中的计算方式如下图,第一步先使用3x1的filter,所需计算量为:15x3=45;第二步使用1x3的filter,所需计算量为:9x3=27;总共需要72次乘法就可以得到最终结果,要小于普通卷积的81次乘法。

    虽然空间可分离卷积可以节省计算成本,但一般情况下很少用到。所以我们后面主要以深度可分离卷积展开讲解。

    2. 深度可分离卷积:

    在Google的Xception以及MobileNet论文中都有描述。它的核心思想是将一个完整的卷积运算分解为两步进行,分别为Depthwise Convolution(逐深度卷积)与Pointwise Convolution(逐点1*1卷积)。

    高效的神经网络主要通过:1. 减少参数数量;2. 量化参数,减少每个参数占用内存
    目前的研究总结来看分为两个方向

    一是对训练好的复杂模型进行压缩得到小模型;

    二是直接设计小模型并进行训练。(Mobile Net属于这类)

    首先,我们比较下全卷积和深度可分离卷积:

    • 常规卷积:假设输入层为一个大小为64×64像素、三通道彩色图片。经过一个包含4个Filter的卷积层,最终输出4个Feature Map,且尺寸与输入层相同。我们可以计算出卷积层的参数数量是 4x3x3x3=108,参考下图:
    • 逐深度卷积(滤波):将单个滤波器应用到每一个输入通道。还用上面那个例子,这里的Filter的数量与上一层的Depth相同。所以一个三通道的图像经过运算后生成了3个Feature map,参数数量是 3x3x3=27,参考下图:
    * 逐点卷积(组合):用1*1的卷积组合不同深度卷积的输出,得到一组新的输出。卷积核的尺寸为 1×1×M,M为上一层的depth。这里的卷积运算会将上一步的map在深度方向上进行加权组合,生成新的Feature map。有几个Filter就有几个Feature map,计算参数量为 1x1x3x4=12,参考下图:
    综上,我们比对一下:常规卷积的参数个数为108;深度可分离卷积的参数个数为39,参数个数是常规卷积的约1/3。 下面我们用代码来验证一下!

    代码测试

    1. 普通卷积、深度可分离卷积对比:

    import torch.nn as nn
    import torch
    from torchsummary import summary 
    class Conv_test(nn.Module):
        def __init__(self, in_ch, out_ch, kernel_size, padding, groups):
            super(Conv_test, self).__init__()
            self.conv = nn.Conv2d(
                in_channels=in_ch,
                out_channels=out_ch,
                kernel_size=kernel_size,
                stride=1,
                padding=padding,
                groups=groups,
                bias=False
            )
    
        def forward(self, input):
            out = self.conv(input)
            return out
    
    #标准的卷积层,输入的是3x64x64,目标输出4个feature map
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    conv = Conv_test(3, 4, 3, 1, 1).to(device)
    print(summary(conv,  input_size=(3, 64, 64)))
    ----------------------------------------------------------------
            Layer (type)               Output Shape         Param #
    ================================================================
                Conv2d-1            [-1, 4, 64, 64]             108
    ================================================================
    Total params: 108
    Trainable params: 108
    Non-trainable params: 0
    ----------------------------------------------------------------
    Input size (MB): 0.05
    Forward/backward pass size (MB): 0.12
    Params size (MB): 0.00
    Estimated Total Size (MB): 0.17
    ----------------------------------------------------------------
    None
    
    # 逐深度卷积层,输入同上
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    conv = Conv_test(3, 3, 3, padding=1, groups=3).to(device)
    print(summary(conv,  input_size=(3, 64, 64)))
    ----------------------------------------------------------------
            Layer (type)               Output Shape         Param #
    ================================================================
                Conv2d-1            [-1, 3, 64, 64]              27
    ================================================================
    Total params: 27
    Trainable params: 27
    Non-trainable params: 0
    ----------------------------------------------------------------
    Input size (MB): 0.05
    Forward/backward pass size (MB): 0.09
    Params size (MB): 0.00
    Estimated Total Size (MB): 0.14
    ----------------------------------------------------------------
    None
    
    # 逐点卷积层,输入即逐深度卷积的输出大小,目标输出也是4个feature map
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    conv = Conv_test(3, 4, kernel_size=1, padding=0, groups=1).to(device)
    print(summary(conv,  input_size=(3, 64, 64)))
    ----------------------------------------------------------------
            Layer (type)               Output Shape         Param #
    ================================================================
                Conv2d-1            [-1, 4, 64, 64]              12
    ================================================================
    Total params: 12
    Trainable params: 12
    Non-trainable params: 0
    ----------------------------------------------------------------
    Input size (MB): 0.05
    Forward/backward pass size (MB): 0.12
    Params size (MB): 0.00
    Estimated Total Size (MB): 0.17
    ----------------------------------------------------------------
    None
    

    2. 分组卷积、深度可分离卷积对比:

    • 普通卷积:总参数量是 4x8x3x3=288。
    • 分组卷积:假设输入层为一个大小为64×64像素的彩色图片、in_channels=4,out_channels=8,经过2组卷积层,最终输出8个Feature Map,我们可以计算出卷积层的参数数量是 2x8x3x3=144。
    • 深度可分离卷积:逐深度卷积的卷积数量是 4x3x3=36, 逐点卷积卷积数量是 1x1x4x8=32,总参数量为68。
    #普通卷积层
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    conv = Conv_test(4, 8, 3, padding=1, groups=1).to(device)
    print(summary(conv,  input_size=(4, 64, 64)))
    ----------------------------------------------------------------
            Layer (type)               Output Shape         Param #
    ================================================================
                Conv2d-1            [-1, 8, 64, 64]             288
    ================================================================
    Total params: 288
    Trainable params: 288
    Non-trainable params: 0
    ----------------------------------------------------------------
    Input size (MB): 0.06
    Forward/backward pass size (MB): 0.25
    Params size (MB): 0.00
    Estimated Total Size (MB): 0.31
    ----------------------------------------------------------------
    None
    
    # 分组卷积层,输入的是4x64x64,目标输出8个feature map
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    conv = Conv_test(4, 8, 3, padding=1, groups=2).to(device)
    print(summary(conv,  input_size=(4, 64, 64)))
    ----------------------------------------------------------------
            Layer (type)               Output Shape         Param #
    ================================================================
                Conv2d-1            [-1, 8, 64, 64]             144
    ================================================================
    Total params: 144
    Trainable params: 144
    Non-trainable params: 0
    ----------------------------------------------------------------
    Input size (MB): 0.06
    Forward/backward pass size (MB): 0.25
    Params size (MB): 0.00
    Estimated Total Size (MB): 0.31
    ----------------------------------------------------------------
    None
    
    # 逐深度卷积层,输入同上
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    conv = Conv_test(4, 4, 3, padding=1, groups=4).to(device)
    print(summary(conv,  input_size=(4, 64, 64)))
    ----------------------------------------------------------------
            Layer (type)               Output Shape         Param #
    ================================================================
                Conv2d-1            [-1, 4, 64, 64]              36
    ================================================================
    Total params: 36
    Trainable params: 36
    Non-trainable params: 0
    ----------------------------------------------------------------
    Input size (MB): 0.06
    Forward/backward pass size (MB): 0.12
    Params size (MB): 0.00
    Estimated Total Size (MB): 0.19
    ----------------------------------------------------------------
    None
    
    # 逐点卷积层,输入即逐深度卷积的输出大小,目标输出也是8个feature map
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    conv = Conv_test(4, 8, kernel_size=1, padding=0, groups=1).to(device)
    print(summary(conv,  input_size=(4, 64, 64)))
    ----------------------------------------------------------------
            Layer (type)               Output Shape         Param #
    ================================================================
                Conv2d-1            [-1, 8, 64, 64]              32
    ================================================================
    Total params: 32
    Trainable params: 32
    Non-trainable params: 0
    ----------------------------------------------------------------
    Input size (MB): 0.06
    Forward/backward pass size (MB): 0.25
    Params size (MB): 0.00
    Estimated Total Size (MB): 0.31
    ----------------------------------------------------------------
    None
    

    3. MobileNet V1

    V1这篇文章是17年的提出的一个轻量级神经网络。一句话概括:MobileNetV1就是把VGG中的标准卷积层换成深度可分离卷积

    这种方法能用更少的参数、更少的运算,达到跟跟普通卷差不多的结果。

    3.1 MobileNetV1与普通卷积:

    大致结构对比:

    上图左边是标准卷积层,右边是V1的卷积层。V1的卷积层,首先使用3×3的深度卷积提取特征,接着是一个BN层,随后是一个 ReLU6,在之后就会逐点卷积,最后就是BN和ReLU了。

    卷积过程对比:

    输入尺寸为D_f是输入的特征高度,D_w是输入特征宽度,M是输入的channel,N是输出的channel。

    标准的卷积运算和Depthwise Separable卷积运算计算量的比例为:

    3.2 宽度因子:更薄的模型

    如果需要模型更小更快,可以定义一个宽度因子\alpha,它可以让网络的每一层都变的更薄。如果input的channel是M就变为\alpha M,如果output channel是N就变为\alpha N,那么在有宽度因子情况下的深度分离卷积运算的计算量公式就成了如下形式:

    3.3 分辨率因子:减少表达力

    分辨率因子\rho就是减少计算量的超参数,这个因子是和input的长宽相乘,会缩小input的长宽而导致后面的每一层的长宽都缩小。

    3.4 疑惑解答

    1. ReLU6
    1. 宽度因子和分辨率因子为什么没有出现在V1代码中?

      这是我在看代码时疑惑,因为github上找MobileNetV1的代码官方码是TF的,py给的都是功能块。所以参照官方TF代码可以发现代码对
      \alpha为0.75,0.5和0.25进行了封装,这样当我们调用模型来构建网络的时候,depth_multiplier就已经设置为0.75了:

      separable_conv2d(
          inputs, #size为[batch_size, height, width, channels]的tensor
          num_outputs, # 是pointwise卷积运算output的channel,如果为空,就不进行pointwise卷积运算。
          kernel_size, #是filter的size [kernel_height, kernel_width],如果filter的长宽一样可以只填入一个int值。
          depth_multiplier, #就是前面介绍过的宽度因子,在代码实现中改成了深度因子,因为是影响的channel,确实深度因子更合适。
          stride=1,
          padding='SAME',
          data_format=DATA_FORMAT_NHWC,
          rate=1,
         ...
      )
      

      关于这两个因子是怎么使用的,代码后面写的是:

    3.5 代码部分相关解释:

    torch.nn.Linear(in_features, out_features, bias=True)

    • in_features:输入特征图的大小
    • out_features:输出特征图的大小
    • bias:如果设置为False,该层不会增加偏差;默认为:True

    代码里出现了Sequential,想必我们都不陌生,都知道他是个容器,但我此前并不知道nn.module()里面都包括什么样的“容器”,下面来了解一些常用的:

    • torch.nn.Sequential(*args):用于按顺序包装一组网络层

    • torch.nn.ModuleList(modules=None):用于包装一组网络层,以迭代的方式调用网络层

    • torch.nn.ModuleDict(modules=None):用于包装一组网络层,以索引的方式调用网络层

    import torch.nn as nn
    import torch.nn.functional as F
    from torchsummary import summary 
    
    class Block(nn.Module):
        '''Depthwise conv + Pointwise conv'''
        def __init__(self, in_planes, out_planes, stride=1):
            super(Block, self).__init__()
            # 深度卷积,通道数不变,用于缩小特征图大小
            self.conv1 = nn.Conv2d(in_planes, in_planes, kernel_size=3, stride=stride, padding=1, groups=in_planes, bias=False)
            self.bn1 = nn.BatchNorm2d(in_planes)
            # 逐点卷积,用于增大通道数
            self.conv2 = nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=1, padding=0, bias=False)
            self.bn2 = nn.BatchNorm2d(out_planes)
    
        def forward(self, x):
            out = F.relu(self.bn1(self.conv1(x)))
            out = F.relu(self.bn2(self.conv2(out)))
            return out
    
    class MobileNet(nn.Module):
        cfg = [
            64, (128,2), 128, (256,2), 256, (512,2), 512, 512, 512, 512, 512, (1024,2), 1024
        ]
        def __init__(self, num_classes=10):
            super(MobileNet, self).__init__()
            # 首先是一个标准卷积
            self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=2, padding=1, bias=False)
            self.bn1 = nn.BatchNorm2d(32)
    
            # 然后堆叠深度可分离卷积
            self.layers = self._make_layers(in_planes=32)
         
            self.linear = nn.Linear(1024, num_classes) # 输入的特征图大小为1024,输出特征图大小为10
         
        def _make_layers(self, in_planes):
            laters = [] #将每层添加到此列表
            for x in self.cfg:
                out_planes = x if isinstance(x, int) else x[0] #isinstance返回的是一个布尔值
                stride = 1 if isinstance(x, int) else x[1]
                laters.append(Block(in_planes, out_planes, stride))
                in_planes = out_planes
            return nn.Sequential(*laters)
         
        def forward(self, x):
            # 一个普通卷积
            out = F.relu(self.bn1(self.conv1(x)))
            # 叠加深度可分离卷积
            out = self.layers(out)
            # 平均池化层会将feature变成1x1
            out = F.avg_pool2d(out, 7)
            # 展平
            out = out.view(out.size(0), -1)
            # 全连接层
            out = self.linear(out)
            # softmax层
            output = F.softmax(out, dim=1)
            return output
    def test():
        net = MobileNet()
        x = torch.randn(1, 3, 224, 224) # 输入一组数据,通道数为3,高度为224,宽度为224
        y = net(x)
        print(y.size())
        print(y)
        print(torch.max(y,dim=1))
    test()
    net = MobileNet()
    print(net)
    

    结果:

        torch.Size([1, 10])
        tensor([[0.0943, 0.0682, 0.1063, 0.0994, 0.1305, 0.1021, 0.0594, 0.1143, 0.1494,
                 0.0761]], grad_fn=<SoftmaxBackward>)
        torch.return_types.max(
        values=tensor([0.1494], grad_fn=<MaxBackward0>),
        indices=tensor([8]))
        MobileNet(
          (conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
          (bn1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (layers): Sequential(
            (0): Block(
              (conv1): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)
              (bn1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
              (conv2): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
              (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            )
      		......
            ......
            (12): Block(
              (conv1): Conv2d(1024, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=1024, bias=False)
              (bn1): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
              (conv2): Conv2d(1024, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
              (bn2): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            )
          )
          (linear): Linear(in_features=1024, out_features=10, bias=True)
        )
    

    4. MobileNet V2

    V2这篇文章是18年公开发布的,V2是对V1的改进,同样是一个轻量化卷积神经网络。

    4.1 V1存在的问题

    作者发现V1在训练阶段卷积核比较容易废掉(训练之后发现深度卷积训练出出来的卷积和不少是空的)。作者认为这是RELU的锅,它的结论是对低维度数据做ReLU运算,很容易造成信息丢失;但是在高维度做ReLU运算,信息丢失会降低。

    4.2 V2的改进:

    (改进的东西全写在标题上了)

    1. linear bottleneck:将最后的ReLU6换成Linear
    1. Expansion layer: 逐深度卷积部分的in_channels=out_channels如果输入的通道很少的话,逐深度卷积只能在低维度上工作,这样带来的后果ReLU会告诉你。所以要用逐点卷积(1*1卷积)来“扩张”通道。在逐深度卷积之前使用逐点卷积进行升维(升维倍数为t,t=6),再在一个更高维的空间中进行卷积操作来提取特征:
    1. Inverted residuals: 使用shortcut网络结构(作者命名为Inverted residuals)跟Resnet的网络差不多是相反的:
    • ResNet 是先降维 (0.25倍)、卷积、再升维
    • MobileNetV2 是先升维 (6倍)、卷积、再降维

    4.3 V1和V2的block对比

    看上图的(b)(d)对比:

    • (b)是v1的block,没有Shortcut并且带最后的ReLU6;

    • (d)是v2的加入了1×1升维,引入Shortcut并且去掉了最后的ReLU,改为Linear。
      (1)步长为1时,经过升维,提取特征,再降维。最后将input与output相加,形成残差结构。
      (2)步长为2时,因为input与output的尺寸不符,因此不添加shortcut结构,其余都是一样的。

    import torch
    import torch.nn as nn
    
    class Bottleneck(nn.Module):
        def __init__(self, x):
            super().__init__()
            self.cfg = x
            # 逐点卷积(先升维)
            self.conv1x1_1 = nn.Sequential(
                nn.Conv2d(self.cfg[0], self.cfg[1], kernel_size=1, padding=0, stride=1),
                nn.BatchNorm2d(self.cfg[1]),
                nn.ReLU6()
            )
            # 逐深度卷积(add1)
            self.conv3x3 = nn.Sequential(
                nn.Conv2d(self.cfg[2], self.cfg[3], kernel_size=3, padding=1, stride=self.cfg[6]),
                nn.BatchNorm2d(self.cfg[3]),
                nn.ReLU6()
            )
            # 逐点卷积
            self.conv1x1_2 = nn.Sequential(
                nn.Conv2d(self.cfg[4], self.cfg[5], kernel_size=1, padding=0, stride=1),
                nn.BatchNorm2d(self.cfg[5]),
                nn.ReLU6()
            )
        # Inverted residuals(shortcut网络)
        def forward(self, x):
            if self.cfg[7] == 1:
                residual = x
            output = self.conv1x1_1(x)
            output = self.conv3x3(output)
            output = self.conv1x1_2(output)
            if self.cfg[7] == 1: # stride=1进行相加操作
                output += residual
            return output
    
    class MobileNetV2(nn.Module):
        cfg = [
            # in-out-in-out-in-out-stride-residual
            (32, 32, 32, 32, 32, 16, 1, 0),
            (16, 96, 96, 96, 96, 24, 2, 0),
            (24, 144, 144, 144, 144, 24, 1, 1), # add1
            (24, 144, 144, 144, 144, 32, 2, 0),
            (32, 192, 192, 192, 192, 32, 1, 1), # add2
            (32, 192, 192, 192, 192, 32, 1, 1), # add3
            (32, 192, 192, 192, 192, 64, 1, 0),
            (64, 384, 384, 384, 384, 64, 1, 1), # add4
            (64, 384, 384, 384, 384, 64, 1, 1), # add5
            (64, 384, 384, 384, 384, 64, 1, 1), # add6
            (64, 384, 384, 384, 384, 96, 2, 0),
            (96, 576, 576, 576, 576, 96, 1, 1), # add7
            (96, 576, 576, 576, 576, 96, 1, 1), # add8
            (96, 576, 576, 576, 576, 160, 2, 0),
            (160, 960, 960, 960, 960, 160, 1, 1),  # add9
            (160, 960, 960, 960, 960, 160, 1, 1),  # add10
            (160, 960, 960, 960, 960, 320, 1, 0),  # add11
        ]
        def __init__(self, in_channel=3, NUM_CLASSES=10):
            super().__init__()
    
            # 首先一个普通卷积
            self.conv1 = nn.Sequential(
                nn.Conv2d(in_channel, 32, kernel_size=3, padding=1, stride=2),
                nn.BatchNorm2d(32),
                nn.ReLU6()
            )
            # 深度卷积可分离+Inverted residuals
            self.layers = self._make_layers()
            # 将逐点卷积
            self.conv2 = nn.Sequential(
                nn.Conv2d(320, 1280, kernel_size=1, padding=0, stride=1),
                nn.BatchNorm2d(1280),
                nn.ReLU6()
            )
            # 全局平均池化,将图像变成1x1大小
            self.pool = nn.AvgPool2d(kernel_size=7)
            # 最后为全连接
            self.linear = nn.Sequential(
                nn.Linear(1280, NUM_CLASSES)
            )
        def _make_layers(self):
            layers = []
            for x in self.cfg:
                layers.append(Bottleneck(x))
            return nn.Sequential(*layers)
         
        def forward(self, x):
            output = self.conv1(x)
            output = self.layers(output)
            output = self.conv2(output)
            output = self.pool(output)
            output = output.view(output.size(0), -1)
            output = self.linear(output)
            return output
    def test():
        net = MobileNetV2()
        x = torch.randn(1, 3, 224, 224) # 输入一组数据,通道数为3,高度为224,宽度为224
        y = net(x)
        print(y.size())
    
    test()
    net = MobileNetV2()
    print(net)
    

    结果:

        torch.Size([1, 10])
    

    5. 总结

    MobileNet系列

    1. MobileNet_V1

      创新点:

    • 提出了MobileNet架构,使用深度可分离卷积(depthwise separable convolutions)替代传统卷积

    • 在MobileNet网络中还引入了两个收缩超参数(shrinking hyperparameters):宽度乘子(width multiplier)和分辨率乘子(resolution multiplier)

      存在的问题:

      Depthwise Convolution存在潜在问题,训练后部分信息丢失,导致部分kernel的权值为0。

    1. MobileNet_V2

      创新点:

    • 修改最后一层RELU6,引入Linear Bottleneck

    • 引入Inverted Residual Blocks,想像Resnet一样复用我们的特征,所以我们引入了shortcut结构

      存在的问题:

      MobileNetV2 网络端部最后阶段的计算量很大

    1. MobileNetV3已经在2019年发布了,用神经结构搜索(NAS)

    创新点:

    • 针对V2的问题,提出了新的损失swish x能够有效的改进网络精度:

    由于计算量,对它做的改进:

    关于激活函数Swish:[https://blog.csdn.net/qq_36330643/article/details/78474141]

    展开全文
  • 深度可分离卷积

    万次阅读 多人点赞 2019-06-18 10:11:38
    原文:... 下面这个文章介绍了深度可分离卷积是怎么做的: https://towardsdatascience.com/a-basic-introduction-to-separable-convolutions-b99ec3102728 https://eli.thegreenplace....

    原文:https://blog.csdn.net/makefish/article/details/88716534

    下面这个文章介绍了深度可分离卷积是怎么做的:
    https://towardsdatascience.com/a-basic-introduction-to-separable-convolutions-b99ec3102728
    https://eli.thegreenplace.net/2018/depthwise-separable-convolutions-for-machine-learning/
    本文的很多内容都是在这两个文章的基础上整理的。

    卷积基础
    描述一个二维矩阵,使用row col。三维的,使用channel row col。四维则多了一个参数:batch channel row col。batch channel row col的逻辑顺序则和数据格式有关,常见的有NHWC和NCHW:https://mp.weixin.qq.com/s/I4Q1Bv7yecqYXUra49o7tw?

    2D卷积
    2D卷积只有col row的概念。(略)

    3D卷积和4D卷积
    我们先看3D卷积。
    假设过滤器窗口是3x3x3(其中一个3代表了in_depth)。有四个这样的窗口,用于提取同一个图片的四个属性(out_depth指定,对应输出Out channel 0…3)。那么,针对图片里面的某个Batch(譬如Batch 0),其处理流程如下:

    真正的源码实现,i,j代表了输出的某个属性的任意位置的值。这个值,是窗口和输入卷积得来的。

    参考的源代码(Copy 自引文):

    def conv2d_multi_channel(input, w):
        """Two-dimensional convolution with multiple channels.
    
        Uses SAME padding with 0s, a stride of 1 and no dilation.
    
        input: input array with shape (height, width, in_depth)
        w: filter array with shape (fd, fd, in_depth, out_depth) with odd fd.
           in_depth is the number of input channels, and has the be the same as
           input's in_depth; out_depth is the number of output channels.
    
        Returns a result with shape (height, width, out_depth).
        """
        assert w.shape[0] == w.shape[1] and w.shape[0] % 2 == 1
    
        padw = w.shape[0] // 2
        padded_input = np.pad(input,
                              pad_width=((padw, padw), (padw, padw), (0, 0)),
                              mode='constant',
                              constant_values=0)
    
        height, width, in_depth = input.shape
        assert in_depth == w.shape[2]
        out_depth = w.shape[3]
        output = np.zeros((height, width, out_depth))
    
        for out_c in range(out_depth):
            # For each output channel, perform 2d convolution summed across all
            # input channels.
            for i in range(height):
                for j in range(width):
                    # Now the inner loop also works across all input channels.
                    for c in range(in_depth):
                        #下面这段应该封装为一个新的函数:用于求解输出的某个属性的卷积。
                        for fi in range(w.shape[0]):
                            for fj in range(w.shape[1]):
                                w_element = w[fi, fj, c, out_c]
                                output[i, j, out_c] += (
                                    padded_input[i + fi, j + fj, c] * w_element)
        return output
    


    所谓4D,就是对每个Batch重复上面的过程。

    参考文献:
    https://eli.thegreenplace.net/2018/depthwise-separable-convolutions-for-machine-learning/

    正常卷积
    原始图像是二维的,大小是12x12。由于是RGB格式的,所以有三个通道,这相当于是一个3维的图片。其输入图片格式是:12x12x3。滤波器窗口大小是5x5x3。这样的话,得到的输出图像大小是8x8x1(padding模式是valid)。

    12x12x3 * 5x5x3 => 8x8x1

    一个5x5x3滤波器得到的输出图像8x8x1,仅仅提取到的图片里面的一个属性。如果希望获取图片更多的属性,譬如要提取256个属性,则需要:

    12x12x3 * 5x5x3x256 => 8x8x256

    如下图(图片引用自原网站。感觉应该将8x8x256那个立方体绘制成256个8x8x1,因为他们不是一体的,代表了256个属性):

    正常卷积的问题在于,它的卷积核是针对图片的所有通道设计的(通道的总数就是depth)。那么,每要求增加检测图片的一个属性,卷积核就要增加一个。所以正常卷积,卷积参数的总数=属性的总数x卷积核的大小

    深度可分离卷积
    深度可分离卷积的方法有所不同。正常卷积核是对3个通道同时做卷积。也就是说,3个通道,在一次卷积后,输出一个数。
    深度可分离卷积分为两步:

    第一步用三个卷积对三个通道分别做卷积,这样在一次卷积后,输出3个数。
    这输出的三个数,再通过一个1x1x3的卷积核(pointwise核),得到一个数。
    所以深度可分离卷积其实是通过两次卷积实现的。

    第一步,对三个通道分别做卷积,输出三个通道的属性:

    第二步,用卷积核1x1x3对三个通道再次做卷积,这个时候的输出就和正常卷积一样,是8x8x1:


    如果要提取更多的属性,则需要设计更多的1x1x3卷积核心就可以(图片引用自原网站。感觉应该将8x8x256那个立方体绘制成256个8x8x1,因为他们不是一体的,代表了256个属性):

    可以看到,如果仅仅是提取一个属性,深度可分离卷积的方法,不如正常卷积。随着要提取的属性越来越多,深度可分离卷积就能够节省更多的参数。

    证明过程
    有一篇文章证明了深度可分离卷积和正常卷积是等效的(如果有需要的话,我再整理):
    https://arxiv.org/abs/1808.05517
    https://arxiv.org/abs/1610.02357

    参数的选择过程

    展开全文
  • [深度学习] 深度可分离卷积

    千次阅读 2019-12-04 12:11:12
    深度可分离卷积 深度可分离卷积之所以如此命名,是因为它不仅处理空间维度,还处理深度维度-通道的数量。通常输入图像可以有3个通道:RGB。经过几次卷积后,一个图像可能有多个通道。你可以把每个频道想象成那个...

    一 深度可分离卷积

    深度可分离卷积之所以如此命名,是因为它不仅处理空间维度,还处理深度维度-通道的数量。通常输入图像可以有3个通道:RGB。经过几次卷积后,一个图像可能有多个通道。你可以把每个频道想象成那个图像的一个特殊解释;例如,“红色”通道解释每个像素的“红色”,“蓝色”通道解释每个像素的“蓝色”,“绿色”通道解释每个像素的“绿色”。一个有64个频道的图像有64种不同的解释。

    深度可分离卷积将一个核分裂成两个独立的核,分别做两个卷积:深度卷积和点向卷积。

    标准的卷积过程可以看上图,一个5×5的卷积核在卷积时,对应图像区域中的所有通道均被同时考虑,问题在于,为什么一定要同时考虑图像区域和通道?我们为什么不能把通道和空间区域分开考虑?

    深度可分离卷积提出了一种新的思路:对于不同的输入channel采取不同的卷积核进行卷积,它将普通的卷积操作分解为两个过程。

     卷积过程

    假设有 的输入,同时有 k个 3x3的卷积。如果设置 padding=1且 stride=1,那么普通卷积输出为  。

    Depthwise 过程

    Depthwise是指将 的输入分为 C 组,然后每一组做 3x3卷积。这样相当于收集了每个Channel的空间特征,即Depthwise特征。   输出

    Pointwise 过程

    Pointwise是指对 的输入(上面的输出)做 k个普通的1x1卷积。这样相当于收集了每个点的特征,即Pointwise特征。Depthwise+Pointwise最终输出也是 .

     

    二 实际例子

    例子1

    常规卷积操作

    对于一张5×5像素、三通道(shape为5×5×3),经过3×3卷积核的卷积层(假设输出通道数为4,则卷积核shape为3×3×3×4,最终输出4个Feature Map,如果有same padding则尺寸与输入层相同(5×5),如果没有则为尺寸变为3×3

    卷积层共4个Filter,每个Filter包含了3个Kernel,每个Kernel的大小为3×3。因此卷积层的参数数量可以用如下公式来计算:

    N_std = 4 × 3 × 3 × 3 = 108

     

    第1部分-深度卷积

    Depthwise Convolution的一个卷积核负责一个通道,一个通道只被一个卷积核卷积

    一张5×5像素、三通道彩色输入图片(shape为5×5×3),Depthwise Convolution首先经过第一次卷积运算,DW完全是在二维平面内进行。卷积核的数量与上一层的通道数相同(通道和卷积核一一对应)。所以一个三通道的图像经过运算后生成了3个Feature map(如果有same padding则尺寸与输入层相同为5×5),如下图所示。

    其中一个Filter只包含一个大小为3×3的Kernel,卷积部分的参数个数计算如下:

    N_depthwise = 3 × 3 × 3 = 27

    Depthwise Convolution完成后的Feature map数量与输入层的通道数相同,无法扩展Feature map。而且这种运算对输入层的每个通道独立进行卷积运算,没有有效的利用不同通道在相同空间位置上的feature信息。因此需要Pointwise Convolution来将这些Feature map进行组合生成新的Feature map

    第2部分-逐点卷积

    Pointwise Convolution的运算与常规卷积运算非常相似,它的卷积核的尺寸为 1×1×M,M为上一层的通道数。所以这里的卷积运算会将上一步的map在深度方向上进行加权组合,生成新的Feature map。有几个卷积核就有几个输出Feature map

    由于采用的是1×1卷积的方式,此步中卷积涉及到的参数个数可以计算为:

    N_pointwise = 1 × 1 × 3 × 4 = 12

    经过Pointwise Convolution之后,同样输出了4张Feature map,与常规卷积的输出维度相同

    参数对比

    回顾一下,常规卷积的参数个数为:
    N_std = 4 × 3 × 3 × 3 = 108

    Separable Convolution的参数由两部分相加得到:
    N_depthwise = 3 × 3 × 3 = 27
    N_pointwise = 1 × 1 × 3 × 4 = 12
    N_separable = N_depthwise + N_pointwise = 39

    相同的输入,同样是得到4张Feature map,Separable Convolution的参数个数是常规卷积的约1/3。因此,在参数量相同的前提下,采用Separable Convolution的神经网络层数可以做的更深。

     

     

    例子2

    常规普通卷积

    一个典型的图像不是二维的;它也有深度,宽度和高度。假设我们有一个12x12x3像素的输入图像,大小为12×12的RGB图像。

    我们对图像做一个5×5的卷积,不加填充,步幅为1。如果我们只考虑图像的宽度和高度,卷积过程是这样的:12×12 – (5×5) – >8×8。5×5内核每25个像素进行标量乘法,每次输出一个数字。由于没有填充(12-5 +1 = 8),我们最终得到一个8×8像素的图像。

    然而,由于图像有3个通道,卷积核也需要有3个通道。这意味着,不是做5×5=25次乘法,而是每次内核移动时做5x5x3=75次乘法。

    就像二维解释一样,我们对每25个像素做标量矩阵乘法,输出一个数字。在经过5x5x3内核之后,12x12x3图像将变成8x8x1图像。

    可分离卷积基本介绍

    如果我们想增加输出图像中的通道数量呢?如果我们想要大小为8x8x256的输出呢?

    我们可以创建256个内核来创建256个8x8x1图像,然后将它们堆叠在一起,创建出8x8x256图像输出。

    可分离卷积基本介绍

    这就是正常卷积的工作原理。

    我喜欢把它看做一个函数:12x12x3-(5x5x3x256)->8x8x256(其中5x5x3x256表示内核的高度、宽度、输入通道数和输出通道数)。并不是说这不是矩阵乘法;我们不是将整个图像乘以内核,而是将内核移动到图像的每个部分,并分别乘以图像的小部分。

    深度可分离卷积将这个过程分为两部分:深度卷积和点向卷积。

     

    第1部分-深度卷积:

    不使用padding, 并使用same

    在第一部分深度卷积中,我们在不改变深度的情况下,对输入图像进行了反方向卷积。我们使用3个5x5x1形状的内核。

    可以参考一下视频:https://youtu.be/D_VJoaSew7Q

    可分离卷积基本介绍

    每个5x5x1内核迭代图像的1个通道(注意:1个通道,而不是所有通道),得到每25个像素组的标量积,得到一个8x8x1图像。将这些图像叠加在一起可创建8x8x3图像。

     

    第2部分-点向卷积:

    记住,原始卷积将12x12x3图像转换为8x8x256图像。目前,深度卷积已经将12x12x3图像转换为8x8x3图像。现在,我们需要增加每个图像的通道数。

    点向卷积之所以如此命名是因为它使用了一个1×1内核,或者说是一个遍历每个点的内核。该内核有一个深度,不管输入图像有多少通道;在我们的例子中,它是3。因此,我们通过8x8x3图像迭代1x1x3内核,得到8x8x1图像。

    可分离卷积基本介绍

    我们可以创建256个1x1x3内核,每个内核输出一个8x8x1图像,以得到形状为8x8x256的最终图像。

    可分离卷积基本介绍

    就这样!我们把卷积分解成2:深度卷积和点向卷积。

    更抽象地说,如果原始卷积函数是12x12x3 – (5x5x3x256)→8x8x256,

    我们可以将这个新的卷积表示为12x12x3 – (5x5x1x3) →8x8x3 – > (1x1x3x256) – >8x8x256。

     

    三 深度可分离卷积优势与创新

    我们来计算一下计算机在原始卷积中要做的乘法的个数。有256个5x5x3内核可以移动8×8次。这是256x5x5x3x8x8 = 1228800 次乘法。

    可分离卷积呢? 在深度卷积中,我们有3个5x5x1的内核它们移动了8×8次。也就是3x5x5x1x8x8 = 4800。在点向卷积中,我们有256个1x1x3的内核它们移动了8×8次。这是256x1x1x3x8x8 = 49152。把它们加起来,就是53952次乘法。

    52,952比1,228,800小很多。计算量越少,网络就能在更短的时间内处理更多的数据。

    然而,这是如何实现的呢? 我第一次遇到这种解释时,我的直觉并没有真正理解它。这两个卷积不是做同样的事情吗?在这两种情况下,我们都通过一个5×5内核传递图像,将其缩小到一个通道,然后将其扩展到256个通道。为什么一个的速度是另一个的两倍多?

    经过一段时间的思考,我意识到主要的区别是:在普通卷积中,我们对图像进行了256次变换。每个变换都要用到5x5x3x8x8=4800次乘法。在可分离卷积中,我们只对图像做一次变换——在深度卷积中。然后,我们将转换后的图像简单地延长到256通道。不需要一遍又一遍地变换图像,我们可以节省计算能力。

    值得注意的是,在Keras和Tensorflow中,都有一个称为“深度乘数”的参数。默认设置为1。通过改变这个参数,我们可以改变深度卷积中输出通道的数量。例如,如果我们将深度乘法器设置为2,每个5x5x1内核将输出8x8x2的图像,使深度卷积的总输出(堆叠)为8x8x6,而不是8x8x3。有些人可能会选择手动设置深度乘法器来增加神经网络中的参数数量,以便更好地学习更多的特征。

    深度可分离卷积是不是很棒?当然!因为它减少了卷积中参数的数量,如果你的网络已经很小,你可能会得到很少的参数,你的网络可能无法在训练中正确运作。然而,如果使用得当,它可以在适当的情况下提高效率,这使得它成为一个非常受欢迎的选择。

     

    1×1 Kernels:

    最后,由于点向卷积使用了这个概念,我想讨论一下1×1内核的用法。

    一个1×1内核——或者更确切地说,n个1x1xm内核,其中n是输出通道的数量,m是输入通道的数量——可以在可分离卷积之外使用。1×1内核的一个明显目的是增加或减少图像的深度。如果你发现卷积有太多或太少的通道,1×1核可以帮助平衡它。

    然而,对我来说,1×1核的主要目的是应用非线性。在神经网络的每一层之后,我们都可以应用一个激活层。无论是ReLU、PReLU、Softmax还是其他,与卷积层不同,激活层是非线性的。直线的线性组合仍然是直线。非线性层扩展了模型的可能性,这也是通常使“深度”网络优于“宽”网络的原因。为了在不显著增加参数和计算量的情况下增加非线性层的数量,我们可以应用一个1×1内核并在它之后添加一个激活层。这有助于给网络增加一层深度。

     

     

     

    参考:

    http://www.atyun.com/39076.html

    https://zhuanlan.zhihu.com/p/92134485

    『高性能模型』深度可分离卷积和MobileNet_v1

     

     

    展开全文
  • MobileNet_v1:深度可分离卷积

    千次阅读 2020-04-10 17:39:43
    MobileNet是专用于移动和嵌入式视觉应用的卷积神经网络,是基于一个流线型的架构,它使用深度可分离卷积来构建轻量级的深层神经网络。通过引入两个简单的全局超参数,MobileNet在延迟度和准确度之间有效地进行平衡...

    转载请注明作者和出处: http://blog.csdn.net/john_bh/

    论文链接:MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications
    作者及团队:Google
    会议及时间:CVPR 2017
    TensorFlow实现: mobilenet_v1.py

    1.深度可分离卷积

    标准的卷积过程可以看上图,一个3×3的卷积核在卷积时,对应图像区域中的所有通道均被同时考虑,问题在于,为什么一定要同时考虑图像区域和通道?我们为什么不能把通道和空间区域分开考虑?
    深度可分离卷积提出了一种新的思路:对于不同的输入channel采取不同的卷积核进行卷积,它将普通的卷积操作分解为两个过程。
    在这里插入图片描述
    卷积过程
    假设有 N × H × W × C N\times H\times W \times C N×H×W×C的输入,同时有 k 个 3 × 3 k 个 3\times3 k3×3 的卷积。如果设置 p a d = 1 且 s t r i d e = 1 pad=1 且 stride=1 pad=1stride=1 ,那么普通卷积输出为 N × H × W × k N\times H\times W \times k N×H×W×k
    在这里插入图片描述
    Depthwise 过程
    Depthwise是指将 N × H × W × C N\times H\times W \times C N×H×W×C 的输入分为 g r o u p = C group=C group=C 组,然后每一组做 3 × 3 3\times3 3×3 卷积。这样相当于收集了每个Channel的空间特征,即Depthwise特征。逐层卷积对每个输入通道(输入特征图的深度)执行单个滤波器卷积。
    在这里插入图片描述
    Pointwise 过程
    逐点卷积(1x1卷积)用来创建逐深度卷积层的线性组合。Pointwise是指对 N × H × W × C N\times H\times W \times C N×H×W×C 的输入做 k k k 个普通的 1 × 1 1\times1 1×1 卷积。这样相当于收集了每个点的特征,即Pointwise特征。Depthwise+Pointwise最终输出也是 N × H × W × k N\times H\times W \times k N×H×W×k
    在这里插入图片描述

    逐层卷积相对于标准的卷积效率极高,但他只是给输入通道做了个滤波,而不能结合各个通道的特征图生成新的特征。所以为了生成这些新的特征,一个额外的,由1x1卷积构成的逐点卷积被运用在了MobileNet中。深度可分离卷积,就是上述两种卷积层操作的组合。

    TensorFlow 分步执行
    1、depthwise_conv2d 分离卷积部分
    我们定义一张4*4的双通道图片

    import tensorflow as tf
     
    img1 = tf.constant(value=[[[[1],[2],[3],[4]],
                               [[1],[2],[3],[4]],
                               [[1],[2],[3],[4]],
                               [[1],[2],[3],[4]]]],dtype=tf.float32)
     
    img2 = tf.constant(value=[[[[1],[1],[1],[1]],
                               [[1],[1],[1],[1]],
                               [[1],[1],[1],[1]],
                               [[1],[1],[1],[1]]]],dtype=tf.float32)
     
    img = tf.concat(values=[img1,img2],axis=3)
     
    img
    

    <tf.Tensor ‘concat_1:0’ shape=(1, 4, 4, 2) dtype=float32>

    使用3*3的卷积核,输入channel为2,输出channel为2(卷积核数目为2),

    filter1 = tf.constant(value=0, shape=[3,3,1,1],dtype=tf.float32)
    filter2 = tf.constant(value=1, shape=[3,3,1,1],dtype=tf.float32)
    filter3 = tf.constant(value=2, shape=[3,3,1,1],dtype=tf.float32)
    filter4 = tf.constant(value=3, shape=[3,3,1,1],dtype=tf.float32)
    filter_out1 = tf.concat(values=[filter1,filter2],axis=2)
    filter_out2 = tf.concat(values=[filter3,filter4],axis=2)
    filter = tf.concat(values=[filter_out1,filter_out2],axis=3)
     
    filter
    

    <tf.Tensor ‘concat_4:0’ shape=(3, 3, 2, 2) dtype=float32>

    同时执行卷积操作,和深度可分离卷积操作,

    out_img_conv = tf.nn.conv2d(input=img, filter=filter,
                                strides=[1,1,1,1], padding='VALID')
    out_img_depthwise = tf.nn.depthwise_conv2d(input=img,
                                               filter=filter, strides=[1,1,1,1],
                              rate=[1,1], padding='VALID')
     
    with tf.Session() as sess:
        res1 = sess.run(out_img_conv)
        res2 = sess.run(out_img_depthwise)
    print(res1, '\n', res1.shape)
    print(res2, '\n', res2.shape)
    
    [[[[  9.  63.]
       [  9.  81.]]
         
      [[  9.  63.]
       [  9.  81.]]]] 
     (1, 2, 2, 2)  # 《----------
    
    [[[[  0.  36.   9.  27.]
       [  0.  54.   9.  27.]]
    
      [[  0.  36.   9.  27.]
       [  0.  54.   9.  27.]]]] 
     (1, 2, 2, 4)# 《----------
    

    对比输出shape,depthwise_conv2d输出的channel数目为in_channel * 卷积核数目,每一个卷积核对应通道都会对对应的channel进行一次卷积,所以输出通道数更多,
    在这里插入图片描述
    在这里插入图片描述
    看到这里大家可能会误解深度可分离卷积的输出通道数大于普通卷积,其实这只是“分离”部分,后面还有组合的步骤,而普通卷积只不过直接完成了组合:通过对应点相加,将四个卷积中间结果合并为卷积核个数(这里是2)
    在这里插入图片描述
    在这里插入图片描述
    2、合并特征
    合并过程如下,可分离卷积中的合并过程变成可学习的了,使用一个1*1的普通卷积进行特征合并,

    point_filter = tf.constant(value=1, shape=[1,1,4,4],dtype=tf.float32)
    out_img_s = tf.nn.conv2d(input=out_img_depthwise, filter=point_filter, strides=[1,1,1,1], padding='VALID')
    with tf.Session() as sess:
        res3 = sess.run(out_img_s)
    print(res3, '\n', res3.shape)
    
    [[[[ 72.  72.  72.  72.]
       [ 90.  90.  90.  90.]]
    
      [[ 72.  72.  72.  72.]
       [ 90.  90.  90.  90.]]]] 
     (1, 2, 2, 4)
    

    TensorFlow 一步执行

    out_img_se = tf.nn.separable_conv2d(input=img,
                                        depthwise_filter=filter,
                                        pointwise_filter=point_filter,
                                        strides=[1,1,1,1], rate=[1,1], padding='VALID')
     
    with tf.Session() as sess:
        print(sess.run(out_img_se))
    
    [[[[ 72.  72.  72.  72.]
       [ 90.  90.  90.  90.]]
    
      [[ 72.  72.  72.  72.]
       [ 90.  90.  90.  90.]]]]
    (1, 2, 2, 4)
    

    slim 库API介绍

    def separable_convolution2d(
        inputs,
        num_outputs,
        kernel_size,
        depth_multiplier=1,
        stride=1,
        padding='SAME',
        data_format=DATA_FORMAT_NHWC,
        rate=1,
        activation_fn=nn.relu,
        normalizer_fn=None,
        normalizer_params=None,
        weights_initializer=initializers.xavier_initializer(),
        pointwise_initializer=None,
        weights_regularizer=None,
        biases_initializer=init_ops.zeros_initializer(),
        biases_regularizer=None,
        reuse=None,
        variables_collections=None,
        outputs_collections=None,
        trainable=True,
        scope=None):
     
      """一个2维的可分离卷积,可以选择是否增加BN层。
      这个操作首先执行逐通道的卷积(每个通道分别执行卷积),创建一个称为depthwise_weights的变量。如果num_outputs
    不为空,它将增加一个pointwise的卷积(混合通道间的信息),创建一个称为pointwise_weights的变量。如果
    normalizer_fn为空,它将给结果加上一个偏置,并且创建一个为biases的变量,如果不为空,那么归一化函数将被调用。
    最后再调用一个激活函数然后得到最终的结果。
     
      Args:
        inputs: 一个形状为[batch_size, height, width, channels]的tensor
        num_outputs: pointwise 卷积的卷积核个数,如果为空,将跳过pointwise卷积的步骤.
        kernel_size: 卷积核的尺寸:[kernel_height, kernel_width],如果两个的值相同,则可以为一个整数。
        depth_multiplier: 卷积乘子,即每个输入通道经过卷积后的输出通道数。总共的输出通道数将为:
    num_filters_in * depth_multiplier。
        stride:卷积步长,[stride_height, stride_width],如果两个值相同的话,为一个整数值。
        padding:  填充方式,'VALID' 或者 'SAME'.
        data_format:数据格式, `NHWC` (默认) 和 `NCHW`
        rate: 空洞卷积的膨胀率:[rate_height, rate_width],如果两个值相同的话,可以为整数值。如果这两个值
    任意一个大于1,那么stride的值必须为1.    
        activation_fn: 激活函数,默认为ReLU。如果设置为None,将跳过。
        normalizer_fn: 归一化函数,用来替代biase。如果归一化函数不为空,那么biases_initializer
    和biases_regularizer将被忽略。 biases将不会被创建。如果设为None,将不会有归一化。
        normalizer_params: 归一化函数的参数。
        weights_initializer: depthwise卷积的权重初始化器
        pointwise_initializer: pointwise卷积的权重初始化器。如果设为None,将使用weights_initializer。
        weights_regularizer: (可选)权重正则化器。
        biases_initializer: 偏置初始化器,如果为None,将跳过偏置。
        biases_regularizer: (可选)偏置正则化器。
        reuse: 网络层和它的变量是否可以被重用,为了重用,网络层的scope必须被提供。
        variables_collections: (可选)所有变量的collection列表,或者是一个关键字为变量值为collection的字典。
        outputs_collections: 输出被添加的collection.
        trainable: 变量是否可以被训练
        scope: (可选)变量的命名空间。
      Returns:
        代表这个操作的输出的一个tensor"""
    

    2.优势与创新

    Depthwise+Pointwise可以近似看作一个卷积层:

    • 普通卷积:3x3 Conv+BN+ReLU
    • Mobilenet卷积:3x3 Depthwise Conv+BN+ReLU 和 1x1 Pointwise Conv+BN+ReLU

    计算加速
    参数量降低:假设输入通道数为3,要求输出通道数为256,两种做法:

    1.直接接一个 3 × 3 × 256 3×3×256 3×3×256的卷积核,参数量为: 3 × 3 × 3 × 256 = 6 , 912 3×3×3×256 = 6,912 3×3×3×256=6,912

    2.DW操作,分两步完成,参数量为: 3 × 3 × 3 + 3 × 1 × 1 × 256 = 795 ( 3 个 特 征 层 ∗ ( 3 ∗ 3 的 卷 积 核 ) ) 3×3×3 + 3×1×1×256 = 795(3个特征层*(3*3的卷积核)) 3×3×3+3×1×1×256=795333,卷积深度参数通常取为1

    乘法运算次数降低:对比一下不同卷积的乘法次数:
    普通卷积计算量为: H × W × C × k × 3 × 3 H\times W \times C\times k \times 3\times 3 H×W×C×k×3×3
    Depthwise计算量为: H × W × C × 3 × 3 H\times W \times C \times 3\times 3 H×W×C×3×3
    Pointwise计算量为: H × W × C × k H\times W\times C\times k H×W×C×k
    通过Depthwise+Pointwise的拆分,相当于将普通卷积的计算量压缩为:
    d e p t h w i s e + p o i n t w i s e c o n v = H × W × C × 3 × 3 + H × W × C × k H × W × C × k × 3 × 3 = 1 k + 1 3 × 3 \frac{depthwise+pointwise}{conv}=\frac{H\times W \times C \times 3\times 3 + H\times W\times C\times k}{H\times W \times C\times k \times 3\times 3}=\frac{1}{k} +\frac{1}{3\times 3} convdepthwise+pointwise=H×W×C×k×3×3H×W×C×3×3+H×W×C×k=k1+3×31

    通道区域分离
    深度可分离卷积将以往普通卷积操作同时考虑通道和区域改变(卷积先只考虑区域,然后再考虑通道),实现了通道和区域的分离。

    3.Mobilenet v1

    Mobilenet v1利用深度可分离卷积进行加速,MobileNets结构建立在深度可分解卷积中(只有第一层是标准卷积)。该网络允许算法探索网络拓扑,找到一个适合的良好网络。其具体架构在表1说明。除了最后的全连接层,所有层后面跟了batchnorm和ReLU,最终输入到softmax进行分类。图3对比了标准卷积和分解卷积的结构,二者都附带了BN和ReLU层。按照原文的计算方法,MobileNets总共28层(1 + 2 × 13 + 1 = 28),
    在这里插入图片描述
    在这里插入图片描述
    MobileNet将95%的计算时间用于有75%的参数的1×1卷积,其他额外的参数几乎都集中于全连接层,在论文中,采用tensorflow框架进行训练。
    在这里插入图片描述

    Width Multiplier: Thinner Models: 还可以对所有卷积层 k e r n e l kernel kernel 数量统一乘以缩小因子 α ( 其 中 α ∈ ( 0 , 1 ] , 典 型 值 为 1 , 0.75 , 0.5 和 0.25 ) \alpha (其中 \alpha\in(0,1],典型值为1,0.75,0.5和0.25 ) αα(0,1]10.750.50.25以压缩网络。这样Depthwise+Pointwise总计算量可以进一降低为:
    H × W × α C × 3 × 3 + H × W × α C × α k H\times W \times \alpha C \times 3\times 3 + H\times W\times \alpha C\times \alpha k H×W×αC×3×3+H×W×αC×αk

    Resolution Multiplier: Reduced Representation 通过设置ρ来隐式的设置输入的分辨率大小。我们现在可以对网络中的核心层的深度可分离卷积加上宽度乘法器α以及分辨率乘法器ρ来表达计算量: 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 DKDKαMρDFρDF+αMαNρDFρDF其中ρ∈(0,1],一般隐式的设置以便于输入网络的图像分辨率为224\192\160\128等。当ρ=1时为最基本的MobileNet,当ρ<1时,则为薄化的MobileNet。分辨率乘法器对网络约化大约ρ2倍。接下来举个例子,MobileNet中的一个典型的层以及深度可分离卷积、宽度乘法器、分辨率乘法器是如何约化计算量和参数量。表3中展示了一层的计算量和参数量以及结构收缩的这些方法应用在这些层之后的变化。第一行显示了全连接层的Mult-Adds和参数量,其输入特征图为 14 × 14 × 512 14\times14\times512 14×14×512,并且卷积核的尺寸为 3 × 3 × 512 × 512 3\times3\times512\times512 3×3×512×512
    在这里插入图片描述

    4.实验结果

    运用深度可分离卷积的MobileNet与全标准卷积网络的对比,如表4,我们可以看见在ImageNet数据集上使用深度可分离卷积相较于标准卷积准确率只减少了1%,但在计算量和参数量上却减少了很多。
    在这里插入图片描述
    利用宽度是乘法器的薄化模型与只有少数层的千层神经网络进行对比,为了使MobileNet更浅,表1中的5层14x14x512的特征尺寸的可分离卷积层都被去掉了。表5展示了相同计算量参数量的情况下,让MobileNets薄化3%比让它更浅效果更好。
    在这里插入图片描述
    模型压缩超参数
    表6展示了利用宽度乘法器α对MobileNet网络结构进行薄化后准确率,计算量和尺寸之间的权衡关系。准确率直到宽度乘法器α下降到0.25才显示下降很多。
    在这里插入图片描述
    表7展示了通过利用约化的MobileNets时不同分辨率乘法器时准确率、计算量和尺寸之间的权衡关系。准确率随着分辨率下降而平滑减小。
    在这里插入图片描述

    图4该图显示了计算之间的权衡(mult - add)和ImageNet基准测试的准确性。注意精度和计算之间的对数线性关系。
    在这里插入图片描述
    图5图显示了在ImageNet基准测试中参数数量和精度之间的权衡。颜色编码输入分辨率。参数的数量不随输入分辨率的不同而变化。
    在这里插入图片描述
    表8比较了最基本的MobileNet与原始GoogleNet和VGG16。MobileNet和VGG16准确率几乎一样,但是参数量少了32倍,计算量少了27倍。相较于GoogleNet而言,准确率更高,并且参数量和计算量都少了2.5倍。
    在这里插入图片描述
    表9比较了约化后的MobileNet(α=0.5,并且分辨率为160x160,原本为224x224)与AlexNet以及SqueezeNet( Squeezenet: Alexnet-level accuracy with 50x fewer parameters and¡ 1mb model size. )。约化后的MobileNet相较于这两个模型,准确率都高,并且计算量相较于AlexNet少了9.4倍比SqueezeNet少了22倍。
    在这里插入图片描述
    表10中使用MobileNet来进行细粒度识别,在斯坦福狗数据集实现最好的结果,并且大大减少了计算量和尺寸。
    在这里插入图片描述
    表11 表示使用MobileNet架构的PlaNet的性能。
    在这里插入图片描述
    表12使用MobileNet架构的人脸属性分类,每行对应一个不同的超参数设置(宽度倍数和图像分辨率)。
    在这里插入图片描述
    在表13中,使用不同的框架和网络架构比较COCO对象检测结果。 报告的mAP具有COCO主要挑战指标(AP在IoU = 0.50:0.05:0.95)。在这里插入图片描述
    图6.使用MobileNet SSD的目标检测结果。
    在这里插入图片描述
     FaceNet是艺术人脸识别模型中最好的(Facenet: A uni- fied embedding for face recognition and clustering.)它构建了基于三次损失的人脸嵌入。为了构建移动端FaceNet模型,我们在训练集上通过最小化FaceNet和MobileNet之间的平方差来蒸馏训练。结果展示在表14中。
    在这里插入图片描述

    5.关于速度的思考

    深度可分离卷积将一个标准卷积分割成了两个卷积(逐深度,逐点),因此减小了参数量,对应也减小了总计算量,但深度可分离卷积的层数变多了。在cpu是 一般的计算是采用串行计算的方式(一个一个算),层数的增多换来的较高的缓存命中率,就会加速计算速度,但是GPU是并行处理大规模数据(矩阵内积)的运算平台,在gpu上采用并行计算,有足够大的显存,并不会有提升。

    若GPU的显存足够大(假设无限大),因为每层的计算都可以并行一次处理,则此时总运算时间的主导因素是网络的层数。

    而对于缺乏并行能力的CPU,总的运算时间的主导因素则是总计算量有关。

    参考链接:
    『高性能模型』深度可分离卷积和MobileNet_v1
    MobileNet 详解深度可分离卷积,它真的又好又快吗

    展开全文
  • 什么是深度可分离卷积深度可分离卷积的优势

    千次阅读 多人点赞 2020-09-05 15:13:06
    1、什么是深度可分离卷积? 2、传统卷积的参数和计算量: 可分离卷积包括两部(逐通道卷积+逐点卷积) 3、参数对比 4、计算量对比
  • 文章目录定义具体代码分析 定义 空间可分离卷积:是因为它主要处理图像和卷积核(kernel)的空间维度...深度可分离卷积:对输入图像的每个通道分别设置一个卷积核,卷积出的结果通道数与原图保持一致,后使用point-wi
  • 什么是深度可分离卷积网络 实现深度可分离卷积网络实战代码 一,什么是深度可分离卷积网络 1,常规的卷及操作 常规的卷及操作如上图所示:参数量共计3 x 3 x 3 x 4 = 108. 2,深度可分离卷积 depthwise ...
  • 1、深度可分离卷积网络的理论 深度可分离卷积是普通卷积操作的一个变种,它可以替代不同卷积,从而构成卷积神经网络。 以精度损失为代价去换取计算量的减少和参数量的减少,从而使得深度可分离卷积网络可以在手机端...
  • 可分离卷积基本介绍

    2019-04-11 14:58:29
    可分离卷积基本介绍,A Basic Introduction to Separable Convolutions。
  • 深度可分离卷积(Xception 相关)

    千次阅读 2020-07-20 09:23:56
    今天主要讲一下深度可分离卷积(depthwise separable convolutions),这是 Xception 以及 MobileNet 系列的精华所在。 从 Inception module 到深度可分离卷积 Xception 的论文中提到,对于卷积来说,卷积核可以...
  • yolov3--19--一文读懂深度可分离卷积

    千次阅读 2019-12-17 22:20:04
    MobileNet 的核心层使用了一种称之为 深度可分离卷积的操作来替代传统的标准卷积,减少了卷积核的冗余表达。深度可分离卷积可以被分解为深度卷积和卷积核尺寸为1×1的逐点卷积组合。 可以将产生和组合步骤被分为...
  •  之前介绍MobileNet的时候说过深度可分离卷积,不过说起来这篇Xception的论文还要更早利用/借鉴到深度可分离卷积(当然两者都不是原创,而是借鉴了2014年的一篇博士论文:《L. Sifre. Rigid-motion scattering for ...
  • 全连接-普通卷积-深度可分离卷积-分组卷积-计算量和参数量 一个全连接的参数量: params=(Ni+1)×No params=(N_i+1)\times{N_o} params=(Ni​+1)×No​ 其中,NiN_iNi​ 表示输入特征向量权重个数,NoN_oNo​ ...
  • 深度可分离卷积其实是一种分解卷积操作(factorized convolutions)。其可以分解为两个更小的操作:depthwise convolution 和 pointwise convolution。 (1)标准卷积 下面先学习标准的卷积操作: 输入一个12123...
  • A guide to convolution arithmetic for deep learning conv_arithmetic 卷积,空洞卷积、可分离卷积
  • 理解分组卷积与深度可分离卷积

    千次阅读 2020-04-09 16:24:50
    这两种卷积分别是在ResNext论文与MobileNet系列中体现的,貌似Xception中也有深度可分离卷积的体现。 作用都很简单,为了降参。 目录 1. 分组卷积 group convolution 2 深度可分离卷积depthwise separable ...
  • 最近在研究卷积网络的改性,有必要对各种卷积层的结构深入熟悉一下。为此写下这篇学习笔记。 文章大部分内容来自于网络的各种博客总结...深度可分离卷积(depthwise separable convolution) 分组卷积(Group conv...
  • PyTorch实现深度可分离卷积(以MobileNet为例)

    千次阅读 多人点赞 2019-12-17 21:48:18
    介绍深度可分离卷积之前首先要先介绍一下分组卷积。 分组卷积 参考链接[1]已经将分组卷积介绍的比较详细了,这里就不再赘述。原理可以参考一下参考链接。但是这篇文章对于分组卷积的具体代码讲解,感觉不太好。这里...
  • 深度可分离卷积(Depthwise separable convolution)是
  • 一、标准卷积流程: 一次性完成,输入为, 过滤器为(个...二、深度可分离卷积(Depthwise Seperable Convolution)流程: 分两步完成,分别为Depthwise Conv 和 Pointwise Conv 1. Depthwise Conv:filtering sta...
  • 深度可分离卷积神经网络1. 深度可分离卷积网络介绍1. 1 深度可分离卷积网络与普通卷积网络1.2 普通卷积与深度可分离卷积计算量对比2. 深度可分离卷积网络实现2.1 导入相应的库2.2 数据集的加载与处理2.3 构建模型2.4...
  • 为了计算方便,这里先固定stride=1,padding=1,卷积核大小DK=3,这样卷积后和之前的特征图大小相等,都是DF。 普通卷积: M=3为通道数,DF为特征图大小,N=5为卷积核个数,经过卷积特征图大小为DF不变,通道变为...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,048
精华内容 5,219
关键字:

深度可分离卷积