MobileNetV_3代码理解

class MobileNetV3(nn.Module):
    # def __init__(self, model_mode="SMALL", num_classes=30, multiplier=1.0):
    def __init__(self, model_mode="SMALL", num_classes=30, multiplier=1.0, dropout_rate=0.0):
        super(MobileNetV3, self).__init__()
        self.num_classes = num_classes

        if model_mode == "LARGE":
            # 参数设置
            layers = [
                [16, 16, 3, 1, "RE", False, 16],
                [16, 24, 3, 2, "RE", False, 64],
                [24, 24, 3, 1, "RE", False, 72],
                [24, 40, 5, 2, "RE", True, 72],
                [40, 40, 5, 1, "RE", True, 120],

                [40, 40, 5, 1, "RE", True, 120],
                [40, 80, 3, 2, "HS", False, 240],
                [80, 80, 3, 1, "HS", False, 200],
                [80, 80, 3, 1, "HS", False, 184],
                [80, 80, 3, 1, "HS", False, 184],

                [80, 112, 3, 1, "HS", True, 480],
                [112, 112, 3, 1, "HS", True, 672],
                [112, 160, 5, 1, "HS", True, 672],
                [160, 160, 5, 2, "HS", True, 672],
                [160, 160, 5, 1, "HS", True, 960],
            ]
            init_conv_out = _make_divisible(16 * multiplier)
            self.init_conv = nn.Sequential(
                nn.Conv2d(in_channels=3, out_channels=init_conv_out, kernel_size=3, stride=2, padding=1),
                nn.BatchNorm2d(init_conv_out),
                h_swish(inplace=True),
            )  # 3x3卷积 第一个卷积层

            self.block = []
            for in_channels, out_channels, kernal_size, stride, nonlinear, se, exp_size in layers:  # 依次遍历15个参数设置
                # 输入通道数,输出通道数,卷积核大小,步长,非线性,se模块,扩张因子exp_size
                in_channels = _make_divisible(in_channels * multiplier)
                out_channels = _make_divisible(out_channels * multiplier)
                exp_size = _make_divisible(exp_size * multiplier)  # 新的层数
                self.block.append(MobileBlock(in_channels, out_channels, kernal_size, stride, nonlinear, se, exp_size))
            self.block = nn.Sequential(*self.block)  # 15个bneck

            out_conv1_in = _make_divisible(160 * multiplier)
            out_conv1_out = _make_divisible(960 * multiplier)
            self.out_conv1 = nn.Sequential(
                nn.Conv2d(out_conv1_in, out_conv1_out, kernel_size=1, stride=1),  # 1x1卷积  倒数第三个conv2d, 1x1
                nn.BatchNorm2d(out_conv1_out),
                h_swish(inplace=True),
            )
            self.avg_pool = nn.AdaptiveAvgPool2d(1)  # 自适应池化操作
            out_conv2_in = _make_divisible(960 * multiplier)
            out_conv2_out = _make_divisible(1280 * multiplier)
            self.out_conv2 = nn.Sequential(
                nn.Conv2d(out_conv2_in, out_conv2_out, kernel_size=1, stride=1),  # 1x1卷积 倒数第二个conv2d 1x1, NBN
                h_swish(inplace=True),
                nn.Dropout(dropout_rate),  # dropout
                nn.Conv2d(out_conv2_out, self.num_classes, kernel_size=1, stride=1),  # 1x1卷积 最后一个conv2d 1x1, NBN
            )

        elif model_mode == "SMALL":
            layers = [
                [16, 16, 3, 2, "RE", True, 16],
                [16, 24, 3, 2, "RE", False, 72],
                [24, 24, 3, 1, "RE", False, 88],
                [24, 40, 5, 2, "RE", True, 96],
                [40, 40, 5, 1, "RE", True, 240],
                [40, 40, 5, 1, "RE", True, 240],
                [40, 48, 5, 1, "HS", True, 120],
                [48, 48, 5, 1, "HS", True, 144],
                [48, 96, 5, 2, "HS", True, 288],
                [96, 96, 5, 1, "HS", True, 576],
                [96, 96, 5, 1, "HS", True, 576],
            ]

            init_conv_out = _make_divisible(16 * multiplier)
            self.init_conv = nn.Sequential(
                nn.Conv2d(in_channels=3, out_channels=init_conv_out, kernel_size=3, stride=2, padding=1),
                nn.BatchNorm2d(init_conv_out),
                h_swish(inplace=True),
            )
            self.block = []
            for in_channels, out_channels, kernal_size, stride, nonlinear, se, exp_size in layers:  # 依次遍历15个参数设置
                in_channels = _make_divisible(in_channels * multiplier)
                out_channels = _make_divisible(out_channels * multiplier)
                exp_size = _make_divisible(exp_size * multiplier)
                self.block.append(MobileBlock(in_channels, out_channels, kernal_size, stride, nonlinear, se, exp_size))
            self.block = nn.Sequential(*self.block)

            out_conv1_in = _make_divisible(96 * multiplier)
            out_conv1_out = _make_divisible(576 * multiplier)
            self.out_conv1 = nn.Sequential(
                nn.Conv2d(out_conv1_in, out_conv1_out, kernel_size=1, stride=1),  # 1x1卷积 倒数第三个 用了se模块
                SqueezeBlock(out_conv1_out),  # se模块
                nn.BatchNorm2d(out_conv1_out),
                h_swish(inplace=True),
            )
            self.avg_pool = nn.AdaptiveAvgPool2d(1)  # 自适应池化操作

            out_conv2_in = _make_divisible(576 * multiplier)
            out_conv2_out = _make_divisible(1280 * multiplier)
            self.out_conv2 = nn.Sequential(
                nn.Conv2d(out_conv2_in, out_conv2_out, kernel_size=1, stride=1),   # 1x1卷积 倒数第二个
                h_swish(inplace=True),
                nn.Dropout(dropout_rate),  # dropout
                nn.Conv2d(out_conv2_out, self.num_classes, kernel_size=1, stride=1),   # 1x1卷积 倒数第一个
            )

        self.apply(_weights_init)

    def forward(self, x):
        out = self.init_conv(x)
        out = self.block(out)
        out = self.out_conv1(out)
        batch, channels, height, width = out.size()
        out = self.avg_pool(out)  #
        out = self.out_conv2(out).view(batch, -1)
        return out
def _make_divisible(v, divisor=8, min_value=None):
	# 计算新的通道数divisor=8
    if min_value is None:
        min_value = divisor
    new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
    # Make sure that round down does not go down by more than 10%.
    if new_v < 0.9 * v:
        new_v += divisor
    return new_v
class MobileBlock(nn.Module):
    # def __init__(self, in_channels, out_channels, kernal_size, stride, nonLinear, SE, exp_size, dropout_rate=1.0):
    def __init__(self, in_channels, out_channels, kernal_size, stride, nonLinear, SE, exp_size):
        super(MobileBlock, self).__init__()
        self.out_channels = out_channels
        self.nonLinear = nonLinear
        self.SE = SE
        # self.dropout_rate = dropout_rate
        padding = (kernal_size - 1) // 2

        self.use_connect = stride == 1 and in_channels == out_channels  # 残差选择

        if self.nonLinear == "RE":  # 激活函数选择
            activation = nn.ReLU
        else:
            activation = h_swish

        self.conv = nn.Sequential(
            nn.Conv2d(in_channels, exp_size, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(exp_size),
            activation(inplace=True)
        )
        self.depth_conv = nn.Sequential(
            nn.Conv2d(exp_size, exp_size, kernel_size=kernal_size, stride=stride, padding=padding, groups=exp_size),  # 可分离卷积
            nn.BatchNorm2d(exp_size),
        )

        if self.SE:
            self.squeeze_block = SqueezeBlock(exp_size)  # se模块

        self.point_conv = nn.Sequential(
            nn.Conv2d(exp_size, out_channels, kernel_size=1, stride=1, padding=0),  # 点卷积 1x1卷积
            nn.BatchNorm2d(out_channels),
            activation(inplace=True)
        )

    def forward(self, x):
        # MobileNetV2
        out = self.conv(x)
        out = self.depth_conv(out)

        # Squeeze and Excite
        if self.SE:  # se模块计算
            out = self.squeeze_block(out)

        # point-wise conv
        out = self.point_conv(out)

        # connection
        if self.use_connect:  # 残差计算
            return x + out
        else:
            return out
class SqueezeBlock(nn.Module):
    def __init__(self, exp_size, divide=4):
        super(SqueezeBlock, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.dense = nn.Sequential(
            nn.Linear(exp_size, exp_size // divide),
            nn.ReLU(inplace=True),
            nn.Linear(exp_size // divide, exp_size),
            h_sigmoid()
        )

    def forward(self, x):
        batch, channels, height, width = x.size()
        # out = F.avg_pool2d(x, kernel_size=[height, width]).view(batch, -1)
        out = self.avg_pool(x).view(batch, channels)
        out = self.dense(out)
        out = out.view(batch, channels, 1, 1)
        # out = hard_sigmoid(out)

        return out * x
def _weights_init(m):
    if isinstance(m, nn.Conv2d):
        torch.nn.init.xavier_uniform_(m.weight)
        if m.bias is not None:
            torch.nn.init.zeros_(m.bias)
    elif isinstance(m, nn.BatchNorm2d):
        m.weight.data.fill_(1)
        m.bias.data.zero_()
    elif isinstance(m, nn.Linear):
        n = m.weight.size(1)
        m.weight.data.normal_(0, 0.01)
        m.bias.data.zero_()


class h_sigmoid(nn.Module):
    def __init__(self, inplace=True):
        super(h_sigmoid, self).__init__()
        self.inplace = inplace

    def forward(self, x):
        return F.relu6(x + 3., inplace=self.inplace) / 6.


class h_swish(nn.Module):
    def __init__(self, inplace=True):
        super(h_swish, self).__init__()
        self.inplace = inplace

    def forward(self, x):
        out = F.relu6(x + 3., self.inplace) / 6.
        return out * x

论文链接
代码链接

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这里是 MobileNetV3_Small 的代码实现: ``` import torch import torch.nn as nn class ConvBNReLU(nn.Sequential): """Convolutional layer with batch normalization and ReLU activation.""" def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1): padding = (kernel_size - 1) // 2 super(ConvBNReLU, self).__init__( nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False), nn.BatchNorm2d(out_planes), nn.ReLU6(inplace=True) ) class InvertedResidual(nn.Module): """Inverted residual block.""" def __init__(self, inp, oup, stride, expand_ratio): super(InvertedResidual, self).__init__() hidden_dim = round(inp * expand_ratio) self.use_res_connect = stride == 1 and inp == oup layers = [] if expand_ratio != 1: # pointwise layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1)) layers.extend([ # depthwise ConvBNReLU(hidden_dim, hidden_dim, stride=stride, groups=hidden_dim), # pointwise-linear nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), nn.BatchNorm2d(oup), ]) self.conv = nn.Sequential(*layers) def forward(self, x): if self.use_res_connect: return x + self.conv(x) else: return self.conv(x) class MobileNetv3_small(nn.Module): """MobileNetV3_Small model.""" def __init__(self, num_classes=1000, width_mult=1.0): super(MobileNetv3_small, self).__init__() # setting of inverted residual blocks self.cfgs = [ # k, t, c, SE, s [3, 16, 16, 0, 1], [3, 72, 24, 0, 2], [3, 88, 24, 0, 1], [5, 96, 40, 1, 2], [5, 240, 40, 1, 1], [5, 240, 40, 1, 1], [5, 120, 48, 1, 1], [5, 144, 48, 1, 1], [5, 288, 96, 1, 2], [5, 576, 96, 1, 1], [5, 576, 96, 1, 1], ] # building first layer input_channel = int(16 * width_mult) last_channel = int(576 * width_mult) if width_mult > 1.0 else 576 self.features = [ConvBNReLU(3, input_channel, stride=2)] # building inverted residual blocks for k, exp_size, c, se, s in self.cfgs: output_channel = int(c * width_mult) self.features.append(InvertedResidual(input_channel, output_channel, stride=s, expand_ratio=exp_size/ input_channel)) input_channel = output_channel # building last several layers self.features.append(ConvBNReLU(input_channel, last_channel, kernel_size=1)) self.features.append(nn.AdaptiveAvgPool2d((1, 1))) self.features.append(nn.Conv2d(last_channel, 1024, kernel_size=1, stride=1, padding=0, bias=False)) self.features.append(nn.Hardswish()) self.features.append(nn.Conv2d(1024, num_classes, kernel_size=1, stride=1, padding=0, bias=True)) self.features = nn.Sequential(*self.features) def forward(self, x): x = self.features(x) x = x.view(x.size(0), -1) return x ``` 这个实现中包含了 MobileNetV3_Small 模型中使用的 Inverted Residual Block 和 ConvBNReLU 等基本组件。同时,在实现模型的过程中,作者还考虑到了模型的可配置性,可以通过调整宽度因子 `width_mult` 来控制模型的宽度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值