【轻量级网络】SqueezeNet、MobileNet、ShuffleNet等的网络结构图以及pytorch代码

0.写在前面:

        轻量级网络SqueezeNet,MobileNet,ShuffleNet等。一切为了速度。纯pyotrch代码干货,无理论,理论自己去查吧一堆堆的,代码可直接用。在代码中加打印与网络图一一对应着看效果更佳。全连接层用平均池化层代替是这几个网络参数量大幅下降的基础。

1.SqueezeNet

网络结构图:盗个图。多说一句,这个所谓比alexnet轻量,去掉全连接层才是参数下降的大头,用了个全局池化。

                    

代码:直接可用,这个代码实现的是中间这个图的结构

代码注意:a.x = self.avg(c10)这行代码强制把特征图给做平均值了因为它是个1*1的池化,就相当于求平均了。这块是替换掉全连接层,使得此网络参数大幅降低的根本所在

#encoding=utf-8
import warnings
from collections import namedtuple
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.model_zoo import load_url as load_state_dict_from_url

class Fire(nn.Module):
    def __init__(self, in_channel, out_channel, squzee_channel):
        super().__init__()
        self.squeeze = nn.Sequential(
            nn.Conv2d(in_channel, squzee_channel, 1),
            nn.BatchNorm2d(squzee_channel),
            nn.ReLU(inplace=True)
        )
        self.expand_1x1 = nn.Sequential(
            nn.Conv2d(squzee_channel, int(out_channel / 2), 1),
            nn.BatchNorm2d(int(out_channel / 2)),
            nn.ReLU(inplace=True)
        )
        self.expand_3x3 = nn.Sequential(
            nn.Conv2d(squzee_channel, int(out_channel / 2), 3, padding=1),
            nn.BatchNorm2d(int(out_channel / 2)),
            nn.ReLU(inplace=True)
        )
    
    def forward(self, x):
        x = self.squeeze(x)
        x = torch.cat([
            self.expand_1x1(x),
            self.expand_3x3(x)
        ], 1)
        return x
        
        
class SqueezeNet(nn.Module):
    """mobile net with simple bypass"""
    def __init__(self, class_num=100):
        super().__init__()
        self.stem = nn.Sequential(
            nn.Conv2d(3, 96, 3, padding=1),
            nn.BatchNorm2d(96),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(2, 2)
        )
        self.fire2 = Fire(96, 128, 16)
        self.fire3 = Fire(128, 128, 16)
        self.fire4 = Fire(128, 256, 32)
        self.fire5 = Fire(256, 256, 32)
        self.fire6 = Fire(256, 384, 48)
        self.fire7 = Fire(384, 384, 48)
        self.fire8 = Fire(384, 512, 64)
        self.fire9 = Fire(512, 512, 64)
 
        self.conv10 = nn.Conv2d(512, class_num, 1)
        self.avg = nn.AdaptiveAvgPool2d(1)
        self.maxpool = nn.MaxPool2d(2, 2)
            
    def forward(self, x):
        x = self.stem(x)
 
        f2 = self.fire2(x)
        f3 = self.fire3(f2) + f2
        f4 = self.fire4(f3)
        f4 = self.maxpool(f4)
 
        f5 = self.fire5(f4) + f4
        f6 = self.fire6(f5)
        f7 = self.fire7(f6) + f6
        f8 = self.fire8(f7)
        f8 = self.maxpool(f8)
 
        f9 = self.fire9(f8)
        c10 = self.conv10(f9)
 
        x = self.avg(c10)
        x = x.view(x.size(0), -1)
 
        return x
 
def squeezenet(class_num=7):
    return SqueezeNet(class_num=class_num)
        
if __name__=="__main__":
    net  = squeezenet()
    print("0000000000")
    img = torch.randn(1,3,224,224)
    y = net(img)
    print(y)

2.MobileNet

2.1MobileNet-v1

网络结构图:盗个图..

                                 

代码:带测试例子,可以直接运行。

深度可分离卷积来也,先Depthwise Separable Convolution再Depthwise Convolution。

链接https://blog.csdn.net/gbz3300255/article/details/108749709  写的超详细,清晰。

注意地方:

    a.代码第18行groups=input_channels就是对单通道单独实施卷积的代码位置

    b.代码第24行nn.Conv2d(input_channels, output_channels, 1),就是对通道做1*1卷积升维和降维的过程。

    c.代码与上面的网络结构一一对应去看吧。想看哪块直接在下面代码打印就好。

#encoding=utf-8
import warnings
from collections import namedtuple
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.model_zoo import load_url as load_state_dict_from_url
 

class DepthSeperabelConv2d(nn.Module):
    def __init__(self, input_channels, output_channels, kernel_size, **kwargs):
        super().__init__()
        self.depthwise = nn.Sequential(
            nn.Conv2d(
                input_channels,
                input_channels,
                kernel_size,
                groups=input_channels,
                **kwargs),
            nn.BatchNorm2d(input_channels),
            nn.ReLU(inplace=True)
        )
        self.pointwise = nn.Sequential(
            nn.Conv2d(input_channels, output_channels, 1),
            nn.BatchNorm2d(output_channels),
            nn.ReLU(inplace=True)
        )
    def forward(self, x):
        x = self.depthwise(x)
        x = self.pointwise(x)
 
        return x
    
class BasicConv2d(nn.Module):
    def __init__(self, input_channels, output_channels, kernel_size, **kwargs):
        super().__init__()
        self.conv = nn.Conv2d(
            input_channels, output_channels, kernel_size, **kwargs)
        self.bn = nn.BatchNorm2d(output_channels)
        self.relu = nn.ReLU(inplace=True)
 
    def forward(self, x):
        x = self.conv(x)
        x = self.bn(x)
        x = self.relu(x)
        return x
        


def mobilenet(alpha=1, class_num=7):
    return MobileNet(alpha, class_num)
 
class MobileNet(nn.Module):
    def __init__(self, width_multiplier=1, class_num=7):
       super().__init__()
 
       alpha = width_multiplier
       self.stem = nn.Sequential(
           BasicConv2d(3, int(32 * alpha), 3,stride=2, padding=1, bias=False),
           DepthSeperabelConv2d(
               int(32 * alpha),
               int(64 * alpha),
               3,
               padding=1,
               bias=False
           )
       )
       #downsample
       self.conv1 = nn.Sequential(
           DepthSeperabelConv2d(
               int(64 * alpha),
               int(128 * alpha),
               3,
               stride=2,
               padding=1,
               bias=False
           ),
           DepthSeperabelConv2d(
               int(128 * alpha),
               int(128 * alpha),
               3,
               padding=1,
               bias=False
           )
       )
       #downsample
       self.conv2 = nn.Sequential(
           DepthSeperabelConv2d(
               int(128 * alpha),
               int(256 * alpha),
               3,
               stride=2,
               padding=1,
               bias=False
           ),
           DepthSeperabelConv2d(
               int(256 * alpha),
               int(256 * alpha),
               3,
               padding=1,
               bias=False
           )
       )
       #downsample
       self.conv3 = nn.Sequential(
           DepthSeperabelConv2d(
               int(256 * alpha),
               int(512 * alpha),
               3,
               stride=2,
               padding=1,
               bias=False
           ),
           DepthSeperabelConv2d(
               int(512 * alpha),
               int(512 * alpha),
               3,
               padding=1,
               bias=False
           ),
           DepthSeperabelConv2d(
               int(512 * alpha),
               int(512 * alpha),
               3,
               padding=1,
               bias=False
           ),
           DepthSeperabelConv2d(
               int(512 * alpha),
               int(512 * alpha),
               3,
               padding=1,
               bias=False
           ),
           DepthSeperabelConv2d(
               int(512 * alpha),
               int(512 * alpha),
               3,
               padding=1,
               bias=False
           ),
           DepthSeperabelConv2d(
               int(512 * alpha),
               int(512 * alpha),
               3,
               padding=1,
               bias=False
           )
       )
       #downsample
       self.conv4 = nn.Sequential(
           DepthSeperabelConv2d(
               int(512 * alpha),
               int(1024 * alpha),
               3,
               stride=2,
               padding=1,
               bias=False
           ),
           DepthSeperabelConv2d(
               int(1024 * alpha),
               int(1024 * alpha),
               3,
               padding=1,
               bias=False
           )
       )
       self.fc = nn.Linear(int(1024 * alpha), class_num)
       self.avg = nn.AdaptiveAvgPool2d(1)
 
    def forward(self, x):
        x = self.stem(x)
 
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = self.conv4(x)
 
        x = self.avg(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

def googlenetv1():
    return MobileNet()  
 
if __name__=="__main__":
    net  = googlenetv1()
    print("0000000000")
    img = torch.randn(1,3,224,224)
    y = net(img)
    print(y)

 2.2.MobileNet-v2

网络结构图:

                                                  

代码:

这个也是代码和网络结构图要一一对应。这个我加了些打印,可以看到与上图是对应上的自己看看吧 我的k取7

x = F.adaptive_avg_pool2d(x, 1)这类的语句 就是去掉全连接层呢。

#encoding=utf-8
import warnings
from collections import namedtuple
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.model_zoo import load_url as load_state_dict_from_url

###################################################### MobileNetV2 #########################################################
class LinearBottleNeck(nn.Module):
    def __init__(self, in_channels, out_channels, stride, t=6, class_num=7):
        super().__init__()

        self.residual = nn.Sequential(
            nn.Conv2d(in_channels, in_channels * t, 1),
            nn.BatchNorm2d(in_channels * t),
            nn.ReLU6(inplace=True),

            nn.Conv2d(in_channels * t, in_channels * t, 3, stride=stride, padding=1, groups=in_channels * t),
            nn.BatchNorm2d(in_channels * t),
            nn.ReLU6(inplace=True),

            nn.Conv2d(in_channels * t, out_channels, 1),
            nn.BatchNorm2d(out_channels)
        )

        self.stride = stride
        self.in_channels = in_channels
        self.out_channels = out_channels
    
    def forward(self, x):
        residual = self.residual(x)

        if self.stride == 1 and self.in_channels == self.out_channels:
            residual += x
        
        return residual

class MobileNetV2(nn.Module):
    def __init__(self, class_num=7):
        super().__init__()

        self.pre = nn.Sequential(
            nn.Conv2d(3, 32, 1, stride = 2),
            nn.BatchNorm2d(32),
            nn.ReLU6(inplace=True)
        )

        self.stage1 = LinearBottleNeck(32, 16, 1, 1)
        self.stage2 = self._make_stage(2, 16, 24, 2, 6)
        self.stage3 = self._make_stage(3, 24, 32, 2, 6)
        self.stage4 = self._make_stage(4, 32, 64, 2, 6)
        self.stage5 = self._make_stage(3, 64, 96, 1, 6)
        self.stage6 = self._make_stage(3, 96, 160, 1, 6)
        self.stage7 = LinearBottleNeck(160, 320, 1, 6)

        self.conv1 = nn.Sequential(
            nn.Conv2d(320, 1280, 1),
            nn.BatchNorm2d(1280),
            nn.ReLU6(inplace=True)
        )

        self.conv2 = nn.Conv2d(1280, class_num, 1)
            
    def forward(self, x):
        print("x shape 0000 = ", x.shape)
        x = self.pre(x)
        print("x shape 11111 = ", x.shape)
        x = self.stage1(x)
        print("x shape 222222 = ", x.shape)
        x = self.stage2(x)
        print("x shape 33333 = ", x.shape)
        x = self.stage3(x)
        print("x shape 4444 = ", x.shape)
        x = self.stage4(x)
        print("x shape 5555 = ", x.shape)
        x = self.stage5(x)
        print("x shape 6666 = ", x.shape)
        x = self.stage6(x)
        print("x shape 7777 = ", x.shape)
        x = self.stage7(x)
        print("x shape 8888 = ", x.shape)
        x = self.conv1(x)
        print("x shape 9999 = ", x.shape)
        x = F.adaptive_avg_pool2d(x, 1)
        print("x shape 10101010 = ", x.shape)
        x = self.conv2(x)
        print("x shape xxxxxxxxxx = ", x.shape)
        x = x.view(x.size(0), -1)

        return x
    
    def _make_stage(self, repeat, in_channels, out_channels, stride, t):

        layers = []
        layers.append(LinearBottleNeck(in_channels, out_channels, stride, t))
        
        while repeat - 1:
            layers.append(LinearBottleNeck(out_channels, out_channels, 1, t))
            repeat -= 1
        
        return nn.Sequential(*layers)
        
        
def mobilenetv2():
    return MobileNetV2()  

if __name__=="__main__":
    net  = mobilenetv2()
    img = torch.randn(1,3,224,224)
    y = net(img)
    print(y)

 

3.ShuffleNet

3.1 ShuffleNet v1

网络结构图:

                     

难点:通道混淆,关于解释上链接https://blog.csdn.net/gbz3300255/article/details/108530788

 代码:注意类别数写一下。这块还需要对照代码理解下 先发上去  后续改

# -*- coding: utf-8 -*-
import math
import torch
import torch.nn as nn
import torch.nn.functional as F

class Bottleneck(nn.Module):  
    def __init__(self, in_planes, out_planes, stride, groups):
        super(Bottleneck, self).__init__()
        self.stride = stride

        mid_planes = out_planes // 4  
        self.groups = 1 if in_planes == 24 else groups 
        self.conv1 = nn.Conv2d(in_planes, mid_planes, kernel_size=1, groups=self.groups, bias=False)
        self.bn1 = nn.BatchNorm2d(mid_planes)
        self.conv2 = nn.Conv2d(mid_planes, mid_planes, kernel_size=3, stride=stride, padding=1, groups=mid_planes, bias=False) 
        self.bn2 = nn.BatchNorm2d(mid_planes)
        self.conv3 = nn.Conv2d(mid_planes, out_planes, kernel_size=1, groups=groups, bias=False)
        self.bn3 = nn.BatchNorm2d(out_planes)
        if stride == 2:
            self.shortcut = nn.Sequential(nn.AvgPool2d(3, stride=2, padding=1)) 

    @staticmethod
    def shuffle_channels(x, groups):
        
        N, C, H, W = x.size()
        return x.view(N, groups, C // groups, H, W).permute(0, 2, 1, 3, 4).contiguous().view(N, C, H, W)

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.shuffle_channels(out, self.groups)
        out = F.relu(self.bn2(self.conv2(out)))
        out = self.bn3(self.conv3(out))

        if self.stride == 2:
            res = self.shortcut(x)
            out = F.relu(torch.cat([out, res], 1)) 
        else:
            out = F.relu(out + x)
        return out

class ShuffleNet(nn.Module):

    def __init__(self, out_planes, num_blocks, groups, num_classes=7, depth_multiplier=1.):  
        super(ShuffleNet, self).__init__()
        self.num_classes = num_classes
        self.in_planes = int(24 * depth_multiplier) 
        self.out_planes = [int(depth_multiplier * x) for x in out_planes]

        self.conv1 = nn.Conv2d(3, self.in_planes, kernel_size=3, stride=2,padding = 1 bias=False)
        self.bn1 = nn.BatchNorm2d(self.in_planes)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)


        layers = []
        for out_plane, num_block in zip(out_planes, num_blocks):
            layers.append(self._make_layer(out_plane, num_block, groups))
        self.layers = nn.ModuleList(layers) 
        if num_classes is not None:
            self.avgpool = nn.AdaptiveMaxPool2d(1)
            self.fc = nn.Linear(out_planes[-1], num_classes)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()

    def _make_layer(self, out_planes, num_blocks, groups):
        layers = []
        for i in range(num_blocks):
            stride = 2 if i == 0 else 1  
            cat_planes = self.in_planes if i == 0 else 0  
            layers.append(Bottleneck(self.in_planes, out_planes - cat_planes, stride=stride, groups=groups))
            self.in_planes = out_planes  
        return nn.Sequential(*layers)

    @property
    def layer_channels(self):
        return self.out_planes

    def forward(self, x):
        x = F.relu(self.bn1(self.conv1(x)))
        x = self.maxpool(x)

        c = []
        for i in range(len(self.layers)):
            x = self.layers[i](x)
            c.append(x)

        if self.num_classes is not None:
            x = self.avgpool(x)
            x = x.view(x.size(0), -1)
            print("x000000 = ", x.shape)
            x = self.fc(x)
            print("x1111111 = ", x.shape)
            return x
        else:
            return c

def shufflenet(**kwargs):
    planes = [240, 480, 960]
    layers = [4, 8, 4]
    model = ShuffleNet(planes, layers, groups=3, **kwargs)
    return model


def shufflenet_4(**kwargs):  # group = 4
    planes = [272, 544, 1088]
    layers = [4, 8, 4]
    model = ShuffleNet(planes, layers, groups=4, **kwargs)
    return model


def shufflenet_2(**kwargs):
    planes = [200, 400, 800]
    layers = [4, 8, 4]
    model = ShuffleNet(planes, layers, groups=2, **kwargs)
    return model


def shufflenet_1(**kwargs):
    planes = [144, 288, 576]
    layers = [4, 8, 4]
    model = ShuffleNet(planes, layers, groups=1, **kwargs)
    return model

if __name__=="__main__":
    net  = shufflenet_2()
    img = torch.randn(1,3,224,224)
    y = net(img)
    print(y)

未完,待续......

 

 

参考文献:1 https://www.cnblogs.com/vincent1997/p/10916734.html

参考文献:2 https://blog.csdn.net/weixin_44538273/article/details/88856239

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值