diff --git a/luma/__import__.py b/luma/__import__.py index 3923c58..d078095 100644 --- a/luma/__import__.py +++ b/luma/__import__.py @@ -155,6 +155,7 @@ ResNet_34, ResNet_50, ResNet_101, + ResNet_152, ) from luma.neural.autoprop import LayerNode, LayerGraph @@ -338,7 +339,7 @@ VGGNet_11, VGGNet_13, VGGNet_16, VGGNet_19, Inception_V1, Inception_V2, Inception_V3, Inception_V4, InceptionResNet_V1, InceptionResNet_V2, - ResNet_18, ResNet_34, ResNet_50, ResNet_101 + ResNet_18, ResNet_34, ResNet_50, ResNet_101, ResNet_152 # ------------------- [ luma.metric ] ---------------------- Accuracy, Precision, Recall, F1Score, Specificity diff --git a/luma/neural/_models/resnet.py b/luma/neural/_models/resnet.py index 0f8bb01..56676f8 100644 --- a/luma/neural/_models/resnet.py +++ b/luma/neural/_models/resnet.py @@ -596,3 +596,136 @@ def score( argmax: bool = True, ) -> float: return super(_ResNet_101, self).score_nn(X, y, metric, argmax) + + +class _ResNet_152(Estimator, Supervised, NeuralModel): + def __init__( + self, + activation: Activation.FuncType = Activation.ReLU, + initializer: InitUtil.InitStr = None, + out_features: int = 1000, + batch_size: int = 128, + n_epochs: int = 100, + valid_size: float = 0.1, + lambda_: float = 0.0, + momentum: float = 0.9, + early_stopping: bool = False, + patience: int = 10, + shuffle: bool = True, + random_state: int | None = None, + deep_verbose: bool = False, + ) -> None: + self.activation = activation + self.initializer = initializer + self.out_features = out_features + self.lambda_ = lambda_ + self.momentum = momentum + self.shuffle = shuffle + self.random_state = random_state + self._fitted = False + + super().__init__( + batch_size, + n_epochs, + valid_size, + early_stopping, + patience, + shuffle, + random_state, + deep_verbose, + ) + super().init_model() + self.model = Sequential() + + self.feature_sizes_ = [ + [3, 64], + [64, 64, 256] * 3, + [128, 128, 512] * 8, + [256, 256, 1024] * 36, + [512, 512, 2048] * 3, + ] + self.feature_shapes_ = [ + self._get_feature_shapes(sizes) for sizes in self.feature_sizes_ + ] + + self.set_param_ranges( + { + "out_features": ("0<,+inf", int), + "batch_size": ("0<,+inf", int), + "n_epochs": ("0<,+inf", int), + "valid_size": ("0<,<1", None), + "momentum": ("0,1", None), + "dropout_rate": ("0,1", None), + "lambda_": ("0,+inf", None), + "patience": ("0<,+inf", int), + } + ) + self.check_param_ranges() + self.build_model() + + def build_model(self) -> None: + base_args = { + "initializer": self.initializer, + "lambda_": self.lambda_, + "random_state": self.random_state, + } + res_args = BaseBlockArgs( + activation=self.activation, + do_batch_norm=True, + momentum=self.momentum, + **base_args, + ) + + self.model.extend( + Convolution2D(3, 64, 7, 2, 3, **base_args), + BatchNorm2D(64, self.momentum), + self.activation(), + Pooling2D(3, 2, "max", "same"), + ) + self.layer_2, in_channels = _make_layer( + 64, 64, Bottleneck, 3, 2, base_args, res_args + ) + self.layer_3, in_channels = _make_layer( + in_channels, 128, Bottleneck, 8, 3, base_args, res_args, stride=2 + ) + self.layer_4, in_channels = _make_layer( + in_channels, 256, Bottleneck, 36, 4, base_args, res_args, stride=2 + ) + self.layer_5, in_channels = _make_layer( + in_channels, 512, Bottleneck, 3, 5, base_args, res_args, stride=2 + ) + + self.model.extend( + self.layer_2, + self.layer_3, + self.layer_4, + self.layer_5, + deep_add=True, + ) + self.model.extend( + AdaptiveAvgPooling2D((1, 1)), + Flatten(), + Dense(512 * Bottleneck.expansion, self.out_features, **base_args), + ) + + input_shape: tuple = (-1, 3, 224, 224) + + @Tensor.force_shape(input_shape) + def fit(self, X: Tensor, y: Matrix) -> Self: + return super(_ResNet_152, self).fit_nn(X, y) + + @override + @Tensor.force_shape(input_shape) + def predict(self, X: Tensor, argmax: bool = True) -> Matrix | Vector: + return super(_ResNet_152, self).predict_nn(X, argmax) + + @override + @Tensor.force_shape(input_shape) + def score( + self, + X: Tensor, + y: Matrix, + metric: Evaluator = Accuracy, + argmax: bool = True, + ) -> float: + return super(_ResNet_152, self).score_nn(X, y, metric, argmax) diff --git a/luma/neural/model.py b/luma/neural/model.py index 953cc69..4e30d98 100644 --- a/luma/neural/model.py +++ b/luma/neural/model.py @@ -28,9 +28,13 @@ "ResNet_34", "ResNet_50", "ResNet_101", + "ResNet_152", ) +NUM_MODELS: int = len(__all__) + + class SimpleMLP(_models.simple._SimpleMLP): """ An MLP (Multilayer Perceptron) is a type of artificial neural network @@ -2256,3 +2260,102 @@ def __init__( random_state, deep_verbose, ) + + +class ResNet_152(_models.resnet._ResNet_152): + """ + ResNet-152 is a 152-layer deep neural network that uses residual blocks + to improve training by learning residuals, helping prevent vanishing + gradients and enabling better performance in image recognition tasks. + + Structure + --------- + Input: + ```py + Tensor[..., 3, 224, 224] + ``` + Residual Blocks: + ```py + Convolution2D(3, 64, filter_size=7, stride=2) -> # conv1 + + 3x ResNetBlock.Bottleneck(64, 64) -> # conv2 + 8x ResNetBlock.Bottleneck(128, 128, stride=2) -> # conv3 + 36x ResNetBlock.Bottleneck(256, 256, stride=2) -> # conv4 + 3x ResNetBlock.Bottleneck(512, 512, stride=2) -> # conv5 + + AdaptiveAvgPooling2D((1, 1)) -> # avg pool + ``` + Fully Connected Layers: + ```py + Flatten -> Dense(512 * 4, 1000) + ``` + Output: + ```py + Matrix[..., 1000] + ``` + Parameter Size: + ```txt + 44,548,160 weights, 53,672 biases -> 44,601,832 params + ``` + Parameters + ---------- + `activation` : FuncType, default=Activation.ReLU + Type of activation function + `initializer` : InitStr, default=None + Type of weight initializer + `out_features` : int, default=1000 + Number of output features + `batch_size` : int, default=100 + Size of a single mini-batch + `n_epochs` : int, default=100 + Number of epochs for training + `valid_size` : float, default=0.1 + Fractional size of validation set + `lambda_` : float, default=0.0 + L2 regularization strength + `early_stopping` : bool, default=False + Whether to early-stop the training when the valid score stagnates + `patience` : int, default=10 + Number of epochs to wait until early-stopping + `shuffle` : bool, default=True + Whethter to shuffle the data at the beginning of every epoch + + References + ---------- + 1. He, Kaiming, et al. “Deep Residual Learning for Image Recognition.” + Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition + (CVPR), 2016, pp. 770-778. + + """ + + def __init__( + self, + activation: Activation.FuncType = Activation.ReLU, + initializer: InitUtil.InitStr = None, + out_features: int = 1000, + batch_size: int = 128, + n_epochs: int = 100, + valid_size: float = 0.1, + lambda_: float = 0, + momentum: float = 0.9, + early_stopping: bool = False, + patience: int = 10, + shuffle: bool = True, + random_state: int | None = None, + deep_verbose: bool = False, + ) -> None: + super(ResNet_152, self).__init__( + activation, + initializer, + out_features, + batch_size, + n_epochs, + valid_size, + lambda_, + momentum, + early_stopping, + patience, + shuffle, + random_state, + deep_verbose, + )