ShuffleNetV1 V2模型总结及代码理解

1. ShuffleNet V1 理解

ShuffleNet可以看成是group convolution和depth wise separable convolution的结合。ShuffleNet的创新的点主要有:

  • 利用分组卷积降低了普通卷积的计算量
  • 利用channel shuffle增加了不同通道间的交互能力
    在这里插入图片描述
2. group convolution的参数量与计算量

group convolution与普通卷积的参数量,计算量对比:
假设卷积的输入 ( H , W , c 1 ) (H,W,c_1) (H,W,c1),卷积核大小 ( h 1 , w 1 ) (h_1,w_1) (h1,w1),输出 ( H , W , c 2 ) (H,W,c_2) (H,W,c2),那么对于普通卷积:
参数量: h 1 ⋅ w 1 ⋅ c 1 ⋅ c 2 h_1 \cdot w_1 \cdot c_1 \cdot c_2 h1w1c1c2
计算量: H ⋅ W ⋅ c 2 ⋅ h 1 ⋅ w 1 ⋅ c 1 H \cdot W \cdot c_2 \cdot h_1 \cdot w_1 \cdot c_1 HWc2h1w1c1
conv
同样的输入,对于分组卷积,假设分成g组,那么整个过程的:
参数量: h 1 ⋅ w 1 ⋅ c 1 / g ⋅ c 2 / g ⋅ g h_1 \cdot w_1 \cdot c_1/g \cdot c_2/g \cdot g h1w1c1/gc2/gg = h 1 ⋅ w 1 ⋅ c 1 ⋅ c 2 / g h_1 \cdot w_1 \cdot c_1 \cdot c_2/g h1w1c1c2/g
计算量: H ⋅ W ⋅ c 2 / g ⋅ h 1 ⋅ w 1 ⋅ c 1 / g ⋅ g H \cdot W \cdot c_2/g \cdot h_1 \cdot w_1 \cdot c_1/g \cdot g HWc2/gh1w1c1/gg = H ⋅ W ⋅ c 2 ⋅ h 1 ⋅ w 1 ⋅ c 1 / g H \cdot W \cdot c_2 \cdot h_1 \cdot w_1 \cdot c_1/g HWc2h1w1c1/g
由此可见分组卷积的参数量与计算量均是普通卷积的 1 g \frac{1}{g} g1,所以利用分组卷积代替普通卷积可以降低原始卷积的参数量和计算量。
group conv

3. 分组卷积的问题与channel shuffle

但是分组卷积存在的问题在于,输出的 c 2 / g c_2/g c2/g个通道中只与对应的通道 c 1 / g c_1/g c1/g有信息上的流动,与相邻的group之间缺少交互,所以作者针对上一组组卷积的结果采用了一种channel shuffle的操作,让相邻的group conv有一定的交互能力。
在这里插入图片描述

4. ShuffleNet V1代码理解

以下代码参考自https://github.com/megvii-model/ShuffleNet-Series/blob/master/ShuffleNetV1/blocks.py

import torch
import torch.nn as nn
import torch.nn.functional as F

class ShuffleV1Block(nn.Module):
    def __init__(self, inp, oup, *, group, first_group, mid_channels, ksize, stride):
        super(ShuffleV1Block, self).__init__()
        self.stride = stride
        assert stride in [1, 2]

        self.mid_channels = mid_channels
        self.ksize = ksize
        pad = ksize // 2
        self.pad = pad
        self.inp = inp
        self.group = group

        if stride == 2:
            outputs = oup - inp
        else:
            outputs = oup
		branch_main_1 = [
			# 将pw和dw与分组卷积相结合
            # pw, point wise convolutiuon
            nn.Conv2d(inp, mid_channels, 1, 1, 0, groups=1 if first_group else group, bias=False),
            nn.BatchNorm2d(mid_channels),
            nn.ReLU(inplace=True),
            # dw,depth wise convolution
            nn.Conv2d(mid_channels, mid_channels, ksize, stride, pad, groups=mid_channels, bias=False),
            nn.BatchNorm2d(mid_channels),
        ]
        branch_main_2 = [
            # pw-linear
            nn.Conv2d(mid_channels, outputs, 1, 1, 0, groups=group, bias=False),
            nn.BatchNorm2d(outputs),
        ]
        self.branch_main_1 = nn.Sequential(*branch_main_1)
        self.branch_main_2 = nn.Sequential(*branch_main_2)

        if stride == 2:
            self.branch_proj = nn.AvgPool2d(kernel_size=3, stride=2, padding=1)

channel shuffle 过程:

def channel_shuffle(self, x):
        batchsize, num_channels, height, width = x.data.size()
        assert num_channels % self.group == 0 # 需要提前判断是否能够被整除
        group_channels = num_channels # self.group
        # 分成 self.group组,每一组group_channels个通道
        x = x.reshape(batchsize, group_channels, self.group, height, width)
        x = x.permute(0, 2, 1, 3, 4) # 交换不同组的信息
        x = x.reshape(batchsize, num_channels, height, width)
        return x

前向传递过程:

def forward(self, old_x):
        x = old_x
        x_proj = old_x
        x = self.branch_main_1(x)
        if self.group > 1:
            x = self.channel_shuffle(x)
        x = self.branch_main_2(x)
        if self.stride == 1:
            return F.relu(x + x_proj)
        elif self.stride == 2:
            return torch.cat((self.branch_proj(x_proj), F.relu(x)), 1)
5. shuffleNet V2 理解

shuffleNet V2 的一个Motivation是:在设计网络结构时,除了考了计算量Flops,还应该考虑内存的访问代价(MAC),并行化对应的时间,以及不同的部署环境ARM或者GPU

shuffleNetV2主要实验性的提出了一些网络设计方面的trick:

  1. 卷积的输入的通道数与输出的通道数应该尽量相同( M A C = h w ( c 1 + c 2 ) + c 1 c 2 MAC=hw(c_1+c_2)+c_1c_2 MAC=hw(c1+c2)+c1c2
  2. 过多的组卷积只会增加内存访问时间
  3. 网络碎片化会降低并行度
  4. Element wise的运算增加内存访问时间

在上面的基础上,shuffleNet V1 需要组卷积(与2点违背)和 linear bottleneck (与1点违背);mobileNet V2需要残差结构(与4点违背)和expansion/projection layer(与1点违背)

shuffleNet V2 改进点:

  • 在输入层采用channel split,代替group convolution的作用
  • 在输出结果时采用concat 替代add操作
  • 移除bottle neck部分的channel shuffle 操作

图©为shuffleNet V2 原始版本,图(d)为V2 的下采样版本
shuffleNet V2

参考资料

  • shufflenet系列 pytorch 代码: https://github.com/megvii-model/ShuffleNet-Series
  • group convolution: https://blog.yani.ai/filter-group-tutorial/
  • group convolution 计算量理解:https://zhuanlan.zhihu.com/p/65377955
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值