Skip to content

Commit de335cd

Browse files
Feat/efficientnet (#68)
Adding EfficientNet models.
1 parent b3a1ea9 commit de335cd

39 files changed

+2917
-184
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Change Log
22

3+
## Unpublished
4+
5+
- Added EfficinentNet and MobileNet-V2 models.
6+
37
## v0.2.6 - 2022-05-13
48

59
- Added tiny and small ConvNeXt models.

README.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ weights, obtained by porting architectures from
1919
[timm](https://github.com/rwightman/pytorch-image-models) to TensorFlow. The hope is
2020
that the number of available architectures will grow over time. For now, it contains
2121
vision transformers (ViT, DeiT, CaiT, PVT and Swin Transformers), MLP-Mixer models
22-
(MLP-Mixer, ResMLP, gMLP, PoolFormer and ConvMixer) and various ResNet flavours (ResNet,
23-
ResNeXt, ECA-ResNet, SE-ResNet) as well as the recent ConvNeXt.
22+
(MLP-Mixer, ResMLP, gMLP, PoolFormer and ConvMixer), various ResNet flavours (ResNet,
23+
ResNeXt, ECA-ResNet, SE-ResNet), the EfficientNet family (including AdvProp,
24+
NoisyStudent, Edge-TPU, V2 and Lite versions), MobileNet-V2, as well as the recent
25+
ConvNeXt.
2426

2527
This work would not have been possible wihout Ross Wightman's `timm` library and the
2628
work on PyTorch/TensorFlow interoperability in HuggingFace's `transformer` repository.
@@ -144,6 +146,22 @@ The following architectures are currently available:
144146
[\[github\]](https://github.com/tmp-iclr/convmixer)
145147
- Patches Are All You Need?
146148
[\[ICLR 2022 submission\]](https://openreview.net/forum?id=TVHS5Y4dNvM)
149+
- EfficientNet family
150+
- EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks
151+
[\[arXiv:1905.11946\]](https://arxiv.org/abs/1905.11946)
152+
- Adversarial Examples Improve Image Recognition
153+
[\[arXiv:1911.09665\]](https://arxiv.org/abs/1911.09665)
154+
- Self-training with Noisy Student improves ImageNet classification
155+
[\[arXiv:1911.04252\]](https://arxiv.org/abs/1911.04252)
156+
- EfficientNet-EdgeTPU
157+
[\[Blog\]](https://ai.googleblog.com/2019/08/efficientnet-edgetpu-creating.html)
158+
- EfficientNet-Lite
159+
[\[Blog\]](https://blog.tensorflow.org/2020/03/higher-accuracy-on-vision-models-with-efficientnet-lite.html)
160+
- EfficientNetV2: Smaller Models and Faster Training
161+
[\[arXiv:2104.00298\]](https://arxiv.org/abs/2104.00298)
162+
- MobileNet-V2
163+
- MobileNetV2: Inverted Residuals and Linear Bottlenecks
164+
[\[arXiv:1801.04381\]](https://arxiv.org/abs/1801.04381)
147165
- Pyramid Vision Transformer
148166
[\[github\]](https://github.com/whai362/PVT)
149167
- Pyramid Vision Transformer: A Versatile Backbone for Dense Prediction without
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
EfficientNet
2+
============
3+
4+
.. py:module:: tfimm.architectures.efficientnet
5+
6+
.. automodule:: tfimm.architectures.efficientnet
7+
8+
.. autoclass:: EfficientNetConfig
9+
10+
.. autoclass:: EfficientNet
11+
:members: call, forward_features, dummy_inputs, feature_names

docs/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Contents
3333
:caption: Architectures
3434

3535
content/convnext
36+
content/efficientnet
3637
content/pit
3738
content/poolformer
3839

scripts/test_conversion.py

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""
22
This script is used when converting models from PyTorch to TF.
33
"""
4+
import logging
5+
46
import numpy as np
57
import tensorflow as tf
68
import timm
@@ -10,14 +12,19 @@
1012
import tfimm # noqa: F401
1113
from tfimm.utils.timm import load_pytorch_weights_in_tf2_model # noqa: F401
1214

13-
model_name = "resnet18"
15+
logging.basicConfig(level=logging.INFO)
16+
17+
model_name = "efficientnet_b0"
18+
pt_model_name = "tf_efficientnet_b0"
1419

1520
# We need to test models in both training and inference mode (BN)
1621
training = False
1722
nb_calls = 3
1823

1924
# Load PyTorch model
20-
pt_model = timm.create_model(model_name, pretrained=True)
25+
pt_model = timm.create_model(
26+
pt_model_name, pretrained=True, drop_rate=0.0, drop_path_rate=0.0
27+
)
2128
# If a model is not part of the `timm` library, we can load the state dict directly
2229
# state_dict = load_state_dict_from_url(
2330
# url="https://github.com/sail-sg/poolformer/releases/download/v1.0/poolformer_m48.pth.tar" # noqa: E501
@@ -35,17 +42,32 @@
3542
if not training: # Set PyTorch model to inference mode
3643
pt_model.eval()
3744

45+
# Create test input
46+
img = np.random.rand(5, 224, 224, 3).astype("float32")
47+
48+
# Run inference for PyTorch model
49+
pt_img = torch.Tensor(img.transpose([0, 3, 1, 2]))
50+
if training:
51+
for _ in range(nb_calls):
52+
_ = pt_model.forward(pt_img)
53+
pt_res = pt_model.forward(pt_img)
54+
pt_res = pt_res.detach().numpy()
55+
# When we look at output of intermediate layers, we have to transpose PyTorch data
56+
# format (NCHW) to TF data format (NHWC). We don't have to do this, if we only look
57+
# at the final logits
58+
# pt_res = pt_res.transpose([0, 2, 3, 1])
59+
print(pt_res.shape)
60+
3861
# Load TF model
39-
tf_model = tfimm.create_model(model_name, pretrained="timm")
62+
tf_model = tfimm.create_model(
63+
model_name, pretrained=True, drop_rate=0.0, drop_path_rate=0.0
64+
)
4065
# If we want to load the weights from a pytorch model outside the model factory:
4166
# load_pytorch_weights_in_tf2_model(tf_model, pt_model.state_dict())
4267
# For debug purposes we may want to print variable names
4368
# for w in tf_model.weights:
4469
# print(w.name)
4570

46-
# Create test input
47-
img = np.random.rand(5, 224, 224, 3).astype("float32")
48-
4971
# Run inference for TF model
5072
tf_img = tf.constant(img)
5173
if training: # If training we do multiple forward passes to test BN param updates
@@ -59,19 +81,6 @@
5981
tf_res = tf_res.numpy()
6082
print(tf_res.shape)
6183

62-
# Run inference for PyTorch model
63-
pt_img = torch.Tensor(img.transpose([0, 3, 1, 2]))
64-
if training:
65-
for _ in range(nb_calls):
66-
_ = pt_model.forward(pt_img)
67-
pt_res = pt_model.forward(pt_img)
68-
pt_res = pt_res.detach().numpy()
69-
# When we look at output of intermediate layers, we have to transpose PyTorch data
70-
# format (NCHW) to TF data format (NHWC). We don't have to do this, if we only look
71-
# at the final logits
72-
# pt_res = pt_res.transpose([0, 2, 3, 1])
73-
print(pt_res.shape)
74-
7584
# Compare outputs between PyTorch and Tensorflow. We should expect the relative error
7685
# to be <1e-5. It won't be much lower, because TF and PyTorch implement BN slightly
7786
# differently. The two formulas are mathematically, but not numerically equivalent.

tests/models/architectures.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
ConvMixerConfig,
66
ConvNeXt,
77
ConvNeXtConfig,
8+
EfficientNet,
9+
EfficientNetConfig,
810
MLPMixer,
911
MLPMixerConfig,
1012
PoolFormer,
@@ -30,6 +32,7 @@
3032
"cait_test_model", # cait.py
3133
"convmixer_test_model", # convmixer.py
3234
"convnext_test_model", # convnext.py
35+
"efficientnet_test_model", # efficientnet.py
3336
"mixer_test_model", # mlp_mixer.py
3437
"resmlp_test_model",
3538
"gmlp_test_model",
@@ -90,6 +93,21 @@ def convnext_test_model():
9093
return ConvNeXt, cfg
9194

9295

96+
@register_model
97+
def efficientnet_test_model():
98+
cfg = EfficientNetConfig(
99+
name="efficientnet_test_model",
100+
input_size=(32, 32),
101+
architecture=(
102+
("ds_r1_k3_s1_e1_c16_se0.25",),
103+
("ir_r2_k3_s2_e6_c24_se0.25",),
104+
("er_r1_k3_s1_e4_c24_fc24_noskip",),
105+
),
106+
nb_features=32,
107+
)
108+
return EfficientNet, cfg
109+
110+
93111
@register_model
94112
def mixer_test_model():
95113
cfg = MLPMixerConfig(

tests/models/test_factory.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def test_change_in_channels(model_name, in_channels):
8787
# based on ResNetV2, because they use `StdConv`, which normalizes weight
8888
# statistics internally. The models are still adaptable, but results won't be
8989
# the same.
90-
assert (np.max(np.abs(y_1 - y_2))) / (np.max(np.abs(y_1)) + 1e-8) < 1e-5
90+
assert np.all(np.isclose(y_1, y_2, rtol=1e-5, atol=1e-5))
9191

9292

9393
@pytest.mark.parametrize("model_name", TEST_ARCHITECTURES)
@@ -96,7 +96,7 @@ def test_save_load_model(model_name):
9696
model = create_model(model_name)
9797
with tempfile.TemporaryDirectory() as tmpdir:
9898
model.save(tmpdir)
99-
loaded_model = tf.keras.models.load_model(tmpdir)
99+
loaded_model = tf.keras.models.load_model(tmpdir, compile=False)
100100

101101
assert type(model) is type(loaded_model)
102102

@@ -179,6 +179,17 @@ def test_change_input_size_inference(model_name):
179179
flexible_model(img)
180180

181181

182+
@pytest.mark.parametrize("model_name", TEST_ARCHITECTURES)
183+
def test_model_name_keras(model_name):
184+
"""
185+
We test if model.name == model.cfg.name, i.e., the keras model name is set
186+
correctly.
187+
"""
188+
tf.keras.backend.clear_session()
189+
model = create_model(model_name)
190+
assert model.name == model_name == model.cfg.name
191+
192+
182193
@pytest.mark.parametrize("model_name", TEST_ARCHITECTURES)
183194
def test_variable_prefix(model_name):
184195
"""

tests/test_timm.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Tuple, Union
2+
13
import numpy as np
24
import pytest
35
import tensorflow as tf
@@ -12,6 +14,7 @@
1214
"cait_xxs24_224", # cait.py
1315
"convmixer_768_32", # convmixer.py
1416
"convnext_tiny", # convnext.py
17+
("efficientnet_b0", "tf_efficientnet_b0"), # efficientnet.py
1518
"mixer_s32_224", # mlp_mixer.py
1619
"resmlp_12_224",
1720
"gmlp_ti16_224",
@@ -31,15 +34,21 @@
3134

3235

3336
@pytest.mark.parametrize("model_name", TIMM_ARCHITECTURES)
34-
def test_load_timm_model(model_name: str):
37+
def test_load_timm_model(model_name: Union[str, Tuple[str, str]]):
3538
"""Test if we can load models from timm."""
39+
# To cater for those models, where TIMM name differs from TFIMM name
40+
if isinstance(model_name, tuple):
41+
tf_model_name, pt_model_name = model_name
42+
else:
43+
tf_model_name = pt_model_name = model_name
44+
3645
# We don't need to load the pretrained weights from timm, we only need a PyTorch
3746
# model, that we then convert to tensorflow. This allows us to run these tests
3847
# in GitHub CI without data transfer issues.
39-
pt_model = timm.create_model(model_name, pretrained=False)
48+
pt_model = timm.create_model(pt_model_name, pretrained=False)
4049
pt_model.eval()
4150

42-
tf_model = create_model(model_name, pretrained=False)
51+
tf_model = create_model(tf_model_name, pretrained=False)
4352
load_pytorch_weights_in_tf2_model(tf_model, pt_model.state_dict())
4453

4554
rng = np.random.default_rng(2021)

tfimm/architectures/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from .cait import * # noqa: F401
22
from .convmixer import * # noqa: F401
33
from .convnext import * # noqa: F401
4+
from .efficientnet import * # noqa: F401
45
from .mlp_mixer import * # noqa: F401
56
from .pit import * # noqa: F401
67
from .poolformer import * # noqa: F401

tfimm/architectures/cait.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ class CaiT(tf.keras.Model):
319319
cfg_class = CaiTConfig
320320

321321
def __init__(self, cfg: CaiTConfig, *args, **kwargs):
322+
kwargs["name"] = kwargs.get("name", cfg.name)
322323
super().__init__(*args, **kwargs)
323324
self.cfg = cfg
324325
self.nb_features = cfg.embed_dim

0 commit comments

Comments
 (0)