Skip to content

Commit 1186201

Browse files
Add MobileNetV2 and MobileNetV3 (#3)
* Fix features and add mobilenet_v2 * Fix se block naming * Add mobilenet v3 * Add mobilenet v3 large minimal model
1 parent d46785a commit 1186201

16 files changed

+1670
-74
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,10 @@
77
CUDA_VISIBLE_DEVICES= KERAS_BACKEND=tensorflow pytest
88
```
99

10+
## Work in Progress
11+
12+
- Test pretrained weights
13+
- Add `MobileNet100V3SmallMinimal` pretrained weights
14+
- Add `MobileNet100V3LargeMinimal` pretrained weights
15+
1016
## Acknowledgments

kimm/blocks/base_block.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ def apply_activation(x, activation=None, name="activation"):
1818

1919
def apply_conv2d_block(
2020
inputs,
21-
filters,
22-
kernel_size,
21+
filters=None,
22+
kernel_size=None,
2323
strides=1,
2424
groups=1,
2525
activation=None,
@@ -28,6 +28,10 @@ def apply_conv2d_block(
2828
bn_epsilon=1e-5,
2929
name="conv2d_block",
3030
):
31+
if kernel_size is None:
32+
raise ValueError(
33+
f"kernel_size must be passed. Received: kernel_size={kernel_size}"
34+
)
3135
x = inputs
3236

3337
padding = "same"
@@ -80,12 +84,12 @@ def apply_se_block(
8084
x = inputs
8185
x = layers.GlobalAveragePooling2D(keepdims=True, name=f"{name}_mean")(x)
8286
x = layers.Conv2D(
83-
se_channels, 1, use_bias=True, name=f"{name}_reduce_conv2d"
87+
se_channels, 1, use_bias=True, name=f"{name}_conv_reduce"
8488
)(x)
85-
x = apply_activation(x, activation, name=f"{name}_act")
89+
x = apply_activation(x, activation, name=f"{name}_act1")
8690
x = layers.Conv2D(
87-
input_channels, 1, use_bias=True, name=f"{name}_expand_conv2d"
91+
input_channels, 1, use_bias=True, name=f"{name}_conv_expand"
8892
)(x)
89-
x = apply_activation(x, gate_activation, name=f"{name}_gate_act")
93+
x = apply_activation(x, gate_activation, name=f"{name}_gate")
9094
out = layers.Multiply(name=name)([ori_x, x])
9195
return out

kimm/models/ghostnet.py

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -290,42 +290,40 @@ def __init__(
290290
x = apply_conv2d_block(
291291
x, stem_channels, 3, 2, activation="relu", name="conv_stem"
292292
)
293-
features["S2"] = x
293+
features["STEM_S2"] = x
294294

295295
# blocks
296296
total_layer_idx = 0
297-
block_idx = 0
298-
net_stride = 2
299-
for cfg in config:
300-
layer_idx = 0
301-
strides = 1
302-
for kernel_size, expand_size, channels, se_ratio, strides in cfg:
303-
output_channels = make_divisible(channels * width, 4)
304-
hidden_channels = make_divisible(expand_size * width, 4)
297+
current_stride = 2
298+
for current_block_idx, cfg in enumerate(config):
299+
for current_layer_idx, (k, e, c, se, s) in enumerate(cfg):
300+
output_channels = make_divisible(c * width, 4)
301+
hidden_channels = make_divisible(e * width, 4)
305302
use_attention = False
306303
if version == "v2" and total_layer_idx > 1:
307304
use_attention = True
305+
name = f"blocks_{current_block_idx}_{current_layer_idx}"
308306
x = apply_ghost_bottleneck(
309307
x,
310308
hidden_channels,
311309
output_channels,
312-
kernel_size,
313-
strides,
314-
se_ratio=se_ratio,
310+
k,
311+
s,
312+
se_ratio=se,
315313
use_attention=use_attention,
316-
name=f"blocks{block_idx}_{layer_idx}",
314+
name=name,
317315
)
318-
layer_idx += 1
319316
total_layer_idx += 1
320-
if strides > 1:
321-
net_stride *= strides
322-
# add feature
323-
features[f"S{net_stride}"] = x
324-
block_idx += 1
317+
current_stride *= s
318+
features[f"BLOCK{current_block_idx}_S{current_stride}"] = x
325319
# post stages conv block
326-
output_channels = make_divisible(expand_size * width, 4)
320+
output_channels = make_divisible(e * width, 4)
327321
x = apply_conv2d_block(
328-
x, output_channels, 1, activation="relu", name=f"blocks{block_idx}"
322+
x,
323+
output_channels,
324+
1,
325+
activation="relu",
326+
name=f"blocks_{current_block_idx+1}",
329327
)
330328

331329
if include_top:
@@ -366,8 +364,14 @@ def __init__(
366364

367365
@staticmethod
368366
def available_feature_keys():
369-
# predefined for better UX
370-
return [f"S{2**i}" for i in range(1, 6)]
367+
feature_keys = ["STEM_S2"]
368+
feature_keys.extend(
369+
[
370+
f"BLOCK{i}_S{j}"
371+
for i, j in zip(range(9), [2, 4, 4, 8, 8, 16, 16, 32, 32])
372+
]
373+
)
374+
return feature_keys
371375

372376
def get_config(self):
373377
config = super().get_config()

kimm/models/ghostnet_test.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ def test_ghostnet_feature_extractor(self, model_class):
2525
y = model.predict(x)
2626

2727
self.assertIsInstance(y, dict)
28-
self.assertEqual(list(y["S2"].shape), [1, 112, 112, 16])
29-
self.assertEqual(list(y["S4"].shape), [1, 56, 56, 24])
30-
self.assertEqual(list(y["S8"].shape), [1, 28, 28, 40])
31-
self.assertEqual(list(y["S16"].shape), [1, 14, 14, 80])
32-
self.assertEqual(list(y["S32"].shape), [1, 7, 7, 160])
28+
self.assertAllEqual(
29+
list(y.keys()), model_class.available_feature_keys()
30+
)
31+
self.assertEqual(list(y["STEM_S2"].shape), [1, 112, 112, 16])
32+
self.assertEqual(list(y["BLOCK1_S4"].shape), [1, 56, 56, 24])
33+
self.assertEqual(list(y["BLOCK3_S8"].shape), [1, 28, 28, 40])
34+
self.assertEqual(list(y["BLOCK5_S16"].shape), [1, 14, 14, 80])
35+
self.assertEqual(list(y["BLOCK7_S32"].shape), [1, 7, 7, 160])
3336

3437
@parameterized.named_parameters([(GhostNet100V2.__name__, GhostNet100V2)])
3538
def test_ghostnetv2_base(self, model_class):
@@ -49,8 +52,11 @@ def test_ghostnetv2_feature_extractor(self, model_class):
4952
y = model.predict(x)
5053

5154
self.assertIsInstance(y, dict)
52-
self.assertEqual(list(y["S2"].shape), [1, 112, 112, 16])
53-
self.assertEqual(list(y["S4"].shape), [1, 56, 56, 24])
54-
self.assertEqual(list(y["S8"].shape), [1, 28, 28, 40])
55-
self.assertEqual(list(y["S16"].shape), [1, 14, 14, 80])
56-
self.assertEqual(list(y["S32"].shape), [1, 7, 7, 160])
55+
self.assertAllEqual(
56+
list(y.keys()), model_class.available_feature_keys()
57+
)
58+
self.assertEqual(list(y["STEM_S2"].shape), [1, 112, 112, 16])
59+
self.assertEqual(list(y["BLOCK1_S4"].shape), [1, 56, 56, 24])
60+
self.assertEqual(list(y["BLOCK3_S8"].shape), [1, 28, 28, 40])
61+
self.assertEqual(list(y["BLOCK5_S16"].shape), [1, 14, 14, 80])
62+
self.assertEqual(list(y["BLOCK7_S32"].shape), [1, 7, 7, 160])

0 commit comments

Comments
 (0)