shuffleNet-V1论文阅读及代码实现


前言

shufflenetV1是卷积神经网络向轻量化发展的又一方向,是继Mobilenet的轻量化网络。


一、论文阅读总结

论文地址
Tricks: Group Convolution在1*1卷积上的应用;channel shuffle提高通道之间的信息传递;

1.Channel Shuffle

在ResNeXt中只考虑了33卷积的群卷积模式,使大部分运算量集中于11卷积(pointwise conv)。因此本文对1*1卷积也采用群卷积,保证每个卷积操作只作用于少量通道,保证通道连接稀疏性,减少计算量。
什么是群卷积(group convolution)?
假设上一层的输出feature map有N个,即通道数channel=N,也就是说上一层有N个卷积核。再假设群卷积的群数目M。那么该群卷积层的操作就是,先将channel分成M份。每一个group对应N/M个channel,与之独立连接。然后各个group卷积完成后将输出叠在一起(concatenate),作为这一层的输出channel。

2.Group Conv

若在全网络采用群卷积的方法,其对应每一个通道输出的特征图只与少数几个输入通道的特征图有关,造成了信息的阻塞。因此需要将N个(通道数)输入特征图分为多个子组,在子组中选出不同的特征图组成新的子组送往下一个群卷积,其示意图如图1所示。在这里插入图片描述

3.消融实验

1)群卷积的通道数(g=1,2,3,4,8)
在1*1卷积上采用群卷积比不采用要强,在某些模型(如ShuffleNet 0.5×),当组数变大(如g= 8)时,分类评分达到饱和甚至下降。随着组数的增加(因此更宽的特征映射),每个卷积滤波器的输入通道变得更少,这可能会损害表示能力。对于较小的模型,如ShuffleNet 0.25×,群组号码往往会得到更好的结果,这表明更大的特征地图为较小的模型带来更多的好处。
在这里插入图片描述
2)比较shuffle和no shuffle:
通道shuffle在不同的设置下都能提高分类分数。特别是当组数较大时(如g= 8),采用信道随机的模型表现明显优于同类模型,这说明了跨组信息交换的重要性。
在这里插入图片描述

二、代码实现

代码引用参考

1.shuffle实现

代码如下:

class shuffle(nn.Module):
    def __init__(self,group=2):
        super(shuffle, self).__init__()
        self.group=group
    def forward(self,x):
        """shuffle操作:[N,C,H,W] -> [N,g,C/g,H,W] -> [N,C/g,g,H,w] -> [N,C,H,W]"""
        num,channel,height,width=x.size()
        x=x.view(num,self.group,channel//self.group,height,width)
        x=x.permute(0,2,1,3,4)
        x=x.reshape(num,channel,height,width)
        return x

2.瓶颈模块实现

代码如下:

class bottleblock(nn.Module):
    def __init__(self,in_channel,out_channel,stride,group):
        super(bottleblock, self).__init__()
        self.stride=stride
        if in_channel==24:
            group=1
        else:
            group=group
        self.conv1_with_group=nn.Sequential(nn.Conv2d(in_channels=in_channel,out_channels=out_channel//4,kernel_size=1,stride=1,groups=group,bias=False),
                                            nn.BatchNorm2d(out_channel//4),
                                            nn.ReLU(inplace=True))
        self.shuffle=shuffle(group)
        self.conv2_with_depth=nn.Sequential(nn.Conv2d(in_channels=out_channel//4,out_channels=out_channel//4,stride=stride,kernel_size=3,groups=out_channel//4,padding=1,bias=False),
                                            nn.BatchNorm2d(out_channel//4))
        self.conv3_with_group=nn.Sequential(nn.Conv2d(in_channels=out_channel//4,out_channels=out_channel,kernel_size=1,stride=1,groups=group),
                                            nn.BatchNorm2d(out_channel))
        if stride==2:
            self.shortcut=nn.AvgPool2d(stride=stride,kernel_size=3,padding=1)
        else:
            self.shortcut=nn.Sequential()
    def forward(self,a):
        x=self.conv1_with_group(a)
        x=self.shuffle(x)
        x=self.conv2_with_depth(x)
        x=self.conv3_with_group(x)
        residual=self.shortcut(a)
        if self.stride==2:
            return F.relu(torch.cat([x,residual],1))
        else:
            return F.relu(residual+x)


3.shufflenet网络实现

代码如下:

class shufflenet(nn.Module):
    def __init__(self,num_class,group):
        super(shufflenet, self).__init__()
        self.num_class=num_class
        self.inchannel=24
        if group==8:
            stage_dict={'bolck_num':[4,8,4],
                         'outchannel':[384,768,1536],
                         'group':group}
        elif group==4:
            stage_dict = {'bolck_num': [4, 8, 4],
                               'outchannel': [272, 544, 1088],
                               'group': group}
        elif group==3:
            stage_dict = {'bolck_num': [4, 8, 4],
                               'outchannel': [240, 480, 960],
                               'group': group}
        elif group==2:
            stage_dict = {'bolck_num': [4, 8, 4],
                               'outchannel': [200, 400, 800],
                               'group': group}
        elif group==1:
            stage_dict = {'bolck_num': [4, 8, 4],
                               'outchannel': [144, 288, 576],
                               'group': group}
        block_num=stage_dict['bolck_num']
        outchannel=stage_dict['outchannel']
        group=stage_dict['group']
        self.initial=nn.Sequential(nn.Conv2d(kernel_size=3,padding=1,in_channels=3,out_channels=24,stride=2),
                                   nn.BatchNorm2d(24),
                                   nn.ReLU(inplace=True),
                                   nn.MaxPool2d(kernel_size=3,stride=2,padding=1))

        self.layer1 = self.make_layer(block_num[0],outchannel[0],group)
        self.layer2 = self.make_layer(block_num[1], outchannel[1], group)
        self.layer3 = self.make_layer(block_num[2], outchannel[2], group)

        self.pool=nn.AdaptiveAvgPool2d(1)
        self.fc=nn.Linear(outchannel[2],num_class)
    def make_layer(self,block_num,outchannel,group):
        layer_list=[]
        for i in range(block_num):
            if i==0:
                stride=2
                catchannel=self.inchannel
            else:
                stride=1
                catchannel=0
            layer_list.append(bottleblock(self.inchannel,outchannel-catchannel,stride,group))
            self.inchannel=outchannel
        return nn.Sequential(*layer_list)
    def forward(self,x):
        x=self.initial(x)
        x=self.layer1(x)
        x=self.layer2(x)
        x=self.layer3(x)
        x=self.pool(x)
        x=x.view(x.size(0),-1)
        x=self.fc(x)
        return F.softmax(x,dim=1)

总结

本文介绍了shuffleNetV1的核心思想及其代码实现,以供大家交流讨论!
往期回顾:
(1)CBAM论文解读+CBAM-ResNeXt的Pytorch实现
(2)SENet论文解读及代码实例
下期预告:
shuffleNet-V2论文阅读及代码实现

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是ShuffleNetV1PyTorch代码实现,包括ShuffleNetV1的网络结构和训练代码: ```python import torch.nn as nn import math __all__ = ['ShuffleNetV1', 'shufflenetv1'] def conv3x3(in_planes, out_planes, stride=1): """3x3 convolution with padding""" return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False) class BasicBlock(nn.Module): def __init__(self, inplanes, planes, stride=1, downsample=None): super(BasicBlock, self).__init__() self.conv1 = conv3x3(inplanes, planes, stride) self.bn1 = nn.BatchNorm2d(planes) self.relu = nn.ReLU(inplace=True) self.conv2 = conv3x3(planes, planes) self.bn2 = nn.BatchNorm2d(planes) self.downsample = downsample self.stride = stride def forward(self, x): residual = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) if self.downsample is not None: residual = self.downsample(x) out += residual out = self.relu(out) return out class ShuffleNetV1(nn.Module): def __init__(self, num_classes=1000, groups=3, width_mult=1): super(ShuffleNetV1, self).__init__() self.groups = groups self.stage_repeats = [3, 7, 3] if groups == 1: self.stage_out_channels = [-1, 24, 144, 288, 576] elif groups == 2: self.stage_out_channels = [-1, 24, 200, 400, 800] elif groups == 3: self.stage_out_channels = [-1, 24, 240, 480, 960] elif groups == 4: self.stage_out_channels = [-1, 24, 272, 544, 1088] elif groups == 8: self.stage_out_channels = [-1, 24, 384, 768, 1536] else: raise ValueError("""{} groups is not supported for 1x1 Grouped Convolutions""".format(num_groups)) # building first layer input_channels = 3 output_channels = self.stage_out_channels[1] output_channels = int(output_channels * width_mult) self.conv1 = nn.Conv2d(input_channels, output_channels, kernel_size=3, stride=2, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(output_channels) self.relu = nn.ReLU(inplace=True) self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) # building stages self.stage2 = self._make_stage(2, width_mult) self.stage3 = self._make_stage(3, width_mult) self.stage4 = self._make_stage(4, width_mult) # building last several layers self.conv_last = nn.Conv2d(self.stage_out_channels[-2], self.stage_out_channels[-1], kernel_size=1, stride=1, padding=0, bias=False) self.globalpool = nn.AvgPool2d(7) self.fc = nn.Linear(self.stage_out_channels[-1], num_classes) def _make_stage(self, stage, width_mult): modules = OrderedDict() stage_name = "ShuffleUnit_Stage{}".format(stage) # stage_repeats = self.stage_repeats[stage] unit1 = ShuffleUnit(self.stage_out_channels[stage-1], self.stage_out_channels[stage], 2, groups=self.groups, width_mult=width_mult) modules[stage_name+"_unit1"] = unit1 for i in range(self.stage_repeats[stage-2]): name = stage_name + "_unit" + str(i+2) module = ShuffleUnit(self.stage_out_channels[stage], self.stage_out_channels[stage], 1, groups=self.groups, width_mult=width_mult) modules[name] = module return nn.Sequential(modules) def forward(self, x): x = self.conv1(x) x = self.bn1(x) x = self.relu(x) x = self.maxpool(x) x = self.stage2(x) x = self.stage3(x) x = self.stage4(x) x = self.conv_last(x) x = self.globalpool(x) x = x.view(-1, self.stage_out_channels[-1]) x = self.fc(x) return x class ShuffleUnit(nn.Module): def __init__(self, in_channels, out_channels, stride, groups=3, width_mult=1): super(ShuffleUnit, self).__init__() if stride != 1 or in_channels != out_channels: self.use_res_connect = False self.shortcut = nn.Sequential( nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=stride, padding=1, groups=in_channels, bias=False), nn.BatchNorm2d(in_channels), nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0, bias=False), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True), ) else: self.use_res_connect = True self.shortcut = nn.Sequential() self.groups = groups mid_channels = int(out_channels / 4 * width_mult) self.conv1 = nn.Conv2d(in_channels, mid_channels, kernel_size=1, stride=1, padding=0, bias=False) self.bn1 = nn.BatchNorm2d(mid_channels) self.conv2 = nn.Conv2d(mid_channels, mid_channels, kernel_size=3, stride=stride, padding=1, groups=groups, bias=False) self.bn2 = nn.BatchNorm2d(mid_channels) self.conv3 = nn.Conv2d(mid_channels, out_channels, kernel_size=1, stride=1, padding=0, bias=False) self.bn3 = nn.BatchNorm2d(out_channels) self.relu = nn.ReLU(inplace=True) def _channel_shuffle(self, x, groups): batchsize, num_channels, height, width = x.data.size() channels_per_group = num_channels // groups x = x.view(batchsize, groups, channels_per_group, height, width) x = torch.transpose(x, 1, 2).contiguous() x = x.view(batchsize, -1, height, width) return x def forward(self, x): if self.use_res_connect: shortcut = 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._channel_shuffle(x, self.groups) x = self.conv3(x) x = self.bn3(x) if self.use_res_connect: shortcut = self.shortcut(shortcut) x += shortcut x = self.relu(x) return x def shufflenetv1(**kwargs): """ Constructs a ShuffleNetV1 model """ return ShuffleNetV1(**kwargs) ``` 在使用ShuffleNetV1时,可以通过以下方式进行实例化: ```python import torch from shufflenetv1 import shufflenetv1 model = shufflenetv1(groups=3, width_mult=1) input = torch.randn(1, 3, 224, 224) output = model(input) print(output.shape) ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

炼丹代师

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

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

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

打赏作者

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

抵扣说明:

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

余额充值