Skip to content

Commit 3e69462

Browse files
authored
Add support for other normalizations in MobileNetV2 (#2267)
* Add norm_layer to MobileNetV2 * Add simple test case * Small fix
1 parent c8064cd commit 3e69462

File tree

2 files changed

+34
-11
lines changed

2 files changed

+34
-11
lines changed

test/test_models.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from collections import OrderedDict
33
from itertools import product
44
import torch
5+
import torch.nn as nn
56
import numpy as np
67
from torchvision import models
78
import unittest
@@ -240,6 +241,17 @@ def test_mobilenetv2_residual_setting(self):
240241
out = model(x)
241242
self.assertEqual(out.shape[-1], 1000)
242243

244+
def test_mobilenetv2_norm_layer(self):
245+
model = models.__dict__["mobilenet_v2"]()
246+
self.assertTrue(any(isinstance(x, nn.BatchNorm2d) for x in model.modules()))
247+
248+
def get_gn(num_channels):
249+
return nn.GroupNorm(32, num_channels)
250+
251+
model = models.__dict__["mobilenet_v2"](norm_layer=get_gn)
252+
self.assertFalse(any(isinstance(x, nn.BatchNorm2d) for x in model.modules()))
253+
self.assertTrue(any(isinstance(x, nn.GroupNorm) for x in model.modules()))
254+
243255
def test_fasterrcnn_double(self):
244256
model = models.detection.fasterrcnn_resnet50_fpn(num_classes=50, pretrained_backbone=False)
245257
model.double()

torchvision/models/mobilenet.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,34 +31,39 @@ def _make_divisible(v, divisor, min_value=None):
3131

3232

3333
class ConvBNReLU(nn.Sequential):
34-
def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1):
34+
def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1, norm_layer=None):
3535
padding = (kernel_size - 1) // 2
36+
if norm_layer is None:
37+
norm_layer = nn.BatchNorm2d
3638
super(ConvBNReLU, self).__init__(
3739
nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False),
38-
nn.BatchNorm2d(out_planes),
40+
norm_layer(out_planes),
3941
nn.ReLU6(inplace=True)
4042
)
4143

4244

4345
class InvertedResidual(nn.Module):
44-
def __init__(self, inp, oup, stride, expand_ratio):
46+
def __init__(self, inp, oup, stride, expand_ratio, norm_layer=None):
4547
super(InvertedResidual, self).__init__()
4648
self.stride = stride
4749
assert stride in [1, 2]
4850

51+
if norm_layer is None:
52+
norm_layer = nn.BatchNorm2d
53+
4954
hidden_dim = int(round(inp * expand_ratio))
5055
self.use_res_connect = self.stride == 1 and inp == oup
5156

5257
layers = []
5358
if expand_ratio != 1:
5459
# pw
55-
layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1))
60+
layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1, norm_layer=norm_layer))
5661
layers.extend([
5762
# dw
58-
ConvBNReLU(hidden_dim, hidden_dim, stride=stride, groups=hidden_dim),
63+
ConvBNReLU(hidden_dim, hidden_dim, stride=stride, groups=hidden_dim, norm_layer=norm_layer),
5964
# pw-linear
6065
nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
61-
nn.BatchNorm2d(oup),
66+
norm_layer(oup),
6267
])
6368
self.conv = nn.Sequential(*layers)
6469

@@ -75,7 +80,8 @@ def __init__(self,
7580
width_mult=1.0,
7681
inverted_residual_setting=None,
7782
round_nearest=8,
78-
block=None):
83+
block=None,
84+
norm_layer=None):
7985
"""
8086
MobileNet V2 main class
8187
@@ -86,12 +92,17 @@ def __init__(self,
8692
round_nearest (int): Round the number of channels in each layer to be a multiple of this number
8793
Set to 1 to turn off rounding
8894
block: Module specifying inverted residual building block for mobilenet
95+
norm_layer: Module specifying the normalization layer to use
8996
9097
"""
9198
super(MobileNetV2, self).__init__()
9299

93100
if block is None:
94101
block = InvertedResidual
102+
103+
if norm_layer is None:
104+
norm_layer = nn.BatchNorm2d
105+
95106
input_channel = 32
96107
last_channel = 1280
97108

@@ -115,16 +126,16 @@ def __init__(self,
115126
# building first layer
116127
input_channel = _make_divisible(input_channel * width_mult, round_nearest)
117128
self.last_channel = _make_divisible(last_channel * max(1.0, width_mult), round_nearest)
118-
features = [ConvBNReLU(3, input_channel, stride=2)]
129+
features = [ConvBNReLU(3, input_channel, stride=2, norm_layer=norm_layer)]
119130
# building inverted residual blocks
120131
for t, c, n, s in inverted_residual_setting:
121132
output_channel = _make_divisible(c * width_mult, round_nearest)
122133
for i in range(n):
123134
stride = s if i == 0 else 1
124-
features.append(block(input_channel, output_channel, stride, expand_ratio=t))
135+
features.append(block(input_channel, output_channel, stride, expand_ratio=t, norm_layer=norm_layer))
125136
input_channel = output_channel
126137
# building last several layers
127-
features.append(ConvBNReLU(input_channel, self.last_channel, kernel_size=1))
138+
features.append(ConvBNReLU(input_channel, self.last_channel, kernel_size=1, norm_layer=norm_layer))
128139
# make it nn.Sequential
129140
self.features = nn.Sequential(*features)
130141

@@ -140,7 +151,7 @@ def __init__(self,
140151
nn.init.kaiming_normal_(m.weight, mode='fan_out')
141152
if m.bias is not None:
142153
nn.init.zeros_(m.bias)
143-
elif isinstance(m, nn.BatchNorm2d):
154+
elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
144155
nn.init.ones_(m.weight)
145156
nn.init.zeros_(m.bias)
146157
elif isinstance(m, nn.Linear):

0 commit comments

Comments
 (0)