From 79110fbf200cea4d12e7efaa2f925de3f6c5c8ea Mon Sep 17 00:00:00 2001 From: Piseth Ky Date: Mon, 4 Nov 2024 16:33:06 -0800 Subject: [PATCH 01/10] WIP initially adding edge presets --- .../efficientnet/efficientnet_backbone.py | 12 ++ .../src/models/efficientnet/fusedmbconv.py | 10 +- .../src/utils/timm/convert_efficientnet.py | 161 +++++++++++++++--- .../convert_efficientnet_checkpoints.py | 13 +- 4 files changed, 160 insertions(+), 36 deletions(-) diff --git a/keras_hub/src/models/efficientnet/efficientnet_backbone.py b/keras_hub/src/models/efficientnet/efficientnet_backbone.py index 4016bb01e4..1deef08cf7 100644 --- a/keras_hub/src/models/efficientnet/efficientnet_backbone.py +++ b/keras_hub/src/models/efficientnet/efficientnet_backbone.py @@ -100,6 +100,7 @@ def __init__( stackwise_squeeze_and_excite_ratios, stackwise_strides, stackwise_block_types, + stackwise_force_input_filters=[0]*7, dropout=0.2, depth_divisor=8, min_depth=8, @@ -163,6 +164,7 @@ def __init__( num_repeats = stackwise_num_repeats[i] input_filters = stackwise_input_filters[i] output_filters = stackwise_output_filters[i] + force_input_filters = stackwise_force_input_filters[i] # Update block input and output filters based on depth multiplier. input_filters = round_filters( @@ -200,6 +202,16 @@ def __init__( self._pyramid_outputs[f"P{curr_pyramid_level}"] = x curr_pyramid_level += 1 + if force_input_filters > 0: + input_filters = round_filters( + filters=force_input_filters, + width_coefficient=width_coefficient, + min_depth=min_depth, + depth_divisor=depth_divisor, + use_depth_divisor_as_min_depth=use_depth_divisor_as_min_depth, + cap_round_filter_decrease=cap_round_filter_decrease, + ) + # 97 is the start of the lowercase alphabet. letter_identifier = chr(j + 97) stackwise_block_type = stackwise_block_types[i] diff --git a/keras_hub/src/models/efficientnet/fusedmbconv.py b/keras_hub/src/models/efficientnet/fusedmbconv.py index 96f55a22b8..fc17414bd6 100644 --- a/keras_hub/src/models/efficientnet/fusedmbconv.py +++ b/keras_hub/src/models/efficientnet/fusedmbconv.py @@ -107,12 +107,6 @@ def __init__( self.activation, name=self.name + "expand_activation" ) - self.bn2 = keras.layers.BatchNormalization( - axis=BN_AXIS, - momentum=self.batch_norm_momentum, - name=self.name + "bn", - ) - self.se_conv1 = keras.layers.Conv2D( self.filters_se, 1, @@ -144,7 +138,7 @@ def __init__( name=self.name + "project_conv", ) - self.bn3 = keras.layers.BatchNormalization( + self.bn2 = keras.layers.BatchNormalization( axis=BN_AXIS, momentum=self.batch_norm_momentum, name=self.name + "project_bn", @@ -192,7 +186,7 @@ def call(self, inputs): # Output phase: x = self.output_conv(x) - x = self.bn3(x) + x = self.bn2(x) if self.expand_ratio == 1: x = self.act(x) diff --git a/keras_hub/src/utils/timm/convert_efficientnet.py b/keras_hub/src/utils/timm/convert_efficientnet.py index 609c26d355..ad47e70c1f 100644 --- a/keras_hub/src/utils/timm/convert_efficientnet.py +++ b/keras_hub/src/utils/timm/convert_efficientnet.py @@ -18,6 +18,25 @@ "width_coefficient": 1.0, "depth_coefficient": 1.1, }, + "el": { + "width_coefficient": 1.2, + "depth_coefficient": 1.4, + "stackwise_kernel_sizes": [3, 3, 3, 5, 5, 5], + "stackwise_num_repeats": [1, 2, 4, 5, 4, 2], + "stackwise_input_filters": [32, 24, 32, 48, 96, 144], + "stackwise_output_filters": [24, 32, 48, 96, 144, 192], + "stackwise_expansion_ratios": [4, 8, 8, 8, 8, 8], + "stackwise_strides": [1, 2, 2, 2, 1, 2], + "stackwise_squeeze_and_excite_ratios": [0] * 6, + "stackwise_block_types": ["fused"] * 3 + ["unfused"] * 3, + "stackwise_force_input_filters": [24, 0, 0, 0, 0, 0], + }, + "em": { + + }, + "es": { + + }, } @@ -68,21 +87,21 @@ def convert_weights(backbone, loader, timm_config): timm_architecture = timm_config["architecture"] variant = "_".join(timm_architecture.split("_")[1:]) - def port_conv2d(keras_layer_name, hf_weight_prefix, port_bias=True): + def port_conv2d(keras_layer, hf_weight_prefix, port_bias=True): loader.port_weight( - backbone.get_layer(keras_layer_name).kernel, + keras_layer.kernel, hf_weight_key=f"{hf_weight_prefix}.weight", hook_fn=lambda x, _: np.transpose(x, (2, 3, 1, 0)), ) if port_bias: loader.port_weight( - backbone.get_layer(keras_layer_name).bias, + keras_layer.bias, hf_weight_key=f"{hf_weight_prefix}.bias", ) def port_depthwise_conv2d( - keras_layer_name, + keras_layer, hf_weight_prefix, port_bias=True, depth_multiplier=1, @@ -99,39 +118,39 @@ def convert_pt_conv2d_kernel(pt_kernel): ) loader.port_weight( - backbone.get_layer(keras_layer_name).kernel, + keras_layer.kernel, hf_weight_key=f"{hf_weight_prefix}.weight", hook_fn=lambda x, _: convert_pt_conv2d_kernel(x), ) if port_bias: loader.port_weight( - backbone.get_layer(keras_layer_name).bias, + keras_layer.bias, hf_weight_key=f"{hf_weight_prefix}.bias", ) - def port_batch_normalization(keras_layer_name, hf_weight_prefix): + def port_batch_normalization(keras_layer, hf_weight_prefix): loader.port_weight( - backbone.get_layer(keras_layer_name).gamma, + keras_layer.gamma, hf_weight_key=f"{hf_weight_prefix}.weight", ) loader.port_weight( - backbone.get_layer(keras_layer_name).beta, + keras_layer.beta, hf_weight_key=f"{hf_weight_prefix}.bias", ) loader.port_weight( - backbone.get_layer(keras_layer_name).moving_mean, + keras_layer.moving_mean, hf_weight_key=f"{hf_weight_prefix}.running_mean", ) loader.port_weight( - backbone.get_layer(keras_layer_name).moving_variance, + keras_layer.moving_variance, hf_weight_key=f"{hf_weight_prefix}.running_var", ) # do we need num batches tracked? # Stem - port_conv2d("stem_conv", "conv_stem", port_bias=False) - port_batch_normalization("stem_bn", "bn1") + port_conv2d(backbone.get_layer("stem_conv"), "conv_stem", port_bias=False) + port_batch_normalization(backbone.get_layer("stem_bn"), "bn1") # Stages num_stacks = len(backbone.stackwise_kernel_sizes) @@ -149,67 +168,157 @@ def port_batch_normalization(keras_layer_name, hf_weight_prefix): conv_pw_count = 0 bn_count = 1 - conv_pw_name_map = ["conv_pw", "conv_pwl"] # 97 is the start of the lowercase alphabet. letter_identifier = chr(block_idx + 97) + keras_block_prefix = f"block{stack_index+1}{letter_identifier}_" + hf_block_prefix = f"blocks.{stack_index}.{block_idx}." + if block_type == "v1": - keras_block_prefix = f"block{stack_index+1}{letter_identifier}_" - hf_block_prefix = f"blocks.{stack_index}.{block_idx}." + conv_pw_name_map = ["conv_pw", "conv_pwl"] + # Initial Expansion Conv + if expansion_ratio != 1: + port_conv2d( + backbone.get_layer(keras_block_prefix + "expand_conv"), + hf_block_prefix + conv_pw_name_map[conv_pw_count], + port_bias=False, + ) + conv_pw_count += 1 + port_batch_normalization( + backbone.get_layer(keras_block_prefix + "expand_bn"), + hf_block_prefix + f"bn{bn_count}", + ) + bn_count += 1 + + # Depthwise Conv + port_depthwise_conv2d( + backbone.get_layer(keras_block_prefix + "dwconv"), + hf_block_prefix + "conv_dw", + port_bias=False, + ) + port_batch_normalization( + backbone.get_layer(keras_block_prefix + "dwconv_bn"), + hf_block_prefix + f"bn{bn_count}", + ) + bn_count += 1 + + # Squeeze and Excite + port_conv2d( + backbone.get_layer(keras_block_prefix + "se_reduce"), + hf_block_prefix + "se.conv_reduce", + ) + port_conv2d( + backbone.get_layer(keras_block_prefix + "se_expand"), + hf_block_prefix + "se.conv_expand", + ) + + # Output/Projection + port_conv2d( + backbone.get_layer(keras_block_prefix + "project"), + hf_block_prefix + conv_pw_name_map[conv_pw_count], + port_bias=False, + ) + conv_pw_count += 1 + port_batch_normalization( + backbone.get_layer(keras_block_prefix + "project_bn"), + hf_block_prefix + f"bn{bn_count}", + ) + bn_count += 1 + elif block_type == "fused": + fused_block_layer = backbone.get_layer(keras_block_prefix) + + # Initial Expansion Conv + if expansion_ratio != 1: + port_conv2d( + fused_block_layer.conv1, + hf_block_prefix + "conv_exp", + port_bias=False, + ) + conv_pw_count += 1 + port_batch_normalization( + fused_block_layer.bn1, + hf_block_prefix + f"bn{bn_count}", + ) + bn_count += 1 + + # Squeeze and Excite + port_conv2d( + fused_block_layer.se_conv1, + hf_block_prefix + "se.conv_reduce", + ) + port_conv2d( + fused_block_layer.se_conv2, + hf_block_prefix + "se.conv_expand", + ) + + # Output/Projection + port_conv2d( + fused_block_layer.output_conv, + hf_block_prefix + "conv_pwl", + port_bias=False, + ) + conv_pw_count += 1 + port_batch_normalization( + fused_block_layer.bn3, + hf_block_prefix + f"bn{bn_count}", + ) + bn_count += 1 + elif block_type == "unfused": + unfused_block_layer = backbone.get_layer(keras_block_prefix) # Initial Expansion Conv if expansion_ratio != 1: port_conv2d( - keras_block_prefix + "expand_conv", + unfused_block_layer.get_layer(keras_block_prefix + "expand_conv"), hf_block_prefix + conv_pw_name_map[conv_pw_count], port_bias=False, ) conv_pw_count += 1 port_batch_normalization( - keras_block_prefix + "expand_bn", + unfused_block_layer.get_layer(keras_block_prefix + "expand_bn"), hf_block_prefix + f"bn{bn_count}", ) bn_count += 1 # Depthwise Conv port_depthwise_conv2d( - keras_block_prefix + "dwconv", + unfused_block_layer.get_layer(keras_block_prefix + "dwconv"), hf_block_prefix + "conv_dw", port_bias=False, ) port_batch_normalization( - keras_block_prefix + "dwconv_bn", + unfused_block_layer.get_layer(keras_block_prefix + "dwconv_bn"), hf_block_prefix + f"bn{bn_count}", ) bn_count += 1 # Squeeze and Excite port_conv2d( - keras_block_prefix + "se_reduce", + unfused_block_layer.get_layer(keras_block_prefix + "se_reduce"), hf_block_prefix + "se.conv_reduce", ) port_conv2d( - keras_block_prefix + "se_expand", + unfused_block_layer.get_layer(keras_block_prefix + "se_expand"), hf_block_prefix + "se.conv_expand", ) # Output/Projection port_conv2d( - keras_block_prefix + "project", + unfused_block_layer.get_layer(keras_block_prefix + "project"), hf_block_prefix + conv_pw_name_map[conv_pw_count], port_bias=False, ) conv_pw_count += 1 port_batch_normalization( - keras_block_prefix + "project_bn", + unfused_block_layer.get_layer(keras_block_prefix + "project_bn"), hf_block_prefix + f"bn{bn_count}", ) bn_count += 1 # Head/Top - port_conv2d("top_conv", "conv_head", port_bias=False) - port_batch_normalization("top_bn", "bn2") + port_conv2d(backbone.get_layer("top_conv"), "conv_head", port_bias=False) + port_batch_normalization(backbone.get_layer("top_bn"), "bn2") def convert_head(task, loader, timm_config): diff --git a/tools/checkpoint_conversion/convert_efficientnet_checkpoints.py b/tools/checkpoint_conversion/convert_efficientnet_checkpoints.py index 5790d6130c..75810a19a9 100644 --- a/tools/checkpoint_conversion/convert_efficientnet_checkpoints.py +++ b/tools/checkpoint_conversion/convert_efficientnet_checkpoints.py @@ -2,9 +2,15 @@ Convert efficientnet checkpoints. python tools/checkpoint_conversion/convert_efficientnet_checkpoints.py \ - --preset efficientnet_b0_ra_imagenet --upload_uri kaggle://kerashub/efficientnet/keras/efficientnet_b0_ra_imagenet + --preset efficientnet_b0_ra_imagenet --upload_uri kaggle://keras/efficientnet/keras/efficientnet_b0_ra_imagenet python tools/checkpoint_conversion/convert_efficientnet_checkpoints.py \ - --preset efficientnet_b1_ft_imagenet --upload_uri kaggle://kerashub/efficientnet/keras/efficientnet_b1_ft_imagenet + --preset efficientnet_b1_ft_imagenet --upload_uri kaggle://keras/efficientnet/keras/efficientnet_b1_ft_imagenet +python tools/checkpoint_conversion/convert_efficientnet_checkpoints.py \ + --preset efficientnet_el_ra_imagenet --upload_uri kaggle://keras/efficientnet/keras/efficientnet_el_ra_imagenet +python tools/checkpoint_conversion/convert_efficientnet_checkpoints.py \ + --preset efficientnet_em_ra2_imagenet --upload_uri kaggle://keras/efficientnet/keras/efficientnet_em_ra2_imagenet +python tools/checkpoint_conversion/convert_efficientnet_checkpoints.py \ + --preset efficientnet_es_ra_imagenet --upload_uri kaggle://keras/efficientnet/keras/efficientnet_es_ra_imagenet """ import os @@ -23,6 +29,9 @@ PRESET_MAP = { "efficientnet_b0_ra_imagenet": "timm/efficientnet_b0.ra_in1k", "efficientnet_b1_ft_imagenet": "timm/efficientnet_b1.ft_in1k", + "efficientnet_el_ra_imagenet": "timm/efficientnet_el.ra_in1k", + "efficientnet_em_ra2_imagenet": "timm/efficientnet_em.ra2_in1k", + "efficientnet_es_ra_imagenet": "timm/efficientnet_es.ra_in1k", } FLAGS = flags.FLAGS From 9cb5c1318f9e0f014d1c76ca7c6c3795a951dce3 Mon Sep 17 00:00:00 2001 From: Piseth Ky Date: Tue, 5 Nov 2024 16:44:53 -0800 Subject: [PATCH 02/10] WIP el variant working --- .../efficientnet/efficientnet_backbone.py | 6 ++ .../src/models/efficientnet/fusedmbconv.py | 20 ++++- keras_hub/src/models/efficientnet/mbconv.py | 11 ++- .../src/utils/timm/convert_efficientnet.py | 81 ++++++++++--------- 4 files changed, 80 insertions(+), 38 deletions(-) diff --git a/keras_hub/src/models/efficientnet/efficientnet_backbone.py b/keras_hub/src/models/efficientnet/efficientnet_backbone.py index 1deef08cf7..a6f7639b5c 100644 --- a/keras_hub/src/models/efficientnet/efficientnet_backbone.py +++ b/keras_hub/src/models/efficientnet/efficientnet_backbone.py @@ -101,6 +101,7 @@ def __init__( stackwise_strides, stackwise_block_types, stackwise_force_input_filters=[0]*7, + stackwise_nores_option=[False]*7, dropout=0.2, depth_divisor=8, min_depth=8, @@ -165,6 +166,7 @@ def __init__( input_filters = stackwise_input_filters[i] output_filters = stackwise_output_filters[i] force_input_filters = stackwise_force_input_filters[i] + nores = stackwise_nores_option[i] # Update block input and output filters based on depth multiplier. input_filters = round_filters( @@ -244,6 +246,8 @@ def __init__( activation=activation, dropout=dropout * block_id / blocks, batch_norm_momentum=batch_norm_momentum, + batch_norm_epsilon=batch_norm_epsilon, + nores=nores, name=block_name, ) x = block(x) @@ -303,6 +307,7 @@ def __init__( self.stackwise_strides = stackwise_strides self.stackwise_block_types = stackwise_block_types + self.stackwise_force_input_filters=stackwise_force_input_filters, self.include_stem_padding = include_stem_padding self.use_depth_divisor_as_min_depth = use_depth_divisor_as_min_depth self.cap_round_filter_decrease = cap_round_filter_decrease @@ -330,6 +335,7 @@ def get_config(self): "stackwise_squeeze_and_excite_ratios": self.stackwise_squeeze_and_excite_ratios, "stackwise_strides": self.stackwise_strides, "stackwise_block_types": self.stackwise_block_types, + "stackwise_force_input_filters": self.stackwise_force_input_filters, "include_stem_padding": self.include_stem_padding, "use_depth_divisor_as_min_depth": self.use_depth_divisor_as_min_depth, "cap_round_filter_decrease": self.cap_round_filter_decrease, diff --git a/keras_hub/src/models/efficientnet/fusedmbconv.py b/keras_hub/src/models/efficientnet/fusedmbconv.py index fc17414bd6..9e06e5bee6 100644 --- a/keras_hub/src/models/efficientnet/fusedmbconv.py +++ b/keras_hub/src/models/efficientnet/fusedmbconv.py @@ -70,8 +70,10 @@ def __init__( data_format="channels_last", se_ratio=0.0, batch_norm_momentum=0.9, + batch_norm_epsilon=1e-3, activation="swish", dropout=0.2, + nores=False, **kwargs ): super().__init__(**kwargs) @@ -83,8 +85,10 @@ def __init__( self.data_format = data_format self.se_ratio = se_ratio self.batch_norm_momentum = batch_norm_momentum + self.batch_norm_epsilon = batch_norm_epsilon self.activation = activation self.dropout = dropout + self.nores = nores self.filters = self.input_filters * self.expand_ratio self.filters_se = max(1, int(input_filters * se_ratio)) @@ -101,6 +105,7 @@ def __init__( self.bn1 = keras.layers.BatchNormalization( axis=BN_AXIS, momentum=self.batch_norm_momentum, + epsilon=self.batch_norm_epsilon, name=self.name + "expand_bn", ) self.act = keras.layers.Activation( @@ -141,6 +146,7 @@ def __init__( self.bn2 = keras.layers.BatchNormalization( axis=BN_AXIS, momentum=self.batch_norm_momentum, + epsilon=self.batch_norm_epsilon, name=self.name + "project_bn", ) @@ -190,8 +196,18 @@ def call(self, inputs): if self.expand_ratio == 1: x = self.act(x) + # For EdgeTPU Version the stem output does not match the parameterized + # input filters, thus this check needs to be dynamic and not based + # on initial parameterization. This hack is ported from timm. + # if self.data_format == "channels_last": + # input_filters = inputs.shape[-1] + # x_filters = x.shape[-1] + # else: + # input_filters = inputs.shape[1] + # x_filters = x.shape[1] + # Residual: - if self.strides == 1 and self.input_filters == self.output_filters: + if self.strides == 1 and self.input_filters == self.output_filters and not self.nores: if self.dropout: x = self.dropout_layer(x) x = keras.layers.Add(name=self.name + "add")([x, inputs]) @@ -207,8 +223,10 @@ def get_config(self): "data_format": self.data_format, "se_ratio": self.se_ratio, "batch_norm_momentum": self.batch_norm_momentum, + "batch_norm_epsilon": self.batch_norm_epsilon, "activation": self.activation, "dropout": self.dropout, + "nores": self.nores, } base_config = super().get_config() diff --git a/keras_hub/src/models/efficientnet/mbconv.py b/keras_hub/src/models/efficientnet/mbconv.py index 392e62c04f..80178bbba6 100644 --- a/keras_hub/src/models/efficientnet/mbconv.py +++ b/keras_hub/src/models/efficientnet/mbconv.py @@ -23,8 +23,10 @@ def __init__( data_format="channels_last", se_ratio=0.0, batch_norm_momentum=0.9, + batch_norm_epsilon=1e-3, activation="swish", dropout=0.2, + nores=False, **kwargs ): """Implementation of the MBConv block @@ -83,8 +85,10 @@ def __init__( self.data_format = data_format self.se_ratio = se_ratio self.batch_norm_momentum = batch_norm_momentum + self.batch_norm_epsilon = batch_norm_epsilon self.activation = activation self.dropout = dropout + self.nores = nores self.filters = self.input_filters * self.expand_ratio self.filters_se = max(1, int(input_filters * se_ratio)) @@ -101,6 +105,7 @@ def __init__( self.bn1 = keras.layers.BatchNormalization( axis=BN_AXIS, momentum=self.batch_norm_momentum, + epsilon=self.batch_norm_epsilon, name=self.name + "expand_bn", ) self.act = keras.layers.Activation( @@ -119,6 +124,7 @@ def __init__( self.bn2 = keras.layers.BatchNormalization( axis=BN_AXIS, momentum=self.batch_norm_momentum, + epsilon=self.batch_norm_epsilon, name=self.name + "bn", ) @@ -156,6 +162,7 @@ def __init__( self.bn3 = keras.layers.BatchNormalization( axis=BN_AXIS, momentum=self.batch_norm_momentum, + epsilon=self.batch_norm_epsilon, name=self.name + "project_bn", ) @@ -207,7 +214,7 @@ def call(self, inputs): x = self.output_conv(x) x = self.bn3(x) - if self.strides == 1 and self.input_filters == self.output_filters: + if self.strides == 1 and self.input_filters == self.output_filters and not self.nores: if self.dropout: x = self.dropout_layer(x) x = keras.layers.Add(name=self.name + "add")([x, inputs]) @@ -223,8 +230,10 @@ def get_config(self): "data_format": self.data_format, "se_ratio": self.se_ratio, "batch_norm_momentum": self.batch_norm_momentum, + "batch_norm_epsilon": self.batch_norm_epsilon, "activation": self.activation, "dropout": self.dropout, + "nores": self.nores, } base_config = super().get_config() return dict(list(base_config.items()) + list(config.items())) diff --git a/keras_hub/src/utils/timm/convert_efficientnet.py b/keras_hub/src/utils/timm/convert_efficientnet.py index ad47e70c1f..c8293135f4 100644 --- a/keras_hub/src/utils/timm/convert_efficientnet.py +++ b/keras_hub/src/utils/timm/convert_efficientnet.py @@ -30,6 +30,8 @@ "stackwise_squeeze_and_excite_ratios": [0] * 6, "stackwise_block_types": ["fused"] * 3 + ["unfused"] * 3, "stackwise_force_input_filters": [24, 0, 0, 0, 0, 0], + "stackwise_nores_option": [True] + [False] * 5, + "activation": "relu", }, "em": { @@ -86,6 +88,7 @@ def convert_backbone_config(timm_config): def convert_weights(backbone, loader, timm_config): timm_architecture = timm_config["architecture"] variant = "_".join(timm_architecture.split("_")[1:]) + # backbone.build(input_shape=timm_config["pretrained_cfg"]["input_size"]) def port_conv2d(keras_layer, hf_weight_prefix, port_bias=True): loader.port_weight( @@ -163,6 +166,9 @@ def port_batch_normalization(keras_layer, hf_weight_prefix): repeats = int( math.ceil(VARIANT_MAP[variant]["depth_coefficient"] * repeats) ) + se_ratio = VARIANT_MAP[variant]["stackwise_squeeze_and_excite_ratios"][ + stack_index + ] for block_idx in range(repeats): @@ -203,15 +209,16 @@ def port_batch_normalization(keras_layer, hf_weight_prefix): ) bn_count += 1 - # Squeeze and Excite - port_conv2d( - backbone.get_layer(keras_block_prefix + "se_reduce"), - hf_block_prefix + "se.conv_reduce", - ) - port_conv2d( - backbone.get_layer(keras_block_prefix + "se_expand"), - hf_block_prefix + "se.conv_expand", - ) + if 0 < se_ratio <= 1: + # Squeeze and Excite + port_conv2d( + backbone.get_layer(keras_block_prefix + "se_reduce"), + hf_block_prefix + "se.conv_reduce", + ) + port_conv2d( + backbone.get_layer(keras_block_prefix + "se_expand"), + hf_block_prefix + "se.conv_expand", + ) # Output/Projection port_conv2d( @@ -242,15 +249,16 @@ def port_batch_normalization(keras_layer, hf_weight_prefix): ) bn_count += 1 - # Squeeze and Excite - port_conv2d( - fused_block_layer.se_conv1, - hf_block_prefix + "se.conv_reduce", - ) - port_conv2d( - fused_block_layer.se_conv2, - hf_block_prefix + "se.conv_expand", - ) + if 0 < se_ratio <= 1: + # Squeeze and Excite + port_conv2d( + fused_block_layer.se_conv1, + hf_block_prefix + "se.conv_reduce", + ) + port_conv2d( + fused_block_layer.se_conv2, + hf_block_prefix + "se.conv_expand", + ) # Output/Projection port_conv2d( @@ -260,7 +268,7 @@ def port_batch_normalization(keras_layer, hf_weight_prefix): ) conv_pw_count += 1 port_batch_normalization( - fused_block_layer.bn3, + fused_block_layer.bn2, hf_block_prefix + f"bn{bn_count}", ) bn_count += 1 @@ -270,48 +278,49 @@ def port_batch_normalization(keras_layer, hf_weight_prefix): # Initial Expansion Conv if expansion_ratio != 1: port_conv2d( - unfused_block_layer.get_layer(keras_block_prefix + "expand_conv"), - hf_block_prefix + conv_pw_name_map[conv_pw_count], + unfused_block_layer.conv1, + hf_block_prefix + "conv_pw", port_bias=False, ) conv_pw_count += 1 port_batch_normalization( - unfused_block_layer.get_layer(keras_block_prefix + "expand_bn"), + unfused_block_layer.bn1, hf_block_prefix + f"bn{bn_count}", ) bn_count += 1 # Depthwise Conv port_depthwise_conv2d( - unfused_block_layer.get_layer(keras_block_prefix + "dwconv"), + unfused_block_layer.depthwise, hf_block_prefix + "conv_dw", port_bias=False, ) port_batch_normalization( - unfused_block_layer.get_layer(keras_block_prefix + "dwconv_bn"), + unfused_block_layer.bn2, hf_block_prefix + f"bn{bn_count}", ) bn_count += 1 - # Squeeze and Excite - port_conv2d( - unfused_block_layer.get_layer(keras_block_prefix + "se_reduce"), - hf_block_prefix + "se.conv_reduce", - ) - port_conv2d( - unfused_block_layer.get_layer(keras_block_prefix + "se_expand"), - hf_block_prefix + "se.conv_expand", - ) + if 0 < se_ratio <= 1: + # Squeeze and Excite + port_conv2d( + unfused_block_layer.se_conv1, + hf_block_prefix + "se.conv_reduce", + ) + port_conv2d( + unfused_block_layer.se_conv2, + hf_block_prefix + "se.conv_expand", + ) # Output/Projection port_conv2d( - unfused_block_layer.get_layer(keras_block_prefix + "project"), - hf_block_prefix + conv_pw_name_map[conv_pw_count], + unfused_block_layer.output_conv, + hf_block_prefix + "conv_pwl", port_bias=False, ) conv_pw_count += 1 port_batch_normalization( - unfused_block_layer.get_layer(keras_block_prefix + "project_bn"), + unfused_block_layer.bn3, hf_block_prefix + f"bn{bn_count}", ) bn_count += 1 From 64b6af8471152aaef86021bc9cc26c36b4813dd5 Mon Sep 17 00:00:00 2001 From: Piseth Ky Date: Thu, 7 Nov 2024 14:14:51 -0800 Subject: [PATCH 03/10] added all hf timm edge presets --- .../src/utils/timm/convert_efficientnet.py | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/keras_hub/src/utils/timm/convert_efficientnet.py b/keras_hub/src/utils/timm/convert_efficientnet.py index c8293135f4..68fed5fb5d 100644 --- a/keras_hub/src/utils/timm/convert_efficientnet.py +++ b/keras_hub/src/utils/timm/convert_efficientnet.py @@ -13,10 +13,12 @@ "b0": { "width_coefficient": 1.0, "depth_coefficient": 1.0, + "stackwise_squeeze_and_excite_ratios": [0.25] * 7, }, "b1": { "width_coefficient": 1.0, "depth_coefficient": 1.1, + "stackwise_squeeze_and_excite_ratios": [0.25] * 7, }, "el": { "width_coefficient": 1.2, @@ -34,10 +36,34 @@ "activation": "relu", }, "em": { - + "width_coefficient": 1.0, + "depth_coefficient": 1.1, + "stackwise_kernel_sizes": [3, 3, 3, 5, 5, 5], + "stackwise_num_repeats": [1, 2, 4, 5, 4, 2], + "stackwise_input_filters": [32, 24, 32, 48, 96, 144], + "stackwise_output_filters": [24, 32, 48, 96, 144, 192], + "stackwise_expansion_ratios": [4, 8, 8, 8, 8, 8], + "stackwise_strides": [1, 2, 2, 2, 1, 2], + "stackwise_squeeze_and_excite_ratios": [0] * 6, + "stackwise_block_types": ["fused"] * 3 + ["unfused"] * 3, + "stackwise_force_input_filters": [24, 0, 0, 0, 0, 0], + "stackwise_nores_option": [True] + [False] * 5, + "activation": "relu", }, "es": { - + "width_coefficient": 1.0, + "depth_coefficient": 1.0, + "stackwise_kernel_sizes": [3, 3, 3, 5, 5, 5], + "stackwise_num_repeats": [1, 2, 4, 5, 4, 2], + "stackwise_input_filters": [32, 24, 32, 48, 96, 144], + "stackwise_output_filters": [24, 32, 48, 96, 144, 192], + "stackwise_expansion_ratios": [4, 8, 8, 8, 8, 8], + "stackwise_strides": [1, 2, 2, 2, 1, 2], + "stackwise_squeeze_and_excite_ratios": [0] * 6, + "stackwise_block_types": ["fused"] * 3 + ["unfused"] * 3, + "stackwise_force_input_filters": [24, 0, 0, 0, 0, 0], + "stackwise_nores_option": [True] + [False] * 5, + "activation": "relu", }, } @@ -52,15 +78,6 @@ def convert_backbone_config(timm_config): "stackwise_output_filters": [16, 24, 40, 80, 112, 192, 320], "stackwise_expansion_ratios": [1, 6, 6, 6, 6, 6, 6], "stackwise_strides": [1, 2, 2, 2, 1, 2, 1], - "stackwise_squeeze_and_excite_ratios": [ - 0.25, - 0.25, - 0.25, - 0.25, - 0.25, - 0.25, - 0.25, - ], "stackwise_block_types": ["v1"] * 7, "min_depth": None, "include_stem_padding": True, From ccb508b9df074c60167d9f2c6d54aed2f547391a Mon Sep 17 00:00:00 2001 From: Piseth Ky Date: Thu, 7 Nov 2024 14:20:02 -0800 Subject: [PATCH 04/10] removing irrelevant note --- keras_hub/src/models/efficientnet/fusedmbconv.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/keras_hub/src/models/efficientnet/fusedmbconv.py b/keras_hub/src/models/efficientnet/fusedmbconv.py index 9e06e5bee6..7bb706ca9c 100644 --- a/keras_hub/src/models/efficientnet/fusedmbconv.py +++ b/keras_hub/src/models/efficientnet/fusedmbconv.py @@ -196,16 +196,6 @@ def call(self, inputs): if self.expand_ratio == 1: x = self.act(x) - # For EdgeTPU Version the stem output does not match the parameterized - # input filters, thus this check needs to be dynamic and not based - # on initial parameterization. This hack is ported from timm. - # if self.data_format == "channels_last": - # input_filters = inputs.shape[-1] - # x_filters = x.shape[-1] - # else: - # input_filters = inputs.shape[1] - # x_filters = x.shape[1] - # Residual: if self.strides == 1 and self.input_filters == self.output_filters and not self.nores: if self.dropout: From cebc921b5810c9ea854f95f56758f24a1bc1b5ef Mon Sep 17 00:00:00 2001 From: Piseth Ky Date: Thu, 7 Nov 2024 14:25:13 -0800 Subject: [PATCH 05/10] format pass --- keras_hub/src/models/efficientnet/efficientnet_backbone.py | 6 +++--- keras_hub/src/models/efficientnet/fusedmbconv.py | 6 +++++- keras_hub/src/models/efficientnet/mbconv.py | 6 +++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/keras_hub/src/models/efficientnet/efficientnet_backbone.py b/keras_hub/src/models/efficientnet/efficientnet_backbone.py index a6f7639b5c..9f3825ea1b 100644 --- a/keras_hub/src/models/efficientnet/efficientnet_backbone.py +++ b/keras_hub/src/models/efficientnet/efficientnet_backbone.py @@ -100,8 +100,8 @@ def __init__( stackwise_squeeze_and_excite_ratios, stackwise_strides, stackwise_block_types, - stackwise_force_input_filters=[0]*7, - stackwise_nores_option=[False]*7, + stackwise_force_input_filters=[0] * 7, + stackwise_nores_option=[False] * 7, dropout=0.2, depth_divisor=8, min_depth=8, @@ -307,7 +307,7 @@ def __init__( self.stackwise_strides = stackwise_strides self.stackwise_block_types = stackwise_block_types - self.stackwise_force_input_filters=stackwise_force_input_filters, + self.stackwise_force_input_filters = (stackwise_force_input_filters,) self.include_stem_padding = include_stem_padding self.use_depth_divisor_as_min_depth = use_depth_divisor_as_min_depth self.cap_round_filter_decrease = cap_round_filter_decrease diff --git a/keras_hub/src/models/efficientnet/fusedmbconv.py b/keras_hub/src/models/efficientnet/fusedmbconv.py index 7bb706ca9c..8d2cc2fdef 100644 --- a/keras_hub/src/models/efficientnet/fusedmbconv.py +++ b/keras_hub/src/models/efficientnet/fusedmbconv.py @@ -197,7 +197,11 @@ def call(self, inputs): x = self.act(x) # Residual: - if self.strides == 1 and self.input_filters == self.output_filters and not self.nores: + if ( + self.strides == 1 + and self.input_filters == self.output_filters + and not self.nores + ): if self.dropout: x = self.dropout_layer(x) x = keras.layers.Add(name=self.name + "add")([x, inputs]) diff --git a/keras_hub/src/models/efficientnet/mbconv.py b/keras_hub/src/models/efficientnet/mbconv.py index 80178bbba6..e9acbfeb9a 100644 --- a/keras_hub/src/models/efficientnet/mbconv.py +++ b/keras_hub/src/models/efficientnet/mbconv.py @@ -214,7 +214,11 @@ def call(self, inputs): x = self.output_conv(x) x = self.bn3(x) - if self.strides == 1 and self.input_filters == self.output_filters and not self.nores: + if ( + self.strides == 1 + and self.input_filters == self.output_filters + and not self.nores + ): if self.dropout: x = self.dropout_layer(x) x = keras.layers.Add(name=self.name + "add")([x, inputs]) From 11a86a20cc706d801f9dafd53488ece5bb892208 Mon Sep 17 00:00:00 2001 From: Piseth Ky Date: Thu, 7 Nov 2024 14:28:28 -0800 Subject: [PATCH 06/10] remove irrelevant old commented code --- keras_hub/src/utils/timm/convert_efficientnet.py | 1 - 1 file changed, 1 deletion(-) diff --git a/keras_hub/src/utils/timm/convert_efficientnet.py b/keras_hub/src/utils/timm/convert_efficientnet.py index 68fed5fb5d..5c58c7c04b 100644 --- a/keras_hub/src/utils/timm/convert_efficientnet.py +++ b/keras_hub/src/utils/timm/convert_efficientnet.py @@ -105,7 +105,6 @@ def convert_backbone_config(timm_config): def convert_weights(backbone, loader, timm_config): timm_architecture = timm_config["architecture"] variant = "_".join(timm_architecture.split("_")[1:]) - # backbone.build(input_shape=timm_config["pretrained_cfg"]["input_size"]) def port_conv2d(keras_layer, hf_weight_prefix, port_bias=True): loader.port_weight( From 5533922b55847b6557118e51c4fdf361b38791f1 Mon Sep 17 00:00:00 2001 From: Piseth Ky Date: Thu, 7 Nov 2024 15:16:53 -0800 Subject: [PATCH 07/10] fix unit test regression --- .../models/efficientnet/efficientnet_backbone.py | 2 +- .../efficientnet/efficientnet_backbone_test.py | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/keras_hub/src/models/efficientnet/efficientnet_backbone.py b/keras_hub/src/models/efficientnet/efficientnet_backbone.py index 9f3825ea1b..f2a3b70912 100644 --- a/keras_hub/src/models/efficientnet/efficientnet_backbone.py +++ b/keras_hub/src/models/efficientnet/efficientnet_backbone.py @@ -307,7 +307,7 @@ def __init__( self.stackwise_strides = stackwise_strides self.stackwise_block_types = stackwise_block_types - self.stackwise_force_input_filters = (stackwise_force_input_filters,) + self.stackwise_force_input_filters = stackwise_force_input_filters self.include_stem_padding = include_stem_padding self.use_depth_divisor_as_min_depth = use_depth_divisor_as_min_depth self.cap_round_filter_decrease = cap_round_filter_decrease diff --git a/keras_hub/src/models/efficientnet/efficientnet_backbone_test.py b/keras_hub/src/models/efficientnet/efficientnet_backbone_test.py index f31004b5dc..c11e636540 100644 --- a/keras_hub/src/models/efficientnet/efficientnet_backbone_test.py +++ b/keras_hub/src/models/efficientnet/efficientnet_backbone_test.py @@ -26,6 +26,8 @@ def setUp(self): ], "stackwise_strides": [1, 2, 2, 2, 1, 2], "stackwise_block_types": ["fused"] * 3 + ["unfused"] * 3, + "stackwise_force_input_filters": [0] * 6, + "stackwise_nores_option": [False] * 6, "width_coefficient": 1.0, "depth_coefficient": 1.0, } @@ -60,15 +62,9 @@ def test_valid_call_original_v1(self): "stackwise_output_filters": [16, 24, 40, 80, 112, 192, 320], "stackwise_expansion_ratios": [1, 6, 6, 6, 6, 6, 6], "stackwise_strides": [1, 2, 2, 2, 1, 2, 1], - "stackwise_squeeze_and_excite_ratios": [ - 0.25, - 0.25, - 0.25, - 0.25, - 0.25, - 0.25, - 0.25, - ], + "stackwise_squeeze_and_excite_ratios": [0.25] * 7, + "stackwise_force_input_filters": [0] * 7, + "stackwise_nores_option": [False] * 7, "width_coefficient": 1.0, "depth_coefficient": 1.0, "stackwise_block_types": ["v1"] * 7, From a935e6240b9d053bac00bf8aceca4dc5a3b353bc Mon Sep 17 00:00:00 2001 From: Piseth Ky Date: Thu, 7 Nov 2024 16:12:34 -0800 Subject: [PATCH 08/10] add presets to preset file --- .../efficientnet/efficientnet_presets.py | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/keras_hub/src/models/efficientnet/efficientnet_presets.py b/keras_hub/src/models/efficientnet/efficientnet_presets.py index 39c9514816..2a3c279350 100644 --- a/keras_hub/src/models/efficientnet/efficientnet_presets.py +++ b/keras_hub/src/models/efficientnet/efficientnet_presets.py @@ -17,7 +17,7 @@ "efficientnet_b1_ft_imagenet": { "metadata": { "description": ( - "EfficientNet B1 model fine-trained on the ImageNet 1k dataset." + "EfficientNet B1 model fine-tuned on the ImageNet 1k dataset." ), "params": 7794184, "official_name": "EfficientNet", @@ -26,4 +26,43 @@ }, "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_b1_ft_imagenet", }, + "efficientnet_el_ra_imagenet": { + "metadata": { + "description": ( + "EfficientNet-EdgeTPU Large model trained on the ImageNet 1k " + "dataset with RandAugment recipe." + ), + "params": 10589712, + "official_name": "EfficientNet", + "path": "efficientnet", + "model_card": "https://arxiv.org/abs/1905.11946", + }, + "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_b1_ft_imagenet", + }, + "efficientnet_em_ra2_imagenet": { + "metadata": { + "description": ( + "EfficientNet-EdgeTPU Medium model trained on the ImageNet 1k " + "dataset with RandAugment2 recipe." + ), + "params": 6899496, + "official_name": "EfficientNet", + "path": "efficientnet", + "model_card": "https://arxiv.org/abs/1905.11946", + }, + "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_b1_ft_imagenet", + }, + "efficientnet_es_ra_imagenet": { + "metadata": { + "description": ( + "EfficientNet-EdgeTPU Small model trained on the ImageNet 1k " + "dataset with RandAugment recipe." + ), + "params": 5438392, + "official_name": "EfficientNet", + "path": "efficientnet", + "model_card": "https://arxiv.org/abs/1905.11946", + }, + "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_b1_ft_imagenet", + }, } From e307ba8a11b0d6012bbfb5e52795c3ab81399ace Mon Sep 17 00:00:00 2001 From: Piseth Ky Date: Tue, 12 Nov 2024 12:19:29 -0800 Subject: [PATCH 09/10] added arg docstrings and version handles --- .../src/models/efficientnet/efficientnet_backbone.py | 9 +++++++++ .../src/models/efficientnet/efficientnet_presets.py | 10 +++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/keras_hub/src/models/efficientnet/efficientnet_backbone.py b/keras_hub/src/models/efficientnet/efficientnet_backbone.py index f2a3b70912..95f4341492 100644 --- a/keras_hub/src/models/efficientnet/efficientnet_backbone.py +++ b/keras_hub/src/models/efficientnet/efficientnet_backbone.py @@ -54,6 +54,13 @@ class EfficientNetBackbone(FeaturePyramidBackbone): MBConvBlock, but instead of using a depthwise convolution and a 1x1 output convolution blocks fused blocks use a single 3x3 convolution block. + stackwise_force_input_filters: list of ints, overrides + stackwise_input_filters if > 0. Primarily used to parameterize stem + filters (usually stackwise_input_filters[0]) differrently than stack + input filters. + stackwise_nores_option: list of bools, toggles if residiual connection + is not used. If False (default), the stack will use residual + connections, otherwise not. min_depth: integer, minimum number of filters. Can be None and ignored if use_depth_divisor_as_min_depth is set to True. include_initial_padding: bool, whether to include initial zero padding @@ -66,6 +73,8 @@ class EfficientNetBackbone(FeaturePyramidBackbone): stem_conv_padding: str, can be 'same' or 'valid'. Padding for the stem. batch_norm_momentum: float, momentum for the moving average calcualtion in the batch normalization layers. + batch_norm_epsilon: float, epsilon for batch norm calcualtions. Used + in denominator for calculations to prevent divide by 0 errors. Example: ```python diff --git a/keras_hub/src/models/efficientnet/efficientnet_presets.py b/keras_hub/src/models/efficientnet/efficientnet_presets.py index 2a3c279350..860a48a685 100644 --- a/keras_hub/src/models/efficientnet/efficientnet_presets.py +++ b/keras_hub/src/models/efficientnet/efficientnet_presets.py @@ -12,7 +12,7 @@ "path": "efficientnet", "model_card": "https://arxiv.org/abs/1905.11946", }, - "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_b0_ra_imagenet", + "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_b0_ra_imagenet/1", }, "efficientnet_b1_ft_imagenet": { "metadata": { @@ -24,7 +24,7 @@ "path": "efficientnet", "model_card": "https://arxiv.org/abs/1905.11946", }, - "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_b1_ft_imagenet", + "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_b1_ft_imagenet/1", }, "efficientnet_el_ra_imagenet": { "metadata": { @@ -37,7 +37,7 @@ "path": "efficientnet", "model_card": "https://arxiv.org/abs/1905.11946", }, - "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_b1_ft_imagenet", + "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_el_ra_imagenet/1", }, "efficientnet_em_ra2_imagenet": { "metadata": { @@ -50,7 +50,7 @@ "path": "efficientnet", "model_card": "https://arxiv.org/abs/1905.11946", }, - "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_b1_ft_imagenet", + "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_em_ra2_imagenet/1", }, "efficientnet_es_ra_imagenet": { "metadata": { @@ -63,6 +63,6 @@ "path": "efficientnet", "model_card": "https://arxiv.org/abs/1905.11946", }, - "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_b1_ft_imagenet", + "kaggle_handle": "kaggle://keras/efficientnet/keras/efficientnet_es_ra_imagenet/1", }, } From f9dd3c85675c987aaf7b58f3bd4c55fa78fbccb5 Mon Sep 17 00:00:00 2001 From: Piseth Ky Date: Tue, 12 Nov 2024 12:23:36 -0800 Subject: [PATCH 10/10] updated block specific docstring for batch_norm_epsilon --- keras_hub/src/models/efficientnet/fusedmbconv.py | 3 +++ keras_hub/src/models/efficientnet/mbconv.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/keras_hub/src/models/efficientnet/fusedmbconv.py b/keras_hub/src/models/efficientnet/fusedmbconv.py index 8d2cc2fdef..51a7f95fef 100644 --- a/keras_hub/src/models/efficientnet/fusedmbconv.py +++ b/keras_hub/src/models/efficientnet/fusedmbconv.py @@ -47,6 +47,9 @@ class FusedMBConvBlock(keras.layers.Layer): se_ratio: default 0.0, The filters used in the Squeeze-Excitation phase, and are chosen as the maximum between 1 and input_filters*se_ratio batch_norm_momentum: default 0.9, the BatchNormalization momentum + batch_norm_epsilon: default 1e-3, float, epsilon for batch norm + calcualtions. Used in denominator for calculations to prevent divide + by 0 errors. activation: default "swish", the activation function used between convolution operations dropout: float, the optional dropout rate to apply before the output diff --git a/keras_hub/src/models/efficientnet/mbconv.py b/keras_hub/src/models/efficientnet/mbconv.py index e9acbfeb9a..b4dc05f7c9 100644 --- a/keras_hub/src/models/efficientnet/mbconv.py +++ b/keras_hub/src/models/efficientnet/mbconv.py @@ -62,6 +62,9 @@ def __init__( is above 0. The filters used in this phase are chosen as the maximum between 1 and input_filters*se_ratio batch_norm_momentum: default 0.9, the BatchNormalization momentum + batch_norm_epsilon: default 1e-3, float, epsilon for batch norm + calcualtions. Used in denominator for calculations to prevent + divide by 0 errors. activation: default "swish", the activation function used between convolution operations dropout: float, the optional dropout rate to apply before the output