instantiates the mobilenetv2 architecture mobilenetv2 is very similar to the original mobilenet except that it uses inverted residual blocks with bottlenecking features it has a drastically lower parameter count than the original mobilenet mobilenets support any input size greater than 32 x 32 with larger image sizes offering better performance reference mobilenetv2 inverted residuals and linear bottlenecks https arxiv orgabs1801 04381 cvpr 2018 this function returns a keras image classification model optionally loaded with weights pretrained on imagenet for image classification use cases see this page for detailed examples https keras ioapiapplicationsusageexamplesforimageclassificationmodels for transfer learning use cases make sure to read the guide to transfer learning finetuning https keras ioguidestransferlearning note each keras application expects a specific kind of input preprocessing for mobilenetv2 call keras applications mobilenetv2 preprocessinput on your inputs before passing them to the model mobilenetv2 preprocessinput will scale input pixels between 1 and 1 args inputshape optional shape tuple only to be specified if includetop is false otherwise the input shape has to be 224 224 3 with channelslast data format or 3 224 224 with channelsfirst data format it should have exactly 3 inputs channels and width and height should be no smaller than 32 e g 200 200 3 would be one valid value defaults to none inputshape will be ignored if the inputtensor is provided alpha controls the width of the network this is known as the width multiplier in the mobilenet paper if alpha 1 0 proportionally decreases the number of filters in each layer if alpha 1 0 proportionally increases the number of filters in each layer if alpha 1 default number of filters from the paper are used at each layer defaults to 1 0 includetop boolean whether to include the fullyconnected layer at the top of the network defaults to true weights one of none random initialization imagenet pretraining on imagenet or the path to the weights file to be loaded defaults to imagenet inputtensor optional keras tensor i e output of layers input to use as image input for the model inputtensor is useful for sharing inputs between multiple different networks defaults to none pooling optional pooling mode for feature extraction when includetop is false none default means that the output of the model will be the 4d tensor output of the last convolutional block avg means that global average pooling will be applied to the output of the last convolutional block 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 includetop is true and if no weights argument is specified defaults to 1000 classifieractivation a str or callable the activation function to use on the top layer ignored unless includetoptrue set classifieractivationnone to return the logits of the top layer when loading pretrained weights classifieractivation can only be none or softmax returns a model instance determine proper input shape and default size if both inputshape and inputtensor are used they should match if inputshape is none infer shape from inputtensor if inputshape is none and no inputtensor if inputshape is not none assume default size no alpha applied to last conv as stated in the paper if the width multiplier is greater than 1 we increase the number of output channels ensure that the model takes into account any potential predecessors of inputtensor create model load weights inverted resnet block channelaxis 1 if backend imagedataformat channelsfirst else 1 inchannels inputs shapechannelaxis pointwiseconvfilters intfilters alpha ensure the number of filters on the last 1x1 convolution is divisible by 8 pointwisefilters makedivisiblepointwiseconvfilters 8 x inputs prefix fblockblockid if blockid expand with a pointwise 1x1 convolution x layers conv2d expansion inchannels kernelsize1 paddingsame usebiasfalse activationnone nameprefix expand x x layers batchnormalization axischannelaxis epsilon1e3 momentum0 999 nameprefix expandbn x x layers relu6 0 nameprefix expandrelux else prefix expandedconv depthwise 3x3 convolution if stride 2 x layers zeropadding2d paddingimagenetutils correctpadx 3 nameprefix pad x x layers depthwiseconv2d kernelsize3 stridesstride activationnone usebiasfalse paddingsame if stride 1 else valid nameprefix depthwise x x layers batchnormalization axischannelaxis epsilon1e3 momentum0 999 nameprefix depthwisebn x x layers relu6 0 nameprefix depthwiserelux project with a pointwise 1x1 convolution x layers conv2d pointwisefilters kernelsize1 paddingsame usebiasfalse activationnone nameprefix project x x layers batchnormalization axischannelaxis epsilon1e3 momentum0 999 nameprefix projectbn x if inchannels pointwisefilters and stride 1 return layers addnameprefix addinputs x return x def makedivisiblev divisor minvaluenone if minvalue is none minvalue divisor newv maxminvalue intv divisor 2 divisor divisor make sure that round down does not go down by more than 10 if newv 0 9 v newv divisor return newv kerasexportkeras applications mobilenetv2 preprocessinput def preprocessinputx dataformatnone return imagenetutils preprocessinput x dataformatdataformat modetf kerasexportkeras applications mobilenetv2 decodepredictions def decodepredictionspreds top5 return imagenetutils decodepredictionspreds toptop preprocessinput doc imagenetutils preprocessinputdoc format mode retimagenetutils preprocessinputretdoctf errorimagenetutils preprocessinputerrordoc decodepredictions doc imagenetutils decodepredictions doc instantiates the mobilenetv2 architecture mobilenetv2 is very similar to the original mobilenet except that it uses inverted residual blocks with bottlenecking features it has a drastically lower parameter count than the original mobilenet mobilenets support any input size greater than 32 x 32 with larger image sizes offering better performance reference mobilenetv2 inverted residuals and linear bottlenecks https arxiv org abs 1801 04381 cvpr 2018 this function returns a keras image classification model optionally loaded with weights pre trained on imagenet for image classification use cases see this page for detailed examples https keras io api applications usage examples for image classification models for transfer learning use cases make sure to read the guide to transfer learning fine tuning https keras io guides transfer_learning note each keras application expects a specific kind of input preprocessing for mobilenetv2 call keras applications mobilenet_v2 preprocess_input on your inputs before passing them to the model mobilenet_v2 preprocess_input will scale input pixels between 1 and 1 args input_shape optional shape tuple only to be specified if include_top is false otherwise the input shape has to be 224 224 3 with channels_last data format or 3 224 224 with channels_first data format it should have exactly 3 inputs channels and width and height should be no smaller than 32 e g 200 200 3 would be one valid value defaults to none input_shape will be ignored if the input_tensor is provided alpha controls the width of the network this is known as the width multiplier in the mobilenet paper if alpha 1 0 proportionally decreases the number of filters in each layer if alpha 1 0 proportionally increases the number of filters in each layer if alpha 1 default number of filters from the paper are used at each layer defaults to 1 0 include_top boolean whether to include the fully connected layer at the top of the network defaults to true weights one of none random initialization imagenet pre training on imagenet or the path to the weights file to be loaded defaults to imagenet input_tensor optional keras tensor i e output of layers input to use as image input for the model input_tensor is useful for sharing inputs between multiple different networks defaults to none pooling optional pooling mode for feature extraction when include_top is false none default means that the output of the model will be the 4d tensor output of the last convolutional block avg means that global average pooling will be applied to the output of the last convolutional block 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 defaults to 1000 classifier_activation a str or callable the activation function to use on the top layer ignored unless include_top true set classifier_activation none to return the logits of the top layer when loading pretrained weights classifier_activation can only be none or softmax returns a model instance determine proper input shape and default size if both input_shape and input_tensor are used they should match if input_shape is none infer shape from input_tensor if input_shape is none and no input_tensor if input_shape is not none assume default size no alpha applied to last conv as stated in the paper if the width multiplier is greater than 1 we increase the number of output channels ensure that the model takes into account any potential predecessors of input_tensor create model load weights inverted resnet block ensure the number of filters on the last 1x1 convolution is divisible by 8 expand with a pointwise 1x1 convolution depthwise 3x3 convolution project with a pointwise 1x1 convolution make sure that round down does not go down by more than 10
import warnings

from keras import backend
from keras import layers
from keras.api_export import keras_export
from keras.applications import imagenet_utils
from keras.models import Functional
from keras.ops import operation_utils
from keras.utils import file_utils

BASE_WEIGHT_PATH = (
    "https://storage.googleapis.com/tensorflow/keras-applications/mobilenet_v2/"
)


@keras_export(
    [
        "keras.applications.mobilenet_v2.MobileNetV2",
        "keras.applications.MobileNetV2",
    ]
)
def MobileNetV2(
    input_shape=None,
    alpha=1.0,
    include_top=True,
    weights="imagenet",
    input_tensor=None,
    pooling=None,
    classes=1000,
    classifier_activation="softmax",
):
    
    if not (weights in {"imagenet", None} or file_utils.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.  "
            f"Received `weights={weights}`"
        )

    if weights == "imagenet" and include_top and classes != 1000:
        raise ValueError(
            'If using `weights="imagenet"` with `include_top` '
            f"as true, `classes` should be 1000. Received `classes={classes}`"
        )

    
    
    if input_shape is not None and input_tensor is not None:
        try:
            is_input_t_tensor = backend.is_keras_tensor(input_tensor)
        except ValueError:
            try:
                is_input_t_tensor = backend.is_keras_tensor(
                    operation_utils.get_source_inputs(input_tensor)
                )
            except ValueError:
                raise ValueError(
                    f"input_tensor: {input_tensor}"
                    "is not type input_tensor. "
                    f"Received `type(input_tensor)={type(input_tensor)}`"
                )
        if is_input_t_tensor:
            if backend.image_data_format() == "channels_first":
                if input_tensor.shape[1] != input_shape[1]:
                    raise ValueError(
                        "input_shape[1] must equal shape(input_tensor)[1] "
                        "when `image_data_format` is `channels_first`; "
                        "Received `input_tensor.shape="
                        f"{input_tensor.shape}`"
                        f", `input_shape={input_shape}`"
                    )
            else:
                if input_tensor.shape[2] != input_shape[1]:
                    raise ValueError(
                        "input_tensor.shape[2] must equal input_shape[1]; "
                        "Received `input_tensor.shape="
                        f"{input_tensor.shape}`, "
                        f"`input_shape={input_shape}`"
                    )
        else:
            raise ValueError(
                "input_tensor is not a Keras tensor; "
                f"Received `input_tensor={input_tensor}`"
            )

    
    if input_shape is None and input_tensor is not None:
        try:
            backend.is_keras_tensor(input_tensor)
        except ValueError:
            raise ValueError(
                "input_tensor must be a valid Keras tensor type; "
                f"Received {input_tensor} of type {type(input_tensor)}"
            )

        if input_shape is None and not backend.is_keras_tensor(input_tensor):
            default_size = 224
        elif input_shape is None and backend.is_keras_tensor(input_tensor):
            if backend.image_data_format() == "channels_first":
                rows = input_tensor.shape[2]
                cols = input_tensor.shape[3]
            else:
                rows = input_tensor.shape[1]
                cols = input_tensor.shape[2]

            if rows == cols and rows in [96, 128, 160, 192, 224]:
                default_size = rows
            else:
                default_size = 224

    
    elif input_shape is None:
        default_size = 224

    
    else:
        if backend.image_data_format() == "channels_first":
            rows = input_shape[1]
            cols = input_shape[2]
        else:
            rows = input_shape[0]
            cols = input_shape[1]

        if rows == cols and rows in [96, 128, 160, 192, 224]:
            default_size = rows
        else:
            default_size = 224

    input_shape = imagenet_utils.obtain_input_shape(
        input_shape,
        default_size=default_size,
        min_size=32,
        data_format=backend.image_data_format(),
        require_flatten=include_top,
        weights=weights,
    )

    if backend.image_data_format() == "channels_last":
        row_axis, col_axis = (0, 1)
    else:
        row_axis, col_axis = (1, 2)
    rows = input_shape[row_axis]
    cols = input_shape[col_axis]

    if weights == "imagenet":
        if alpha not in [0.35, 0.50, 0.75, 1.0, 1.3, 1.4]:
            raise ValueError(
                "If imagenet weights are being loaded, "
                "alpha must be one of `0.35`, `0.50`, `0.75`, "
                "`1.0`, `1.3` or `1.4` only;"
                f" Received `alpha={alpha}`"
            )

        if rows != cols or rows not in [96, 128, 160, 192, 224]:
            rows = 224
            warnings.warn(
                "`input_shape` is undefined or non-square, "
                "or `rows` is not in [96, 128, 160, 192, 224]. "
                "Weights for input shape (224, 224) will be "
                "loaded as the default.",
                stacklevel=2,
            )

    if input_tensor is None:
        img_input = layers.Input(shape=input_shape)
    else:
        if not backend.is_keras_tensor(input_tensor):
            img_input = layers.Input(tensor=input_tensor, shape=input_shape)
        else:
            img_input = input_tensor

    channel_axis = 1 if backend.image_data_format() == "channels_first" else -1

    first_block_filters = _make_divisible(32 * alpha, 8)
    x = layers.Conv2D(
        first_block_filters,
        kernel_size=3,
        strides=(2, 2),
        padding="same",
        use_bias=False,
        name="Conv1",
    )(img_input)
    x = layers.BatchNormalization(
        axis=channel_axis, epsilon=1e-3, momentum=0.999, name="bn_Conv1"
    )(x)
    x = layers.ReLU(6.0, name="Conv1_relu")(x)

    x = _inverted_res_block(
        x, filters=16, alpha=alpha, stride=1, expansion=1, block_id=0
    )

    x = _inverted_res_block(
        x, filters=24, alpha=alpha, stride=2, expansion=6, block_id=1
    )
    x = _inverted_res_block(
        x, filters=24, alpha=alpha, stride=1, expansion=6, block_id=2
    )

    x = _inverted_res_block(
        x, filters=32, alpha=alpha, stride=2, expansion=6, block_id=3
    )
    x = _inverted_res_block(
        x, filters=32, alpha=alpha, stride=1, expansion=6, block_id=4
    )
    x = _inverted_res_block(
        x, filters=32, alpha=alpha, stride=1, expansion=6, block_id=5
    )

    x = _inverted_res_block(
        x, filters=64, alpha=alpha, stride=2, expansion=6, block_id=6
    )
    x = _inverted_res_block(
        x, filters=64, alpha=alpha, stride=1, expansion=6, block_id=7
    )
    x = _inverted_res_block(
        x, filters=64, alpha=alpha, stride=1, expansion=6, block_id=8
    )
    x = _inverted_res_block(
        x, filters=64, alpha=alpha, stride=1, expansion=6, block_id=9
    )

    x = _inverted_res_block(
        x, filters=96, alpha=alpha, stride=1, expansion=6, block_id=10
    )
    x = _inverted_res_block(
        x, filters=96, alpha=alpha, stride=1, expansion=6, block_id=11
    )
    x = _inverted_res_block(
        x, filters=96, alpha=alpha, stride=1, expansion=6, block_id=12
    )

    x = _inverted_res_block(
        x, filters=160, alpha=alpha, stride=2, expansion=6, block_id=13
    )
    x = _inverted_res_block(
        x, filters=160, alpha=alpha, stride=1, expansion=6, block_id=14
    )
    x = _inverted_res_block(
        x, filters=160, alpha=alpha, stride=1, expansion=6, block_id=15
    )

    x = _inverted_res_block(
        x, filters=320, alpha=alpha, stride=1, expansion=6, block_id=16
    )

    
    
    
    if alpha > 1.0:
        last_block_filters = _make_divisible(1280 * alpha, 8)
    else:
        last_block_filters = 1280

    x = layers.Conv2D(
        last_block_filters, kernel_size=1, use_bias=False, name="Conv_1"
    )(x)
    x = layers.BatchNormalization(
        axis=channel_axis, epsilon=1e-3, momentum=0.999, name="Conv_1_bn"
    )(x)
    x = layers.ReLU(6.0, name="out_relu")(x)

    if include_top:
        x = layers.GlobalAveragePooling2D()(x)
        imagenet_utils.validate_activation(classifier_activation, weights)
        x = layers.Dense(
            classes, activation=classifier_activation, name="predictions"
        )(x)

    else:
        if pooling == "avg":
            x = layers.GlobalAveragePooling2D()(x)
        elif pooling == "max":
            x = layers.GlobalMaxPooling2D()(x)

    
    
    if input_tensor is not None:
        inputs = operation_utils.get_source_inputs(input_tensor)
    else:
        inputs = img_input

    
    model = Functional(inputs, x, name=f"mobilenetv2_{alpha:0.2f}_{rows}")

    
    if weights == "imagenet":
        if include_top:
            model_name = (
                "mobilenet_v2_weights_tf_dim_ordering_tf_kernels_"
                + str(float(alpha))
                + "_"
                + str(rows)
                + ".h5"
            )
            weight_path = BASE_WEIGHT_PATH + model_name
            weights_path = file_utils.get_file(
                model_name, weight_path, cache_subdir="models"
            )
        else:
            model_name = (
                "mobilenet_v2_weights_tf_dim_ordering_tf_kernels_"
                + str(float(alpha))
                + "_"
                + str(rows)
                + "_no_top"
                + ".h5"
            )
            weight_path = BASE_WEIGHT_PATH + model_name
            weights_path = file_utils.get_file(
                model_name, weight_path, cache_subdir="models"
            )
        model.load_weights(weights_path)
    elif weights is not None:
        model.load_weights(weights)

    return model


def _inverted_res_block(inputs, expansion, stride, alpha, filters, block_id):
    
    channel_axis = 1 if backend.image_data_format() == "channels_first" else -1

    in_channels = inputs.shape[channel_axis]
    pointwise_conv_filters = int(filters * alpha)
    
    
    pointwise_filters = _make_divisible(pointwise_conv_filters, 8)
    x = inputs
    prefix = f"block_{block_id}_"

    if block_id:
        
        x = layers.Conv2D(
            expansion * in_channels,
            kernel_size=1,
            padding="same",
            use_bias=False,
            activation=None,
            name=prefix + "expand",
        )(x)
        x = layers.BatchNormalization(
            axis=channel_axis,
            epsilon=1e-3,
            momentum=0.999,
            name=prefix + "expand_BN",
        )(x)
        x = layers.ReLU(6.0, name=prefix + "expand_relu")(x)
    else:
        prefix = "expanded_conv_"

    
    if stride == 2:
        x = layers.ZeroPadding2D(
            padding=imagenet_utils.correct_pad(x, 3), name=prefix + "pad"
        )(x)
    x = layers.DepthwiseConv2D(
        kernel_size=3,
        strides=stride,
        activation=None,
        use_bias=False,
        padding="same" if stride == 1 else "valid",
        name=prefix + "depthwise",
    )(x)
    x = layers.BatchNormalization(
        axis=channel_axis,
        epsilon=1e-3,
        momentum=0.999,
        name=prefix + "depthwise_BN",
    )(x)

    x = layers.ReLU(6.0, name=prefix + "depthwise_relu")(x)

    
    x = layers.Conv2D(
        pointwise_filters,
        kernel_size=1,
        padding="same",
        use_bias=False,
        activation=None,
        name=prefix + "project",
    )(x)
    x = layers.BatchNormalization(
        axis=channel_axis,
        epsilon=1e-3,
        momentum=0.999,
        name=prefix + "project_BN",
    )(x)

    if in_channels == pointwise_filters and stride == 1:
        return layers.Add(name=prefix + "add")([inputs, x])
    return x


def _make_divisible(v, divisor, min_value=None):
    if min_value is None:
        min_value = divisor
    new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
    
    if new_v < 0.9 * v:
        new_v += divisor
    return new_v


@keras_export("keras.applications.mobilenet_v2.preprocess_input")
def preprocess_input(x, data_format=None):
    return imagenet_utils.preprocess_input(
        x, data_format=data_format, mode="tf"
    )


@keras_export("keras.applications.mobilenet_v2.decode_predictions")
def decode_predictions(preds, top=5):
    return imagenet_utils.decode_predictions(preds, top=top)


preprocess_input.__doc__ = imagenet_utils.PREPROCESS_INPUT_DOC.format(
    mode="",
    ret=imagenet_utils.PREPROCESS_INPUT_RET_DOC_TF,
    error=imagenet_utils.PREPROCESS_INPUT_ERROR_DOC,
)
decode_predictions.__doc__ = imagenet_utils.decode_predictions.__doc__