Repository: keras-team/keras-cv

Issue:
Refactor `ResNetBackbone` for readability
Our backbone modeling files are very large and hard to read. @LukeWood proposed two changes in #1745:

1. move aliases to their own file
2. move helpers below model declarations

#1745 provides a nice template for this change for `MobileNetBackbone`. Replicate this change for `keras_cv/models/backbones/resnet_v1/`.

Discussion:
ianstenbit: /gcbrun
ianstenbit: Test failures are unrelated to this PR. See #1855.

Once that is merged, we can merge from master into this dev branch and then CI should be happy.
ianstenbit: /gcbrun

Patch:
diff --git a/keras_cv/models/__init__.py b/keras_cv/models/__init__.py
index bd8bd25..933c7ff 100644
--- a/keras_cv/models/__init__.py
+++ b/keras_cv/models/__init__.py
@@ -61,22 +61,19 @@ from keras_cv.models.backbones.mobilenet_v3.mobilenet_v3_aliases import (
 from keras_cv.models.backbones.mobilenet_v3.mobilenet_v3_aliases import (
     MobileNetV3SmallBackbone,
 )
-from keras_cv.models.backbones.mobilenet_v3.mobilenet_v3_backbone import (
-    MobileNetV3Backbone,
-)
-from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (
+from keras_cv.models.backbones.resnet_v1.resnet_v1_aliases import (
     ResNet18Backbone,
 )
-from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (
+from keras_cv.models.backbones.resnet_v1.resnet_v1_aliases import (
     ResNet34Backbone,
 )
-from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (
+from keras_cv.models.backbones.resnet_v1.resnet_v1_aliases import (
     ResNet50Backbone,
 )
-from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (
+from keras_cv.models.backbones.resnet_v1.resnet_v1_aliases import (
     ResNet101Backbone,
 )
-from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (
+from keras_cv.models.backbones.resnet_v1.resnet_v1_aliases import (
     ResNet152Backbone,
 )
 from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (
diff --git a/keras_cv/models/backbones/resnet_v1/resnet_v1_aliases.py b/keras_cv/models/backbones/resnet_v1/resnet_v1_aliases.py
new file mode 100644
index 0000000..f7fcc52
--- /dev/null
+++ b/keras_cv/models/backbones/resnet_v1/resnet_v1_aliases.py
@@ -0,0 +1,216 @@
+# Copyright 2023 The KerasCV Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy
+
+from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (
+    ResNetBackbone,
+)
+from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone_presets import (
+    backbone_presets,
+)
+from keras_cv.utils.python_utils import classproperty
+
+ALIAS_DOCSTRING = """ResNetBackbone (V1) model with {num_layers} layers.
+
+    Reference:
+        - [Deep Residual Learning for Image Recognition](https://arxiv.org/abs/1512.03385)
+
+    The difference in ResNetV1 and ResNetV2 rests in the structure of their
+    individual building blocks. In ResNetV2, the batch normalization and
+    ReLU activation precede the convolution layers, as opposed to ResNetV1 where
+    the batch normalization and ReLU activation are applied after the
+    convolution layers.
+
+    For transfer learning use cases, make sure to read the
+    [guide to transfer learning & fine-tuning](https://keras.io/guides/transfer_learning/).
+
+    Args:
+        include_rescaling: bool, whether to rescale the inputs. If set
+            to `True`, inputs will be passed through a `Rescaling(1/255.0)`
+            layer.
+        input_shape: optional shape tuple, defaults to (None, None, 3).
+        input_tensor: optional Keras tensor (i.e. output of `layers.Input()`)
+            to use as image input for the model.
+
+    Examples:
+    ```python
+    input_data = tf.ones(shape=(8, 224, 224, 3))
+
+    # Randomly initialized backbone
+    model = ResNet{num_layers}Backbone()
+    output = model(input_data)
+    ```
+"""  # noqa: E501
+
+
+class ResNet18Backbone(ResNetBackbone):
+    def __new__(
+        cls,
+        include_rescaling=True,
+        input_shape=(None, None, 3),
+        input_tensor=None,
+        **kwargs,
+    ):
+        # Pack args in kwargs
+        kwargs.update(
+            {
+                "include_rescaling": include_rescaling,
+                "input_shape": input_shape,
+                "input_tensor": input_tensor,
+            }
+        )
+        return ResNetBackbone.from_preset("resnet18", **kwargs)
+
+    @classproperty
+    def presets(cls):
+        """Dictionary of preset names and configurations."""
+        return {}
+
+    @classproperty
+    def presets_with_weights(cls):
+        """Dictionary of preset names and configurations that include
+        weights."""
+        return {}
+
+
+class ResNet34Backbone(ResNetBackbone):
+    def __new__(
+        cls,
+        include_rescaling=True,
+        input_shape=(None, None, 3),
+        input_tensor=None,
+        **kwargs,
+    ):
+        # Pack args in kwargs
+        kwargs.update(
+            {
+                "include_rescaling": include_rescaling,
+                "input_shape": input_shape,
+                "input_tensor": input_tensor,
+            }
+        )
+        return ResNetBackbone.from_preset("resnet34", **kwargs)
+
+    @classproperty
+    def presets(cls):
+        """Dictionary of preset names and configurations."""
+        return {}
+
+    @classproperty
+    def presets_with_weights(cls):
+        """Dictionary of preset names and configurations that include
+        weights."""
+        return {}
+
+
+class ResNet50Backbone(ResNetBackbone):
+    def __new__(
+        cls,
+        include_rescaling=True,
+        input_shape=(None, None, 3),
+        input_tensor=None,
+        **kwargs,
+    ):
+        # Pack args in kwargs
+        kwargs.update(
+            {
+                "include_rescaling": include_rescaling,
+                "input_shape": input_shape,
+                "input_tensor": input_tensor,
+            }
+        )
+        return ResNetBackbone.from_preset("resnet50", **kwargs)
+
+    @classproperty
+    def presets(cls):
+        """Dictionary of preset names and configurations."""
+        return {
+            "resnet50_imagenet": copy.deepcopy(
+                backbone_presets["resnet50_imagenet"]
+            ),
+        }
+
+    @classproperty
+    def presets_with_weights(cls):
+        """Dictionary of preset names and configurations that include
+        weights."""
+        return cls.presets
+
+
+class ResNet101Backbone(ResNetBackbone):
+    def __new__(
+        cls,
+        include_rescaling=True,
+        input_shape=(None, None, 3),
+        input_tensor=None,
+        **kwargs,
+    ):
+        # Pack args in kwargs
+        kwargs.update(
+            {
+                "include_rescaling": include_rescaling,
+                "input_shape": input_shape,
+                "input_tensor": input_tensor,
+            }
+        )
+        return ResNetBackbone.from_preset("resnet101", **kwargs)
+
+    @classproperty
+    def presets(cls):
+        """Dictionary of preset names and configurations."""
+        return {}
+
+    @classproperty
+    def presets_with_weights(cls):
+        """Dictionary of preset names and configurations that include
+        weights."""
+        return {}
+
+
+class ResNet152Backbone(ResNetBackbone):
+    def __new__(
+        cls,
+        include_rescaling=True,
+        input_shape=(None, None, 3),
+        input_tensor=None,
+        **kwargs,
+    ):
+        # Pack args in kwargs
+        kwargs.update(
+            {
+                "include_rescaling": include_rescaling,
+                "input_shape": input_shape,
+                "input_tensor": input_tensor,
+            }
+        )
+        return ResNetBackbone.from_preset("resnet152", **kwargs)
+
+    @classproperty
+    def presets(cls):
+        """Dictionary of preset names and configurations."""
+        return {}
+
+    @classproperty
+    def presets_with_weights(cls):
+        """Dictionary of preset names and configurations that include
+        weights."""
+        return {}
+
+
+setattr(ResNet18Backbone, "__doc__", ALIAS_DOCSTRING.format(num_layers=18))
+setattr(ResNet34Backbone, "__doc__", ALIAS_DOCSTRING.format(num_layers=34))
+setattr(ResNet50Backbone, "__doc__", ALIAS_DOCSTRING.format(num_layers=50))
+setattr(ResNet101Backbone, "__doc__", ALIAS_DOCSTRING.format(num_layers=101))
+setattr(ResNet152Backbone, "__doc__", ALIAS_DOCSTRING.format(num_layers=152))
diff --git a/keras_cv/models/backbones/resnet_v1/resnet_v1_backbone.py b/keras_cv/models/backbones/resnet_v1/resnet_v1_backbone.py
index fd21b17..42b26d5 100644
--- a/keras_cv/models/backbones/resnet_v1/resnet_v1_backbone.py
+++ b/keras_cv/models/backbones/resnet_v1/resnet_v1_backbone.py
@@ -11,7 +11,7 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-"""ResNet models for KerasCV.
+"""ResNet backbone model.
 Reference:
   - [Deep Residual Learning for Image Recognition](https://arxiv.org/abs/1512.03385)
     (CVPR 2015)
@@ -355,197 +355,3 @@ class ResNetBackbone(Backbone):
         """Dictionary of preset names and configurations that include
         weights."""
         return copy.deepcopy(backbone_presets_with_weights)
-
-
-ALIAS_DOCSTRING = """ResNetBackbone (V1) model with {num_layers} layers.
-
-    Reference:
-        - [Deep Residual Learning for Image Recognition](https://arxiv.org/abs/1512.03385)
-
-    The difference in ResNetV1 and ResNetV2 rests in the structure of their
-    individual building blocks. In ResNetV2, the batch normalization and
-    ReLU activation precede the convolution layers, as opposed to ResNetV1 where
-    the batch normalization and ReLU activation are applied after the
-    convolution layers.
-
-    For transfer learning use cases, make sure to read the
-    [guide to transfer learning & fine-tuning](https://keras.io/guides/transfer_learning/).
-
-    Args:
-        include_rescaling: bool, whether to rescale the inputs. If set
-            to `True`, inputs will be passed through a `Rescaling(1/255.0)`
-            layer.
-        input_shape: optional shape tuple, defaults to (None, None, 3).
-        input_tensor: optional Keras tensor (i.e. output of `layers.Input()`)
-            to use as image input for the model.
-
-    Examples:
-    ```python
-    input_data = tf.ones(shape=(8, 224, 224, 3))
-
-    # Randomly initialized backbone
-    model = ResNet{num_layers}Backbone()
-    output = model(input_data)
-    ```
-"""  # noqa: E501
-
-
-class ResNet18Backbone(ResNetBackbone):
-    def __new__(
-        cls,
-        include_rescaling=True,
-        input_shape=(None, None, 3),
-        input_tensor=None,
-        **kwargs,
-    ):
-        # Pack args in kwargs
-        kwargs.update(
-            {
-                "include_rescaling": include_rescaling,
-                "input_shape": input_shape,
-                "input_tensor": input_tensor,
-            }
-        )
-        return ResNetBackbone.from_preset("resnet18", **kwargs)
-
-    @classproperty
-    def presets(cls):
-        """Dictionary of preset names and configurations."""
-        return {}
-
-    @classproperty
-    def presets_with_weights(cls):
-        """Dictionary of preset names and configurations that include
-        weights."""
-        return {}
-
-
-class ResNet34Backbone(ResNetBackbone):
-    def __new__(
-        cls,
-        include_rescaling=True,
-        input_shape=(None, None, 3),
-        input_tensor=None,
-        **kwargs,
-    ):
-        # Pack args in kwargs
-        kwargs.update(
-            {
-                "include_rescaling": include_rescaling,
-                "input_shape": input_shape,
-                "input_tensor": input_tensor,
-            }
-        )
-        return ResNetBackbone.from_preset("resnet34", **kwargs)
-
-    @classproperty
-    def presets(cls):
-        """Dictionary of preset names and configurations."""
-        return {}
-
-    @classproperty
-    def presets_with_weights(cls):
-        """Dictionary of preset names and configurations that include
-        weights."""
-        return {}
-
-
-class ResNet50Backbone(ResNetBackbone):
-    def __new__(
-        cls,
-        include_rescaling=True,
-        input_shape=(None, None, 3),
-        input_tensor=None,
-        **kwargs,
-    ):
-        # Pack args in kwargs
-        kwargs.update(
-            {
-                "include_rescaling": include_rescaling,
-                "input_shape": input_shape,
-                "input_tensor": input_tensor,
-            }
-        )
-        return ResNetBackbone.from_preset("resnet50", **kwargs)
-
-    @classproperty
-    def presets(cls):
-        """Dictionary of preset names and configurations."""
-        return {
-            "resnet50_imagenet": copy.deepcopy(
-                backbone_presets["resnet50_imagenet"]
-            ),
-        }
-
-    @classproperty
-    def presets_with_weights(cls):
-        """Dictionary of preset names and configurations that include
-        weights."""
-        return cls.presets
-
-
-class ResNet101Backbone(ResNetBackbone):
-    def __new__(
-        cls,
-        include_rescaling=True,
-        input_shape=(None, None, 3),
-        input_tensor=None,
-        **kwargs,
-    ):
-        # Pack args in kwargs
-        kwargs.update(
-            {
-                "include_rescaling": include_rescaling,
-                "input_shape": input_shape,
-                "input_tensor": input_tensor,
-            }
-        )
-        return ResNetBackbone.from_preset("resnet101", **kwargs)
-
-    @classproperty
-    def presets(cls):
-        """Dictionary of preset names and configurations."""
-        return {}
-
-    @classproperty
-    def presets_with_weights(cls):
-        """Dictionary of preset names and configurations that include
-        weights."""
-        return {}
-
-
-class ResNet152Backbone(ResNetBackbone):
-    def __new__(
-        self,
-        include_rescaling=True,
-        input_shape=(None, None, 3),
-        input_tensor=None,
-        **kwargs,
-    ):
-        # Pack args in kwargs
-        kwargs.update(
-            {
-                "include_rescaling": include_rescaling,
-                "input_shape": input_shape,
-                "input_tensor": input_tensor,
-            }
-        )
-        return ResNetBackbone.from_preset("resnet152", **kwargs)
-
-    @classproperty
-    def presets(cls):
-        """Dictionary of preset names and configurations."""
-        return {}
-
-    @classproperty
-    def presets_with_weights(cls):
-        """Dictionary of preset names and configurations that include
-        weights."""
-        return {}
-
-
-setattr(ResNet18Backbone, "__doc__", ALIAS_DOCSTRING.format(num_layers=18))
-setattr(ResNet34Backbone, "__doc__", ALIAS_DOCSTRING.format(num_layers=34))
-setattr(ResNet50Backbone, "__doc__", ALIAS_DOCSTRING.format(num_layers=50))
-setattr(ResNet101Backbone, "__doc__", ALIAS_DOCSTRING.format(num_layers=101))
-setattr(ResNet152Backbone, "__doc__", ALIAS_DOCSTRING.format(num_layers=152))


Test patch:
diff --git a/keras_cv/models/backbones/resnet_v1/resnet_v1_backbone_presets_test.py b/keras_cv/models/backbones/resnet_v1/resnet_v1_backbone_presets_test.py
index 59bc84f..c44451f 100644
--- a/keras_cv/models/backbones/resnet_v1/resnet_v1_backbone_presets_test.py
+++ b/keras_cv/models/backbones/resnet_v1/resnet_v1_backbone_presets_test.py
@@ -16,7 +16,7 @@
 import pytest
 import tensorflow as tf
 
-from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (
+from keras_cv.models.backbones.resnet_v1.resnet_v1_aliases import (
     ResNet50Backbone,
 )
 from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (
@@ -83,7 +83,7 @@ class ResNetPresetSmokeTest(tf.test.TestCase):
 class ResNetPresetFullTest(tf.test.TestCase):
     """
     Test the full enumeration of our preset.
-    This every presets for ResNet and is only run manually.
+    This tests every preset for ResNet and is only run manually.
     Run with:
     `pytest keras_cv/models/backbones/resnet_v1/resnet_v1_backbone_presets_test.py --run_extra_large`  # noqa: E501
     """
diff --git a/keras_cv/models/backbones/resnet_v1/resnet_v1_backbone_test.py b/keras_cv/models/backbones/resnet_v1/resnet_v1_backbone_test.py
index 9c6a149..8b1cf9e 100644
--- a/keras_cv/models/backbones/resnet_v1/resnet_v1_backbone_test.py
+++ b/keras_cv/models/backbones/resnet_v1/resnet_v1_backbone_test.py
@@ -19,16 +19,16 @@ import tensorflow as tf
 from absl.testing import parameterized
 from tensorflow import keras
 
-from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (
+from keras_cv.models.backbones.resnet_v1.resnet_v1_aliases import (
     ResNet18Backbone,
 )
-from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (
+from keras_cv.models.backbones.resnet_v1.resnet_v1_aliases import (
     ResNet50Backbone,
 )
-from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (
+from keras_cv.models.backbones.resnet_v1.resnet_v1_aliases import (
     ResNet101Backbone,
 )
-from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (
+from keras_cv.models.backbones.resnet_v1.resnet_v1_aliases import (
     ResNet152Backbone,
 )
 from keras_cv.models.backbones.resnet_v1.resnet_v1_backbone import (