def block(inputs): if expand_ratio != 1: x = tf.keras.layers.Conv2D( filters, kernel_size=[1, 1], strides=[1, 1], kernel_initializer=EfficientNetConvInitializer(), padding="same", use_bias=False, )(inputs) x = tf.keras.layers.BatchNormalization( axis=channel_axis, momentum=batch_norm_momentum, epsilon=batch_norm_epsilon, )(x) x = Swish()(x) else: x = inputs x = tf.keras.layers.DepthwiseConv2D( [kernel_size, kernel_size], strides=strides, depthwise_initializer=EfficientNetConvInitializer(), padding="same", use_bias=False, )(x) x = tf.keras.layers.BatchNormalization(axis=channel_axis, momentum=batch_norm_momentum, epsilon=batch_norm_epsilon)(x) x = Swish()(x) if has_se: x = SEBlock(input_filters, se_ratio, expand_ratio, data_format)(x) # output phase x = tf.keras.layers.Conv2D( output_filters, kernel_size=[1, 1], strides=[1, 1], kernel_initializer=EfficientNetConvInitializer(), padding="same", use_bias=False, )(x) x = tf.keras.layers.BatchNormalization(axis=channel_axis, momentum=batch_norm_momentum, epsilon=batch_norm_epsilon)(x) if id_skip: if all(s == 1 for s in strides) and (input_filters == output_filters): # only apply drop_connect if skip presents. if drop_connect_rate: x = DropConnect(drop_connect_rate)(x) x = tf.keras.layers.Add()([x, inputs]) return x
def block(inputs): x = inputs x = tf.keras.layers.Lambda(lambda a: tf.keras.backend.mean( a, axis=spatial_dims, keepdims=True))(x) x = tf.keras.layers.Conv2D( num_reduced_filters, kernel_size=[1, 1], strides=[1, 1], kernel_initializer=EfficientNetConvInitializer(), padding="same", use_bias=True, )(x) x = Swish()(x) # Excite x = tf.keras.layers.Conv2D( filters, kernel_size=[1, 1], strides=[1, 1], kernel_initializer=EfficientNetConvInitializer(), padding="same", use_bias=True, )(x) x = tf.keras.layers.Activation("sigmoid")(x) out = tf.keras.layers.Multiply()([x, inputs]) return out
def EfficientNet(input_shape, block_args_list: List[BlockArgs], width_coefficient: float, depth_coefficient: float, include_top=True, weights=None, input_tensor=None, pooling=None, classes=1000, dropout_rate=0., drop_connect_rate=0., batch_norm_momentum=0.99, batch_norm_epsilon=1e-3, depth_divisor=8, min_depth=None, data_format=None, default_size=None, **kwargs): """ Builder model for EfficientNets. # Arguments: input_shape: Optional shape tuple, the input shape depends on the configuration, with a minimum decided by the number of stride 2 operations. When None is provided, it defaults to 224. Considered the "Resolution" parameter from the paper (inherently Resolution coefficient). block_args_list: Optional List of BlockArgs, each of which detail the arguments of the MBConvBlock. If left as None, it defaults to the blocks from the paper. width_coefficient: Determines the number of channels available per layer. Compound Coefficient that needs to be found using grid search on a base configuration model. depth_coefficient: Determines the number of layers available to the model. Compound Coefficient that needs to be found using grid search on a base configuration model. include_top: Whether to include the fully-connected layer at the top of the network. weights: `None` (random initialization) or `imagenet` (ImageNet weights) input_tensor: Optional Keras tensor (i.e. output of `layers.Input()`) to use as image input for the model. pooling: Optional pooling mode for feature extraction when `include_top` is `False`. - `None` means that the output of the model will be the 4D tensor output of the last convolutional layer. - `avg` means that global average pooling will be applied to the output of the last convolutional layer, and thus the output of the model will be a 2D tensor. - `max` means that global max pooling will be applied. classes: Optional number of classes to classify images into, only to be specified if `include_top` is True, and if no `weights` argument is specified. dropout_rate: Float, percentage of random dropout. drop_connect_rate: Float, percentage of random droped connections. batch_norm_momentum: Float, default batch normalization momentum. Obtained from the paper. batch_norm_epsilon: Float, default batch normalization epsilon. Obtained from the paper. depth_divisor: Optional. Used when rounding off the coefficient scaled channels and depth of the layers. min_depth: Optional. Minimum depth value in order to avoid blocks with 0 layers. data_format: "channels_first" or "channels_last". If left as None, defaults to the value set in ~/.keras. default_size: Specifies the default image size of the model # Raises: - ValueError: If weights are not in 'imagenet' or None. - ValueError: If weights are 'imagenet' and `classes` is not 1000. # Returns: A Keras Model. """ if not (weights in {'imagenet', None} or os.path.exists(weights)): raise ValueError('The `weights` argument should be either ' '`None` (random initialization), `imagenet` ' '(pre-training on ImageNet), ' 'or the path to the weights file to be loaded.') if weights == 'imagenet' and include_top and classes != 1000: raise ValueError('If using `weights` as `"imagenet"` with `include_top` ' 'as true, `classes` should be 1000') if data_format is None: data_format = K.image_data_format() if data_format == 'channels_first': channel_axis = 1 else: channel_axis = -1 if default_size is None: default_size = 224 if block_args_list is None: block_args_list = DEFAULT_BLOCK_LIST # count number of strides to compute min size stride_count = 1 for block_args in block_args_list: if block_args.strides is not None and block_args.strides[0] > 1: stride_count += 1 min_size = int(2 ** stride_count) # Determine proper input shape and default size. input_shape = _obtain_input_shape(input_shape, default_size=default_size, min_size=min_size, data_format=data_format, require_flatten=include_top, weights=weights) # Stem part if input_tensor is None: inputs = layers.Input(shape=input_shape) else: if not K.is_keras_tensor(input_tensor): inputs = layers.Input(tensor=input_tensor, shape=input_shape) else: inputs = input_tensor x = inputs x = layers.Conv2D( filters=round_filters(32, width_coefficient, depth_divisor, min_depth), kernel_size=[3, 3], strides=[2, 2], kernel_initializer=EfficientNetConvInitializer(), padding='same', use_bias=False)(x) x = layers.BatchNormalization( axis=channel_axis, momentum=batch_norm_momentum, epsilon=batch_norm_epsilon)(x) x = Swish()(x) num_blocks = sum([block_args.num_repeat for block_args in block_args_list]) drop_connect_rate_per_block = drop_connect_rate / float(num_blocks) # Blocks part for block_idx, block_args in enumerate(block_args_list): assert block_args.num_repeat > 0 # Update block input and output filters based on depth multiplier. block_args.input_filters = round_filters(block_args.input_filters, width_coefficient, depth_divisor, min_depth) block_args.output_filters = round_filters(block_args.output_filters, width_coefficient, depth_divisor, min_depth) block_args.num_repeat = round_repeats(block_args.num_repeat, depth_coefficient) # The first block needs to take care of stride and filter size increase. x = MBConvBlock(block_args.input_filters, block_args.output_filters, block_args.kernel_size, block_args.strides, block_args.expand_ratio, block_args.se_ratio, block_args.identity_skip, drop_connect_rate_per_block * block_idx, batch_norm_momentum, batch_norm_epsilon, data_format)(x) if block_args.num_repeat > 1: block_args.input_filters = block_args.output_filters block_args.strides = [1, 1] for _ in range(block_args.num_repeat - 1): x = MBConvBlock(block_args.input_filters, block_args.output_filters, block_args.kernel_size, block_args.strides, block_args.expand_ratio, block_args.se_ratio, block_args.identity_skip, drop_connect_rate_per_block * block_idx, batch_norm_momentum, batch_norm_epsilon, data_format)(x) # Head part x = layers.Conv2D( filters=round_filters(1280, width_coefficient, depth_coefficient, min_depth), kernel_size=[1, 1], strides=[1, 1], kernel_initializer=EfficientNetConvInitializer(), padding='same', use_bias=False)(x) x = layers.BatchNormalization( axis=channel_axis, momentum=batch_norm_momentum, epsilon=batch_norm_epsilon)(x) x = Swish()(x) if include_top: x = layers.GlobalAveragePooling2D(data_format=data_format)(x) if dropout_rate > 0: x = layers.Dropout(dropout_rate)(x) x = layers.Dense(classes, kernel_initializer=EfficientNetDenseInitializer())(x) x = layers.Activation('softmax')(x) else: if pooling == 'avg': x = layers.GlobalAveragePooling2D()(x) elif pooling == 'max': x = layers.GlobalMaxPooling2D()(x) outputs = x # Ensure that the model takes into account # any potential predecessors of `input_tensor`. if input_tensor is not None: inputs = get_source_inputs(input_tensor) model = Model(inputs, outputs) # Load weights if weights == 'imagenet': if default_size == 224: if include_top: weights_path = get_file( 'efficientnet-b0.h5', "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b0.h5", cache_subdir='models') else: weights_path = get_file( 'efficientnet-b0_notop.h5.h5', "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b0_notop.h5", cache_subdir='models') model.load_weights(weights_path) elif default_size == 240: if include_top: weights_path = get_file( 'efficientnet-b1.h5', "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b1.h5", cache_subdir='models') else: weights_path = get_file( 'efficientnet-b1_notop.h5.h5', "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b1_notop.h5", cache_subdir='models') model.load_weights(weights_path) elif default_size == 260: if include_top: weights_path = get_file( 'efficientnet-b2.h5', "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b2.h5", cache_subdir='models') else: weights_path = get_file( 'efficientnet-b2_notop.h5.h5', "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b2_notop.h5", cache_subdir='models') model.load_weights(weights_path) elif default_size == 300: if include_top: weights_path = get_file( 'efficientnet-b3.h5', "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b3.h5", cache_subdir='models') else: weights_path = get_file( 'efficientnet-b3_notop.h5', "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b3_notop.h5", cache_subdir='models') model.load_weights(weights_path) # elif default_size == 380: # if include_top: # weights_path = get_file( # 'efficientnet-b4.h5', # "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b4.h5", # cache_subdir='models') # else: # weights_path = get_file( # 'efficientnet-b4_notoph5', # "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b4_notop.h5", # cache_subdir='models') # model.load_weights(weights_path) # # elif default_size == 456: # if include_top: # weights_path = get_file( # 'efficientnet-b5.h5', # "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b5.h5", # cache_subdir='models') # else: # weights_path = get_file( # 'efficientnet-b5_notop.h5', # "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b5_notop.h5", # cache_subdir='models') # model.load_weights(weights_path) # # elif default_size == 528: # if include_top: # weights_path = get_file( # 'efficientnet-b6.h5', # "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b6.h5", # cache_subdir='models') # else: # weights_path = get_file( # 'efficientnet-b6_notop.h5', # "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b6_notop.h5", # cache_subdir='models') # model.load_weights(weights_path) # # elif default_size == 600: # if include_top: # weights_path = get_file( # 'efficientnet-b7.h5', # "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b7.h5", # cache_subdir='models') # else: # weights_path = get_file( # 'efficientnet-b7_notop.h5', # "https://github.com/titu1994/keras-efficientnets/releases/download/v0.1/efficientnet-b7_notop.h5", # cache_subdir='models') # model.load_weights(weights_path) else: raise ValueError('ImageNet weights can only be loaded with EfficientNetB0-7') elif weights is not None: model.load_weights(weights) return model
def EfficientNet(input_shape, block_args_list: List[BlockArgs], width_coefficient: float, depth_coefficient: float, include_top=True, weights=None, input_tensor=None, pooling=None, classes=1000, dropout_rate=0., drop_connect_rate=0., batch_norm_momentum=0.99, batch_norm_epsilon=1e-3, depth_divisor=8, min_depth=None, data_format=None, default_size=None, **kwargs): if data_format is None: data_format = K.image_data_format() # if data_format == 'channels_first': # channel_axis = 1 # else: # channel_axis = -1 if default_size is None: default_size = 224 if block_args_list is None: block_args_list = get_default_block_list() # count number of strides to compute min size stride_count = 1 for block_args in block_args_list: if block_args.strides is not None and block_args.strides[0] > 1: stride_count += 1 min_size = int(2 ** stride_count) # Determine proper input shape and default size. input_shape = _obtain_input_shape(input_shape, default_size=default_size, min_size=min_size, data_format=data_format, require_flatten=include_top, weights=weights) # Stem part if input_tensor is None: inputs = layers.Input(shape=input_shape) else: if not K.is_keras_tensor(input_tensor): inputs = layers.Input(tensor=input_tensor, shape=input_shape) else: inputs = input_tensor x = inputs x = layers.Conv2D( filters=round_filters(32, width_coefficient, depth_divisor, min_depth), kernel_size=[3, 3], strides=[2, 2], kernel_initializer=EfficientNetConvInitializer(), padding='same', use_bias=False)(x) x = layers.BatchNormalization( axis=channel_axis, momentum=batch_norm_momentum, epsilon=batch_norm_epsilon)(x) x = Swish()(x) num_blocks = sum([block_args.num_repeat for block_args in block_args_list]) drop_connect_rate_per_block = drop_connect_rate / float(num_blocks) # Blocks part for block_idx, block_args in enumerate(block_args_list): assert block_args.num_repeat > 0 # Update block input and output filters based on depth multiplier. block_args.input_filters = round_filters(block_args.input_filters, width_coefficient, depth_divisor, min_depth) block_args.output_filters = round_filters(block_args.output_filters, width_coefficient, depth_divisor, min_depth) block_args.num_repeat = round_repeats(block_args.num_repeat, depth_coefficient) # The first block needs to take care of stride and filter size increase. x = MBConvBlock(block_args.input_filters, block_args.output_filters, block_args.kernel_size, block_args.strides, block_args.expand_ratio, block_args.se_ratio, block_args.identity_skip, drop_connect_rate_per_block * block_idx, batch_norm_momentum, batch_norm_epsilon, data_format)(x) if block_args.num_repeat > 1: block_args.input_filters = block_args.output_filters block_args.strides = [1, 1] for _ in range(block_args.num_repeat - 1): x = MBConvBlock(block_args.input_filters, block_args.output_filters, block_args.kernel_size, block_args.strides, block_args.expand_ratio, block_args.se_ratio, block_args.identity_skip, drop_connect_rate_per_block * block_idx, batch_norm_momentum, batch_norm_epsilon, data_format)(x) # Head part x = layers.Conv2D( filters=round_filters(1280, width_coefficient, depth_coefficient, min_depth), kernel_size=[1, 1], strides=[1, 1], kernel_initializer=EfficientNetConvInitializer(), padding='same', use_bias=False)(x) x = layers.BatchNormalization( axis=channel_axis, momentum=batch_norm_momentum, epsilon=batch_norm_epsilon)(x) x = Swish()(x) x = layers.GlobalAveragePooling2D(data_format=data_format)(x) if dropout_rate > 0: x = layers.Dropout(dropout_rate)(x) x = layers.Dense(classes, kernel_initializer=EfficientNetDenseInitializer())(x) x = layers.Activation('softmax')(x) outputs = x # Ensure that the model takes into account # any potential predecessors of `input_tensor`. if input_tensor is not None: inputs = get_source_inputs(input_tensor) model = Model(inputs, outputs) # Load weights elif weights is not None: model.load_weights(weights)