Skip to content

Commit f55d5ac

Browse files
Add efficientnet family including efficient, efficientlite and efficientv2 (#4)
* Fix tf pretrained model * Code cleanup * Add comments * Add draft efficientnet * Add unit test for efficientnet * Add efficientnet lite * Improve unit tests * Merge efficientnet lite into efficientnet * Add efficientnet v2
1 parent 1186201 commit f55d5ac

16 files changed

+1880
-101
lines changed

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,5 @@ CUDA_VISIBLE_DEVICES= KERAS_BACKEND=tensorflow pytest
1010
## Work in Progress
1111

1212
- Test pretrained weights
13-
- Add `MobileNet100V3SmallMinimal` pretrained weights
14-
- Add `MobileNet100V3LargeMinimal` pretrained weights
1513

1614
## Acknowledgments

kimm/blocks/base_block.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,25 @@ def apply_conv2d_block(
2424
groups=1,
2525
activation=None,
2626
use_depthwise=False,
27+
add_skip=False,
2728
bn_momentum=0.9,
2829
bn_epsilon=1e-5,
30+
padding=None,
2931
name="conv2d_block",
3032
):
3133
if kernel_size is None:
3234
raise ValueError(
3335
f"kernel_size must be passed. Received: kernel_size={kernel_size}"
3436
)
37+
input_channels = inputs.shape[-1]
38+
has_skip = add_skip and strides == 1 and input_channels == filters
3539
x = inputs
3640

37-
padding = "same"
38-
if strides > 1:
39-
padding = "valid"
40-
x = layers.ZeroPadding2D(kernel_size // 2, name=f"{name}_pad")(x)
41+
if padding is None:
42+
padding = "same"
43+
if strides > 1:
44+
padding = "valid"
45+
x = layers.ZeroPadding2D(kernel_size // 2, name=f"{name}_pad")(x)
4146

4247
if not use_depthwise:
4348
x = layers.Conv2D(
@@ -61,6 +66,8 @@ def apply_conv2d_block(
6166
name=f"{name}_bn", momentum=bn_momentum, epsilon=bn_epsilon
6267
)(x)
6368
x = apply_activation(x, activation, name=name)
69+
if has_skip:
70+
x = layers.Add()([x, inputs])
6471
return x
6572

6673

@@ -70,14 +77,17 @@ def apply_se_block(
7077
activation="relu",
7178
gate_activation="sigmoid",
7279
make_divisible_number=None,
80+
se_input_channels=None,
7381
name="se_block",
7482
):
7583
input_channels = inputs.shape[-1]
84+
if se_input_channels is None:
85+
se_input_channels = input_channels
7686
if make_divisible_number is None:
77-
se_channels = round(input_channels * se_ratio)
87+
se_channels = round(se_input_channels * se_ratio)
7888
else:
7989
se_channels = make_divisible(
80-
input_channels * se_ratio, make_divisible_number
90+
se_input_channels * se_ratio, make_divisible_number
8191
)
8292

8393
ori_x = inputs

kimm/layers/attention.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,53 @@ def __init__(
2626
self.name = name
2727

2828
self.qkv = layers.Dense(
29-
hidden_dim * 3, use_bias=use_qkv_bias, name=f"{name}_qkv"
29+
hidden_dim * 3,
30+
use_bias=use_qkv_bias,
31+
dtype=self.dtype,
32+
name=f"{name}_qkv",
3033
)
3134
if use_qk_norm:
32-
self.q_norm = layers.LayerNormalization(name=f"{name}_q_norm")
33-
self.k_norm = layers.LayerNormalization(name=f"{name}_k_norm")
35+
self.q_norm = layers.LayerNormalization(
36+
dtype=self.dtype_policy, name=f"{name}_q_norm"
37+
)
38+
self.k_norm = layers.LayerNormalization(
39+
dtype=self.dtype_policy, name=f"{name}_k_norm"
40+
)
3441
else:
35-
self.q_norm = layers.Identity()
36-
self.k_norm = layers.Identity()
42+
self.q_norm = layers.Identity(dtype=self.dtype_policy)
43+
self.k_norm = layers.Identity(dtype=self.dtype_policy)
3744

3845
self.attention_dropout = layers.Dropout(
39-
attention_dropout_rate, name=f"{name}_attn_drop"
46+
attention_dropout_rate,
47+
dtype=self.dtype_policy,
48+
name=f"{name}_attn_drop",
49+
)
50+
self.projection = layers.Dense(
51+
hidden_dim, dtype=self.dtype_policy, name=f"{name}_proj"
4052
)
41-
self.projection = layers.Dense(hidden_dim, name=f"{name}_proj")
4253
self.projection_dropout = layers.Dropout(
43-
projection_dropout_rate, name=f"{name}_proj_drop"
54+
projection_dropout_rate,
55+
dtype=self.dtype_policy,
56+
name=f"{name}_proj_drop",
4457
)
4558

59+
def build(self, input_shape):
60+
self.qkv.build(input_shape)
61+
qkv_output_shape = list(input_shape)
62+
qkv_output_shape[-1] = qkv_output_shape[-1] * 3
63+
self.q_norm.build(qkv_output_shape)
64+
self.k_norm.build(qkv_output_shape)
65+
attention_input_shape = [
66+
input_shape[0],
67+
self.num_heads,
68+
input_shape[1],
69+
input_shape[1],
70+
]
71+
self.attention_dropout.build(attention_input_shape)
72+
self.projection.build(input_shape)
73+
self.projection_dropout.build(input_shape)
74+
self.built = True
75+
4676
def call(self, inputs, training=None, mask=None):
4777
input_shape = ops.shape(inputs)
4878
qkv = self.qkv(inputs)

kimm/layers/attention_test.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
from absl.testing import parameterized
2-
from keras import random
32
from keras.src import testing
43

54
from kimm.layers.attention import Attention
65

76

87
class AttentionTest(testing.TestCase, parameterized.TestCase):
9-
def test_attention(self):
10-
x = random.uniform([1, 197, 768])
11-
layer = Attention(768)
12-
13-
y = layer(x)
14-
15-
self.assertEqual(y.shape, [1, 197, 768])
8+
def test_attention_basic(self):
9+
self.run_layer_test(
10+
Attention,
11+
init_kwargs={"hidden_dim": 20, "num_heads": 2},
12+
input_shape=(1, 10, 20),
13+
expected_output_shape=(1, 10, 20),
14+
expected_num_trainable_weights=3,
15+
expected_num_non_trainable_weights=0,
16+
expected_num_losses=0,
17+
supports_masking=False,
18+
)

kimm/layers/layer_scale.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def get_config(self):
3030
config.update(
3131
{
3232
"hidden_size": self.hidden_size,
33-
"initializer": self.initializer,
33+
"initializer": initializers.serialize(self.initializer),
3434
"name": self.name,
3535
}
3636
)

kimm/layers/layer_scale_test.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
from absl.testing import parameterized
2-
from keras import random
32
from keras.src import testing
43

54
from kimm.layers.layer_scale import LayerScale
65

76

87
class LayerScaleTest(testing.TestCase, parameterized.TestCase):
9-
def test_layer_scale(self):
10-
x = random.uniform([1, 123])
11-
layer = LayerScale(123)
12-
13-
y = layer(x)
14-
15-
self.assertEqual(y.shape, [1, 123])
8+
def test_layer_scale_basic(self):
9+
self.run_layer_test(
10+
LayerScale,
11+
init_kwargs={"hidden_size": 10},
12+
input_shape=(1, 10),
13+
expected_output_shape=(1, 10),
14+
expected_num_trainable_weights=1,
15+
expected_num_non_trainable_weights=0,
16+
expected_num_losses=0,
17+
supports_masking=False,
18+
)

kimm/layers/position_embedding.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ def __init__(self, **kwargs):
77
super().__init__(**kwargs)
88

99
def build(self, input_shape):
10+
if len(input_shape) != 3:
11+
raise ValueError(
12+
"PositionEmbedding only accepts 3-dimensional input. "
13+
f"Received: input_shape={input_shape}"
14+
)
1015
self.pos_embed = self.add_weight(
1116
shape=[1, input_shape[-2] + 1, input_shape[-1]],
1217
initializer="random_normal",
@@ -26,6 +31,11 @@ def call(self, inputs, training=None, mask=None):
2631
x = ops.add(x, self.pos_embed)
2732
return x
2833

34+
def compute_output_shape(self, input_shape):
35+
output_shape = list(input_shape)
36+
output_shape[1] = output_shape[1] + 1
37+
return output_shape
38+
2939
def get_config(self):
3040
return super().get_config()
3141

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
from absl.testing import parameterized
2-
from keras import random
2+
from keras import layers
33
from keras.src import testing
44

55
from kimm.layers.position_embedding import PositionEmbedding
66

77

88
class PositionEmbeddingTest(testing.TestCase, parameterized.TestCase):
9-
def test_position_embedding(self):
10-
x = random.uniform([1, 123, 768])
11-
layer = PositionEmbedding()
9+
def test_position_embedding_basic(self):
10+
self.run_layer_test(
11+
PositionEmbedding,
12+
init_kwargs={},
13+
input_shape=(1, 10, 10),
14+
expected_output_shape=(1, 11, 10),
15+
expected_num_trainable_weights=2,
16+
expected_num_non_trainable_weights=0,
17+
expected_num_losses=0,
18+
supports_masking=False,
19+
)
1220

13-
y = layer(x)
14-
15-
self.assertEqual(y.shape, [1, 124, 768])
21+
def test_position_embedding_invalid_input_shape(self):
22+
inputs = layers.Input([3])
23+
with self.assertRaisesRegex(
24+
ValueError, "PositionEmbedding only accepts 3-dimensional input."
25+
):
26+
PositionEmbedding()(inputs)

0 commit comments

Comments
 (0)