├── README.md ├── MobileNetV1.py └── MobileNetV2.py /README.md: -------------------------------------------------------------------------------- 1 | # Implementation of MobileNet 2 | ### MobileNetV1 3 | MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications 4 |
paper: https://arxiv.org/abs/1704.04861 5 | 6 | ### MobileNetV2 7 | MobileNetV2: Inverted Residuals and Linear Bottlenecks 8 |
paper: https://arxiv.org/abs/1801.04381 9 | 10 | ### MobileNetV3 11 | Searching for MobileNetV3 12 |
paper: https://arxiv.org/abs/1905.02244 13 | -------------------------------------------------------------------------------- /MobileNetV1.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | from torchsummary import summary 3 | 4 | class MobileNetV1(nn.Module): 5 | def __init__(self, ch_in, n_classes): 6 | super(MobileNetV1, self).__init__() 7 | 8 | def conv_bn(inp, oup, stride): 9 | return nn.Sequential( 10 | nn.Conv2d(inp, oup, 3, stride, 1, bias=False), 11 | nn.BatchNorm2d(oup), 12 | nn.ReLU(inplace=True) 13 | ) 14 | 15 | def conv_dw(inp, oup, stride): 16 | return nn.Sequential( 17 | # dw 18 | nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False), 19 | nn.BatchNorm2d(inp), 20 | nn.ReLU(inplace=True), 21 | 22 | # pw 23 | nn.Conv2d(inp, oup, 1, 1, 0, bias=False), 24 | nn.BatchNorm2d(oup), 25 | nn.ReLU(inplace=True), 26 | ) 27 | 28 | self.model = nn.Sequential( 29 | conv_bn(ch_in, 32, 2), 30 | conv_dw(32, 64, 1), 31 | conv_dw(64, 128, 2), 32 | conv_dw(128, 128, 1), 33 | conv_dw(128, 256, 2), 34 | conv_dw(256, 256, 1), 35 | conv_dw(256, 512, 2), 36 | conv_dw(512, 512, 1), 37 | conv_dw(512, 512, 1), 38 | conv_dw(512, 512, 1), 39 | conv_dw(512, 512, 1), 40 | conv_dw(512, 512, 1), 41 | conv_dw(512, 1024, 2), 42 | conv_dw(1024, 1024, 1), 43 | nn.AdaptiveAvgPool2d(1) 44 | ) 45 | self.fc = nn.Linear(1024, n_classes) 46 | 47 | def forward(self, x): 48 | x = self.model(x) 49 | x = x.view(-1, 1024) 50 | x = self.fc(x) 51 | return x 52 | 53 | if __name__=='__main__': 54 | # model check 55 | model = MobileNetV1(ch_in=3, n_classes=1000) 56 | summary(model, input_size=(3, 224, 224), device='cpu') 57 | -------------------------------------------------------------------------------- /MobileNetV2.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | from torchsummary import summary 3 | 4 | def dwise_conv(ch_in, stride=1): 5 | return ( 6 | nn.Sequential( 7 | #depthwise 8 | nn.Conv2d(ch_in, ch_in, kernel_size=3, padding=1, stride=stride, groups=ch_in, bias=False), 9 | nn.BatchNorm2d(ch_in), 10 | nn.ReLU6(inplace=True), 11 | ) 12 | ) 13 | 14 | def conv1x1(ch_in, ch_out): 15 | return ( 16 | nn.Sequential( 17 | nn.Conv2d(ch_in, ch_out, kernel_size=1, padding=0, stride=1, bias=False), 18 | nn.BatchNorm2d(ch_out), 19 | nn.ReLU6(inplace=True) 20 | ) 21 | ) 22 | 23 | def conv3x3(ch_in, ch_out, stride): 24 | return ( 25 | nn.Sequential( 26 | nn.Conv2d(ch_in, ch_out, kernel_size=3, padding=1, stride=stride, bias=False), 27 | nn.BatchNorm2d(ch_out), 28 | nn.ReLU6(inplace=True) 29 | ) 30 | ) 31 | 32 | class InvertedBlock(nn.Module): 33 | def __init__(self, ch_in, ch_out, expand_ratio, stride): 34 | super(InvertedBlock, self).__init__() 35 | 36 | self.stride = stride 37 | assert stride in [1,2] 38 | 39 | hidden_dim = ch_in * expand_ratio 40 | 41 | self.use_res_connect = self.stride==1 and ch_in==ch_out 42 | 43 | layers = [] 44 | if expand_ratio != 1: 45 | layers.append(conv1x1(ch_in, hidden_dim)) 46 | layers.extend([ 47 | #dw 48 | dwise_conv(hidden_dim, stride=stride), 49 | #pw 50 | conv1x1(hidden_dim, ch_out) 51 | ]) 52 | 53 | self.layers = nn.Sequential(*layers) 54 | 55 | def forward(self, x): 56 | if self.use_res_connect: 57 | return x + self.layers(x) 58 | else: 59 | return self.layers(x) 60 | 61 | class MobileNetV2(nn.Module): 62 | def __init__(self, ch_in=3, n_classes=1000): 63 | super(MobileNetV2, self).__init__() 64 | 65 | self.configs=[ 66 | # t, c, n, s 67 | [1, 16, 1, 1], 68 | [6, 24, 2, 2], 69 | [6, 32, 3, 2], 70 | [6, 64, 4, 2], 71 | [6, 96, 3, 1], 72 | [6, 160, 3, 2], 73 | [6, 320, 1, 1] 74 | ] 75 | 76 | self.stem_conv = conv3x3(ch_in, 32, stride=2) 77 | 78 | layers = [] 79 | input_channel = 32 80 | for t, c, n, s in self.configs: 81 | for i in range(n): 82 | stride = s if i == 0 else 1 83 | layers.append(InvertedBlock(ch_in=input_channel, ch_out=c, expand_ratio=t, stride=stride)) 84 | input_channel = c 85 | 86 | self.layers = nn.Sequential(*layers) 87 | 88 | self.last_conv = conv1x1(input_channel, 1280) 89 | 90 | self.classifier = nn.Sequential( 91 | nn.Dropout2d(0.2), 92 | nn.Linear(1280, n_classes) 93 | ) 94 | self.avg_pool = nn.AdaptiveAvgPool2d(1) 95 | 96 | def forward(self, x): 97 | x = self.stem_conv(x) 98 | x = self.layers(x) 99 | x = self.last_conv(x) 100 | x = self.avg_pool(x).view(-1, 1280) 101 | x = self.classifier(x) 102 | return x 103 | 104 | 105 | if __name__=="__main__": 106 | # model check 107 | model = MobileNetV2(ch_in=3, n_classes=1000) 108 | summary(model, (3, 224, 224), device='cpu') --------------------------------------------------------------------------------