Repository: pytorch/vision
Pull request: Multi-weight support for MNASNet prototype models
Addresses #4672

cc @datumbox @vfdev-5 @pmeier @bjuncek



Problem statement:
Multi-pretrained weights: Add model builders for Classification
### 🚀 The feature

Update the model builders for all Classification models:
- [x] AlexNet
- [x] DenseNet
- [x] EfficientNet
- [x] GoogleNet
- [x] InceptionV3
- [x] MNASNet
- [x] MobileNetV2
- [x] MobileNetV3
- [x] RegNet
- [x] ResNet
- [x] ShuffleNetV2
- [x] SqueezeNet
- [x] VGG


cc @datumbox @vfdev-5 @pmeier @bjuncek

Hints from issue discussion:

Commit message: Merge branch 'main' into multiweight/mnasnet

Patch to review:
diff --git a/torchvision/prototype/models/__init__.py b/torchvision/prototype/models/__init__.py
index ed4cfadfa63..217acfe61a0 100644
--- a/torchvision/prototype/models/__init__.py
+++ b/torchvision/prototype/models/__init__.py
@@ -4,5 +4,6 @@
 from .vgg import *
 from .efficientnet import *
 from .mobilenetv3 import *
+from .mnasnet import *
 from . import detection
 from . import quantization
diff --git a/torchvision/prototype/models/mnasnet.py b/torchvision/prototype/models/mnasnet.py
new file mode 100644
index 00000000000..4ab77e7526e
--- /dev/null
+++ b/torchvision/prototype/models/mnasnet.py
@@ -0,0 +1,130 @@
+import warnings
+from functools import partial
+from typing import Any, Optional
+
+from torchvision.transforms.functional import InterpolationMode
+
+from ...models.mnasnet import MNASNet
+from ..transforms.presets import ImageNetEval
+from ._api import Weights, WeightEntry
+from ._meta import _IMAGENET_CATEGORIES
+
+
+__all__ = [
+    "MNASNet",
+    "MNASNet0_5Weights",
+    "MNASNet0_75Weights",
+    "MNASNet1_0Weights",
+    "MNASNet1_3Weights",
+    "mnasnet0_5",
+    "mnasnet0_75",
+    "mnasnet1_0",
+    "mnasnet1_3",
+]
+
+
+_common_meta = {"size": (224, 224), "categories": _IMAGENET_CATEGORIES, "interpolation": InterpolationMode.BILINEAR}
+
+
+class MNASNet0_5Weights(Weights):
+    ImageNet1K_TrainerV1 = WeightEntry(
+        url="https://download.pytorch.org/models/mnasnet0.5_top1_67.823-3ffadce67e.pth",
+        transforms=partial(ImageNetEval, crop_size=224),
+        meta={
+            **_common_meta,
+            "recipe": "https://github.com/1e100/mnasnet_trainer",
+            "acc@1": 67.734,
+            "acc@5": 87.490,
+        },
+    )
+
+
+class MNASNet0_75Weights(Weights):
+    # If a default model is added here the corresponding changes need to be done in mnasnet0_75
+    pass
+
+
+class MNASNet1_0Weights(Weights):
+    ImageNet1K_TrainerV1 = WeightEntry(
+        url="https://download.pytorch.org/models/mnasnet1.0_top1_73.512-f206786ef8.pth",
+        transforms=partial(ImageNetEval, crop_size=224),
+        meta={
+            **_common_meta,
+            "recipe": "https://github.com/1e100/mnasnet_trainer",
+            "acc@1": 73.456,
+            "acc@5": 91.510,
+        },
+    )
+
+
+class MNASNet1_3Weights(Weights):
+    # If a default model is added here the corresponding changes need to be done in mnasnet1_3
+    pass
+
+
+def mnasnet0_5(weights: Optional[MNASNet0_5Weights] = None, progress: bool = True, **kwargs: Any) -> MNASNet:
+    if "pretrained" in kwargs:
+        warnings.warn("The argument pretrained is deprecated, please use weights instead.")
+        weights = MNASNet0_5Weights.ImageNet1K_TrainerV1 if kwargs.pop("pretrained") else None
+    weights = MNASNet0_5Weights.verify(weights)
+
+    if weights is not None:
+        kwargs["num_classes"] = len(weights.meta["categories"])
+
+    model = MNASNet(0.5, **kwargs)
+
+    if weights:
+        model.load_state_dict(weights.state_dict(progress=progress))
+    return model
+
+
+def mnasnet0_75(weights: Optional[MNASNet0_75Weights] = None, progress: bool = True, **kwargs: Any) -> MNASNet:
+    if "pretrained" in kwargs:
+        warnings.warn("The argument pretrained is deprecated, please use weights instead.")
+        if kwargs.pop("pretrained"):
+            raise ValueError("No checkpoint is available for model type mnasnet0_75")
+
+    weights = MNASNet0_75Weights.verify(weights)
+
+    if weights is not None:
+        kwargs["num_classes"] = len(weights.meta["categories"])
+
+    model = MNASNet(0.75, **kwargs)
+
+    if weights:
+        model.load_state_dict(weights.state_dict(progress=progress))
+    return model
+
+
+def mnasnet1_0(weights: Optional[MNASNet1_0Weights] = None, progress: bool = True, **kwargs: Any) -> MNASNet:
+    if "pretrained" in kwargs:
+        warnings.warn("The argument pretrained is deprecated, please use weights instead.")
+        weights = MNASNet1_0Weights.ImageNet1K_TrainerV1 if kwargs.pop("pretrained") else None
+    weights = MNASNet1_0Weights.verify(weights)
+
+    if weights is not None:
+        kwargs["num_classes"] = len(weights.meta["categories"])
+
+    model = MNASNet(1.0, **kwargs)
+
+    if weights:
+        model.load_state_dict(weights.state_dict(progress=progress))
+    return model
+
+
+def mnasnet1_3(weights: Optional[MNASNet1_3Weights] = None, progress: bool = True, **kwargs: Any) -> MNASNet:
+    if "pretrained" in kwargs:
+        warnings.warn("The argument pretrained is deprecated, please use weights instead.")
+        if kwargs.pop("pretrained"):
+            raise ValueError("No checkpoint is available for model type mnasnet1_3")
+
+    weights = MNASNet1_3Weights.verify(weights)
+
+    if weights is not None:
+        kwargs["num_classes"] = len(weights.meta["categories"])
+
+    model = MNASNet(1.3, **kwargs)
+
+    if weights:
+        model.load_state_dict(weights.state_dict(progress=progress))
+    return model


Code review comments:
diff_hunk: @@ -0,0 +1,130 @@
+import warnings
+from functools import partial
+from typing import Any, Optional
+
+from torchvision.transforms.functional import InterpolationMode
+
+from ...models.mnasnet import MNASNet
+from ..transforms.presets import ImageNetEval
+from ._api import Weights, WeightEntry
+from ._meta import _IMAGENET_CATEGORIES
+
+
+__all__ = [
+    "MNASNet",
+    "MNASNet0_5Weights",
+    "MNASNet0_75Weights",
+    "MNASNet1_0Weights",
+    "MNASNet1_3Weights",
+    "mnasnet0_5",
+    "mnasnet0_75",
+    "mnasnet1_0",
+    "mnasnet1_3",
+]
+
+
+_common_meta = {"size": (224, 224), "categories": _IMAGENET_CATEGORIES, "interpolation": InterpolationMode.BILINEAR}
+
+
+class MNASNet0_5Weights(Weights):
+    ImageNet1K_TrainerV1 = WeightEntry(
+        url="https://download.pytorch.org/models/mnasnet0.5_top1_67.823-3ffadce67e.pth",
+        transforms=partial(ImageNetEval, crop_size=224),
+        meta={
+            **_common_meta,
+            "recipe": "https://github.com/1e100/mnasnet_trainer",
+            "acc@1": 67.734,
+            "acc@5": 87.490,
+        },
+    )
+
+
+class MNASNet0_75Weights(Weights):
+    # If a default model is added here the corresponding changes need to be done in mnasnet0_75
+    pass
+
+
+class MNASNet1_0Weights(Weights):
+    ImageNet1K_TrainerV1 = WeightEntry(
+        url="https://download.pytorch.org/models/mnasnet1.0_top1_73.512-f206786ef8.pth",
+        transforms=partial(ImageNetEval, crop_size=224),
+        meta={
+            **_common_meta,
+            "recipe": "https://github.com/1e100/mnasnet_trainer",
+            "acc@1": 73.456,
+            "acc@5": 91.510,
+        },
+    )
+
+
+class MNASNet1_3Weights(Weights):
+    # If a default model is added here the corresponding changes need to be done in mnasnet1_3
+    pass
+
+
+def mnasnet0_5(weights: Optional[MNASNet0_5Weights] = None, progress: bool = True, **kwargs: Any) -> MNASNet:
path: torchvision/prototype/models/mnasnet.py
text: @user1:
Just curious, why can't we refactor the logic into a single private method and call it from all these public methods. Looks like a lot of code repetitions... Otherwise, OK for me.

@user2:
Yeap. We could do this for lines 71-77. I think Joao was following the same paradigm as the Vision main code but we can deviate a little to reduce code duplication. No strong opinions on my side.

@author:
You are right, Im refactoring now. I will leave the weights checks in the beginning of each on to be in line with what we have in other models (for example Resnet),