-
Notifications
You must be signed in to change notification settings - Fork 1
/
MobileNetV2.py
147 lines (130 loc) · 5.49 KB
/
MobileNetV2.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import torch.nn as nn
import math
import numpy as np
import torch
from torchsummary import summary
def conv_bn(inp,oup,stride):
return nn.Sequential(
nn.Conv2d(inp,oup,3,stride,1,bias=False),
nn.BatchNorm2d(oup),
nn.ReLU6(inplace=True)
)
def conv_1x1_bn(inp,oup):
return nn.Sequential(
nn.Conv2d(inp,oup,1,1,0,bias=False),
nn.BatchNorm2d(oup),
nn.ReLU6(inplace=True)
)
def make_divisible(x,divisible_by=8):
return int(np.ceil(x * 1./divisible_by) * divisible_by)
class InvertedResidual(nn.Module):
def __init__(self,inp,oup,stride,expand_ratio):
super(InvertedResidual,self).__init__()
self.stride = stride
assert stride in [1,2]
hidden_dim = int(inp * expand_ratio)
# 如果stide == 1 并且 输入 等于 输出 则使用残差连接
self.use_res_connect = self.stride == 1 and inp == oup
# 这里为什么 扩大比例为1时 不需要pw
if expand_ratio == 1:
self.conv = nn.Sequential(
# dw groups作用是控制分组卷积,默认不分组,为1组
nn.Conv2d(hidden_dim,hidden_dim,3,stride,1,groups=hidden_dim,bias=False),
nn.BatchNorm2d(hidden_dim),
nn.ReLU6(inplace=True),
# pw-linear
nn.Conv2d(hidden_dim,oup,1,1,0,bias=False),
nn.BatchNorm2d(oup)
)
else:
self.conv = nn.Sequential(
# pw
nn.Conv2d(inp,hidden_dim,1,1,0,bias=False),
nn.BatchNorm2d(oup),
nn.ReLU6(inplace=True),
# dw
nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False),
nn.BatchNorm2d(hidden_dim),
nn.ReLU6(inplace=True),
# pw-linear
nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup),
)
def forward(self,x):
if self.use_res_connect:
return x + self.conv(x)
else:
return self.conv(x)
class MobileNetV2(nn.Module):
def __init__(self,n_class=1000,input_size=224,width_mult=1):
super(MobileNetV2, self).__init__()
block = InvertedResidual
input_channel = 32
last_channel = 1280
interverted_residual_setting = [
# t:扩展系数,c:输出通道数,n:该模块重复次数,s:第一次出现时的步长(后面重复该模块步长都是1)
# 注意: 扩展系数即invertedResidual Block中第一个1x1卷积核的个数是输入的通道数的多少倍,也就是中间部分的通道数是输入的通道数的多少倍
[1,16,1,1],
[6,24,2,2],
[6,32,3,2],
[6,64,4,2],
[6,96,3,1],
[6,160,3,2],
[6,320,1,1],
]
# building first layer 第一层是正常的卷积层 224x224x3 输入为3通道的图片
assert input_size%32 == 0
# input_channel = make_divisible(input_channel * width_mult) # first channel is always 32!
self.last_channel = make_divisible(last_channel * width_mult) if width_mult > 1.0 else last_channel
self.features = [conv_bn(1,input_channel,2)]
# building inverted residual blocks
for t,c,n,s in interverted_residual_setting:
# 乘放大系数
output_channel = make_divisible(c * width_mult) if t > 1 else c
for i in range(n):
if i == 0:
self.features.append(block(input_channel,output_channel,s,expand_ratio=1))
else:
self.features.append(block(input_channel, output_channel, 1, expand_ratio=1))
input_channel = output_channel
# building last several layers
self.features.append(conv_1x1_bn(input_channel, self.last_channel))
# make it nn.Sequential
self.features = nn.Sequential(*self.features)
# building classifier
self.classifier = nn.Linear(self.last_channel,n_class)
self._initialize_weights()
def forward(self,x):
x = self.features(x)
x = x.mean(3).mean(2)
x = self.classifier(x)
return x
def _initialize_weights(self):
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))
if m.bias is not None:
m.bias.data.zero_()
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_()
def mobilenet_v2(pretrained = True):
model = MobileNetV2(width_mult=1)
# 根据实际需求调整最后一层全连接层 model.classifier = nn.Linear(1280,1000)
if pretrained:
try:
from torch.hub import load_state_dict_from_url
except ImportError:
from torch.utils.model_zoo import load_url as load_state_dict_from_url
model.load_state_dict(torch.load('./mobienetv2.pth'),strict=False)
return model
if __name__ == "__main__":
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
net = mobilenet_v2(True).to(device)
# summary params:模型、输入尺寸、批数量、设备
print(summary(net,(3,224,224)))