Repository: voxel51/fiftyone

Issue:
[BUG] loading segmentation torch models errror, has no attribute 'load_state_dict_from_url'
### System information

-   **OS Platform and Distribution**: `Ubuntu 20.04`
-   **FiftyOne installed from (pip or source)**: `pip`, from within `conda` environment
-   **FiftyOne version (run `fiftyone --version`)**: `FiftyOne v0.16.0, Voxel51, Inc.`
-   **Python version**: `Python 3.9.7`
-   **Torch version**: `1.11.0`
-   **Torchvision version**: `0.12.0`

### Code to reproduce

```
import fiftyone.zoo as foz
model = foz.load_zoo_model("deeplabv3-resnet101-coco-torch")
```

### Describe the problem
The model fails to load (was taken from the [semantic segmentation evaluation doc](https://voxel51.com/docs/fiftyone/user_guide/evaluation.html#semantic-segmentations). Ultimately the error is  `AttributeError: module 'torchvision.models.segmentation.deeplabv3' has no attribute 'load_state_dict_from_url'`. This applies to all the PyTorch segmentation models listed [here](https://voxel51.com/docs/fiftyone/user_guide/model_zoo/models.html).

### Other info / logs
```
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [2], in <cell line: 3>()
      1 import fiftyone.zoo as foz
----> 3 model = foz.load_zoo_model("deeplabv3-resnet101-coco-torch")

File ~/miniconda3/lib/python3.9/site-packages/fiftyone/zoo/models/__init__.py:207, in load_zoo_model(name, download_if_necessary, install_requirements, error_level, **kwargs)
    204 config_dict = deepcopy(model.default_deployment_config_dict)
    205 model_path = model.get_path_in_dir(models_dir)
--> 207 return fom.load_model(config_dict, model_path=model_path, **kwargs)

File ~/miniconda3/lib/python3.9/site-packages/fiftyone/core/models.py:1505, in load_model(model_config_dict, model_path, **kwargs)
   1499         raise ValueError(
   1500             "Model config must implement the %s interface"
   1501             % etal.HasPublishedModel
   1502         )
   1504 # Build model
-> 1505 return config.build()

File ~/miniconda3/lib/python3.9/site-packages/eta/core/learning.py:296, in ModelConfig.build(self)
    289 def build(self):
    290     """Factory method that builds the Model instance from the config
    291     specified by this class.
    292 
    293     Returns:
    294         a Model instance
    295     """
--> 296     return self._model_cls(self.config)

File ~/miniconda3/lib/python3.9/site-packages/fiftyone/utils/torch.py:230, in TorchImageModel.__init__(self, config)
    226 self._device = torch.device("cuda:0" if self._using_gpu else "cpu")
    227 self._using_half_precision = (
    228     self.config.use_half_precision is True
    229 ) and self._using_gpu
--> 230 self._model = self._load_model(config)
    231 self._no_grad = None
    232 self._benchmark_orig = None

File ~/miniconda3/lib/python3.9/site-packages/fiftyone/utils/torch.py:471, in TorchImageModel._load_model(self, config)
    468 def _load_model(self, config):
    469     self._download_model(config)
--> 471     model = self._load_network(config)
    473     model = model.to(self._device)
    474     if self._using_half_precision:

File ~/miniconda3/lib/python3.9/site-packages/fiftyone/zoo/models/torch.py:97, in TorchvisionImageModel._load_network(self, config)
     94 kwargs = config.entrypoint_args or {}
     95 model_dir = fo.config.model_zoo_dir
---> 97 monkey_patcher = _make_load_state_dict_from_url_monkey_patcher(
     98     entrypoint, model_dir
     99 )
    100 with monkey_patcher:
    101     # Builds net and loads state dict from `model_dir`
    102     model = entrypoint(**kwargs)

File ~/miniconda3/lib/python3.9/site-packages/fiftyone/zoo/models/torch.py:114, in _make_load_state_dict_from_url_monkey_patcher(entrypoint, model_dir)
    108 """Monkey patches all instances of ``load_state_dict_from_url()`` that are
    109 reachable from the given ``entrypoint`` function in the
    110 :mod:`torchvision:torchvision.models` namespace so that models will be
    111 loaded from ``model_dir`` and not from the Torch Hub cache directory.
    112 """
    113 entrypoint_module = inspect.getmodule(entrypoint)
--> 114 load_state_dict_from_url = entrypoint_module.load_state_dict_from_url
    116 def custom_load_state_dict_from_url(url, **kwargs):
    117     return load_state_dict_from_url(url, model_dir=model_dir, **kwargs)

AttributeError: module 'torchvision.models.segmentation.deeplabv3' has no attribute 'load_state_dict_from_url'
```

### What areas of FiftyOne does this bug affect?

-   [ ] `App`: FiftyOne application issue
-   [x] `Core`: Core `fiftyone` Python library issue
-   [ ] `Server`: Fiftyone server issue

### Willingness to contribute

The FiftyOne Community encourages bug fix contributions. Would you or another
member of your organization be willing to contribute a fix for this bug to the
FiftyOne codebase?

-   [ ] Yes. I can contribute a fix for this bug independently.
-   [x] Yes. I would be willing to contribute a fix for this bug with guidance
        from the FiftyOne community.
-   [ ] No. I cannot contribute a bug fix at this time.



Discussion:
Hi @phil0stine, have you found any fix/workaround for this issue?
Hi @Gor4 I just ended up loading a tensorflow model.

Resolving patch:
diff --git a/fiftyone/zoo/models/torch.py b/fiftyone/zoo/models/torch.py
--- a/fiftyone/zoo/models/torch.py
+++ b/fiftyone/zoo/models/torch.py
@@ -6,6 +6,7 @@
 |
 """
 import inspect
+from packaging import version
 
 import eta.core.utils as etau
 
@@ -111,6 +112,8 @@ def _make_load_state_dict_from_url_monkey_patcher(entrypoint, model_dir):
     loaded from ``model_dir`` and not from the Torch Hub cache directory.
     """
     entrypoint_module = inspect.getmodule(entrypoint)
+    if version.parse(torchvision.__version__) >= version.parse("0.12.0"):
+        entrypoint_module = torchvision._internally_replaced_utils
     load_state_dict_from_url = entrypoint_module.load_state_dict_from_url
 
     def custom_load_state_dict_from_url(url, **kwargs):


Test patch: