File size: 5,132 Bytes
747451d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# /*---------------------------------------------------------------------------------------------
#  * Copyright (c) 2022 STMicroelectronics.
#  * All rights reserved.
#  * This software is licensed under terms that can be found in the LICENSE file in
#  * the root directory of this software component.
#  * If no LICENSE file comes with this software, it is provided AS-IS.
#  *--------------------------------------------------------------------------------------------*/

import keras
from keras import layers
from keras.regularizers import l2
from typing import Tuple


def _resnet_layer(inputs: layers.Input, num_filters: int = 16, kernel_size: int = 3, strides: int = 1,
                 activation: str = 'relu', batch_normalization: bool = True,
                 conv_first: bool = True, **kwargs) -> layers.Activation:
    """
    2D Convolution-Batch Normalization-Activation stack builder for ResNet models.

    Args:
        inputs: Input tensor from input image or previous layer.
        num_filters: Conv2D number of filters.
        kernel_size: Conv2D square kernel dimensions.
        strides: Conv2D square stride dimensions.
        activation: Activation name.
        batch_normalization: Whether to include batch normalization.
        conv_first: Conv-BN-Activation (True) or BN-Activation-Conv (False).

    Returns:
        A tensor as input to the next layer.
    """
    conv = layers.Conv2D(num_filters,
                         kernel_size=kernel_size,
                         strides=strides,
                         padding='same',
                         kernel_initializer='he_normal',
                         kernel_regularizer=l2(1e-4))

    x = inputs
    if conv_first:
        x = conv(x)
        if batch_normalization:
            x = layers.BatchNormalization()(x)
        if activation is not None:
            x = layers.Activation(activation)(x)
    else:
        if batch_normalization:
            x = layers.BatchNormalization()(x)
        if activation is not None:
            x = layers.Activation(activation)(x)
        x = conv(x)
    return x


def get_resnet(num_classes: int = None, input_shape: Tuple[int, int, int] = None,
                 depth: int = None, dropout: float = None, pretrained: bool = False, **kwargs) -> keras.Model:
    """
    ResNet Version 1 Model builder.

    Stacks of 2 x (3 x 3) Conv2D-BN-ReLU. Last ReLU is after the shortcut connection.
    At the beginning of each stage, the feature map size is halved (down-sampled)
    by a convolutional layer with strides=2, while the number of filters is doubled.
    Within each stage, the layers have the same number filters and the same number of filters.

    Args:
        num_classes: Number of classes in the dataset.
        input_shape: Shape of the input tensor.
        depth: Depth of the ResNet model. Must be one of [8, 20, 32].
        dropout: Dropout rate to be applied to the fully connected layer.

    Returns:
        A Keras model instance.
    """
    
    if pretrained:
      print("WARNING: No pretrained weights are found for 'resnet' model. Random weights are used instead.")

    allowed_depths = [8, 20, 32]
    if depth not in allowed_depths:
        raise ValueError(f"depth must be one of {allowed_depths}, got {depth}")

    # Start model definition.
    num_filters = 16
    num_res_blocks = int((depth - 2) / 6)

    inputs = keras.Input(shape=input_shape)
    x = _resnet_layer(inputs=inputs)

    # Instantiate the stack of residual units
    for stack in range(3):
        for res_block in range(num_res_blocks):
            strides = 1
            if stack > 0 and res_block == 0:  # first layer but not first stack
                strides = 2  # down sample
            y = _resnet_layer(inputs=x,
                             num_filters=num_filters,
                             strides=strides)
            y = _resnet_layer(inputs=y,
                             num_filters=num_filters,
                             activation=None)
            if stack > 0 and res_block == 0:  # first layer but not first stack
                # linear projection residual shortcut connection to match changed dims
                x = _resnet_layer(inputs=x,
                                 num_filters=num_filters,
                                 kernel_size=1,
                                 strides=strides,
                                 activation=None,
                                 batch_normalization=False)
            x = layers.add([x, y])
            x = layers.Activation('relu')(x)
        num_filters *= 2

    # Add classifier on top.
    # v1 does not use BN after last shortcut connection-ReLU
    x = layers.AveragePooling2D(pool_size=8)(x)
    x = layers.Flatten()(x)
    if dropout:
        x = layers.Dropout(dropout)(x)

    if num_classes > 2:
        outputs = layers.Dense(num_classes, activation="softmax", kernel_initializer='he_normal')(x)
    else:
        outputs = layers.Dense(1, activation="sigmoid")(x)

    # Instantiate model.
    model = keras.Model(inputs=inputs, outputs=outputs, name=f"resnet{depth}")
    return model