├── 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')
--------------------------------------------------------------------------------