MobileNet V2 model - pytorch implementation

Paper Portal: https://arxiv.org/pdf/1801.04381.pdf

The purpose of MobileNet V2: to extract features from pictures and classify them according to features. (It can also be used as a backbone to complete detection and segmentation tasks)

Advantages of MobileNet V2: Compared with V1, the model is smaller and the accuracy is higher .

The method of MobileNet V2:

①Use the inverted residual structure (Inverted Residuals) , 1x1 convolution upscale + 3x3 depth convolution + 1x1 convolution dimensionality reduction;

②The last layer of the structure uses the linear layer (Linear Bottlenecks) , and the ReLU6 activation function is no longer performed.

The difference between Inverted residual block and Residual block:

Inverted residual block: 1x1ConvBNReLU6 (dimensional increase)+3x3DepthwiseConvBNReLU6+1x1ConvBN (dimensional reduction)+x;

Residual block: 1x1ConvBNReLU (dimension reduction)+3x3ConvBNReLU+1x1ConvBN (dimension enhancement)+x+ReLU;

The structure of the Inverted residual block: (Only when the depth convolution step is 1 and the number of channels before and after the structure is constant, the residual edge connection is performed)

The structure of MobileNet V2: convolutional block + stacked inverted residual structure + convolutional block + average pooling + convolution

 Note: If you want to use pre-trained weights, please refer to the official pytorch implementation code.

import torch
import torch.nn as nn


def conv_block(in_channel, out_channel, kernel_size=3, strid=1, groups=1):  # 定义卷积块,conv+bn+relu6
    padding = 0 if kernel_size == 1 else 1  # 计算padding
    return nn.Sequential(
        nn.Conv2d(in_channel, out_channel, kernel_size, strid, padding=padding, groups=groups, bias=False),  # conv
        nn.BatchNorm2d(out_channel),  # bn
        nn.ReLU6(inplace=True)  # relu6
    )


class InvertedResidual(nn.Module):  # 定义倒置残差结构,Inverted Residual
    def __init__(self, in_channel, out_channel, strid, t=6):  # 初始化方法
        super(InvertedResidual, self).__init__()  # 继承初始化方法
        self.in_channel = in_channel  # 输入通道数
        self.out_channel = out_channel  # 输出通道数
        self.strid = strid  # 步长
        self.t = t  # 中间层通道扩大倍数,对应原文expansion ratio
        self.hidden_channel = in_channel * t  # 计算中间层通道数

        layers = []  # 存放模型结构
        if self.t != 1:  # 如果expansion ratio不为1
            layers += [conv_block(self.in_channel, self.hidden_channel, kernel_size=1)]  # 添加conv+bn+relu6
        layers += [conv_block(self.hidden_channel, self.hidden_channel, strid=self.strid, groups=self.hidden_channel),
                   # 添加conv+bn+relu6,此处使用组数等于输入通道数的分组卷积实现depthwise conv
                   conv_block(self.hidden_channel, self.out_channel, kernel_size=1)[
                   :-1]]  # 添加1x1conv+bn,此处不再进行relu6
        self.residul_block = nn.Sequential(*layers)  # 倒置残差结构块

    def forward(self, x):  # 前传函数
        if self.strid == 1 and self.in_channel == self.out_channel:  # 如果卷积步长为1且前后通道数一致,则连接残差边
            return x + self.residul_block(x)  # x+F(x)
        else:  # 否则不进行残差连接
            return self.residul_block(x)  # F(x)


class MobileNetV2(nn.Module):  # 定义MobileNet v2网络
    def __init__(self, num_classes):  # 初始化方法
        super(MobileNetV2, self).__init__()  # 继承初始化方法

        self.num_classes = num_classes  # 类别数量
        self.feature = nn.Sequential(  # 特征提取部分
            conv_block(3, 32, strid=2),  # conv+bn+relu6,(n,3,224,224)-->(n,32,112,112)
            InvertedResidual(32, 16, strid=1, t=1),  # inverted residual block,(n,32,112,112)-->(n,16,112,112)
            InvertedResidual(16, 24, strid=2),  # inverted residual block,(n,16,112,112)-->(n,24,56,56)
            InvertedResidual(24, 24, strid=1),  # inverted residual block,(n,24,56,56)-->(n,24,56,56)
            InvertedResidual(24, 32, strid=2),  # inverted residual block,(n,24,56,56)-->(n,32,28,28)
            InvertedResidual(32, 32, strid=1),  # inverted residual block,(n,32,28,28)-->(n,32,28,28)
            InvertedResidual(32, 32, strid=1),  # inverted residual block,(n,32,28,28)-->(n,32,28,28)
            InvertedResidual(32, 64, strid=2),  # inverted residual block,(n,32,28,28)-->(n,64,14,14)
            InvertedResidual(64, 64, strid=1),  # inverted residual block,(n,64,14,14)-->(n,64,14,14)
            InvertedResidual(64, 64, strid=1),  # inverted residual block,(n,64,14,14)-->(n,64,14,14)
            InvertedResidual(64, 64, strid=1),  # inverted residual block,(n,64,14,14)-->(n,64,14,14)
            InvertedResidual(64, 96, strid=1),  # inverted residual block,(n,64,14,14)-->(n,96,14,14)
            InvertedResidual(96, 96, strid=1),  # inverted residual block,(n,96,14,14)-->(n,96,14,14)
            InvertedResidual(96, 96, strid=1),  # inverted residual block,(n,96,14,14)-->(n,96,14,14)
            InvertedResidual(96, 160, strid=2),  # inverted residual block,(n,96,14,14)-->(n,160,7,7)
            InvertedResidual(160, 160, strid=1),  # inverted residual block,(n,160,7,7)-->(n,160,7,7)
            InvertedResidual(160, 160, strid=1),  # inverted residual block,(n,160,7,7)-->(n,160,7,7)
            InvertedResidual(160, 320, strid=1),  # inverted residual block,(n,160,7,7)-->(n,320,7,7)
            conv_block(320, 1280, kernel_size=1)  # conv+bn+relu6,(n,320,7,7)-->(n,1280,7,7)
        )

        self.classifier = nn.Sequential(  # 分类部分
            nn.AdaptiveAvgPool2d(1),  # avgpool,(n,1280,7,7)-->(n,1280,1,1)
            nn.Conv2d(1280, self.num_classes, 1, 1, 0)  # 1x1conv,(n,1280,1,1)-->(n,num_classes,1,1),等同于linear
        )

    def forward(self, x):  # 前传函数
        x = self.feature(x)  # 提取特征
        x = self.classifier(x)  # 分类
        return x.view(-1, self.num_classes)  # 压缩不需要的维度,返回分类结果,(n,num_classes,1,1)-->(n,num_classes)

Guess you like

Origin blog.csdn.net/Peach_____/article/details/128642749