diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 7cd9c3ec..91367cea 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -14,9 +14,7 @@ jobs: with: python-version: '3.9' - name: Install dependencies - run: | - pip install --upgrade pip uv - uv pip install build twine + run: pip install --upgrade pip twine - name: Build and publish env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bf04ab0a..fbce9a45 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -51,7 +51,7 @@ jobs: run: uv pip list - name: Test with PyTest - run: uv run pytest -v -rsx -n 2 --cov=segmentation_models_pytorch --cov-report=xml --cov-config=pyproject.toml -k "not logits_match" + run: uv run pytest -v -rsx -n 2 --cov=segmentation_models_pytorch --cov-report=xml --cov-config=pyproject.toml --non-marked-only - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v5 @@ -73,7 +73,52 @@ jobs: - name: Show installed packages run: uv pip list - name: Test with PyTest - run: RUN_SLOW=1 uv run pytest -v -rsx -n 2 -k "logits_match" + run: RUN_SLOW=1 uv run pytest -v -rsx -n 2 -m "logits_match" + + test_torch_compile: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: astral-sh/setup-uv@v5 + with: + python-version: "3.10" + - name: Install dependencies + run: uv pip install -r requirements/required.txt -r requirements/test.txt + - name: Show installed packages + run: uv pip list + - name: Test with PyTest + run: uv run pytest -v -rsx -n 2 -m "compile" + + test_torch_export: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: astral-sh/setup-uv@v5 + with: + python-version: "3.10" + - name: Install dependencies + run: uv pip install -r requirements/required.txt -r requirements/test.txt + - name: Show installed packages + run: uv pip list + - name: Test with PyTest + run: uv run pytest -v -rsx -n 2 -m "torch_export" + + test_torch_script: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: astral-sh/setup-uv@v5 + with: + python-version: "3.10" + - name: Install dependencies + run: uv pip install -r requirements/required.txt -r requirements/test.txt + - name: Show installed packages + run: uv pip list + - name: Test with PyTest + run: uv run pytest -v -rsx -n 2 -m "torch_script" minimum: runs-on: ubuntu-latest @@ -88,4 +133,4 @@ jobs: - name: Show installed packages run: uv pip list - name: Test with pytest - run: uv run pytest -v -rsx -n 2 -k "not logits_match" + run: uv run pytest -v -rsx -n 2 --non-marked-only diff --git a/Makefile b/Makefile index a58d230f..9cbf9bdc 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,39 @@ -.PHONY: test +.PHONY: test # Declare the 'test' target as phony to avoid conflicts with files named 'test' +# Variables to store the paths of the python, pip, pytest, and ruff executables +PYTHON := $(shell which python) +PIP := $(shell which pip) +PYTEST := $(shell which pytest) +RUFF := $(shell which ruff) + +# Target to create a Python virtual environment .venv: - python3 -m venv .venv + $(PYTHON) -m venv $(shell dirname $(PYTHON)) +# Target to install development dependencies in the virtual environment install_dev: .venv - .venv/bin/pip install -e ".[test]" + $(PIP) install -e ".[test]" +# Target to run tests with pytest, using 2 parallel processes and only non-marked tests test: .venv - .venv/bin/pytest -v -rsx -n 2 tests/ -k "not logits_match" + $(PYTEST) -v -rsx -n 2 tests/ --non-marked-only +# Target to run all tests with pytest, including slow tests, using 2 parallel processes test_all: .venv - RUN_SLOW=1 .venv/bin/pytest -v -rsx -n 2 tests/ + RUN_SLOW=1 $(PYTEST) -v -rsx -n 2 tests/ +# Target to generate a table by running a Python script table: - .venv/bin/python misc/generate_table.py + $(PYTHON) misc/generate_table.py +# Target to generate a table for timm by running a Python script table_timm: - .venv/bin/python misc/generate_table_timm.py + $(PYTHON) misc/generate_table_timm.py +# Target to fix and format code using ruff fixup: - .venv/bin/ruff check --fix - .venv/bin/ruff format + $(RUFF) check --fix + $(RUFF) format +# Target to run code formatting and tests all: fixup test - diff --git a/README.md b/README.md index b3a0b3ff..c3df5718 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,51 @@
![logo](https://i.ibb.co/dc1XdhT/Segmentation-Models-V2-Side-1-1.png) -**Python library with Neural Networks for Image +**Python library with Neural Networks for Image Semantic Segmentation based on [PyTorch](https://pytorch.org/).** -[![Generic badge](https://img.shields.io/badge/License-MIT-.svg?style=for-the-badge)](https://github.com/qubvel/segmentation_models.pytorch/blob/main/LICENSE) + [![GitHub Workflow Status (branch)](https://img.shields.io/github/actions/workflow/status/qubvel/segmentation_models.pytorch/tests.yml?branch=main&style=for-the-badge)](https://github.com/qubvel/segmentation_models.pytorch/actions/workflows/tests.yml) +![Codecov](https://img.shields.io/codecov/c/github/qubvel-org/segmentation_models.pytorch?style=for-the-badge) [![Read the Docs](https://img.shields.io/readthedocs/smp?style=for-the-badge&logo=readthedocs&logoColor=white)](https://smp.readthedocs.io/en/latest/)
-[![PyPI](https://img.shields.io/pypi/v/segmentation-models-pytorch?color=blue&style=for-the-badge&logo=pypi&logoColor=white)](https://pypi.org/project/segmentation-models-pytorch/) -[![PyPI - Downloads](https://img.shields.io/pypi/dm/segmentation-models-pytorch?style=for-the-badge&color=blue)](https://pepy.tech/project/segmentation-models-pytorch) -
-[![PyTorch - Version](https://img.shields.io/badge/PYTORCH-1.4+-red?style=for-the-badge&logo=pytorch)](https://pepy.tech/project/segmentation-models-pytorch) +[![PyPI](https://img.shields.io/pypi/v/segmentation-models-pytorch?color=red&style=for-the-badge&logo=pypi&logoColor=white)](https://pypi.org/project/segmentation-models-pytorch/) +[![PyTorch - Version](https://img.shields.io/badge/PYTORCH-1.9+-red?style=for-the-badge&logo=pytorch)](https://pepy.tech/project/segmentation-models-pytorch) [![Python - Version](https://img.shields.io/badge/PYTHON-3.9+-red?style=for-the-badge&logo=python&logoColor=white)](https://pepy.tech/project/segmentation-models-pytorch) +
+[![Generic badge](https://img.shields.io/badge/License-MIT-.svg?style=for-the-badge&color=blue)](https://github.com/qubvel/segmentation_models.pytorch/blob/main/LICENSE) +[![PyPI - Downloads](https://img.shields.io/pypi/dm/segmentation-models-pytorch?style=for-the-badge&color=blue)](https://pepy.tech/project/segmentation-models-pytorch)
-The main features of this library are: - - - High-level API (just two lines to create a neural network) - - 11 models architectures for binary and multi class segmentation (including legendary Unet) - - 124 available encoders (and 500+ encoders from [timm](https://github.com/rwightman/pytorch-image-models)) - - All encoders have pre-trained weights for faster and better convergence - - Popular metrics and losses for training routines +The main features of the library are: + + - Super simple high-level API (just two lines to create a neural network) + - 12 encoder-decoder model architectures (Unet, Unet++, Segformer, DPT, ...) + - 800+ **pretrained** convolution- and transform-based encoders, including [timm](https://github.com/huggingface/pytorch-image-models) support + - Popular metrics and losses for training routines (Dice, Jaccard, Tversky, ...) + - ONNX export and torch script/trace/compile friendly + +### Community-Driven Project, Supported By + + + + + +
+ + withoutBG API Logo + + + withoutBG API +
+ https://withoutbg.com +
+

+ High-quality background removal API +
+

+
### [πŸ“š Project Documentation πŸ“š](http://smp.readthedocs.io/) @@ -31,21 +54,18 @@ Visit [Read The Docs Project Page](https://smp.readthedocs.io/) or read the foll ### πŸ“‹ Table of content 1. [Quick start](#start) 2. [Examples](#examples) - 3. [Models](#models) - 1. [Architectures](#architectures) - 2. [Encoders](#encoders) - 3. [Timm Encoders](#timm) + 3. [Models and encoders](#models-and-encoders) 4. [Models API](#api) 1. [Input channels](#input-channels) 2. [Auxiliary classification output](#auxiliary-classification-output) 3. [Depth](#depth) 5. [Installation](#installation) - 6. [Competitions won with the library](#competitions-won-with-the-library) + 6. [Competitions won with the library](#competitions) 7. [Contributing](#contributing) 8. [Citing](#citing) 9. [License](#license) -### ⏳ Quick start +## ⏳ Quick start #### 1. Create your first Segmentation model with SMP @@ -76,361 +96,71 @@ preprocess_input = get_preprocessing_fn('resnet18', pretrained='imagenet') Congratulations! You are done! Now you can train your model with your favorite framework! -### πŸ’‘ Examples - - Training model for pets binary segmentation with Pytorch-Lightning [notebook](https://github.com/qubvel/segmentation_models.pytorch/blob/main/examples/binary_segmentation_intro.ipynb) and [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/qubvel/segmentation_models.pytorch/blob/main/examples/binary_segmentation_intro.ipynb) - - Training model for cars segmentation on CamVid dataset [here](https://github.com/qubvel/segmentation_models.pytorch/blob/main/examples/cars%20segmentation%20(camvid).ipynb). - - Training SMP model with [Catalyst](https://github.com/catalyst-team/catalyst) (high-level framework for PyTorch), [TTAch](https://github.com/qubvel/ttach) (TTA library for PyTorch) and [Albumentations](https://github.com/albu/albumentations) (fast image augmentation library) - [here](https://github.com/catalyst-team/catalyst/blob/v21.02rc0/examples/notebooks/segmentation-tutorial.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/catalyst-team/catalyst/blob/v21.02rc0/examples/notebooks/segmentation-tutorial.ipynb) - - Training SMP model with [Pytorch-Lightning](https://pytorch-lightning.readthedocs.io) framework - [here](https://github.com/ternaus/cloths_segmentation) (clothes binary segmentation by [@ternaus](https://github.com/ternaus)). - - Export trained model to ONNX - [notebook](https://github.com/qubvel/segmentation_models.pytorch/blob/main/examples/convert_to_onnx.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/qubvel/segmentation_models.pytorch/blob/main/examples/convert_to_onnx.ipynb) - -### πŸ“¦ Models - -#### Architectures - - Unet [[paper](https://arxiv.org/abs/1505.04597)] [[docs](https://smp.readthedocs.io/en/latest/models.html#unet)] - - Unet++ [[paper](https://arxiv.org/pdf/1807.10165.pdf)] [[docs](https://smp.readthedocs.io/en/latest/models.html#id2)] - - MAnet [[paper](https://ieeexplore.ieee.org/abstract/document/9201310)] [[docs](https://smp.readthedocs.io/en/latest/models.html#manet)] - - Linknet [[paper](https://arxiv.org/abs/1707.03718)] [[docs](https://smp.readthedocs.io/en/latest/models.html#linknet)] - - FPN [[paper](http://presentations.cocodataset.org/COCO17-Stuff-FAIR.pdf)] [[docs](https://smp.readthedocs.io/en/latest/models.html#fpn)] - - PSPNet [[paper](https://arxiv.org/abs/1612.01105)] [[docs](https://smp.readthedocs.io/en/latest/models.html#pspnet)] - - PAN [[paper](https://arxiv.org/abs/1805.10180)] [[docs](https://smp.readthedocs.io/en/latest/models.html#pan)] - - DeepLabV3 [[paper](https://arxiv.org/abs/1706.05587)] [[docs](https://smp.readthedocs.io/en/latest/models.html#deeplabv3)] - - DeepLabV3+ [[paper](https://arxiv.org/abs/1802.02611)] [[docs](https://smp.readthedocs.io/en/latest/models.html#id9)] - - UPerNet [[paper](https://arxiv.org/abs/1807.10221)] [[docs](https://smp.readthedocs.io/en/latest/models.html#upernet)] - - Segformer [[paper](https://arxiv.org/abs/2105.15203)] [[docs](https://smp.readthedocs.io/en/latest/models.html#segformer)] - -#### Encoders - -The following is a list of supported encoders in the SMP. Select the appropriate family of encoders and click to expand the table and select a specific encoder and its pre-trained weights (`encoder_name` and `encoder_weights` parameters). - -
-ResNet -
- -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|resnet18 |imagenet / ssl / swsl |11M | -|resnet34 |imagenet |21M | -|resnet50 |imagenet / ssl / swsl |23M | -|resnet101 |imagenet |42M | -|resnet152 |imagenet |58M | - -
-
- -
-ResNeXt -
- -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|resnext50_32x4d |imagenet / ssl / swsl |22M | -|resnext101_32x4d |ssl / swsl |42M | -|resnext101_32x8d |imagenet / instagram / ssl / swsl|86M | -|resnext101_32x16d |instagram / ssl / swsl |191M | -|resnext101_32x32d |instagram |466M | -|resnext101_32x48d |instagram |826M | - -
-
- -
-ResNeSt -
- -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|timm-resnest14d |imagenet |8M | -|timm-resnest26d |imagenet |15M | -|timm-resnest50d |imagenet |25M | -|timm-resnest101e |imagenet |46M | -|timm-resnest200e |imagenet |68M | -|timm-resnest269e |imagenet |108M | -|timm-resnest50d_4s2x40d |imagenet |28M | -|timm-resnest50d_1s4x24d |imagenet |23M | - -
-
- -
-Res2Ne(X)t -
- -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|timm-res2net50_26w_4s |imagenet |23M | -|timm-res2net101_26w_4s |imagenet |43M | -|timm-res2net50_26w_6s |imagenet |35M | -|timm-res2net50_26w_8s |imagenet |46M | -|timm-res2net50_48w_2s |imagenet |23M | -|timm-res2net50_14w_8s |imagenet |23M | -|timm-res2next50 |imagenet |22M | - -
-
- -
-RegNet(x/y) -
- -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|timm-regnetx_002 |imagenet |2M | -|timm-regnetx_004 |imagenet |4M | -|timm-regnetx_006 |imagenet |5M | -|timm-regnetx_008 |imagenet |6M | -|timm-regnetx_016 |imagenet |8M | -|timm-regnetx_032 |imagenet |14M | -|timm-regnetx_040 |imagenet |20M | -|timm-regnetx_064 |imagenet |24M | -|timm-regnetx_080 |imagenet |37M | -|timm-regnetx_120 |imagenet |43M | -|timm-regnetx_160 |imagenet |52M | -|timm-regnetx_320 |imagenet |105M | -|timm-regnety_002 |imagenet |2M | -|timm-regnety_004 |imagenet |3M | -|timm-regnety_006 |imagenet |5M | -|timm-regnety_008 |imagenet |5M | -|timm-regnety_016 |imagenet |10M | -|timm-regnety_032 |imagenet |17M | -|timm-regnety_040 |imagenet |19M | -|timm-regnety_064 |imagenet |29M | -|timm-regnety_080 |imagenet |37M | -|timm-regnety_120 |imagenet |49M | -|timm-regnety_160 |imagenet |80M | -|timm-regnety_320 |imagenet |141M | - -
-
- -
-GERNet -
- -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|timm-gernet_s |imagenet |6M | -|timm-gernet_m |imagenet |18M | -|timm-gernet_l |imagenet |28M | - -
-
- -
-SE-Net -
- -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|senet154 |imagenet |113M | -|se_resnet50 |imagenet |26M | -|se_resnet101 |imagenet |47M | -|se_resnet152 |imagenet |64M | -|se_resnext50_32x4d |imagenet |25M | -|se_resnext101_32x4d |imagenet |46M | +## πŸ’‘ Examples -
-
- -
-SK-ResNe(X)t -
+| Name | Link | Colab | +|-------------------------------------------|-----------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| +| **Train** pets binary segmentation on OxfordPets | [Notebook](https://github.com/qubvel/segmentation_models.pytorch/blob/main/examples/binary_segmentation_intro.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/qubvel/segmentation_models.pytorch/blob/main/examples/binary_segmentation_intro.ipynb) | +| **Train** cars binary segmentation on CamVid | [Notebook](https://github.com/qubvel/segmentation_models.pytorch/blob/main/examples/cars%20segmentation%20(camvid).ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/qubvel/segmentation_models.pytorch/blob/main/examples/cars%20segmentation%20(camvid).ipynb) | +| **Train** multiclass segmentation on CamVid | [Notebook](https://github.com/qubvel-org/segmentation_models.pytorch/blob/main/examples/camvid_segmentation_multiclass.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/qubvel-org/segmentation_models.pytorch/blob/main/examples/camvid_segmentation_multiclass.ipynb) | +| **Train** clothes binary segmentation by @ternaus | [Repo](https://github.com/ternaus/cloths_segmentation) | | +| **Load and inference** pretrained Segformer | [Notebook](https://github.com/qubvel-org/segmentation_models.pytorch/blob/main/examples/segformer_inference_pretrained.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/qubvel/segmentation_models.pytorch/blob/main/examples/segformer_inference_pretrained.ipynb) | +| **Load and inference** pretrained DPT | [Notebook](https://github.com/qubvel-org/segmentation_models.pytorch/blob/main/examples/dpt_inference_pretrained.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/qubvel/segmentation_models.pytorch/blob/main/examples/dpt_inference_pretrained.ipynb) | +| **Load and inference** pretrained DPT | [Notebook](https://github.com/qubvel-org/segmentation_models.pytorch/blob/main/examples/upernet_inference_pretrained.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/qubvel/segmentation_models.pytorch/blob/main/examples/upernet_inference_pretrained.ipynb) | +| **Save and load** models locally / to HuggingFace Hub |[Notebook](https://github.com/qubvel-org/segmentation_models.pytorch/blob/main/examples/save_load_model_and_share_with_hf_hub.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/qubvel/segmentation_models.pytorch/blob/main/examples/save_load_model_and_share_with_hf_hub.ipynb) +| **Export** trained model to ONNX | [Notebook](https://github.com/qubvel/segmentation_models.pytorch/blob/main/examples/convert_to_onnx.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/qubvel/segmentation_models.pytorch/blob/main/examples/convert_to_onnx.ipynb) | -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|timm-skresnet18 |imagenet |11M | -|timm-skresnet34 |imagenet |21M | -|timm-skresnext50_32x4d |imagenet |25M | -
-
+## πŸ“¦ Models and encoders -
-DenseNet -
+### Architectures +| Architecture | Paper | Documentation | Checkpoints | +|--------------|-------|---------------|------------| +| Unet | [paper](https://arxiv.org/abs/1505.04597) | [docs](https://smp.readthedocs.io/en/latest/models.html#unet) | | +| Unet++ | [paper](https://arxiv.org/pdf/1807.10165.pdf) | [docs](https://smp.readthedocs.io/en/latest/models.html#unetplusplus) | | +| MAnet | [paper](https://ieeexplore.ieee.org/abstract/document/9201310) | [docs](https://smp.readthedocs.io/en/latest/models.html#manet) | | +| Linknet | [paper](https://arxiv.org/abs/1707.03718) | [docs](https://smp.readthedocs.io/en/latest/models.html#linknet) | | +| FPN | [paper](http://presentations.cocodataset.org/COCO17-Stuff-FAIR.pdf) | [docs](https://smp.readthedocs.io/en/latest/models.html#fpn) | | +| PSPNet | [paper](https://arxiv.org/abs/1612.01105) | [docs](https://smp.readthedocs.io/en/latest/models.html#pspnet) | | +| PAN | [paper](https://arxiv.org/abs/1805.10180) | [docs](https://smp.readthedocs.io/en/latest/models.html#pan) | | +| DeepLabV3 | [paper](https://arxiv.org/abs/1706.05587) | [docs](https://smp.readthedocs.io/en/latest/models.html#deeplabv3) | | +| DeepLabV3+ | [paper](https://arxiv.org/abs/1802.02611) | [docs](https://smp.readthedocs.io/en/latest/models.html#deeplabv3plus) | | +| UPerNet | [paper](https://arxiv.org/abs/1807.10221) | [docs](https://smp.readthedocs.io/en/latest/models.html#upernet) | [checkpoints](https://huggingface.co/collections/smp-hub/upernet-67fadcdbe08418c6ea94f768) | +| Segformer | [paper](https://arxiv.org/abs/2105.15203) | [docs](https://smp.readthedocs.io/en/latest/models.html#segformer) | [checkpoints](https://huggingface.co/collections/smp-hub/segformer-6749eb4923dea2c355f29a1f) | +| DPT | [paper](https://arxiv.org/abs/2103.13413) | [docs](https://smp.readthedocs.io/en/latest/models.html#dpt) | [checkpoints](https://huggingface.co/collections/smp-hub/dpt-67f30487327c0599a0c62d68) | -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|densenet121 |imagenet |6M | -|densenet169 |imagenet |12M | -|densenet201 |imagenet |18M | -|densenet161 |imagenet |26M | +### Encoders -
-
+The library provides a wide range of **pretrained** encoders (also known as backbones) for segmentation models. Instead of using features from the final layer of a classification model, we extract **intermediate features** and feed them into the decoder for segmentation tasks. -
-Inception -
+All encoders come with **pretrained weights**, which help achieve **faster and more stable convergence** when training segmentation models. -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|inceptionresnetv2 |imagenet / imagenet+background |54M | -|inceptionv4 |imagenet / imagenet+background |41M | -|xception |imagenet |22M | +Given the extensive selection of supported encoders, you can choose the best one for your specific use case, for example: +- **Lightweight encoders** for low-latency applications or real-time inference on edge devices (mobilenet/mobileone). +- **High-capacity architectures** for complex tasks involving a large number of segmented classes, providing superior accuracy (convnext/swin/mit). -
-
- -
-EfficientNet -
- -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|efficientnet-b0 |imagenet |4M | -|efficientnet-b1 |imagenet |6M | -|efficientnet-b2 |imagenet |7M | -|efficientnet-b3 |imagenet |10M | -|efficientnet-b4 |imagenet |17M | -|efficientnet-b5 |imagenet |28M | -|efficientnet-b6 |imagenet |40M | -|efficientnet-b7 |imagenet |63M | -|timm-efficientnet-b0 |imagenet / advprop / noisy-student|4M | -|timm-efficientnet-b1 |imagenet / advprop / noisy-student|6M | -|timm-efficientnet-b2 |imagenet / advprop / noisy-student|7M | -|timm-efficientnet-b3 |imagenet / advprop / noisy-student|10M | -|timm-efficientnet-b4 |imagenet / advprop / noisy-student|17M | -|timm-efficientnet-b5 |imagenet / advprop / noisy-student|28M | -|timm-efficientnet-b6 |imagenet / advprop / noisy-student|40M | -|timm-efficientnet-b7 |imagenet / advprop / noisy-student|63M | -|timm-efficientnet-b8 |imagenet / advprop |84M | -|timm-efficientnet-l2 |noisy-student |474M | -|timm-efficientnet-lite0 |imagenet |4M | -|timm-efficientnet-lite1 |imagenet |5M | -|timm-efficientnet-lite2 |imagenet |6M | -|timm-efficientnet-lite3 |imagenet |8M | -|timm-efficientnet-lite4 |imagenet |13M | +By selecting the right encoder, you can balance **efficiency, performance, and model complexity** to suit your project needs. -
-
- -
-MobileNet -
- -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|mobilenet_v2 |imagenet |2M | -|timm-mobilenetv3_large_075 |imagenet |1.78M | -|timm-mobilenetv3_large_100 |imagenet |2.97M | -|timm-mobilenetv3_large_minimal_100|imagenet |1.41M | -|timm-mobilenetv3_small_075 |imagenet |0.57M | -|timm-mobilenetv3_small_100 |imagenet |0.93M | -|timm-mobilenetv3_small_minimal_100|imagenet |0.43M | +All encoders and corresponding pretrained weight are listed in the documentation: + - [table](https://smp.readthedocs.io/en/latest/encoders.html) with natively ported encoders + - [table](https://smp.readthedocs.io/en/latest/encoders_timm.html) with [timm](https://github.com/huggingface/pytorch-image-models) encoders supported -
-
+## πŸ” Models API -
-DPN -
+### Input channels -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|dpn68 |imagenet |11M | -|dpn68b |imagenet+5k |11M | -|dpn92 |imagenet+5k |34M | -|dpn98 |imagenet |58M | -|dpn107 |imagenet+5k |84M | -|dpn131 |imagenet |76M | +The input channels parameter allows you to create a model that can process a tensor with an arbitrary number of channels. +If you use pretrained weights from ImageNet, the weights of the first convolution will be reused: + - For the 1-channel case, it would be a sum of the weights of the first convolution layer. + - Otherwise, channels would be populated with weights like `new_weight[:, i] = pretrained_weight[:, i % 3]`, and then scaled with `new_weight * 3 / new_in_channels`. -
-
- -
-VGG -
- -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|vgg11 |imagenet |9M | -|vgg11_bn |imagenet |9M | -|vgg13 |imagenet |9M | -|vgg13_bn |imagenet |9M | -|vgg16 |imagenet |14M | -|vgg16_bn |imagenet |14M | -|vgg19 |imagenet |20M | -|vgg19_bn |imagenet |20M | - -
-
- -
-Mix Vision Transformer -
- -Backbone from SegFormer pretrained on Imagenet! Can be used with other decoders from package, you can combine Mix Vision Transformer with Unet, FPN and others! - -Limitations: - - - encoder is **not** supported by Linknet, Unet++ - - encoder is supported by FPN only for encoder **depth = 5** - -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|mit_b0 |imagenet |3M | -|mit_b1 |imagenet |13M | -|mit_b2 |imagenet |24M | -|mit_b3 |imagenet |44M | -|mit_b4 |imagenet |60M | -|mit_b5 |imagenet |81M | - -
-
- -
-MobileOne -
- -Apple's "sub-one-ms" Backbone pretrained on Imagenet! Can be used with all decoders. - -Note: In the official github repo the s0 variant has additional num_conv_branches, leading to more params than s1. - -|Encoder |Weights |Params, M | -|--------------------------------|:------------------------------:|:------------------------------:| -|mobileone_s0 |imagenet |4.6M | -|mobileone_s1 |imagenet |4.0M | -|mobileone_s2 |imagenet |6.5M | -|mobileone_s3 |imagenet |8.8M | -|mobileone_s4 |imagenet |13.6M | - -
-
- - -\* `ssl`, `swsl` - semi-supervised and weakly-supervised learning on ImageNet ([repo](https://github.com/facebookresearch/semi-supervised-ImageNet1K-models)). - -#### Timm Encoders - -[docs](https://smp.readthedocs.io/en/latest/encoders_timm.html) - -Pytorch Image Models (a.k.a. timm) has a lot of pretrained models and interface which allows using these models as encoders in smp, however, not all models are supported - - - not all transformer models have ``features_only`` functionality implemented that is required for encoder - - some models have inappropriate strides - -Total number of supported encoders: 549 - - [table with available encoders](https://smp.readthedocs.io/en/latest/encoders_timm.html) - -### πŸ” Models API - - - `model.encoder` - pretrained backbone to extract features of different spatial resolution - - `model.decoder` - depends on models architecture (`Unet`/`Linknet`/`PSPNet`/`FPN`) - - `model.segmentation_head` - last block to produce required number of mask channels (include also optional upsampling and activation) - - `model.classification_head` - optional block which create classification head on top of encoder - - `model.forward(x)` - sequentially pass `x` through model\`s encoder, decoder and segmentation head (and classification head if specified) - -##### Input channels -Input channels parameter allows you to create models, which process tensors with arbitrary number of channels. -If you use pretrained weights from imagenet - weights of first convolution will be reused. For -1-channel case it would be a sum of weights of first convolution layer, otherwise channels would be -populated with weights like `new_weight[:, i] = pretrained_weight[:, i % 3]` and than scaled with `new_weight * 3 / new_in_channels`. ```python model = smp.FPN('resnet34', in_channels=1) mask = model(torch.ones([1, 1, 64, 64])) ``` -##### Auxiliary classification output +### Auxiliary classification output + All models support `aux_params` parameters, which is default set to `None`. If `aux_params = None` then classification auxiliary output is not created, else model produce not only `mask`, but also `label` output with shape `NC`. @@ -447,50 +177,54 @@ model = smp.Unet('resnet34', classes=4, aux_params=aux_params) mask, label = model(x) ``` -##### Depth +### Depth + Depth parameter specify a number of downsampling operations in encoder, so you can make your model lighter if specify smaller `depth`. ```python model = smp.Unet('resnet34', encoder_depth=4) ``` - -### πŸ›  Installation +## πŸ›  Installation PyPI version: + ```bash $ pip install segmentation-models-pytorch ```` -Latest version from source: + +The latest version from GitHub: + ```bash $ pip install git+https://github.com/qubvel/segmentation_models.pytorch ```` -### πŸ† Competitions won with the library +## πŸ† Competitions won with the library -`Segmentation Models` package is widely used in the image segmentation competitions. +`Segmentation Models` package is widely used in image segmentation competitions. [Here](https://github.com/qubvel/segmentation_models.pytorch/blob/main/HALLOFFAME.md) you can find competitions, names of the winners and links to their solutions. -### 🀝 Contributing +## 🀝 Contributing -#### Install SMP +1. Install SMP in dev mode ```bash -make install_dev # create .venv, install SMP in dev mode +make install_dev # Create .venv, install SMP in dev mode ``` -#### Run tests and code checks +2. Run tests and code checks ```bash +make test # Run tests suite with pytest make fixup # Ruff for formatting and lint checks ``` -#### Update table with encoders +3. Update a table (in case you added an encoder) ```bash -make table # generate a table with encoders and print to stdout +make table # Generates a table with encoders and print to stdout ``` -### πŸ“ Citing +## πŸ“ Citing ``` @misc{Iakubovskii:2019, Author = {Pavel Iakubovskii}, @@ -502,5 +236,5 @@ make table # generate a table with encoders and print to stdout } ``` -### πŸ›‘οΈ License +## πŸ›‘οΈ License The project is primarily distributed under [MIT License](https://github.com/qubvel/segmentation_models.pytorch/blob/main/LICENSE), while some files are subject to other licenses. Please refer to [LICENSES](licenses/LICENSES.md) and license statements in each file for careful check, especially for commercial use. diff --git a/docs/conf.py b/docs/conf.py index c7dde9e5..4cc70a6b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -100,9 +100,7 @@ def get_version(): "timm", "cv2", "PIL", - "pretrainedmodels", "torchvision", - "efficientnet-pytorch", "segmentation_models_pytorch.encoders", "segmentation_models_pytorch.utils", # 'segmentation_models_pytorch.base', diff --git a/docs/encoders.rst b/docs/encoders.rst index 652745b7..2de35dec 100644 --- a/docs/encoders.rst +++ b/docs/encoders.rst @@ -1,363 +1,141 @@ πŸ” Available Encoders ===================== -ResNet -~~~~~~ - -+-------------+-------------------------+-------------+ -| Encoder | Weights | Params, M | -+=============+=========================+=============+ -| resnet18 | imagenet / ssl / swsl | 11M | -+-------------+-------------------------+-------------+ -| resnet34 | imagenet | 21M | -+-------------+-------------------------+-------------+ -| resnet50 | imagenet / ssl / swsl | 23M | -+-------------+-------------------------+-------------+ -| resnet101 | imagenet | 42M | -+-------------+-------------------------+-------------+ -| resnet152 | imagenet | 58M | -+-------------+-------------------------+-------------+ - -ResNeXt -~~~~~~~ - -+----------------------+-------------------------------------+-------------+ -| Encoder | Weights | Params, M | -+======================+=====================================+=============+ -| resnext50\_32x4d | imagenet / ssl / swsl | 22M | -+----------------------+-------------------------------------+-------------+ -| resnext101\_32x4d | ssl / swsl | 42M | -+----------------------+-------------------------------------+-------------+ -| resnext101\_32x8d | imagenet / instagram / ssl / swsl | 86M | -+----------------------+-------------------------------------+-------------+ -| resnext101\_32x16d | instagram / ssl / swsl | 191M | -+----------------------+-------------------------------------+-------------+ -| resnext101\_32x32d | instagram | 466M | -+----------------------+-------------------------------------+-------------+ -| resnext101\_32x48d | instagram | 826M | -+----------------------+-------------------------------------+-------------+ - -ResNeSt -~~~~~~~ - -+----------------------------+------------+-------------+ -| Encoder | Weights | Params, M | -+============================+============+=============+ -| timm-resnest14d | imagenet | 8M | -+----------------------------+------------+-------------+ -| timm-resnest26d | imagenet | 15M | -+----------------------------+------------+-------------+ -| timm-resnest50d | imagenet | 25M | -+----------------------------+------------+-------------+ -| timm-resnest101e | imagenet | 46M | -+----------------------------+------------+-------------+ -| timm-resnest200e | imagenet | 68M | -+----------------------------+------------+-------------+ -| timm-resnest269e | imagenet | 108M | -+----------------------------+------------+-------------+ -| timm-resnest50d\_4s2x40d | imagenet | 28M | -+----------------------------+------------+-------------+ -| timm-resnest50d\_1s4x24d | imagenet | 23M | -+----------------------------+------------+-------------+ - -Res2Ne(X)t -~~~~~~~~~~ - -+----------------------------+------------+-------------+ -| Encoder | Weights | Params, M | -+============================+============+=============+ -| timm-res2net50\_26w\_4s | imagenet | 23M | -+----------------------------+------------+-------------+ -| timm-res2net101\_26w\_4s | imagenet | 43M | -+----------------------------+------------+-------------+ -| timm-res2net50\_26w\_6s | imagenet | 35M | -+----------------------------+------------+-------------+ -| timm-res2net50\_26w\_8s | imagenet | 46M | -+----------------------------+------------+-------------+ -| timm-res2net50\_48w\_2s | imagenet | 23M | -+----------------------------+------------+-------------+ -| timm-res2net50\_14w\_8s | imagenet | 23M | -+----------------------------+------------+-------------+ -| timm-res2next50 | imagenet | 22M | -+----------------------------+------------+-------------+ - -RegNet(x/y) -~~~~~~~~~~ - -+---------------------+------------+-------------+ -| Encoder | Weights | Params, M | -+=====================+============+=============+ -| timm-regnetx\_002 | imagenet | 2M | -+---------------------+------------+-------------+ -| timm-regnetx\_004 | imagenet | 4M | -+---------------------+------------+-------------+ -| timm-regnetx\_006 | imagenet | 5M | -+---------------------+------------+-------------+ -| timm-regnetx\_008 | imagenet | 6M | -+---------------------+------------+-------------+ -| timm-regnetx\_016 | imagenet | 8M | -+---------------------+------------+-------------+ -| timm-regnetx\_032 | imagenet | 14M | -+---------------------+------------+-------------+ -| timm-regnetx\_040 | imagenet | 20M | -+---------------------+------------+-------------+ -| timm-regnetx\_064 | imagenet | 24M | -+---------------------+------------+-------------+ -| timm-regnetx\_080 | imagenet | 37M | -+---------------------+------------+-------------+ -| timm-regnetx\_120 | imagenet | 43M | -+---------------------+------------+-------------+ -| timm-regnetx\_160 | imagenet | 52M | -+---------------------+------------+-------------+ -| timm-regnetx\_320 | imagenet | 105M | -+---------------------+------------+-------------+ -| timm-regnety\_002 | imagenet | 2M | -+---------------------+------------+-------------+ -| timm-regnety\_004 | imagenet | 3M | -+---------------------+------------+-------------+ -| timm-regnety\_006 | imagenet | 5M | -+---------------------+------------+-------------+ -| timm-regnety\_008 | imagenet | 5M | -+---------------------+------------+-------------+ -| timm-regnety\_016 | imagenet | 10M | -+---------------------+------------+-------------+ -| timm-regnety\_032 | imagenet | 17M | -+---------------------+------------+-------------+ -| timm-regnety\_040 | imagenet | 19M | -+---------------------+------------+-------------+ -| timm-regnety\_064 | imagenet | 29M | -+---------------------+------------+-------------+ -| timm-regnety\_080 | imagenet | 37M | -+---------------------+------------+-------------+ -| timm-regnety\_120 | imagenet | 49M | -+---------------------+------------+-------------+ -| timm-regnety\_160 | imagenet | 80M | -+---------------------+------------+-------------+ -| timm-regnety\_320 | imagenet | 141M | -+---------------------+------------+-------------+ - -GERNet -~~~~~~ - -+-------------------------+------------+-------------+ -| Encoder | Weights | Params, M | -+=========================+============+=============+ -| timm-gernet\_s | imagenet | 6M | -+-------------------------+------------+-------------+ -| timm-gernet\_m | imagenet | 18M | -+-------------------------+------------+-------------+ -| timm-gernet\_l | imagenet | 28M | -+-------------------------+------------+-------------+ - -SE-Net -~~~~~~ - -+-------------------------+------------+-------------+ -| Encoder | Weights | Params, M | -+=========================+============+=============+ -| senet154 | imagenet | 113M | -+-------------------------+------------+-------------+ -| se\_resnet50 | imagenet | 26M | -+-------------------------+------------+-------------+ -| se\_resnet101 | imagenet | 47M | -+-------------------------+------------+-------------+ -| se\_resnet152 | imagenet | 64M | -+-------------------------+------------+-------------+ -| se\_resnext50\_32x4d | imagenet | 25M | -+-------------------------+------------+-------------+ -| se\_resnext101\_32x4d | imagenet | 46M | -+-------------------------+------------+-------------+ - -SK-ResNe(X)t -~~~~~~~~~~~~ - -+---------------------------+------------+-------------+ -| Encoder | Weights | Params, M | -+===========================+============+=============+ -| timm-skresnet18 | imagenet | 11M | -+---------------------------+------------+-------------+ -| timm-skresnet34 | imagenet | 21M | -+---------------------------+------------+-------------+ -| timm-skresnext50\_32x4d | imagenet | 25M | -+---------------------------+------------+-------------+ - -DenseNet -~~~~~~~~ - -+---------------+------------+-------------+ -| Encoder | Weights | Params, M | -+===============+============+=============+ -| densenet121 | imagenet | 6M | -+---------------+------------+-------------+ -| densenet169 | imagenet | 12M | -+---------------+------------+-------------+ -| densenet201 | imagenet | 18M | -+---------------+------------+-------------+ -| densenet161 | imagenet | 26M | -+---------------+------------+-------------+ - -Inception -~~~~~~~~~ - -+---------------------+----------------------------------+-------------+ -| Encoder | Weights | Params, M | -+=====================+==================================+=============+ -| inceptionresnetv2 | imagenet / imagenet+background | 54M | -+---------------------+----------------------------------+-------------+ -| inceptionv4 | imagenet / imagenet+background | 41M | -+---------------------+----------------------------------+-------------+ -| xception | imagenet | 22M | -+---------------------+----------------------------------+-------------+ - -EfficientNet -~~~~~~~~~~~~ - -+------------------------+--------------------------------------+-------------+ -| Encoder | Weights | Params, M | -+========================+======================================+=============+ -| efficientnet-b0 | imagenet | 4M | -+------------------------+--------------------------------------+-------------+ -| efficientnet-b1 | imagenet | 6M | -+------------------------+--------------------------------------+-------------+ -| efficientnet-b2 | imagenet | 7M | -+------------------------+--------------------------------------+-------------+ -| efficientnet-b3 | imagenet | 10M | -+------------------------+--------------------------------------+-------------+ -| efficientnet-b4 | imagenet | 17M | -+------------------------+--------------------------------------+-------------+ -| efficientnet-b5 | imagenet | 28M | -+------------------------+--------------------------------------+-------------+ -| efficientnet-b6 | imagenet | 40M | -+------------------------+--------------------------------------+-------------+ -| efficientnet-b7 | imagenet | 63M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-b0 | imagenet / advprop / noisy-student | 4M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-b1 | imagenet / advprop / noisy-student | 6M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-b2 | imagenet / advprop / noisy-student | 7M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-b3 | imagenet / advprop / noisy-student | 10M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-b4 | imagenet / advprop / noisy-student | 17M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-b5 | imagenet / advprop / noisy-student | 28M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-b6 | imagenet / advprop / noisy-student | 40M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-b7 | imagenet / advprop / noisy-student | 63M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-b8 | imagenet / advprop | 84M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-l2 | noisy-student / noisy-student-475 | 474M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-lite0| imagenet | 4M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-lite1| imagenet | 4M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-lite2| imagenet | 6M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-lite3| imagenet | 8M | -+------------------------+--------------------------------------+-------------+ -| timm-efficientnet-lite4| imagenet | 13M | -+------------------------+--------------------------------------+-------------+ - -MobileNet -~~~~~~~~~ - -+---------------------------------------+------------+-------------+ -| Encoder | Weights | Params, M | -+=======================================+============+=============+ -| mobilenet\_v2 | imagenet | 2M | -+---------------------------------------+------------+-------------+ -| timm-mobilenetv3\_large\_075 | imagenet | 1.78M | -+---------------------------------------+------------+-------------+ -| timm-mobilenetv3\_large\_100 | imagenet | 2.97M | -+---------------------------------------+------------+-------------+ -| timm-mobilenetv3\_large\_minimal\_100 | imagenet | 1.41M | -+---------------------------------------+------------+-------------+ -| timm-mobilenetv3\_small\_075 | imagenet | 0.57M | -+---------------------------------------+------------+-------------+ -| timm-mobilenetv3\_small\_100 | imagenet | 0.93M | -+---------------------------------------+------------+-------------+ -| timm-mobilenetv3\_small\_minimal\_100 | imagenet | 0.43M | -+---------------------------------------+------------+-------------+ - -DPN -~~~ - -+-----------+---------------+-------------+ -| Encoder | Weights | Params, M | -+===========+===============+=============+ -| dpn68 | imagenet | 11M | -+-----------+---------------+-------------+ -| dpn68b | imagenet+5k | 11M | -+-----------+---------------+-------------+ -| dpn92 | imagenet+5k | 34M | -+-----------+---------------+-------------+ -| dpn98 | imagenet | 58M | -+-----------+---------------+-------------+ -| dpn107 | imagenet+5k | 84M | -+-----------+---------------+-------------+ -| dpn131 | imagenet | 76M | -+-----------+---------------+-------------+ - -VGG -~~~ - -+-------------+------------+-------------+ -| Encoder | Weights | Params, M | -+=============+============+=============+ -| vgg11 | imagenet | 9M | -+-------------+------------+-------------+ -| vgg11\_bn | imagenet | 9M | -+-------------+------------+-------------+ -| vgg13 | imagenet | 9M | -+-------------+------------+-------------+ -| vgg13\_bn | imagenet | 9M | -+-------------+------------+-------------+ -| vgg16 | imagenet | 14M | -+-------------+------------+-------------+ -| vgg16\_bn | imagenet | 14M | -+-------------+------------+-------------+ -| vgg19 | imagenet | 20M | -+-------------+------------+-------------+ -| vgg19\_bn | imagenet | 20M | -+-------------+------------+-------------+ - - -Mix Visual Transformer -~~~~~~~~~~~~~~~~~~~~~ - -+-----------+----------+------------+ -| Encoder | Weights | Params, M | -+===========+==========+============+ -| mit\_b0 | imagenet | 3M | -+-----------+----------+------------+ -| mit\_b1 | imagenet | 13M | -+-----------+----------+------------+ -| mit\_b2 | imagenet | 24M | -+-----------+----------+------------+ -| mit\_b3 | imagenet | 44M | -+-----------+----------+------------+ -| mit\_b4 | imagenet | 60M | -+-----------+----------+------------+ -| mit\_b5 | imagenet | 81M | -+-----------+----------+------------+ - -MobileOne -~~~~~~~~~~~~~~~~~~~~~ - -+-----------------+----------+------------+ -| Encoder | Weights | Params, M | -+=================+==========+============+ -| mobileone\_s0 | imagenet | 4.6M | -+-----------------+----------+------------+ -| mobileone\_s1 | imagenet | 4.0M | -+-----------------+----------+------------+ -| mobileone\_s2 | imagenet | 6.5M | -+-----------------+----------+------------+ -| mobileone\_s3 | imagenet | 8.8M | -+-----------------+----------+------------+ -| mobileone\_s4 | imagenet | 13.6M | -+-----------------+----------+------------+ +**Segmentation Models PyTorch** provides support for a wide range of encoders. +This flexibility allows you to use these encoders with any model in the library by +specifying the encoder name in the ``encoder_name`` parameter during model initialization. + +Here’s a quick example of using a ResNet34 encoder with the ``Unet`` model: + +.. code-block:: python + + from segmentation_models_pytorch import Unet + + # Initialize Unet with ResNet34 encoder pre-trained on ImageNet + model = Unet(encoder_name="resnet34", encoder_weights="imagenet") + + +The following encoder families are supported by the library, enabling you to choose the one that best fits your use case: + +- **Mix Vision Transformer (mit)** +- **MobileOne** +- **MobileNet** +- **EfficientNet** +- **ResNet** +- **ResNeXt** +- **SENet** +- **DPN** +- **VGG** +- **DenseNet** +- **Xception** +- **Inception** + +Choosing the Right Encoder +-------------------------- + +1. **Small Models for Edge Devices** + Consider encoders like **MobileNet** or **MobileOne**, which have a smaller parameter count and are optimized for lightweight deployment. + +2. **High Performance** + If you require state-of-the-art accuracy **Mix Vision Transformer (mit)**, **EfficientNet** families offer excellent balance between performance and computational efficiency. + +For each encoder, the table below provides detailed information: + +1. **Pretrained Weights** + Specifies the available pretrained weights (e.g., ``imagenet``, ``imagenet21k``). + +2. **Params, M**: + The total number of parameters in the encoder, measured in millions. This metric helps you assess the model's size and computational requirements. + +3. **Script**: + Indicates whether the encoder can be scripted with ``torch.jit.script``. + +4. **Compile**: + Indicates whether the encoder is compatible with ``torch.compile(model, fullgraph=True, dynamic=True, backend="eager")``. + You may still get some issues with another backends, such as ``inductor``, depending on the torch/cuda/... dependencies version, + but most of the time it will work. + +5. **Export**: + Indicates whether the encoder can be exported using ``torch.export.export``, making it suitable for deployment in different environments (e.g., ONNX). + + +============================ ==================================== =========== ======== ========= ======== +Encoder Pretrained weights Params, M Script Compile Export +============================ ==================================== =========== ======== ========= ======== +resnet18 imagenet / ssl / swsl 11M βœ… βœ… βœ… +resnet34 imagenet 21M βœ… βœ… βœ… +resnet50 imagenet / ssl / swsl 23M βœ… βœ… βœ… +resnet101 imagenet 42M βœ… βœ… βœ… +resnet152 imagenet 58M βœ… βœ… βœ… +resnext50_32x4d imagenet / ssl / swsl 22M βœ… βœ… βœ… +resnext101_32x4d ssl / swsl 42M βœ… βœ… βœ… +resnext101_32x8d imagenet / instagram / ssl / swsl 86M βœ… βœ… βœ… +resnext101_32x16d instagram / ssl / swsl 191M βœ… βœ… βœ… +resnext101_32x32d instagram 466M βœ… βœ… βœ… +resnext101_32x48d instagram 826M βœ… βœ… βœ… +dpn68 imagenet 11M ❌ βœ… βœ… +dpn68b imagenet+5k 11M ❌ βœ… βœ… +dpn92 imagenet+5k 34M ❌ βœ… βœ… +dpn98 imagenet 58M ❌ βœ… βœ… +dpn107 imagenet+5k 84M ❌ βœ… βœ… +dpn131 imagenet 76M ❌ βœ… βœ… +vgg11 imagenet 9M βœ… βœ… βœ… +vgg11_bn imagenet 9M βœ… βœ… βœ… +vgg13 imagenet 9M βœ… βœ… βœ… +vgg13_bn imagenet 9M βœ… βœ… βœ… +vgg16 imagenet 14M βœ… βœ… βœ… +vgg16_bn imagenet 14M βœ… βœ… βœ… +vgg19 imagenet 20M βœ… βœ… βœ… +vgg19_bn imagenet 20M βœ… βœ… βœ… +senet154 imagenet 113M βœ… βœ… βœ… +se_resnet50 imagenet 26M βœ… βœ… βœ… +se_resnet101 imagenet 47M βœ… βœ… βœ… +se_resnet152 imagenet 64M βœ… βœ… βœ… +se_resnext50_32x4d imagenet 25M βœ… βœ… βœ… +se_resnext101_32x4d imagenet 46M βœ… βœ… βœ… +densenet121 imagenet 6M βœ… βœ… βœ… +densenet169 imagenet 12M βœ… βœ… βœ… +densenet201 imagenet 18M βœ… βœ… βœ… +densenet161 imagenet 26M βœ… βœ… βœ… +inceptionresnetv2 imagenet / imagenet+background 54M βœ… βœ… βœ… +inceptionv4 imagenet / imagenet+background 41M βœ… βœ… βœ… +efficientnet-b0 imagenet / advprop 4M βœ… βœ… βœ… +efficientnet-b1 imagenet / advprop 6M βœ… βœ… βœ… +efficientnet-b2 imagenet / advprop 7M βœ… βœ… βœ… +efficientnet-b3 imagenet / advprop 10M βœ… βœ… βœ… +efficientnet-b4 imagenet / advprop 17M βœ… βœ… βœ… +efficientnet-b5 imagenet / advprop 28M βœ… βœ… βœ… +efficientnet-b6 imagenet / advprop 40M βœ… βœ… βœ… +efficientnet-b7 imagenet / advprop 63M βœ… βœ… βœ… +mobilenet_v2 imagenet 2M βœ… βœ… βœ… +xception imagenet 20M βœ… βœ… βœ… +timm-efficientnet-b0 imagenet / advprop / noisy-student 4M βœ… βœ… βœ… +timm-efficientnet-b1 imagenet / advprop / noisy-student 6M βœ… βœ… βœ… +timm-efficientnet-b2 imagenet / advprop / noisy-student 7M βœ… βœ… βœ… +timm-efficientnet-b3 imagenet / advprop / noisy-student 10M βœ… βœ… βœ… +timm-efficientnet-b4 imagenet / advprop / noisy-student 17M βœ… βœ… βœ… +timm-efficientnet-b5 imagenet / advprop / noisy-student 28M βœ… βœ… βœ… +timm-efficientnet-b6 imagenet / advprop / noisy-student 40M βœ… βœ… βœ… +timm-efficientnet-b7 imagenet / advprop / noisy-student 63M βœ… βœ… βœ… +timm-efficientnet-b8 imagenet / advprop 84M βœ… βœ… βœ… +timm-efficientnet-l2 noisy-student / noisy-student-475 474M βœ… βœ… βœ… +timm-tf_efficientnet_lite0 imagenet 3M βœ… βœ… βœ… +timm-tf_efficientnet_lite1 imagenet 4M βœ… βœ… βœ… +timm-tf_efficientnet_lite2 imagenet 4M βœ… βœ… βœ… +timm-tf_efficientnet_lite3 imagenet 6M βœ… βœ… βœ… +timm-tf_efficientnet_lite4 imagenet 11M βœ… βœ… βœ… +timm-skresnet18 imagenet 11M βœ… βœ… βœ… +timm-skresnet34 imagenet 21M βœ… βœ… βœ… +timm-skresnext50_32x4d imagenet 23M βœ… βœ… βœ… +mit_b0 imagenet 3M βœ… βœ… βœ… +mit_b1 imagenet 13M βœ… βœ… βœ… +mit_b2 imagenet 24M βœ… βœ… βœ… +mit_b3 imagenet 44M βœ… βœ… βœ… +mit_b4 imagenet 60M βœ… βœ… βœ… +mit_b5 imagenet 81M βœ… βœ… βœ… +mobileone_s0 imagenet 4M βœ… βœ… βœ… +mobileone_s1 imagenet 3M βœ… βœ… βœ… +mobileone_s2 imagenet 5M βœ… βœ… βœ… +mobileone_s3 imagenet 8M βœ… βœ… βœ… +mobileone_s4 imagenet 12M βœ… βœ… βœ… +============================ ==================================== =========== ======== ========= ======== diff --git a/docs/encoders_dpt.rst b/docs/encoders_dpt.rst new file mode 100644 index 00000000..9ce3af31 --- /dev/null +++ b/docs/encoders_dpt.rst @@ -0,0 +1,461 @@ +.. _dpt-encoders: + +DPT Encoders +============ + +This is a list of Vision Transformer encoders that are compatible with the DPT architecture. +While other Vision Transformer encoders from timm may also be compatible, the ones listed below are tested to work properly. + +.. list-table:: Encoder Name + :widths: 100 + :header-rows: 0 + + * - tu-fastvit_ma36.apple_dist_in1k + * - tu-fastvit_ma36.apple_in1k + * - tu-fastvit_mci0.apple_mclip + * - tu-fastvit_mci1.apple_mclip + * - tu-fastvit_mci2.apple_mclip + * - tu-fastvit_s12.apple_dist_in1k + * - tu-fastvit_s12.apple_in1k + * - tu-fastvit_sa12.apple_dist_in1k + * - tu-fastvit_sa12.apple_in1k + * - tu-fastvit_sa24.apple_dist_in1k + * - tu-fastvit_sa24.apple_in1k + * - tu-fastvit_sa36.apple_dist_in1k + * - tu-fastvit_sa36.apple_in1k + * - tu-fastvit_t8.apple_dist_in1k + * - tu-fastvit_t8.apple_in1k + * - tu-fastvit_t12.apple_dist_in1k + * - tu-fastvit_t12.apple_in1k + * - tu-flexivit_base.300ep_in1k + * - tu-flexivit_base.300ep_in21k + * - tu-flexivit_base.600ep_in1k + * - tu-flexivit_base.1000ep_in21k + * - tu-flexivit_base.1200ep_in1k + * - tu-flexivit_base.patch16_in21k + * - tu-flexivit_base.patch30_in21k + * - tu-flexivit_large.300ep_in1k + * - tu-flexivit_large.600ep_in1k + * - tu-flexivit_large.1200ep_in1k + * - tu-flexivit_small.300ep_in1k + * - tu-flexivit_small.600ep_in1k + * - tu-flexivit_small.1200ep_in1k + * - tu-maxvit_base_tf_224.in1k + * - tu-maxvit_base_tf_224.in21k + * - tu-maxvit_base_tf_384.in1k + * - tu-maxvit_base_tf_384.in21k_ft_in1k + * - tu-maxvit_base_tf_512.in1k + * - tu-maxvit_base_tf_512.in21k_ft_in1k + * - tu-maxvit_large_tf_224.in1k + * - tu-maxvit_large_tf_224.in21k + * - tu-maxvit_large_tf_384.in1k + * - tu-maxvit_large_tf_384.in21k_ft_in1k + * - tu-maxvit_large_tf_512.in1k + * - tu-maxvit_large_tf_512.in21k_ft_in1k + * - tu-maxvit_nano_rw_256.sw_in1k + * - tu-maxvit_rmlp_base_rw_224.sw_in12k + * - tu-maxvit_rmlp_base_rw_224.sw_in12k_ft_in1k + * - tu-maxvit_rmlp_base_rw_384.sw_in12k_ft_in1k + * - tu-maxvit_rmlp_nano_rw_256.sw_in1k + * - tu-maxvit_rmlp_pico_rw_256.sw_in1k + * - tu-maxvit_rmlp_small_rw_224.sw_in1k + * - tu-maxvit_rmlp_tiny_rw_256.sw_in1k + * - tu-maxvit_small_tf_224.in1k + * - tu-maxvit_small_tf_384.in1k + * - tu-maxvit_small_tf_512.in1k + * - tu-maxvit_tiny_rw_224.sw_in1k + * - tu-maxvit_tiny_tf_224.in1k + * - tu-maxvit_tiny_tf_384.in1k + * - tu-maxvit_tiny_tf_512.in1k + * - tu-maxvit_xlarge_tf_224.in21k + * - tu-maxvit_xlarge_tf_384.in21k_ft_in1k + * - tu-maxvit_xlarge_tf_512.in21k_ft_in1k + * - tu-maxxvit_rmlp_nano_rw_256.sw_in1k + * - tu-maxxvit_rmlp_small_rw_256.sw_in1k + * - tu-maxxvitv2_nano_rw_256.sw_in1k + * - tu-maxxvitv2_rmlp_base_rw_224.sw_in12k + * - tu-maxxvitv2_rmlp_base_rw_224.sw_in12k_ft_in1k + * - tu-maxxvitv2_rmlp_base_rw_384.sw_in12k_ft_in1k + * - tu-mobilevit_s.cvnets_in1k + * - tu-mobilevit_xs.cvnets_in1k + * - tu-mobilevit_xxs.cvnets_in1k + * - tu-mobilevitv2_050.cvnets_in1k + * - tu-mobilevitv2_075.cvnets_in1k + * - tu-mobilevitv2_100.cvnets_in1k + * - tu-mobilevitv2_125.cvnets_in1k + * - tu-mobilevitv2_150.cvnets_in1k + * - tu-mobilevitv2_150.cvnets_in22k_ft_in1k + * - tu-mobilevitv2_150.cvnets_in22k_ft_in1k_384 + * - tu-mobilevitv2_175.cvnets_in1k + * - tu-mobilevitv2_175.cvnets_in22k_ft_in1k + * - tu-mobilevitv2_175.cvnets_in22k_ft_in1k_384 + * - tu-mobilevitv2_200.cvnets_in1k + * - tu-mobilevitv2_200.cvnets_in22k_ft_in1k + * - tu-mobilevitv2_200.cvnets_in22k_ft_in1k_384 + * - tu-mvitv2_base.fb_in1k + * - tu-mvitv2_base_cls.fb_inw21k + * - tu-mvitv2_huge_cls.fb_inw21k + * - tu-mvitv2_large.fb_in1k + * - tu-mvitv2_large_cls.fb_inw21k + * - tu-mvitv2_small.fb_in1k + * - tu-mvitv2_tiny.fb_in1k + * - tu-samvit_base_patch16.sa1b + * - tu-samvit_huge_patch16.sa1b + * - tu-samvit_large_patch16.sa1b + * - tu-test_vit2.r160_in1k + * - tu-test_vit3.r160_in1k + * - tu-test_vit.r160_in1k + * - tu-vit_base_mci_224.apple_mclip + * - tu-vit_base_mci_224.apple_mclip_lt + * - tu-vit_base_patch8_224.augreg2_in21k_ft_in1k + * - tu-vit_base_patch8_224.augreg_in21k + * - tu-vit_base_patch8_224.augreg_in21k_ft_in1k + * - tu-vit_base_patch8_224.dino + * - tu-vit_base_patch16_224.augreg2_in21k_ft_in1k + * - tu-vit_base_patch16_224.augreg_in1k + * - tu-vit_base_patch16_224.augreg_in21k + * - tu-vit_base_patch16_224.augreg_in21k_ft_in1k + * - tu-vit_base_patch16_224.dino + * - tu-vit_base_patch16_224.mae + * - tu-vit_base_patch16_224.orig_in21k + * - tu-vit_base_patch16_224.orig_in21k_ft_in1k + * - tu-vit_base_patch16_224.sam_in1k + * - tu-vit_base_patch16_224_miil.in21k + * - tu-vit_base_patch16_224_miil.in21k_ft_in1k + * - tu-vit_base_patch16_384.augreg_in1k + * - tu-vit_base_patch16_384.augreg_in21k_ft_in1k + * - tu-vit_base_patch16_384.orig_in21k_ft_in1k + * - tu-vit_base_patch16_clip_224.datacompxl + * - tu-vit_base_patch16_clip_224.dfn2b + * - tu-vit_base_patch16_clip_224.laion2b + * - tu-vit_base_patch16_clip_224.laion2b_ft_in1k + * - tu-vit_base_patch16_clip_224.laion2b_ft_in12k + * - tu-vit_base_patch16_clip_224.laion2b_ft_in12k_in1k + * - tu-vit_base_patch16_clip_224.laion400m_e32 + * - tu-vit_base_patch16_clip_224.metaclip_2pt5b + * - tu-vit_base_patch16_clip_224.metaclip_400m + * - tu-vit_base_patch16_clip_224.openai + * - tu-vit_base_patch16_clip_224.openai_ft_in1k + * - tu-vit_base_patch16_clip_224.openai_ft_in12k + * - tu-vit_base_patch16_clip_224.openai_ft_in12k_in1k + * - tu-vit_base_patch16_clip_384.laion2b_ft_in1k + * - tu-vit_base_patch16_clip_384.laion2b_ft_in12k_in1k + * - tu-vit_base_patch16_clip_384.openai_ft_in1k + * - tu-vit_base_patch16_clip_384.openai_ft_in12k_in1k + * - tu-vit_base_patch16_clip_quickgelu_224.metaclip_2pt5b + * - tu-vit_base_patch16_clip_quickgelu_224.metaclip_400m + * - tu-vit_base_patch16_clip_quickgelu_224.openai + * - tu-vit_base_patch16_plus_clip_240.laion400m_e32 + * - tu-vit_base_patch16_rope_reg1_gap_256.sbb_in1k + * - tu-vit_base_patch16_rpn_224.sw_in1k + * - tu-vit_base_patch16_siglip_224.v2_webli + * - tu-vit_base_patch16_siglip_224.webli + * - tu-vit_base_patch16_siglip_256.v2_webli + * - tu-vit_base_patch16_siglip_256.webli + * - tu-vit_base_patch16_siglip_256.webli_i18n + * - tu-vit_base_patch16_siglip_384.v2_webli + * - tu-vit_base_patch16_siglip_384.webli + * - tu-vit_base_patch16_siglip_512.v2_webli + * - tu-vit_base_patch16_siglip_512.webli + * - tu-vit_base_patch16_siglip_gap_224.v2_webli + * - tu-vit_base_patch16_siglip_gap_224.webli + * - tu-vit_base_patch16_siglip_gap_256.v2_webli + * - tu-vit_base_patch16_siglip_gap_256.webli + * - tu-vit_base_patch16_siglip_gap_256.webli_i18n + * - tu-vit_base_patch16_siglip_gap_384.v2_webli + * - tu-vit_base_patch16_siglip_gap_384.webli + * - tu-vit_base_patch16_siglip_gap_512.v2_webli + * - tu-vit_base_patch16_siglip_gap_512.webli + * - tu-vit_base_patch32_224.augreg_in1k + * - tu-vit_base_patch32_224.augreg_in21k + * - tu-vit_base_patch32_224.augreg_in21k_ft_in1k + * - tu-vit_base_patch32_224.orig_in21k + * - tu-vit_base_patch32_224.sam_in1k + * - tu-vit_base_patch32_384.augreg_in1k + * - tu-vit_base_patch32_384.augreg_in21k_ft_in1k + * - tu-vit_base_patch32_clip_224.datacompxl + * - tu-vit_base_patch32_clip_224.laion2b + * - tu-vit_base_patch32_clip_224.laion2b_ft_in1k + * - tu-vit_base_patch32_clip_224.laion2b_ft_in12k_in1k + * - tu-vit_base_patch32_clip_224.laion400m_e32 + * - tu-vit_base_patch32_clip_224.metaclip_2pt5b + * - tu-vit_base_patch32_clip_224.metaclip_400m + * - tu-vit_base_patch32_clip_224.openai + * - tu-vit_base_patch32_clip_224.openai_ft_in1k + * - tu-vit_base_patch32_clip_256.datacompxl + * - tu-vit_base_patch32_clip_384.laion2b_ft_in12k_in1k + * - tu-vit_base_patch32_clip_384.openai_ft_in12k_in1k + * - tu-vit_base_patch32_clip_448.laion2b_ft_in12k_in1k + * - tu-vit_base_patch32_clip_quickgelu_224.laion400m_e32 + * - tu-vit_base_patch32_clip_quickgelu_224.metaclip_2pt5b + * - tu-vit_base_patch32_clip_quickgelu_224.metaclip_400m + * - tu-vit_base_patch32_clip_quickgelu_224.openai + * - tu-vit_base_patch32_siglip_256.v2_webli + * - tu-vit_base_patch32_siglip_gap_256.v2_webli + * - tu-vit_base_r50_s16_224.orig_in21k + * - tu-vit_base_r50_s16_384.orig_in21k_ft_in1k + * - tu-vit_betwixt_patch16_reg1_gap_256.sbb_in1k + * - tu-vit_betwixt_patch16_reg4_gap_256.sbb2_e200_in12k + * - tu-vit_betwixt_patch16_reg4_gap_256.sbb2_e200_in12k_ft_in1k + * - tu-vit_betwixt_patch16_reg4_gap_256.sbb_in1k + * - tu-vit_betwixt_patch16_reg4_gap_256.sbb_in12k + * - tu-vit_betwixt_patch16_reg4_gap_256.sbb_in12k_ft_in1k + * - tu-vit_betwixt_patch16_reg4_gap_384.sbb2_e200_in12k_ft_in1k + * - tu-vit_betwixt_patch16_rope_reg4_gap_256.sbb_in1k + * - tu-vit_betwixt_patch32_clip_224.tinyclip_laion400m + * - tu-vit_giant_patch16_gap_224.in22k_ijepa + * - tu-vit_giantopt_patch16_siglip_256.v2_webli + * - tu-vit_giantopt_patch16_siglip_384.v2_webli + * - tu-vit_giantopt_patch16_siglip_gap_256.v2_webli + * - tu-vit_giantopt_patch16_siglip_gap_384.v2_webli + * - tu-vit_huge_patch16_gap_448.in1k_ijepa + * - tu-vit_large_patch16_224.augreg_in21k + * - tu-vit_large_patch16_224.augreg_in21k_ft_in1k + * - tu-vit_large_patch16_224.mae + * - tu-vit_large_patch16_224.orig_in21k + * - tu-vit_large_patch16_384.augreg_in21k_ft_in1k + * - tu-vit_large_patch16_siglip_256.v2_webli + * - tu-vit_large_patch16_siglip_256.webli + * - tu-vit_large_patch16_siglip_384.v2_webli + * - tu-vit_large_patch16_siglip_384.webli + * - tu-vit_large_patch16_siglip_512.v2_webli + * - tu-vit_large_patch16_siglip_gap_256.v2_webli + * - tu-vit_large_patch16_siglip_gap_256.webli + * - tu-vit_large_patch16_siglip_gap_384.v2_webli + * - tu-vit_large_patch16_siglip_gap_384.webli + * - tu-vit_large_patch16_siglip_gap_512.v2_webli + * - tu-vit_large_patch32_224.orig_in21k + * - tu-vit_large_patch32_384.orig_in21k_ft_in1k + * - tu-vit_large_r50_s32_224.augreg_in21k + * - tu-vit_large_r50_s32_224.augreg_in21k_ft_in1k + * - tu-vit_large_r50_s32_384.augreg_in21k_ft_in1k + * - tu-vit_little_patch16_reg1_gap_256.sbb_in12k + * - tu-vit_little_patch16_reg1_gap_256.sbb_in12k_ft_in1k + * - tu-vit_little_patch16_reg4_gap_256.sbb_in1k + * - tu-vit_medium_patch16_clip_224.tinyclip_yfcc15m + * - tu-vit_medium_patch16_gap_240.sw_in12k + * - tu-vit_medium_patch16_gap_256.sw_in12k_ft_in1k + * - tu-vit_medium_patch16_gap_384.sw_in12k_ft_in1k + * - tu-vit_medium_patch16_reg1_gap_256.sbb_in1k + * - tu-vit_medium_patch16_reg4_gap_256.sbb_in1k + * - tu-vit_medium_patch16_reg4_gap_256.sbb_in12k + * - tu-vit_medium_patch16_reg4_gap_256.sbb_in12k_ft_in1k + * - tu-vit_medium_patch16_rope_reg1_gap_256.sbb_in1k + * - tu-vit_medium_patch32_clip_224.tinyclip_laion400m + * - tu-vit_mediumd_patch16_reg4_gap_256.sbb2_e200_in12k + * - tu-vit_mediumd_patch16_reg4_gap_256.sbb2_e200_in12k_ft_in1k + * - tu-vit_mediumd_patch16_reg4_gap_256.sbb_in12k + * - tu-vit_mediumd_patch16_reg4_gap_256.sbb_in12k_ft_in1k + * - tu-vit_mediumd_patch16_reg4_gap_384.sbb2_e200_in12k_ft_in1k + * - tu-vit_mediumd_patch16_rope_reg1_gap_256.sbb_in1k + * - tu-vit_pwee_patch16_reg1_gap_256.sbb_in1k + * - tu-vit_relpos_base_patch16_224.sw_in1k + * - tu-vit_relpos_base_patch16_clsgap_224.sw_in1k + * - tu-vit_relpos_base_patch32_plus_rpn_256.sw_in1k + * - tu-vit_relpos_medium_patch16_224.sw_in1k + * - tu-vit_relpos_medium_patch16_cls_224.sw_in1k + * - tu-vit_relpos_medium_patch16_rpn_224.sw_in1k + * - tu-vit_relpos_small_patch16_224.sw_in1k + * - tu-vit_small_patch8_224.dino + * - tu-vit_small_patch16_224.augreg_in1k + * - tu-vit_small_patch16_224.augreg_in21k + * - tu-vit_small_patch16_224.augreg_in21k_ft_in1k + * - tu-vit_small_patch16_224.dino + * - tu-vit_small_patch16_384.augreg_in1k + * - tu-vit_small_patch16_384.augreg_in21k_ft_in1k + * - tu-vit_small_patch32_224.augreg_in21k + * - tu-vit_small_patch32_224.augreg_in21k_ft_in1k + * - tu-vit_small_patch32_384.augreg_in21k_ft_in1k + * - tu-vit_small_r26_s32_224.augreg_in21k + * - tu-vit_small_r26_s32_224.augreg_in21k_ft_in1k + * - tu-vit_small_r26_s32_384.augreg_in21k_ft_in1k + * - tu-vit_so150m2_patch16_reg1_gap_256.sbb_e200_in12k + * - tu-vit_so150m2_patch16_reg1_gap_256.sbb_e200_in12k_ft_in1k + * - tu-vit_so150m2_patch16_reg1_gap_384.sbb_e200_in12k_ft_in1k + * - tu-vit_so150m2_patch16_reg1_gap_448.sbb_e200_in12k_ft_in1k + * - tu-vit_so150m_patch16_reg4_gap_256.sbb_e250_in12k + * - tu-vit_so150m_patch16_reg4_gap_256.sbb_e250_in12k_ft_in1k + * - tu-vit_so150m_patch16_reg4_gap_384.sbb_e250_in12k_ft_in1k + * - tu-vit_so400m_patch16_siglip_256.v2_webli + * - tu-vit_so400m_patch16_siglip_256.webli_i18n + * - tu-vit_so400m_patch16_siglip_384.v2_webli + * - tu-vit_so400m_patch16_siglip_512.v2_webli + * - tu-vit_so400m_patch16_siglip_gap_256.v2_webli + * - tu-vit_so400m_patch16_siglip_gap_256.webli_i18n + * - tu-vit_so400m_patch16_siglip_gap_384.v2_webli + * - tu-vit_so400m_patch16_siglip_gap_512.v2_webli + * - tu-vit_srelpos_medium_patch16_224.sw_in1k + * - tu-vit_srelpos_small_patch16_224.sw_in1k + * - tu-vit_tiny_patch16_224.augreg_in21k + * - tu-vit_tiny_patch16_224.augreg_in21k_ft_in1k + * - tu-vit_tiny_patch16_384.augreg_in21k_ft_in1k + * - tu-vit_tiny_r_s16_p8_224.augreg_in21k + * - tu-vit_tiny_r_s16_p8_224.augreg_in21k_ft_in1k + * - tu-vit_tiny_r_s16_p8_384.augreg_in21k_ft_in1k + * - tu-vit_wee_patch16_reg1_gap_256.sbb_in1k + * - tu-vit_xsmall_patch16_clip_224.tinyclip_yfcc15m + * - tu-vitamin_base_224.datacomp1b_clip + * - tu-vitamin_base_224.datacomp1b_clip_ltt + * - tu-vitamin_large2_224.datacomp1b_clip + * - tu-vitamin_large2_256.datacomp1b_clip + * - tu-vitamin_large2_336.datacomp1b_clip + * - tu-vitamin_large2_384.datacomp1b_clip + * - tu-vitamin_large_224.datacomp1b_clip + * - tu-vitamin_large_256.datacomp1b_clip + * - tu-vitamin_large_336.datacomp1b_clip + * - tu-vitamin_large_384.datacomp1b_clip + * - tu-vitamin_small_224.datacomp1b_clip + * - tu-vitamin_small_224.datacomp1b_clip_ltt + * - tu-vitamin_xlarge_256.datacomp1b_clip + * - tu-vitamin_xlarge_336.datacomp1b_clip + * - tu-vitamin_xlarge_384.datacomp1b_clip + * - tu-hiera_small_abswin_256.sbb2_e200_in12k + * - tu-hiera_small_abswin_256.sbb2_e200_in12k_ft_in1k + * - tu-hiera_small_abswin_256.sbb2_pd_e200_in12k + * - tu-hiera_small_abswin_256.sbb2_pd_e200_in12k_ft_in1k + * - tu-swin_base_patch4_window7_224.ms_in1k + * - tu-swin_base_patch4_window7_224.ms_in22k + * - tu-swin_base_patch4_window7_224.ms_in22k_ft_in1k + * - tu-swin_base_patch4_window12_384.ms_in1k + * - tu-swin_base_patch4_window12_384.ms_in22k + * - tu-swin_base_patch4_window12_384.ms_in22k_ft_in1k + * - tu-swin_large_patch4_window7_224.ms_in22k + * - tu-swin_large_patch4_window7_224.ms_in22k_ft_in1k + * - tu-swin_large_patch4_window12_384.ms_in22k + * - tu-swin_large_patch4_window12_384.ms_in22k_ft_in1k + * - tu-swin_s3_base_224.ms_in1k + * - tu-swin_s3_small_224.ms_in1k + * - tu-swin_s3_tiny_224.ms_in1k + * - tu-swin_small_patch4_window7_224.ms_in1k + * - tu-swin_small_patch4_window7_224.ms_in22k + * - tu-swin_small_patch4_window7_224.ms_in22k_ft_in1k + * - tu-swin_tiny_patch4_window7_224.ms_in1k + * - tu-swin_tiny_patch4_window7_224.ms_in22k + * - tu-swin_tiny_patch4_window7_224.ms_in22k_ft_in1k + * - tu-swinv2_base_window8_256.ms_in1k + * - tu-swinv2_base_window12_192.ms_in22k + * - tu-swinv2_base_window12to16_192to256.ms_in22k_ft_in1k + * - tu-swinv2_base_window12to24_192to384.ms_in22k_ft_in1k + * - tu-swinv2_base_window16_256.ms_in1k + * - tu-swinv2_cr_small_224.sw_in1k + * - tu-swinv2_cr_small_ns_224.sw_in1k + * - tu-swinv2_cr_tiny_ns_224.sw_in1k + * - tu-swinv2_large_window12_192.ms_in22k + * - tu-swinv2_large_window12to16_192to256.ms_in22k_ft_in1k + * - tu-swinv2_large_window12to24_192to384.ms_in22k_ft_in1k + * - tu-swinv2_small_window8_256.ms_in1k + * - tu-swinv2_small_window16_256.ms_in1k + * - tu-swinv2_tiny_window8_256.ms_in1k + * - tu-swinv2_tiny_window16_256.ms_in1k + * - tu-efficientformer_l1.snap_dist_in1k + * - tu-efficientformer_l3.snap_dist_in1k + * - tu-efficientformer_l7.snap_dist_in1k + * - tu-beit_base_patch16_224.in22k_ft_in22k + * - tu-beit_base_patch16_224.in22k_ft_in22k_in1k + * - tu-beit_base_patch16_384.in22k_ft_in22k_in1k + * - tu-beit_large_patch16_224.in22k_ft_in22k + * - tu-beit_large_patch16_224.in22k_ft_in22k_in1k + * - tu-beit_large_patch16_384.in22k_ft_in22k_in1k + * - tu-beit_large_patch16_512.in22k_ft_in22k_in1k + * - tu-beitv2_base_patch16_224.in1k_ft_in1k + * - tu-beitv2_base_patch16_224.in1k_ft_in22k + * - tu-beitv2_base_patch16_224.in1k_ft_in22k_in1k + * - tu-beitv2_large_patch16_224.in1k_ft_in1k + * - tu-beitv2_large_patch16_224.in1k_ft_in22k + * - tu-beitv2_large_patch16_224.in1k_ft_in22k_in1k + * - tu-cait_m36_384.fb_dist_in1k + * - tu-cait_m48_448.fb_dist_in1k + * - tu-cait_s24_224.fb_dist_in1k + * - tu-cait_s24_384.fb_dist_in1k + * - tu-cait_s36_384.fb_dist_in1k + * - tu-cait_xs24_384.fb_dist_in1k + * - tu-cait_xxs24_224.fb_dist_in1k + * - tu-cait_xxs24_384.fb_dist_in1k + * - tu-cait_xxs36_224.fb_dist_in1k + * - tu-cait_xxs36_384.fb_dist_in1k + * - tu-coatnet_0_rw_224.sw_in1k + * - tu-coatnet_1_rw_224.sw_in1k + * - tu-coatnet_2_rw_224.sw_in12k + * - tu-coatnet_2_rw_224.sw_in12k_ft_in1k + * - tu-coatnet_3_rw_224.sw_in12k + * - tu-coatnet_bn_0_rw_224.sw_in1k + * - tu-coatnet_nano_rw_224.sw_in1k + * - tu-coatnet_rmlp_1_rw2_224.sw_in12k + * - tu-coatnet_rmlp_1_rw2_224.sw_in12k_ft_in1k + * - tu-coatnet_rmlp_1_rw_224.sw_in1k + * - tu-coatnet_rmlp_2_rw_224.sw_in1k + * - tu-coatnet_rmlp_2_rw_224.sw_in12k + * - tu-coatnet_rmlp_2_rw_224.sw_in12k_ft_in1k + * - tu-coatnet_rmlp_2_rw_384.sw_in12k_ft_in1k + * - tu-coatnet_rmlp_nano_rw_224.sw_in1k + * - tu-deit3_base_patch16_224.fb_in1k + * - tu-deit3_base_patch16_224.fb_in22k_ft_in1k + * - tu-deit3_base_patch16_384.fb_in1k + * - tu-deit3_base_patch16_384.fb_in22k_ft_in1k + * - tu-deit3_large_patch16_224.fb_in1k + * - tu-deit3_large_patch16_224.fb_in22k_ft_in1k + * - tu-deit3_large_patch16_384.fb_in1k + * - tu-deit3_large_patch16_384.fb_in22k_ft_in1k + * - tu-deit3_medium_patch16_224.fb_in1k + * - tu-deit3_medium_patch16_224.fb_in22k_ft_in1k + * - tu-deit3_small_patch16_224.fb_in1k + * - tu-deit3_small_patch16_224.fb_in22k_ft_in1k + * - tu-deit3_small_patch16_384.fb_in1k + * - tu-deit3_small_patch16_384.fb_in22k_ft_in1k + * - tu-deit_base_distilled_patch16_224.fb_in1k + * - tu-deit_base_distilled_patch16_384.fb_in1k + * - tu-deit_base_patch16_224.fb_in1k + * - tu-deit_base_patch16_384.fb_in1k + * - tu-deit_small_distilled_patch16_224.fb_in1k + * - tu-deit_small_patch16_224.fb_in1k + * - tu-deit_tiny_distilled_patch16_224.fb_in1k + * - tu-deit_tiny_patch16_224.fb_in1k + * - tu-regnety_160.deit_in1k + * - tu-twins_pcpvt_base.in1k + * - tu-twins_pcpvt_large.in1k + * - tu-twins_pcpvt_small.in1k + * - tu-twins_svt_base.in1k + * - tu-twins_svt_large.in1k + * - tu-twins_svt_small.in1k + * - tu-xcit_large_24_p8_224.fb_dist_in1k + * - tu-xcit_large_24_p8_224.fb_in1k + * - tu-xcit_large_24_p8_384.fb_dist_in1k + * - tu-xcit_large_24_p16_224.fb_dist_in1k + * - tu-xcit_large_24_p16_224.fb_in1k + * - tu-xcit_large_24_p16_384.fb_dist_in1k + * - tu-xcit_medium_24_p8_224.fb_dist_in1k + * - tu-xcit_medium_24_p8_224.fb_in1k + * - tu-xcit_medium_24_p8_384.fb_dist_in1k + * - tu-xcit_medium_24_p16_224.fb_dist_in1k + * - tu-xcit_medium_24_p16_224.fb_in1k + * - tu-xcit_medium_24_p16_384.fb_dist_in1k + * - tu-xcit_nano_12_p8_224.fb_dist_in1k + * - tu-xcit_nano_12_p8_224.fb_in1k + * - tu-xcit_nano_12_p8_384.fb_dist_in1k + * - tu-xcit_nano_12_p16_224.fb_dist_in1k + * - tu-xcit_nano_12_p16_224.fb_in1k + * - tu-xcit_nano_12_p16_384.fb_dist_in1k + * - tu-xcit_small_12_p8_224.fb_dist_in1k + * - tu-xcit_small_12_p8_224.fb_in1k + * - tu-xcit_small_12_p8_384.fb_dist_in1k + * - tu-xcit_small_12_p16_224.fb_dist_in1k + * - tu-xcit_small_12_p16_224.fb_in1k + * - tu-xcit_small_12_p16_384.fb_dist_in1k + * - tu-xcit_small_24_p8_224.fb_dist_in1k + * - tu-xcit_small_24_p8_224.fb_in1k + * - tu-xcit_small_24_p8_384.fb_dist_in1k + * - tu-xcit_small_24_p16_224.fb_dist_in1k + * - tu-xcit_small_24_p16_224.fb_in1k + * - tu-xcit_small_24_p16_384.fb_dist_in1k + * - tu-xcit_tiny_12_p8_224.fb_dist_in1k + * - tu-xcit_tiny_12_p8_224.fb_in1k + * - tu-xcit_tiny_12_p8_384.fb_dist_in1k + * - tu-xcit_tiny_12_p16_224.fb_dist_in1k + * - tu-xcit_tiny_12_p16_224.fb_in1k + * - tu-xcit_tiny_12_p16_384.fb_dist_in1k + * - tu-xcit_tiny_24_p8_224.fb_dist_in1k + * - tu-xcit_tiny_24_p8_224.fb_in1k + * - tu-xcit_tiny_24_p8_384.fb_dist_in1k + * - tu-xcit_tiny_24_p16_224.fb_dist_in1k + * - tu-xcit_tiny_24_p16_224.fb_in1k + * - tu-xcit_tiny_24_p16_384.fb_dist_in1k \ No newline at end of file diff --git a/docs/models.rst b/docs/models.rst index c2037afb..ab04bb5e 100644 --- a/docs/models.rst +++ b/docs/models.rst @@ -81,3 +81,18 @@ Segformer ~~~~~~~~~ .. autoclass:: segmentation_models_pytorch.Segformer + +.. _dpt: + +DPT +~~~ + +.. note:: + + See full list of DPT-compatible timm encoders in :ref:`dpt-encoders`. + +.. note:: + + For some encoders, the model requires ``dynamic_img_size=True`` to be passed in order to work with resolutions different from what the encoder was trained for. + +.. autoclass:: segmentation_models_pytorch.DPT diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 7fc04dd7..e6627b83 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -53,7 +53,7 @@ You are done! Now you can train your model with your favorite framework, or as s for images, gt_masks in dataloader: - predicted_mask = model(image) + predicted_mask = model(images) loss = loss_fn(predicted_mask, gt_masks) loss.backward() diff --git a/docs/save_load.rst b/docs/save_load.rst index e90e4eba..15434eb6 100644 --- a/docs/save_load.rst +++ b/docs/save_load.rst @@ -40,6 +40,14 @@ For example: # Alternatively, load the model directly from the Hugging Face Hub model = smp.from_pretrained('username/my-model') +Loading pre-trained model with different number of classes for fine-tuning: + +.. code:: python + + import segmentation_models_pytorch as smp + + model = smp.from_pretrained('', classes=5, strict=False) + Saving model Metrics and Dataset Name ------------------------------------- diff --git a/examples/binary_segmentation_buildings.py b/examples/binary_segmentation_buildings.py new file mode 100644 index 00000000..33636477 --- /dev/null +++ b/examples/binary_segmentation_buildings.py @@ -0,0 +1,498 @@ +""" +This script demonstrates how to train a binary segmentation model using the +CamVid dataset and segmentation_models_pytorch. The CamVid dataset is a +collection of videos with pixel-level annotations for semantic segmentation. +The dataset includes 367 training images, 101 validation images, and 233 test. +Each training image has a corresponding mask that labels each pixel as belonging +to these classes with the numerical labels as follows: +- Sky: 0 +- Building: 1 +- Pole: 2 +- Road: 3 +- Pavement: 4 +- Tree: 5 +- SignSymbol: 6 +- Fence: 7 +- Car: 8 +- Pedestrian: 9 +- Bicyclist: 10 +- Unlabelled: 11 + +In this script, we focus on binary segmentation, where the goal is to classify +each pixel as whether belonging to a certain class (Foregorund) or +not (Background). + +Class Labels: +- 0: Background +- 1: Foreground + +The script includes the following steps: +1. Set the device to GPU if available, otherwise use CPU. +2. Download the CamVid dataset if it is not already present. +3. Define hyperparameters for training. +4. Define a custom dataset class for loading and preprocessing the CamVid + dataset. +5. Define a function to visualize images and masks. +6. Create datasets and dataloaders for training, validation, and testing. +7. Define a model class for the segmentation task. +8. Train the model using the training and validation datasets. +9. Evaluate the model using the test dataset and save the output masks and + metrics. +""" + +import logging +import os + +import cv2 +import matplotlib.pyplot as plt +import numpy as np +import torch +from torch.optim import lr_scheduler +from torch.utils.data import DataLoader +from torch.utils.data import Dataset as BaseDataset +from tqdm import tqdm + +import segmentation_models_pytorch as smp + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(message)s", + datefmt="%d:%m:%Y %H:%M:%S", +) + +# ---------------------------- +# Set the device to GPU if available +# ---------------------------- +device = "cuda" if torch.cuda.is_available() else "cpu" +logging.info(f"Using device: {device}") +if device == "cpu": + os.system("export OMP_NUM_THREADS=64") + torch.set_num_threads(os.cpu_count()) + +# ---------------------------- +# Download the CamVid dataset, if needed +# ---------------------------- +# Change this to your desired directory +main_dir = "./examples/binary_segmentation_data/" + +data_dir = os.path.join(main_dir, "dataset") +if not os.path.exists(data_dir): + logging.info("Loading data...") + os.system(f"git clone https://github.com/alexgkendall/SegNet-Tutorial {data_dir}") + logging.info("Done!") + +# Create a directory to store the output masks +output_dir = os.path.join(main_dir, "output_images") +os.makedirs(output_dir, exist_ok=True) + +# ---------------------------- +# Define the hyperparameters +# ---------------------------- +epochs_max = 200 # Number of epochs to train the model +adam_lr = 2e-4 # Learning rate for the Adam optimizer +eta_min = 1e-5 # Minimum learning rate for the scheduler +batch_size = 8 # Batch size for training +input_image_reshape = (320, 320) # Desired shape for the input images and masks +foreground_class = 1 # 1 for binary segmentation + + +# ---------------------------- +# Define a custom dataset class for the CamVid dataset +# ---------------------------- +class Dataset(BaseDataset): + """ + A custom dataset class for binary segmentation tasks. + + Parameters: + ---------- + + - images_dir (str): Directory containing the input images. + - masks_dir (str): Directory containing the corresponding masks. + - input_image_reshape (tuple, optional): Desired shape for the input + images and masks. Default is (320, 320). + - foreground_class (int, optional): The class value in the mask to be + considered as the foreground. Default is 1. + - augmentation (callable, optional): A function/transform to apply to the + images and masks for data augmentation. + """ + + def __init__( + self, + images_dir, + masks_dir, + input_image_reshape=(320, 320), + foreground_class=1, + augmentation=None, + ): + self.ids = os.listdir(images_dir) + self.images_filepaths = [ + os.path.join(images_dir, image_id) for image_id in self.ids + ] + self.masks_filepaths = [ + os.path.join(masks_dir, image_id) for image_id in self.ids + ] + + self.input_image_reshape = input_image_reshape + self.foreground_class = foreground_class + self.augmentation = augmentation + + def __getitem__(self, i): + """ + Retrieves the image and corresponding mask at index `i`. + + Parameters: + ---------- + + - i (int): Index of the image and mask to retrieve. + Returns: + - A tuple containing: + - image (torch.Tensor): The preprocessed image tensor of shape + (1, input_image_reshape) - e.g., (1, 320, 320) - normalized to [0, 1]. + - mask_remap (torch.Tensor): The preprocessed mask tensor of + shape input_image_reshape with values 0 or 1. + """ + # Read the image + image = cv2.imread( + self.images_filepaths[i], cv2.IMREAD_GRAYSCALE + ) # Read image as grayscale + image = np.expand_dims(image, axis=-1) # Add channel dimension + + # resize image to input_image_reshape + image = cv2.resize(image, self.input_image_reshape) + + # Read the mask in grayscale mode + mask = cv2.imread(self.masks_filepaths[i], 0) + + # Update the mask: Set foreground_class to 1 and the rest to 0 + mask_remap = np.where(mask == self.foreground_class, 1, 0).astype(np.uint8) + + # resize mask to input_image_reshape + mask_remap = cv2.resize(mask_remap, self.input_image_reshape) + + if self.augmentation: + sample = self.augmentation(image=image, mask=mask_remap) + image, mask_remap = sample["image"], sample["mask"] + + # Convert to PyTorch tensors + # Add channel dimension if missing + if image.ndim == 2: + image = np.expand_dims(image, axis=-1) + + # HWC -> CHW and normalize to [0, 1] + image = torch.tensor(image).float().permute(2, 0, 1) / 255.0 + + # Ensure mask is LongTensor + mask_remap = torch.tensor(mask_remap).long() + + return image, mask_remap + + def __len__(self): + return len(self.ids) + + +# Define a class for the CamVid model +class CamVidModel(torch.nn.Module): + """ + A PyTorch model for binary segmentation using the Segmentation Models + PyTorch library. + + Parameters: + ---------- + + - arch (str): The architecture name of the segmentation model + (e.g., 'Unet', 'FPN'). + - encoder_name (str): The name of the encoder to use + (e.g., 'resnet34', 'vgg16'). + - in_channels (int, optional): Number of input channels (e.g., 3 for RGB). + - out_classes (int, optional): Number of output classes (e.g., 1 for binary) + **kwargs: Additional keyword arguments to pass to the model + creation function. + """ + + def __init__(self, arch, encoder_name, in_channels=3, out_classes=1, **kwargs): + super().__init__() + self.mean = torch.tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1).to(device) + self.std = torch.tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1).to(device) + self.model = smp.create_model( + arch, + encoder_name=encoder_name, + in_channels=in_channels, + classes=out_classes, + **kwargs, + ) + + def forward(self, image): + # Normalize image + image = (image - self.mean) / self.std + mask = self.model(image) + return mask + + +def visualize(output_dir, image_filename, **images): + """PLot images in one row.""" + n = len(images) + plt.figure(figsize=(16, 5)) + for i, (name, image) in enumerate(images.items()): + plt.subplot(1, n, i + 1) + plt.xticks([]) + plt.yticks([]) + plt.title(" ".join(name.split("_")).title()) + plt.imshow(image) + plt.show() + plt.savefig(os.path.join(output_dir, image_filename)) + plt.close() + + +# Use multiple CPUs in parallel +def train_and_evaluate_one_epoch( + model, train_dataloader, valid_dataloader, optimizer, scheduler, loss_fn, device +): + # Set the model to training mode + model.train() + train_loss = 0 + for batch in tqdm(train_dataloader, desc="Training"): + images, masks = batch + images, masks = images.to(device), masks.to(device) + + optimizer.zero_grad() + outputs = model(images) + + loss = loss_fn(outputs, masks) + loss.backward() + optimizer.step() + + train_loss += loss.item() + + scheduler.step() + avg_train_loss = train_loss / len(train_dataloader) + + # Set the model to evaluation mode + model.eval() + val_loss = 0 + with torch.no_grad(): + for batch in tqdm(valid_dataloader, desc="Evaluating"): + images, masks = batch + images, masks = images.to(device), masks.to(device) + + outputs = model(images) + loss = loss_fn(outputs, masks) + + val_loss += loss.item() + + avg_val_loss = val_loss / len(valid_dataloader) + return avg_train_loss, avg_val_loss + + +def train_model( + model, + train_dataloader, + valid_dataloader, + optimizer, + scheduler, + loss_fn, + device, + epochs, +): + train_losses = [] + val_losses = [] + + for epoch in range(epochs): + avg_train_loss, avg_val_loss = train_and_evaluate_one_epoch( + model, + train_dataloader, + valid_dataloader, + optimizer, + scheduler, + loss_fn, + device, + ) + train_losses.append(avg_train_loss) + val_losses.append(avg_val_loss) + + logging.info( + f"Epoch {epoch + 1}/{epochs}, Training Loss: {avg_train_loss:.2f}, Validation Loss: {avg_val_loss:.2f}" + ) + + history = { + "train_losses": train_losses, + "val_losses": val_losses, + } + return history + + +def test_model(model, output_dir, test_dataloader, loss_fn, device): + # Set the model to evaluation mode + model.eval() + test_loss = 0 + tp, fp, fn, tn = 0, 0, 0, 0 + with torch.no_grad(): + for batch in tqdm(test_dataloader, desc="Evaluating"): + images, masks = batch + images, masks = images.to(device), masks.to(device) + + outputs = model(images) + loss = loss_fn(outputs, masks) + + for i, output in enumerate(outputs): + input = images[i].cpu().numpy().transpose(1, 2, 0) + output = output.squeeze().cpu().numpy() + + visualize( + output_dir, + f"output_{i}.png", + input_image=input, + output_mask=output, + binary_mask=output > 0.5, + ) + + test_loss += loss.item() + + prob_mask = outputs.sigmoid().squeeze(1) + pred_mask = (prob_mask > 0.5).long() + batch_tp, batch_fp, batch_fn, batch_tn = smp.metrics.get_stats( + pred_mask, masks, mode="binary" + ) + tp += batch_tp.sum().item() + fp += batch_fp.sum().item() + fn += batch_fn.sum().item() + tn += batch_tn.sum().item() + + test_loss_mean = test_loss / len(test_dataloader) + logging.info(f"Test Loss: {test_loss_mean:.2f}") + + iou_score = smp.metrics.iou_score( + torch.tensor([tp]), + torch.tensor([fp]), + torch.tensor([fn]), + torch.tensor([tn]), + reduction="micro", + ) + + return test_loss_mean, iou_score.item() + + +# ---------------------------- +# Define the data directories and create the datasets +# ---------------------------- +x_train_dir = os.path.join(data_dir, "CamVid", "train") +y_train_dir = os.path.join(data_dir, "CamVid", "trainannot") + +x_val_dir = os.path.join(data_dir, "CamVid", "val") +y_val_dir = os.path.join(data_dir, "CamVid", "valannot") + +x_test_dir = os.path.join(data_dir, "CamVid", "test") +y_test_dir = os.path.join(data_dir, "CamVid", "testannot") + +train_dataset = Dataset( + x_train_dir, + y_train_dir, + input_image_reshape=input_image_reshape, + foreground_class=foreground_class, +) +valid_dataset = Dataset( + x_val_dir, + y_val_dir, + input_image_reshape=input_image_reshape, + foreground_class=foreground_class, +) +test_dataset = Dataset( + x_test_dir, + y_test_dir, + input_image_reshape=input_image_reshape, + foreground_class=foreground_class, +) + +image, mask = train_dataset[0] +logging.info(f"Unique values in mask: {np.unique(mask)}") +logging.info(f"Image shape: {image.shape}") +logging.info(f"Mask shape: {mask.shape}") + +# ---------------------------- +# Create the dataloaders using the datasets +# ---------------------------- +logging.info(f"Train size: {len(train_dataset)}") +logging.info(f"Valid size: {len(valid_dataset)}") +logging.info(f"Test size: {len(test_dataset)}") + +train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True) +valid_dataloader = DataLoader(valid_dataset, batch_size=batch_size, shuffle=False) +test_dataloader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False) + +# ---------------------------- +# Lets look at some samples +# ---------------------------- +# Visualize and save train sample +sample = train_dataset[0] +visualize( + output_dir, + "train_sample.png", + train_image=sample[0].numpy().transpose(1, 2, 0), + train_mask=sample[1].squeeze(), +) + +# Visualize and save validation sample +sample = valid_dataset[0] +visualize( + output_dir, + "validation_sample.png", + validation_image=sample[0].numpy().transpose(1, 2, 0), + validation_mask=sample[1].squeeze(), +) + +# Visualize and save test sample +sample = test_dataset[0] +visualize( + output_dir, + "test_sample.png", + test_image=sample[0].numpy().transpose(1, 2, 0), + test_mask=sample[1].squeeze(), +) + +# ---------------------------- +# Create and train the model +# ---------------------------- +max_iter = epochs_max * len(train_dataloader) # Total number of iterations + +model = CamVidModel("Unet", "resnet34", in_channels=3, out_classes=1) + +# Training loop +model = model.to(device) + +# Define the Adam optimizer +optimizer = torch.optim.Adam(model.parameters(), lr=adam_lr) + +# Define the learning rate scheduler +scheduler = lr_scheduler.CosineAnnealingLR(optimizer, T_max=max_iter, eta_min=eta_min) + +# Define the loss function +loss_fn = smp.losses.DiceLoss(smp.losses.BINARY_MODE, from_logits=True) + +# Train the model +history = train_model( + model, + train_dataloader, + valid_dataloader, + optimizer, + scheduler, + loss_fn, + device, + epochs_max, +) + +# Visualize the training and validation losses +plt.figure(figsize=(10, 5)) +plt.plot(history["train_losses"], label="Train Loss") +plt.plot(history["val_losses"], label="Validation Loss") +plt.xlabel("Epochs") +plt.ylabel("Loss") +plt.title("Training and Validation Losses") +plt.legend() +plt.savefig(os.path.join(output_dir, "train_val_losses.png")) +plt.close() + + +# Evaluate the model +test_loss = test_model(model, output_dir, test_dataloader, loss_fn, device) + +logging.info(f"Test Loss: {test_loss[0]}, IoU Score: {test_loss[1]}") +logging.info(f"The output masks are saved in {output_dir}.") diff --git a/examples/dpt_inference_pretrained.ipynb b/examples/dpt_inference_pretrained.ipynb new file mode 100644 index 00000000..adfb5a15 --- /dev/null +++ b/examples/dpt_inference_pretrained.ipynb @@ -0,0 +1,138 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/qubvel/segmentation_models.pytorch/blob/main/examples/dpt_inference_pretrained.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# make sure you have the latest version of the libraries\n", + "!pip install -U segmentation-models-pytorch\n", + "!pip install albumentations matplotlib requests pillow" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "import numpy as np\n", + "import albumentations as A\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import torch\n", + "import segmentation_models_pytorch as smp\n", + "\n", + "from PIL import Image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading weights from local directory\n" + ] + } + ], + "source": [ + "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + "\n", + "# More checkpoints can be found here:\n", + "checkpoint = \"smp-hub/dpt-large-ade20k\"\n", + "\n", + "# Load pretrained model and preprocessing function\n", + "model = smp.from_pretrained(checkpoint).eval().to(device)\n", + "preprocessing = A.Compose.from_pretrained(checkpoint)\n", + "\n", + "# Load image\n", + "url = \"https://huggingface.co/datasets/hf-internal-testing/fixtures_ade20k/resolve/main/ADE_val_00000001.jpg\"\n", + "image = Image.open(requests.get(url, stream=True).raw)\n", + "\n", + "# Preprocess image\n", + "image = np.array(image)\n", + "normalized_image = preprocessing(image=image)[\"image\"]\n", + "input_tensor = torch.as_tensor(normalized_image)\n", + "input_tensor = input_tensor.permute(2, 0, 1).unsqueeze(0) # HWC -> BCHW\n", + "input_tensor = input_tensor.to(device)\n", + "\n", + "# Perform inference\n", + "with torch.no_grad():\n", + " output_mask = model(input_tensor)\n", + "\n", + "# Postprocess mask\n", + "mask = torch.nn.functional.interpolate(\n", + " output_mask, size=image.shape[:2], mode=\"bilinear\", align_corners=False\n", + ")\n", + "mask = mask[0].argmax(0).cpu().numpy()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot results\n", + "plt.figure(figsize=(12, 6))\n", + "\n", + "plt.subplot(121)\n", + "plt.axis(\"off\")\n", + "plt.imshow(image)\n", + "plt.title(\"Input Image\")\n", + "\n", + "plt.subplot(122)\n", + "plt.axis(\"off\")\n", + "plt.imshow(mask)\n", + "plt.title(\"Output Mask\")\n", + "\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/segformer_inference_pretrained.ipynb b/examples/segformer_inference_pretrained.ipynb index a0dda7d4..d2d195fd 100644 --- a/examples/segformer_inference_pretrained.ipynb +++ b/examples/segformer_inference_pretrained.ipynb @@ -13,9 +13,9 @@ "metadata": {}, "outputs": [], "source": [ - "# fix for HF hub download\n", - "# see PR https://github.com/albumentations-team/albumentations/pull/2171\n", - "!pip install -U git+https://github.com/qubvel/albumentations@patch-2" + "# make sure you have the latest version of the libraries\n", + "!pip install -U segmentation-models-pytorch\n", + "!pip install albumentations matplotlib requests pillow" ] }, { diff --git a/examples/upernet_inference_pretrained.ipynb b/examples/upernet_inference_pretrained.ipynb new file mode 100644 index 00000000..aa644858 --- /dev/null +++ b/examples/upernet_inference_pretrained.ipynb @@ -0,0 +1,153 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/qubvel/segmentation_models.pytorch/blob/main/examples/upernet_inference_pretrained.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# make sure you have the latest version of the libraries\n", + "!pip install -U segmentation-models-pytorch\n", + "!pip install albumentations matplotlib requests pillow" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/ubuntu/projects/segmentation_models.pytorch/.venv/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], + "source": [ + "import requests\n", + "import numpy as np\n", + "import albumentations as A\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import torch\n", + "import segmentation_models_pytorch as smp\n", + "\n", + "from PIL import Image" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Preprocessing:\n", + " Compose([\n", + " Resize(p=1.0, height=512, width=512, interpolation=1, mask_interpolation=0),\n", + " Normalize(p=1.0, mean=(123.675, 116.28, 103.53), std=(58.395, 57.12, 57.375), max_pixel_value=1.0, normalization='standard'),\n", + "], p=1.0, bbox_params=None, keypoint_params=None, additional_targets={}, is_check_shapes=True)\n" + ] + } + ], + "source": [ + "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + "\n", + "# More checkpoints can be found here:\n", + "# https://huggingface.co/collections/smp-hub/upernet-67fadcdbe08418c6ea94f768\n", + "checkpoint = \"smp-hub/upernet-swin-tiny\"\n", + "\n", + "# Load pretrained model and preprocessing function\n", + "model = smp.from_pretrained(checkpoint).eval().to(device)\n", + "preprocessing = A.Compose.from_pretrained(checkpoint)\n", + "print(\"Preprocessing:\\n\", preprocessing)\n", + "\n", + "# Load image\n", + "url = \"https://huggingface.co/datasets/hf-internal-testing/fixtures_ade20k/resolve/main/ADE_val_00000001.jpg\"\n", + "image = Image.open(requests.get(url, stream=True).raw)\n", + "\n", + "# Preprocess image\n", + "image = np.array(image)\n", + "normalized_image = preprocessing(image=image)[\"image\"]\n", + "input_tensor = torch.as_tensor(normalized_image)\n", + "input_tensor = input_tensor.permute(2, 0, 1).unsqueeze(0) # HWC -> BCHW\n", + "input_tensor = input_tensor.to(device)\n", + "\n", + "# Perform inference\n", + "with torch.no_grad():\n", + " output_mask = model(input_tensor)\n", + "\n", + "# Postprocess mask\n", + "mask = torch.nn.functional.interpolate(\n", + " output_mask, size=image.shape[:2], mode=\"bilinear\", align_corners=False\n", + ")\n", + "mask = mask[0].argmax(0).cpu().numpy()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot results\n", + "plt.figure(figsize=(12, 6))\n", + "\n", + "plt.subplot(121)\n", + "plt.axis(\"off\")\n", + "plt.imshow(image)\n", + "plt.title(\"Input Image\")\n", + "\n", + "plt.subplot(122)\n", + "plt.axis(\"off\")\n", + "plt.imshow(mask)\n", + "plt.title(\"Output Mask\")\n", + "\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/licenses/LICENSES.md b/licenses/LICENSES.md index 670764a2..e51ad8d0 100644 --- a/licenses/LICENSES.md +++ b/licenses/LICENSES.md @@ -13,13 +13,20 @@ The majority of the code is licensed under the [MIT License](LICENSE). However, * [segmentation_models_pytorch/encoders/mix_transformer.py](https://github.com/qubvel/segmentation_models.pytorch/blob/main/segmentation_models_pytorch/encoders/mix_transformer.py) * [LICENSE_nvidia](LICENSE_nvidia.md) - - Apple License * Applies to the MobileOne encoder * [segmentation_models_pytorch/encoders/mobileone.py](https://github.com/qubvel/segmentation_models.pytorch/blob/main/segmentation_models_pytorch/encoders/mobileone.py) * [LICENSE_apple](LICENSE_apple.md) - BSD 3-Clause License - * Applies to the DeepLabV3 decoder + * Applies to several encoders and the DeepLabV3 decoder + * [segmentation_models_pytorch/encoders/_dpn.py](https://github.com/qubvel/segmentation_models.pytorch/blob/main/segmentation_models_pytorch/encoders/_dpn.py) + * [segmentation_models_pytorch/encoders/_inceptionresnetv2.py](https://github.com/qubvel/segmentation_models.pytorch/blob/main/segmentation_models_pytorch/encoders/_inceptionresnetv2.py) + * [segmentation_models_pytorch/encoders/_inceptionv4.py](https://github.com/qubvel/segmentation_models.pytorch/blob/main/segmentation_models_pytorch/encoders/_inceptionv4.py) + * [segmentation_models_pytorch/encoders/_senet.py](https://github.com/qubvel/segmentation_models.pytorch/blob/main/segmentation_models_pytorch/encoders/_senet.py) + * [segmentation_models_pytorch/encoders/_xception.py](https://github.com/qubvel/segmentation_models.pytorch/blob/main/segmentation_models_pytorch/encoders/_xception.py) * [segmentation_models_pytorch/decoders/deeplabv3/decoder.py](https://github.com/qubvel/segmentation_models.pytorch/blob/main/segmentation_models_pytorch/decoders/deeplabv3/decoder.py) +- Apache-2.0 License + * Applies to the EfficientNet encoder + * [segmentation_models_pytorch/encoders/_efficientnet.py](https://github.com/qubvel/segmentation_models.pytorch/blob/main/segmentation_models_pytorch/encoders/_efficientnet.py) diff --git a/licenses/LICENSE_apache.md b/licenses/LICENSE_apache.md new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/licenses/LICENSE_apache.md @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://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. diff --git a/misc/generate_table.py b/misc/generate_table.py index f14b1a3c..4e0efed5 100644 --- a/misc/generate_table.py +++ b/misc/generate_table.py @@ -1,10 +1,17 @@ +import os import segmentation_models_pytorch as smp +from tqdm import tqdm + encoders = smp.encoders.encoders WIDTH = 32 -COLUMNS = ["Encoder", "Weights", "Params, M"] +COLUMNS = ["Encoder", "Pretrained weights", "Params, M", "Script", "Compile", "Export"] +FILE = "encoders_table.md" + +if os.path.exists(FILE): + os.remove(FILE) def wrap_row(r): @@ -16,18 +23,23 @@ def wrap_row(r): ["-" * WIDTH] + [":" + "-" * (WIDTH - 2) + ":"] * (len(COLUMNS) - 1) ) -print(wrap_row(header)) -print(wrap_row(separator)) +print(wrap_row(header), file=open(FILE, "a")) +print(wrap_row(separator), file=open(FILE, "a")) -for encoder_name, encoder in encoders.items(): +for encoder_name, encoder in tqdm(encoders.items()): weights = "
".join(encoder["pretrained_settings"].keys()) - encoder_name = encoder_name.ljust(WIDTH, " ") - weights = weights.ljust(WIDTH, " ") model = encoder["encoder"](**encoder["params"], depth=5) + + script = "βœ…" if model._is_torch_scriptable else "❌" + compile = "βœ…" if model._is_torch_compilable else "❌" + export = "βœ…" if model._is_torch_exportable else "❌" + params = sum(p.numel() for p in model.parameters()) params = str(params // 1000000) + "M" - params = params.ljust(WIDTH, " ") - row = "|".join([encoder_name, weights, params]) - print(wrap_row(row)) + row = [encoder_name, weights, params, script, compile, export] + row = [str(r).ljust(WIDTH, " ") for r in row] + row = "|".join(row) + + print(wrap_row(row), file=open(FILE, "a")) diff --git a/misc/generate_table_timm.py b/misc/generate_table_timm.py index 6c2a1b24..8e875583 100644 --- a/misc/generate_table_timm.py +++ b/misc/generate_table_timm.py @@ -17,30 +17,68 @@ def has_dilation_support(name): return False +def valid_vit_encoder_for_dpt(name): + if "vit" not in name: + return False + encoder = timm.create_model(name) + feature_info = encoder.feature_info + feature_info_obj = timm.models.FeatureInfo( + feature_info=feature_info, out_indices=[0, 1, 2, 3] + ) + reduction_scales = list(feature_info_obj.reduction()) + + if len(set(reduction_scales)) > 1: + return False + + output_stride = reduction_scales[0] + if bin(output_stride).count("1") != 1: + return False + + return True + + def make_table(data): names = data.keys() max_len1 = max([len(x) for x in names]) + 2 max_len2 = len("support dilation") + 2 + max_len3 = len("Supported for DPT") + 2 - l1 = "+" + "-" * max_len1 + "+" + "-" * max_len2 + "+\n" - l2 = "+" + "=" * max_len1 + "+" + "=" * max_len2 + "+\n" + l1 = "+" + "-" * max_len1 + "+" + "-" * max_len2 + "+" + "-" * max_len3 + "+\n" + l2 = "+" + "=" * max_len1 + "+" + "=" * max_len2 + "+" + "-" * max_len3 + "+\n" top = ( "| " + "Encoder name".ljust(max_len1 - 2) + " | " + "Support dilation".center(max_len2 - 2) + + " | " + + "Supported for DPT".center(max_len3 - 2) + " |\n" ) table = l1 + top + l2 for k in sorted(data.keys()): - support = ( - "βœ…".center(max_len2 - 3) - if data[k]["has_dilation"] - else " ".center(max_len2 - 2) + if "has_dilation" in data[k] and data[k]["has_dilation"]: + support = "βœ…".center(max_len2 - 3) + + else: + support = " ".center(max_len2 - 2) + + if "supported_only_for_dpt" in data[k]: + supported_for_dpt = "βœ…".center(max_len3 - 3) + + else: + supported_for_dpt = " ".center(max_len3 - 2) + + table += ( + "| " + + k.ljust(max_len1 - 2) + + " | " + + support + + " | " + + supported_for_dpt + + " |\n" ) - table += "| " + k.ljust(max_len1 - 2) + " | " + support + " |\n" table += l1 return table @@ -55,8 +93,13 @@ def make_table(data): check_features_and_reduction(name) has_dilation = has_dilation_support(name) supported_models[name] = dict(has_dilation=has_dilation) + except Exception: - continue + try: + if valid_vit_encoder_for_dpt(name): + supported_models[name] = dict(supported_only_for_dpt=True) + except Exception: + continue table = make_table(supported_models) print(table) diff --git a/misc/generate_test_models.py b/misc/generate_test_models.py index 61d6bfd0..a26cbc66 100644 --- a/misc/generate_test_models.py +++ b/misc/generate_test_models.py @@ -9,33 +9,50 @@ api = huggingface_hub.HfApi(token=os.getenv("HF_TOKEN")) -for model_name, model_class in smp.MODEL_ARCHITECTURES_MAPPING.items(): - model = model_class(encoder_name=ENCODER_NAME) - model = model.eval() - - # generate test sample - torch.manual_seed(423553) - sample = torch.rand(1, 3, 256, 256) - - with torch.no_grad(): - output = model(sample) +def save_and_push(model, inputs, outputs, model_name, encoder_name): with tempfile.TemporaryDirectory() as tmpdir: # save model model.save_pretrained(f"{tmpdir}") # save input and output - torch.save(sample, f"{tmpdir}/input-tensor.pth") - torch.save(output, f"{tmpdir}/output-tensor.pth") + torch.save(inputs, f"{tmpdir}/input-tensor.pth") + torch.save(outputs, f"{tmpdir}/output-tensor.pth") # create repo - repo_id = f"{HUB_REPO}/{model_name}-{ENCODER_NAME}" + repo_id = f"{HUB_REPO}/{model_name}-{encoder_name}" if not api.repo_exists(repo_id=repo_id): api.create_repo(repo_id=repo_id, repo_type="model") # upload to hub api.upload_folder( folder_path=tmpdir, - repo_id=f"{HUB_REPO}/{model_name}-{ENCODER_NAME}", + repo_id=f"{HUB_REPO}/{model_name}-{encoder_name}", repo_type="model", ) + + +for model_name, model_class in smp.MODEL_ARCHITECTURES_MAPPING.items(): + if model_name == "dpt": + encoder_name = "tu-test_vit" + model = smp.DPT( + encoder_name=encoder_name, + decoder_readout="cat", + decoder_intermediate_channels=(16, 32, 64, 64), + decoder_fusion_channels=16, + dynamic_img_size=True, + ) + else: + encoder_name = ENCODER_NAME + model = model_class(encoder_name=encoder_name) + + model = model.eval() + + # generate test sample + torch.manual_seed(423553) + sample = torch.rand(1, 3, 256, 256) + + with torch.no_grad(): + output = model(sample) + + save_and_push(model, sample, output, model_name, encoder_name) diff --git a/pyproject.toml b/pyproject.toml index 0e9310b5..f3e55a96 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,12 +17,10 @@ classifiers = [ 'Programming Language :: Python :: Implementation :: PyPy', ] dependencies = [ - 'efficientnet-pytorch>=0.6.1', 'huggingface-hub>=0.24', 'numpy>=1.19.3', 'pillow>=8', - 'pretrainedmodels>=0.7.1', - 'six>=1.5', + 'safetensors>=0.3.1', 'timm>=0.9', 'torch>=1.8', 'torchvision>=0.9', @@ -39,11 +37,13 @@ docs = [ 'sphinx-book-theme', ] test = [ + 'gitpython', 'packaging', 'pytest', 'pytest-cov', 'pytest-xdist', - 'ruff', + 'ruff>=0.9', + 'setuptools', ] [project.urls] @@ -61,18 +61,10 @@ include = ['segmentation_models_pytorch*'] [tool.pytest.ini_options] markers = [ - "deeplabv3", - "deeplabv3plus", - "fpn", - "linknet", - "manet", - "pan", - "psp", - "segformer", - "unet", - "unetplusplus", - "upernet", "logits_match", + "compile", + "torch_export", + "torch_script", ] [tool.coverage.run] diff --git a/requirements/docs.txt b/requirements/docs.txt index 072a7e16..26afc33e 100644 --- a/requirements/docs.txt +++ b/requirements/docs.txt @@ -1,5 +1,5 @@ autodocsumm==0.2.14 -huggingface-hub==0.27.1 +huggingface-hub==0.30.2 six==1.17.0 -sphinx==8.1.3 -sphinx-book-theme==1.1.3 +sphinx==8.2.3 +sphinx-book-theme==1.1.4 diff --git a/requirements/minimum.old b/requirements/minimum.old index 1080bdb4..678f83f4 100644 --- a/requirements/minimum.old +++ b/requirements/minimum.old @@ -1,9 +1,7 @@ -efficientnet-pytorch==0.6.1 huggingface-hub==0.24.0 numpy==1.19.3 pillow==8.0.0 -pretrainedmodels==0.7.1 -six==1.5.0 +safetensors==0.3.1 timm==0.9.0 torch==1.9.0 torchvision==0.10.0 diff --git a/requirements/required.txt b/requirements/required.txt index e04033b5..bdb4d9e3 100644 --- a/requirements/required.txt +++ b/requirements/required.txt @@ -1,10 +1,8 @@ -efficientnet-pytorch==0.7.1 -huggingface_hub==0.27.1 -numpy==2.2.1 -pillow==11.1.0 -pretrainedmodels==0.7.4 -six==1.17.0 -timm==1.0.12 -torch==2.5.1 -torchvision==0.20.1 +huggingface_hub==0.30.2 +numpy==2.2.4 +pillow==11.2.1 +safetensors==0.5.3 +timm==1.0.15 +torch==2.6.0 +torchvision==0.21.0 tqdm==4.67.1 diff --git a/requirements/test.txt b/requirements/test.txt index 5f27affd..23f6025c 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,5 +1,7 @@ +gitpython==3.1.44 packaging==24.2 -pytest==8.3.4 +pytest==8.3.5 pytest-xdist==3.6.1 -pytest-cov==6.0.0 -ruff==0.8.6 +pytest-cov==6.1.1 +ruff==0.11.5 +setuptools==78.1.0 \ No newline at end of file diff --git a/scripts/models-conversions/dpt-original-to-smp.py b/scripts/models-conversions/dpt-original-to-smp.py new file mode 100644 index 00000000..fab1705d --- /dev/null +++ b/scripts/models-conversions/dpt-original-to-smp.py @@ -0,0 +1,122 @@ +import cv2 +import torch +import albumentations as A +import segmentation_models_pytorch as smp + +MODEL_WEIGHTS_PATH = r"dpt_large-ade20k-b12dca68.pt" +HF_HUB_PATH = "qubvel-hf/dpt-large-ade20k" +PUSH_TO_HUB = False + + +def get_transform(): + return A.Compose( + [ + A.LongestMaxSize(max_size=480, interpolation=cv2.INTER_CUBIC), + A.Normalize( + mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5), max_pixel_value=255.0 + ), + # This is not correct transform, ideally image should resized without padding to multiple of 32, + # but we take there is no such transform in albumentations, here is closest one + A.PadIfNeeded( + min_height=None, + min_width=None, + pad_height_divisor=32, + pad_width_divisor=32, + border_mode=cv2.BORDER_CONSTANT, + value=0, + p=1, + ), + ] + ) + + +if __name__ == "__main__": + # fmt: off + smp_model = smp.DPT(encoder_name="tu-vit_large_patch16_384", classes=150, dynamic_img_size=True) + dpt_model_dict = torch.load(MODEL_WEIGHTS_PATH, weights_only=True) + + for layer_index in range(0, 4): + for param in ["running_mean", "running_var", "num_batches_tracked", "weight", "bias"]: + for block_index in [1, 2]: + for bn_index in [1, 2]: + # Assigning weights of 4th fusion layer of original model to 1st layer of SMP DPT model, + # Assigning weights of 3rd fusion layer of original model to 2nd layer of SMP DPT model ... + # and so on ... + # This is because order of calling fusion layers is reversed in original DPT implementation + dpt_model_dict[f"decoder.fusion_blocks.{layer_index}.residual_conv_block{block_index}.batch_norm_{bn_index}.{param}"] = \ + dpt_model_dict.pop(f"scratch.refinenet{4 - layer_index}.resConfUnit{block_index}.bn{bn_index}.{param}") + + if param in ["weight", "bias"]: + if param == "weight": + for block_index in [1, 2]: + for conv_index in [1, 2]: + dpt_model_dict[f"decoder.fusion_blocks.{layer_index}.residual_conv_block{block_index}.conv_{conv_index}.{param}"] = \ + dpt_model_dict.pop(f"scratch.refinenet{4 - layer_index}.resConfUnit{block_index}.conv{conv_index}.{param}") + + dpt_model_dict[f"decoder.reassemble_blocks.{layer_index}.project_to_feature_dim.{param}"] = \ + dpt_model_dict.pop(f"scratch.layer{layer_index + 1}_rn.{param}") + + dpt_model_dict[f"decoder.fusion_blocks.{layer_index}.project.{param}"] = \ + dpt_model_dict.pop(f"scratch.refinenet{4 - layer_index}.out_conv.{param}") + + dpt_model_dict[f"decoder.projection_blocks.{layer_index}.project.0.{param}"] = \ + dpt_model_dict.pop(f"pretrained.act_postprocess{layer_index + 1}.0.project.0.{param}") + + dpt_model_dict[f"decoder.reassemble_blocks.{layer_index}.project_to_out_channel.{param}"] = \ + dpt_model_dict.pop(f"pretrained.act_postprocess{layer_index + 1}.3.{param}") + + if layer_index != 2: + dpt_model_dict[f"decoder.reassemble_blocks.{layer_index}.upsample.{param}"] = \ + dpt_model_dict.pop(f"pretrained.act_postprocess{layer_index + 1}.4.{param}") + + # Changing state dict keys for segmentation head + dpt_model_dict = { + name.replace("scratch.output_conv", "segmentation_head.head"): parameter + for name, parameter in dpt_model_dict.items() + } + + # Changing state dict keys for encoder layers + dpt_model_dict = { + name.replace("pretrained.model", "encoder.model"): parameter + for name, parameter in dpt_model_dict.items() + } + + # Removing keys, value pairs associated with auxiliary head + dpt_model_dict = { + name: parameter + for name, parameter in dpt_model_dict.items() + if not name.startswith("auxlayer") + } + # fmt: on + + smp_model.load_state_dict(dpt_model_dict, strict=True) + + # ------- DO NOT touch this section ------- + smp_model.eval() + + input_tensor = torch.ones((1, 3, 384, 384)) + output = smp_model(input_tensor) + + print(output.shape) + print(output[0, 0, :3, :3]) + + expected_slice = torch.tensor( + [ + [3.4243, 3.4553, 3.4863], + [3.3332, 3.2876, 3.2419], + [3.2422, 3.1199, 2.9975], + ] + ) + + torch.testing.assert_close( + output[0, 0, :3, :3], expected_slice, atol=1e-4, rtol=1e-4 + ) + + # Saving + transform = get_transform() + + transform.save_pretrained(HF_HUB_PATH) + smp_model.save_pretrained(HF_HUB_PATH, push_to_hub=PUSH_TO_HUB) + + # Re-loading to make sure everything is saved correctly + smp_model = smp.from_pretrained(HF_HUB_PATH) diff --git a/scripts/models-conversions/upernet-hf-to-smp.py b/scripts/models-conversions/upernet-hf-to-smp.py new file mode 100644 index 00000000..8cd3162f --- /dev/null +++ b/scripts/models-conversions/upernet-hf-to-smp.py @@ -0,0 +1,249 @@ +import re +import torch +import albumentations as A +import segmentation_models_pytorch as smp +from huggingface_hub import hf_hub_download, HfApi +from collections import defaultdict + +DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") + +# fmt: off +CONVNEXT_MAPPING = { + r"backbone.embeddings.patch_embeddings.(weight|bias)": r"encoder.model.stem_0.\1", + r"backbone.embeddings.layernorm.(weight|bias)": r"encoder.model.stem_1.\1", + r"backbone.encoder.stages.(\d+).layers.(\d+).layer_scale_parameter": r"encoder.model.stages_\1.blocks.\2.gamma", + r"backbone.encoder.stages.(\d+).layers.(\d+).dwconv.(weight|bias)": r"encoder.model.stages_\1.blocks.\2.conv_dw.\3", + r"backbone.encoder.stages.(\d+).layers.(\d+).layernorm.(weight|bias)": r"encoder.model.stages_\1.blocks.\2.norm.\3", + r"backbone.encoder.stages.(\d+).layers.(\d+).pwconv(\d+).(weight|bias)": r"encoder.model.stages_\1.blocks.\2.mlp.fc\3.\4", + r"backbone.encoder.stages.(\d+).downsampling_layer.(\d+).(weight|bias)": r"encoder.model.stages_\1.downsample.\2.\3", +} + +SWIN_MAPPING = { + r"backbone.embeddings.patch_embeddings.projection": r"encoder.model.patch_embed.proj", + r"backbone.embeddings.norm": r"encoder.model.patch_embed.norm", + r"backbone.encoder.layers.(\d+).blocks.(\d+).layernorm_before": r"encoder.model.layers_\1.blocks.\2.norm1", + r"backbone.encoder.layers.(\d+).blocks.(\d+).attention.self.relative_position_bias_table": r"encoder.model.layers_\1.blocks.\2.attn.relative_position_bias_table", + r"backbone.encoder.layers.(\d+).blocks.(\d+).attention.self.(query|key|value)": r"encoder.model.layers_\1.blocks.\2.attn.\3", + r"backbone.encoder.layers.(\d+).blocks.(\d+).attention.output.dense": r"encoder.model.layers_\1.blocks.\2.attn.proj", + r"backbone.encoder.layers.(\d+).blocks.(\d+).layernorm_after": r"encoder.model.layers_\1.blocks.\2.norm2", + r"backbone.encoder.layers.(\d+).blocks.(\d+).intermediate.dense": r"encoder.model.layers_\1.blocks.\2.mlp.fc1", + r"backbone.encoder.layers.(\d+).blocks.(\d+).output.dense": r"encoder.model.layers_\1.blocks.\2.mlp.fc2", + r"backbone.encoder.layers.(\d+).downsample.reduction": lambda x: f"encoder.model.layers_{1 + int(x.group(1))}.downsample.reduction", + r"backbone.encoder.layers.(\d+).downsample.norm": lambda x: f"encoder.model.layers_{1 + int(x.group(1))}.downsample.norm", +} + +DECODER_MAPPING = { + + # started from 1 in hf + r"backbone.hidden_states_norms.stage(\d+)": lambda x: f"decoder.feature_norms.{int(x.group(1)) - 1}", + + r"decode_head.psp_modules.(\d+).(\d+).conv.weight": r"decoder.psp.blocks.\1.\2.0.weight", + r"decode_head.psp_modules.(\d+).(\d+).batch_norm": r"decoder.psp.blocks.\1.\2.1", + r"decode_head.bottleneck.conv.weight": r"decoder.psp.out_conv.0.weight", + r"decode_head.bottleneck.batch_norm": r"decoder.psp.out_conv.1", + + # fpn blocks are in reverse order (3 blocks total, so 2 - i) + r"decode_head.lateral_convs.(\d+).conv.weight": lambda x: f"decoder.fpn_lateral_blocks.{2 - int(x.group(1))}.conv_norm_relu.0.weight", + r"decode_head.lateral_convs.(\d+).batch_norm": lambda x: f"decoder.fpn_lateral_blocks.{2 - int(x.group(1))}.conv_norm_relu.1", + r"decode_head.fpn_convs.(\d+).conv.weight": lambda x: f"decoder.fpn_conv_blocks.{2 - int(x.group(1))}.0.weight", + r"decode_head.fpn_convs.(\d+).batch_norm": lambda x: f"decoder.fpn_conv_blocks.{2 - int(x.group(1))}.1", + + r"decode_head.fpn_bottleneck.conv.weight": r"decoder.fusion_block.0.weight", + r"decode_head.fpn_bottleneck.batch_norm": r"decoder.fusion_block.1", + r"decode_head.classifier": r"segmentation_head.0", +} +# fmt: on + +PRETRAINED_CHECKPOINTS = { + "convnext-tiny": { + "repo_id": "openmmlab/upernet-convnext-tiny", + "encoder_name": "tu-convnext_tiny", + "decoder_channels": 512, + "classes": 150, + "mapping": {**CONVNEXT_MAPPING, **DECODER_MAPPING}, + }, + "convnext-small": { + "repo_id": "openmmlab/upernet-convnext-small", + "encoder_name": "tu-convnext_small", + "decoder_channels": 512, + "classes": 150, + "mapping": {**CONVNEXT_MAPPING, **DECODER_MAPPING}, + }, + "convnext-base": { + "repo_id": "openmmlab/upernet-convnext-base", + "encoder_name": "tu-convnext_base", + "decoder_channels": 512, + "classes": 150, + "mapping": {**CONVNEXT_MAPPING, **DECODER_MAPPING}, + }, + "convnext-large": { + "repo_id": "openmmlab/upernet-convnext-large", + "encoder_name": "tu-convnext_large", + "decoder_channels": 512, + "classes": 150, + "mapping": {**CONVNEXT_MAPPING, **DECODER_MAPPING}, + }, + "convnext-xlarge": { + "repo_id": "openmmlab/upernet-convnext-xlarge", + "encoder_name": "tu-convnext_xlarge", + "decoder_channels": 512, + "classes": 150, + "mapping": {**CONVNEXT_MAPPING, **DECODER_MAPPING}, + }, + "swin-tiny": { + "repo_id": "openmmlab/upernet-swin-tiny", + "encoder_name": "tu-swin_tiny_patch4_window7_224", + "decoder_channels": 512, + "classes": 150, + "extra_kwargs": {"img_size": 512}, + "mapping": {**SWIN_MAPPING, **DECODER_MAPPING}, + }, + "swin-small": { + "repo_id": "openmmlab/upernet-swin-small", + "encoder_name": "tu-swin_small_patch4_window7_224", + "decoder_channels": 512, + "classes": 150, + "extra_kwargs": {"img_size": 512}, + "mapping": {**SWIN_MAPPING, **DECODER_MAPPING}, + }, + "swin-large": { + "repo_id": "openmmlab/upernet-swin-large", + "encoder_name": "tu-swin_large_patch4_window12_384", + "decoder_channels": 512, + "classes": 150, + "extra_kwargs": {"img_size": 512}, + "mapping": {**SWIN_MAPPING, **DECODER_MAPPING}, + }, +} + + +def convert_old_keys_to_new_keys(state_dict_keys: dict, keys_mapping: dict): + """ + This function should be applied only once, on the concatenated keys to efficiently rename using + the key mappings. + """ + output_dict = {} + if state_dict_keys is not None: + old_text = "\n".join(state_dict_keys) + new_text = old_text + for pattern, replacement in keys_mapping.items(): + if replacement is None: + new_text = re.sub(pattern, "", new_text) # an empty line + continue + new_text = re.sub(pattern, replacement, new_text) + output_dict = dict(zip(old_text.split("\n"), new_text.split("\n"))) + return output_dict + + +def group_qkv_layers(state_dict: dict) -> dict: + """Find corresponding layer names for query, key and value layers and stack them in a single layer""" + + state_dict = state_dict.copy() # shallow copy + + result = defaultdict(dict) + layer_names = list(state_dict.keys()) + qkv_names = ["query", "key", "value"] + for layer_name in layer_names: + for pattern in qkv_names: + if pattern in layer_name: + new_key = layer_name.replace(pattern, "qkv") + result[new_key][pattern] = state_dict.pop(layer_name) + break + + # merge them all + for new_key, patterns in result.items(): + state_dict[new_key] = torch.cat( + [patterns[qkv_name] for qkv_name in qkv_names], dim=0 + ) + + return state_dict + + +def convert_model(model_name: str, push_to_hub: bool = False): + params = PRETRAINED_CHECKPOINTS[model_name] + + print(f"Converting model: {model_name}") + print(f"Downloading weights from: {params['repo_id']}") + + hf_weights_path = hf_hub_download( + repo_id=params["repo_id"], filename="pytorch_model.bin" + ) + hf_state_dict = torch.load(hf_weights_path, weights_only=True) + print(f"Loaded HuggingFace state dict with {len(hf_state_dict)} keys") + + # Rename keys + keys_mapping = convert_old_keys_to_new_keys(hf_state_dict.keys(), params["mapping"]) + + smp_state_dict = {} + for old_key, new_key in keys_mapping.items(): + smp_state_dict[new_key] = hf_state_dict[old_key] + + # remove aux head + smp_state_dict = { + k: v for k, v in smp_state_dict.items() if "auxiliary_head." not in k + } + + # [swin] group qkv layers and remove `relative_position_index` + smp_state_dict = group_qkv_layers(smp_state_dict) + smp_state_dict = { + k: v for k, v in smp_state_dict.items() if "relative_position_index" not in k + } + + # Create model + print(f"Creating SMP UPerNet model with encoder: {params['encoder_name']}") + extra_kwargs = params.get("extra_kwargs", {}) + smp_model = smp.UPerNet( + encoder_name=params["encoder_name"], + encoder_weights=None, + decoder_channels=params["decoder_channels"], + classes=params["classes"], + **extra_kwargs, + ) + + print("Loading weights into SMP model...") + smp_model.load_state_dict(smp_state_dict, strict=True) + + # Check we can run the model + print("Verifying model with test inference...") + smp_model.eval() + sample = torch.ones(1, 3, 512, 512) + with torch.no_grad(): + output = smp_model(sample) + print(f"Test inference successful. Output shape: {output.shape}") + + # Save model with preprocessing + smp_repo_id = f"smp-hub/upernet-{model_name}" + print(f"Saving model to: {smp_repo_id}") + smp_model.save_pretrained(save_directory=smp_repo_id) + + transform = A.Compose( + [ + A.Resize(512, 512), + A.Normalize( + mean=(123.675, 116.28, 103.53), + std=(58.395, 57.12, 57.375), + max_pixel_value=1.0, + ), + ] + ) + transform.save_pretrained(save_directory=smp_repo_id) + + if push_to_hub: + print(f"Pushing model to HuggingFace Hub: {smp_repo_id}") + api = HfApi() + if not api.repo_exists(smp_repo_id): + api.create_repo(repo_id=smp_repo_id, repo_type="model") + api.upload_folder( + repo_id=smp_repo_id, + folder_path=smp_repo_id, + repo_type="model", + ) + + print(f"Conversion of {model_name} completed successfully!") + + +if __name__ == "__main__": + print(f"Starting conversion of {len(PRETRAINED_CHECKPOINTS)} UPerNet models") + for model_name in PRETRAINED_CHECKPOINTS.keys(): + convert_model(model_name, push_to_hub=True) + print("All conversions completed!") diff --git a/segmentation_models_pytorch/__init__.py b/segmentation_models_pytorch/__init__.py index f1807836..37c64ef6 100644 --- a/segmentation_models_pytorch/__init__.py +++ b/segmentation_models_pytorch/__init__.py @@ -1,5 +1,3 @@ -import warnings - from . import datasets from . import encoders from . import decoders @@ -16,6 +14,7 @@ from .decoders.pan import PAN from .decoders.upernet import UPerNet from .decoders.segformer import Segformer +from .decoders.dpt import DPT from .base.hub_mixin import from_pretrained from .__version__ import __version__ @@ -24,12 +23,6 @@ from typing import Optional as _Optional import torch as _torch -# Suppress the specific SyntaxWarning for `pretrainedmodels` -warnings.filterwarnings("ignore", message="is with a literal", category=SyntaxWarning) -warnings.filterwarnings( - "ignore", message=r'"is" with \'str\' literal.*', category=SyntaxWarning -) # for python >= 3.12 - _MODEL_ARCHITECTURES = [ Unet, UnetPlusPlus, @@ -42,6 +35,7 @@ PAN, UPerNet, Segformer, + DPT, ] MODEL_ARCHITECTURES_MAPPING = {a.__name__.lower(): a for a in _MODEL_ARCHITECTURES} @@ -92,6 +86,7 @@ def create_model( "PAN", "UPerNet", "Segformer", + "DPT", "from_pretrained", "create_model", "__version__", diff --git a/segmentation_models_pytorch/__version__.py b/segmentation_models_pytorch/__version__.py index b87975ee..3d187266 100644 --- a/segmentation_models_pytorch/__version__.py +++ b/segmentation_models_pytorch/__version__.py @@ -1,3 +1 @@ -VERSION = (0, 4, 0) - -__version__ = ".".join(map(str, VERSION)) +__version__ = "0.5.0" diff --git a/segmentation_models_pytorch/base/hub_mixin.py b/segmentation_models_pytorch/base/hub_mixin.py index 360aa521..a18380d1 100644 --- a/segmentation_models_pytorch/base/hub_mixin.py +++ b/segmentation_models_pytorch/base/hub_mixin.py @@ -1,3 +1,4 @@ +import torch import json from pathlib import Path from typing import Optional, Union @@ -114,12 +115,15 @@ def save_pretrained( return result @property + @torch.jit.unused def config(self) -> dict: return self._hub_mixin_config @wraps(PyTorchModelHubMixin.from_pretrained) -def from_pretrained(pretrained_model_name_or_path: str, *args, **kwargs): +def from_pretrained( + pretrained_model_name_or_path: str, *args, strict: bool = True, **kwargs +): config_path = Path(pretrained_model_name_or_path) / "config.json" if not config_path.exists(): config_path = hf_hub_download( @@ -135,7 +139,9 @@ def from_pretrained(pretrained_model_name_or_path: str, *args, **kwargs): import segmentation_models_pytorch as smp model_class = getattr(smp, model_class_name) - return model_class.from_pretrained(pretrained_model_name_or_path, *args, **kwargs) + return model_class.from_pretrained( + pretrained_model_name_or_path, *args, **kwargs, strict=strict + ) def supports_config_loading(func): diff --git a/segmentation_models_pytorch/base/initialization.py b/segmentation_models_pytorch/base/initialization.py index 4bea4aa6..cf518edd 100644 --- a/segmentation_models_pytorch/base/initialization.py +++ b/segmentation_models_pytorch/base/initialization.py @@ -8,7 +8,9 @@ def initialize_decoder(module): if m.bias is not None: nn.init.constant_(m.bias, 0) - elif isinstance(m, nn.BatchNorm2d): + elif isinstance( + m, (nn.BatchNorm2d, nn.LayerNorm, nn.GroupNorm, nn.InstanceNorm2d) + ): nn.init.constant_(m.weight, 1) nn.init.constant_(m.bias, 0) diff --git a/segmentation_models_pytorch/base/model.py b/segmentation_models_pytorch/base/model.py index 6d7bf643..9b0db714 100644 --- a/segmentation_models_pytorch/base/model.py +++ b/segmentation_models_pytorch/base/model.py @@ -1,16 +1,29 @@ import torch +import warnings +from typing import TypeVar, Type from . import initialization as init from .hub_mixin import SMPHubMixin +from .utils import is_torch_compiling + +T = TypeVar("T", bound="SegmentationModel") class SegmentationModel(torch.nn.Module, SMPHubMixin): """Base class for all segmentation models.""" - # if model supports shape not divisible by 2 ^ n - # set to False + _is_torch_scriptable = True + _is_torch_exportable = True + _is_torch_compilable = True + + # if model supports shape not divisible by 2 ^ n set to False requires_divisible_input_shape = True + # Fix type-hint for models, to avoid HubMixin signature + def __new__(cls: Type[T], *args, **kwargs) -> T: + instance = super().__new__(cls, *args, **kwargs) + return instance + def initialize(self): init.initialize_decoder(self.decoder) init.initialize_head(self.segmentation_head) @@ -21,6 +34,9 @@ def check_input_shape(self, x): """Check if the input shape is divisible by the output stride. If not, raise a RuntimeError. """ + if not self.requires_divisible_input_shape: + return + h, w = x.shape[-2:] output_stride = self.encoder.output_stride if h % output_stride != 0 or w % output_stride != 0: @@ -42,11 +58,13 @@ def check_input_shape(self, x): def forward(self, x): """Sequentially pass `x` trough model`s encoder, decoder and heads""" - if not torch.jit.is_tracing() or self.requires_divisible_input_shape: + if not ( + torch.jit.is_scripting() or torch.jit.is_tracing() or is_torch_compiling() + ): self.check_input_shape(x) features = self.encoder(x) - decoder_output = self.decoder(*features) + decoder_output = self.decoder(features) masks = self.segmentation_head(decoder_output) @@ -69,7 +87,53 @@ def predict(self, x): """ if self.training: self.eval() + x = self(x) + return x - x = self.forward(x) + def load_state_dict(self, state_dict, **kwargs): + # for compatibility of weights for + # timm- ported encoders with TimmUniversalEncoder + from segmentation_models_pytorch.encoders import TimmUniversalEncoder - return x + if isinstance(self.encoder, TimmUniversalEncoder): + patterns = ["regnet", "res2", "resnest", "mobilenetv3", "gernet"] + is_deprecated_encoder = any( + self.encoder.name.startswith(pattern) for pattern in patterns + ) + if is_deprecated_encoder: + keys = list(state_dict.keys()) + for key in keys: + new_key = key + if key.startswith("encoder.") and not key.startswith( + "encoder.model." + ): + new_key = "encoder.model." + key.removeprefix("encoder.") + if "gernet" in self.encoder.name: + new_key = new_key.replace(".stages.", ".stages_") + state_dict[new_key] = state_dict.pop(key) + + # To be able to load weight with mismatched sizes + # We are going to filter mismatched sizes as well if strict=False + strict = kwargs.get("strict", True) + if not strict: + mismatched_keys = [] + model_state_dict = self.state_dict() + common_keys = set(model_state_dict.keys()) & set(state_dict.keys()) + for key in common_keys: + if model_state_dict[key].shape != state_dict[key].shape: + mismatched_keys.append( + (key, model_state_dict[key].shape, state_dict[key].shape) + ) + state_dict.pop(key) + + if mismatched_keys: + str_keys = "\n".join( + [ + f" - {key}: {s} (weights) -> {m} (model)" + for key, m, s in mismatched_keys + ] + ) + text = f"\n\n !!!!!! Mismatched keys !!!!!!\n\nYou should TRAIN the model to use it:\n{str_keys}\n" + warnings.warn(text, stacklevel=-1) + + return super().load_state_dict(state_dict, **kwargs) diff --git a/segmentation_models_pytorch/base/modules.py b/segmentation_models_pytorch/base/modules.py index cbd643b6..15cfdb12 100644 --- a/segmentation_models_pytorch/base/modules.py +++ b/segmentation_models_pytorch/base/modules.py @@ -1,3 +1,5 @@ +from typing import Any, Dict, Union + import torch import torch.nn as nn @@ -7,43 +9,109 @@ InPlaceABN = None +def get_norm_layer( + use_norm: Union[bool, str, Dict[str, Any]], out_channels: int +) -> nn.Module: + supported_norms = ("inplace", "batchnorm", "identity", "layernorm", "instancenorm") + + # Step 1. Convert tot dict representation + + ## Check boolean + if use_norm is True: + norm_params = {"type": "batchnorm"} + elif use_norm is False: + norm_params = {"type": "identity"} + + ## Check string + elif isinstance(use_norm, str): + norm_str = use_norm.lower() + if norm_str == "inplace": + norm_params = { + "type": "inplace", + "activation": "leaky_relu", + "activation_param": 0.0, + } + elif norm_str in supported_norms: + norm_params = {"type": norm_str} + else: + raise ValueError( + f"Unrecognized normalization type string provided: {use_norm}. Should be in " + f"{supported_norms}" + ) + + ## Check dict + elif isinstance(use_norm, dict): + norm_params = use_norm + + else: + raise ValueError( + f"Invalid type for use_norm should either be a bool (batchnorm/identity), " + f"a string in {supported_norms}, or a dict like {{'type': 'batchnorm', **kwargs}}" + ) + + # Step 2. Check if the dict is valid + if "type" not in norm_params: + raise ValueError( + f"Malformed dictionary given in use_norm: {use_norm}. Should contain key 'type'." + ) + if norm_params["type"] not in supported_norms: + raise ValueError( + f"Unrecognized normalization type string provided: {use_norm}. Should be in {supported_norms}" + ) + if norm_params["type"] == "inplace" and InPlaceABN is None: + raise RuntimeError( + "In order to use `use_norm='inplace'` the inplace_abn package must be installed. Use:\n" + " $ pip install -U wheel setuptools\n" + " $ pip install inplace_abn --no-build-isolation\n" + "Also see: https://github.com/mapillary/inplace_abn" + ) + + # Step 3. Initialize the norm layer + norm_type = norm_params["type"] + norm_kwargs = {k: v for k, v in norm_params.items() if k != "type"} + + if norm_type == "inplace": + norm = InPlaceABN(out_channels, **norm_kwargs) + elif norm_type == "batchnorm": + norm = nn.BatchNorm2d(out_channels, **norm_kwargs) + elif norm_type == "identity": + norm = nn.Identity() + elif norm_type == "layernorm": + norm = nn.LayerNorm(out_channels, **norm_kwargs) + elif norm_type == "instancenorm": + norm = nn.InstanceNorm2d(out_channels, **norm_kwargs) + else: + raise ValueError(f"Unrecognized normalization type: {norm_type}") + + return norm + + class Conv2dReLU(nn.Sequential): def __init__( self, - in_channels, - out_channels, - kernel_size, - padding=0, - stride=1, - use_batchnorm=True, + in_channels: int, + out_channels: int, + kernel_size: int, + padding: int = 0, + stride: int = 1, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", ): - if use_batchnorm == "inplace" and InPlaceABN is None: - raise RuntimeError( - "In order to use `use_batchnorm='inplace'` inplace_abn package must be installed. " - + "To install see: https://github.com/mapillary/inplace_abn" - ) + norm = get_norm_layer(use_norm, out_channels) + is_identity = isinstance(norm, nn.Identity) conv = nn.Conv2d( in_channels, out_channels, kernel_size, stride=stride, padding=padding, - bias=not (use_batchnorm), + bias=is_identity, ) - relu = nn.ReLU(inplace=True) - - if use_batchnorm == "inplace": - bn = InPlaceABN(out_channels, activation="leaky_relu", activation_param=0.0) - relu = nn.Identity() - elif use_batchnorm and use_batchnorm != "inplace": - bn = nn.BatchNorm2d(out_channels) - - else: - bn = nn.Identity() + is_inplaceabn = InPlaceABN is not None and isinstance(norm, InPlaceABN) + activation = nn.Identity() if is_inplaceabn else nn.ReLU(inplace=True) - super(Conv2dReLU, self).__init__(conv, bn, relu) + super(Conv2dReLU, self).__init__(conv, norm, activation) class SCSEModule(nn.Module): diff --git a/segmentation_models_pytorch/base/utils.py b/segmentation_models_pytorch/base/utils.py new file mode 100644 index 00000000..a0d41943 --- /dev/null +++ b/segmentation_models_pytorch/base/utils.py @@ -0,0 +1,14 @@ +import torch + + +@torch.jit.unused +def is_torch_compiling(): + try: + return torch.compiler.is_compiling() + except Exception: + try: + import torch._dynamo as dynamo # noqa: F401 + + return dynamo.is_compiling() + except Exception: + return False diff --git a/segmentation_models_pytorch/decoders/deeplabv3/decoder.py b/segmentation_models_pytorch/decoders/deeplabv3/decoder.py index 3fd73786..6a801a70 100644 --- a/segmentation_models_pytorch/decoders/deeplabv3/decoder.py +++ b/segmentation_models_pytorch/decoders/deeplabv3/decoder.py @@ -31,7 +31,7 @@ """ from collections.abc import Iterable, Sequence -from typing import Literal +from typing import Literal, List import torch from torch import nn @@ -40,7 +40,7 @@ __all__ = ["DeepLabV3Decoder", "DeepLabV3PlusDecoder"] -class DeepLabV3Decoder(nn.Sequential): +class DeepLabV3Decoder(nn.Module): def __init__( self, in_channels: int, @@ -49,21 +49,25 @@ def __init__( aspp_separable: bool, aspp_dropout: float, ): - super().__init__( - ASPP( - in_channels, - out_channels, - atrous_rates, - separable=aspp_separable, - dropout=aspp_dropout, - ), - nn.Conv2d(out_channels, out_channels, 3, padding=1, bias=False), - nn.BatchNorm2d(out_channels), - nn.ReLU(), + super().__init__() + self.aspp = ASPP( + in_channels, + out_channels, + atrous_rates, + separable=aspp_separable, + dropout=aspp_dropout, ) + self.conv = nn.Conv2d(out_channels, out_channels, 3, padding=1, bias=False) + self.bn = nn.BatchNorm2d(out_channels) + self.relu = nn.ReLU() - def forward(self, *features): - return super().forward(features[-1]) + def forward(self, features: List[torch.Tensor]) -> torch.Tensor: + x = features[-1] + x = self.aspp(x) + x = self.conv(x) + x = self.bn(x) + x = self.relu(x) + return x class DeepLabV3PlusDecoder(nn.Module): @@ -124,7 +128,7 @@ def __init__( nn.ReLU(), ) - def forward(self, *features): + def forward(self, features: List[torch.Tensor]) -> torch.Tensor: aspp_features = self.aspp(features[-1]) aspp_features = self.up(aspp_features) high_res_features = self.block1(features[2]) @@ -174,7 +178,7 @@ def __init__(self, in_channels: int, out_channels: int): nn.ReLU(), ) - def forward(self, x): + def forward(self, x: torch.Tensor) -> torch.Tensor: size = x.shape[-2:] for mod in self: x = mod(x) @@ -216,7 +220,7 @@ def __init__( nn.Dropout(dropout), ) - def forward(self, x): + def forward(self, x: torch.Tensor) -> torch.Tensor: res = [] for conv in self.convs: res.append(conv(x)) diff --git a/segmentation_models_pytorch/decoders/deeplabv3/model.py b/segmentation_models_pytorch/decoders/deeplabv3/model.py index 654e38d4..38ca9e04 100644 --- a/segmentation_models_pytorch/decoders/deeplabv3/model.py +++ b/segmentation_models_pytorch/decoders/deeplabv3/model.py @@ -34,8 +34,7 @@ class DeepLabV3(SegmentationModel): classes: A number of classes for output mask (or you can think as a number of channels of output mask) activation: An activation function to apply after the final convolution layer. Available options are **"sigmoid"**, **"softmax"**, **"logsoftmax"**, **"tanh"**, **"identity"**, - **callable** and **None**. - Default is **None** + **callable** and **None**. Default is **None**. upsampling: Final upsampling factor. Default is **None** to preserve input-output spatial shape identity aux_params: Dictionary with parameters of the auxiliary output (classification head). Auxiliary output is build on top of encoder if **aux_params** is not **None** (default). Supported params: @@ -121,6 +120,21 @@ def __init__( else: self.classification_head = None + def load_state_dict(self, state_dict, *args, **kwargs): + # For backward compatibility, previously Decoder module was Sequential + # and was not scriptable. + keys = list(state_dict.keys()) + for key in keys: + new_key = key + if key.startswith("decoder.0."): + new_key = key.replace("decoder.0.", "decoder.aspp.") + elif key.startswith("decoder.1."): + new_key = key.replace("decoder.1.", "decoder.conv.") + elif key.startswith("decoder.2."): + new_key = key.replace("decoder.2.", "decoder.bn.") + state_dict[new_key] = state_dict.pop(key) + return super().load_state_dict(state_dict, *args, **kwargs) + class DeepLabV3Plus(SegmentationModel): """DeepLabV3+ implementation from "Encoder-Decoder with Atrous Separable @@ -144,8 +158,7 @@ class DeepLabV3Plus(SegmentationModel): classes: A number of classes for output mask (or you can think as a number of channels of output mask) activation: An activation function to apply after the final convolution layer. Available options are **"sigmoid"**, **"softmax"**, **"logsoftmax"**, **"tanh"**, **"identity"**, - **callable** and **None**. - Default is **None** + **callable** and **None**. Default is **None**. upsampling: Final upsampling factor. Default is 4 to preserve input-output spatial shape identity. aux_params: Dictionary with parameters of the auxiliary output (classification head). Auxiliary output is build on top of encoder if **aux_params** is not **None** (default). Supported params: diff --git a/segmentation_models_pytorch/decoders/dpt/__init__.py b/segmentation_models_pytorch/decoders/dpt/__init__.py new file mode 100644 index 00000000..c729fe90 --- /dev/null +++ b/segmentation_models_pytorch/decoders/dpt/__init__.py @@ -0,0 +1,3 @@ +from .model import DPT + +__all__ = ["DPT"] diff --git a/segmentation_models_pytorch/decoders/dpt/decoder.py b/segmentation_models_pytorch/decoders/dpt/decoder.py new file mode 100644 index 00000000..345ecca1 --- /dev/null +++ b/segmentation_models_pytorch/decoders/dpt/decoder.py @@ -0,0 +1,320 @@ +import torch +import torch.nn as nn +from segmentation_models_pytorch.base.modules import Activation +from typing import Optional, Sequence, Union, Callable, Literal + + +class ReadoutConcatBlock(nn.Module): + """ + Concatenates the cls tokens with the features to make use of the global information aggregated in the prefix (cls) tokens. + Projects the combined feature map to the original embedding dimension using a MLP. + + According to: + https://github.com/isl-org/DPT/blob/cd3fe90bb4c48577535cc4d51b602acca688a2ee/dpt/vit.py#L79-L90 + """ + + def __init__(self, embed_dim: int, has_prefix_tokens: bool): + super().__init__() + in_features = embed_dim * 2 if has_prefix_tokens else embed_dim + out_features = embed_dim + self.project = nn.Sequential( + nn.Linear(in_features, out_features), + nn.GELU(), + ) + + def forward( + self, features: torch.Tensor, prefix_tokens: Optional[torch.Tensor] = None + ) -> torch.Tensor: + batch_size, embed_dim, height, width = features.shape + + # Rearrange to (batch_size, height * width, embed_dim) + features = features.view(batch_size, embed_dim, -1) + features = features.transpose(1, 2).contiguous() + + if prefix_tokens is not None: + # (batch_size, num_prefix_tokens, embed_dim) -> (batch_size, 1, embed_dim) + prefix_tokens = prefix_tokens[:, :1].expand_as(features) + features = torch.cat([features, prefix_tokens], dim=2) + + # Project to embedding dimension + features = self.project(features) + + # Rearrange back to (batch_size, embed_dim, height, width) + features = features.transpose(1, 2) + features = features.view(batch_size, -1, height, width) + + return features + + +class ReadoutAddBlock(nn.Module): + """ + Adds the prefix tokens to the features to make use of the global information aggregated in the prefix (cls) tokens. + + According to: + https://github.com/isl-org/DPT/blob/cd3fe90bb4c48577535cc4d51b602acca688a2ee/dpt/vit.py#L71-L76 + """ + + def forward( + self, features: torch.Tensor, prefix_tokens: Optional[torch.Tensor] = None + ) -> torch.Tensor: + if prefix_tokens is not None: + batch_size, embed_dim, height, width = features.shape + prefix_tokens = prefix_tokens.mean(dim=1) + prefix_tokens = prefix_tokens.view(batch_size, embed_dim, 1, 1) + features = features + prefix_tokens + return features + + +class ReadoutIgnoreBlock(nn.Module): + """ + Ignores the prefix tokens and returns the features as is. + """ + + def forward(self, features: torch.Tensor, *args, **kwargs) -> torch.Tensor: + return features + + +class ReassembleBlock(nn.Module): + """ + Processes the features such that they have progressively increasing embedding size and progressively decreasing + spatial dimension + """ + + def __init__( + self, + in_channels: int, + mid_channels: int, + out_channels: int, + upsample_factor: int, + ): + super().__init__() + + self.project_to_out_channel = nn.Conv2d( + in_channels=in_channels, + out_channels=mid_channels, + kernel_size=1, + ) + + if upsample_factor > 1.0: + self.upsample = nn.ConvTranspose2d( + in_channels=mid_channels, + out_channels=mid_channels, + kernel_size=int(upsample_factor), + stride=int(upsample_factor), + ) + elif upsample_factor == 1.0: + self.upsample = nn.Identity() + else: + self.upsample = nn.Conv2d( + in_channels=mid_channels, + out_channels=mid_channels, + kernel_size=3, + stride=int(1 / upsample_factor), + padding=1, + ) + + self.project_to_feature_dim = nn.Conv2d( + in_channels=mid_channels, + out_channels=out_channels, + kernel_size=3, + padding=1, + bias=False, + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + x = self.project_to_out_channel(x) + x = self.upsample(x) + x = self.project_to_feature_dim(x) + return x + + +class ResidualConvBlock(nn.Module): + def __init__(self, feature_dim: int): + super().__init__() + + self.conv_1 = nn.Conv2d( + in_channels=feature_dim, + out_channels=feature_dim, + kernel_size=3, + padding=1, + bias=False, + ) + self.batch_norm_1 = nn.BatchNorm2d(num_features=feature_dim) + self.conv_2 = nn.Conv2d( + in_channels=feature_dim, + out_channels=feature_dim, + kernel_size=3, + padding=1, + bias=False, + ) + self.batch_norm_2 = nn.BatchNorm2d(num_features=feature_dim) + self.activation = nn.ReLU() + + def forward(self, x: torch.Tensor) -> torch.Tensor: + residual = x + + # Block 1 + x = self.activation(x) + x = self.conv_1(x) + x = self.batch_norm_1(x) + + # Block 2 + x = self.activation(x) + x = self.conv_2(x) + x = self.batch_norm_2(x) + + # Add residual + x = x + residual + + return x + + +class FusionBlock(nn.Module): + """ + Fuses the processed encoder features in a residual manner and upsamples them + """ + + def __init__(self, feature_dim: int): + super().__init__() + self.residual_conv_block1 = ResidualConvBlock(feature_dim) + self.residual_conv_block2 = ResidualConvBlock(feature_dim) + self.project = nn.Conv2d(feature_dim, feature_dim, kernel_size=1) + self.activation = nn.ReLU() + + def forward( + self, + feature: torch.Tensor, + previous_feature: Optional[torch.Tensor] = None, + ) -> torch.Tensor: + feature = self.residual_conv_block1(feature) + if previous_feature is not None: + feature = feature + previous_feature + feature = self.residual_conv_block2(feature) + feature = nn.functional.interpolate( + feature, scale_factor=2, align_corners=True, mode="bilinear" + ) + feature = self.project(feature) + return feature + + +class DPTDecoder(nn.Module): + """ + Decoder part for DPT + + Processes the encoder features and class tokens (if encoder has class_tokens) to have spatial downsampling ratios of + [1/4, 1/8, 1/16, 1/32, ...] relative to the input image spatial dimension. + + The decoder then fuses these features in a residual manner and progressively upsamples them by a factor of 2 so that the + output has a downsampling ratio of 1/2 relative to the input image spatial dimension + + """ + + def __init__( + self, + encoder_out_channels: Sequence[int] = (756, 756, 756, 756), + encoder_output_strides: Sequence[int] = (16, 16, 16, 16), + encoder_has_prefix_tokens: bool = True, + readout: Literal["cat", "add", "ignore"] = "cat", + intermediate_channels: Sequence[int] = (256, 512, 1024, 1024), + fusion_channels: int = 256, + ): + super().__init__() + + if not ( + len(encoder_out_channels) + == len(encoder_output_strides) + == len(intermediate_channels) + ): + raise ValueError( + "encoder_out_channels, encoder_output_strides and intermediate_channels must have the same length" + ) + + num_blocks = len(encoder_out_channels) + + # If encoder has prefix tokens (e.g. cls_token), then we can concat/add/ignore them + # according to the readout mode + if readout == "cat": + blocks = [ + ReadoutConcatBlock(in_channels, encoder_has_prefix_tokens) + for in_channels in encoder_out_channels + ] + elif readout == "add": + blocks = [ReadoutAddBlock() for _ in encoder_out_channels] + elif readout == "ignore": + blocks = [ReadoutIgnoreBlock() for _ in encoder_out_channels] + else: + raise ValueError( + f"Invalid readout mode: {readout}, should be one of: 'cat', 'add', 'ignore'" + ) + self.projection_blocks = nn.ModuleList(blocks) + + # Upsample factors to resize features to [1/4, 1/8, 1/16, 1/32, ...] scales + scale_factors = [ + stride / 2 ** (i + 2) for i, stride in enumerate(encoder_output_strides) + ] + self.reassemble_blocks = nn.ModuleList() + for i in range(num_blocks): + block = ReassembleBlock( + in_channels=encoder_out_channels[i], + mid_channels=intermediate_channels[i], + out_channels=fusion_channels, + upsample_factor=scale_factors[i], + ) + self.reassemble_blocks.append(block) + + # Fusion blocks to fuse the processed features in a sequential manner + fusion_blocks = [FusionBlock(fusion_channels) for _ in range(num_blocks)] + self.fusion_blocks = nn.ModuleList(fusion_blocks) + + def forward( + self, features: list[torch.Tensor], prefix_tokens: list[Optional[torch.Tensor]] + ) -> torch.Tensor: + # Process the encoder features to scale of [1/4, 1/8, 1/16, 1/32, ...] + processed_features = [] + for i, (feature, prefix_tokens_i) in enumerate(zip(features, prefix_tokens)): + projected_feature = self.projection_blocks[i](feature, prefix_tokens_i) + processed_feature = self.reassemble_blocks[i](projected_feature) + processed_features.append(processed_feature) + + # Fusion and progressive upsampling starting from the last processed feature + processed_features = processed_features[::-1] + fused_feature = None + for fusion_block, feature in zip(self.fusion_blocks, processed_features): + fused_feature = fusion_block(feature, fused_feature) + + return fused_feature + + +class DPTSegmentationHead(nn.Module): + def __init__( + self, + in_channels: int, + out_channels: int, + activation: Optional[Union[str, Callable]] = None, + kernel_size: int = 3, + upsampling: float = 2.0, + ): + super().__init__() + + self.head = nn.Sequential( + nn.Conv2d( + in_channels, in_channels, kernel_size=kernel_size, padding=1, bias=False + ), + nn.BatchNorm2d(in_channels), + nn.ReLU(inplace=True), + nn.Dropout(p=0.1, inplace=False), + nn.Conv2d(in_channels, out_channels, kernel_size=1), + ) + self.activation = Activation(activation) + self.upsampling_factor = upsampling + + def forward(self, x: torch.Tensor) -> torch.Tensor: + head_output = self.head(x) + resized_output = nn.functional.interpolate( + head_output, + scale_factor=self.upsampling_factor, + mode="bilinear", + align_corners=True, + ) + activation_output = self.activation(resized_output) + return activation_output diff --git a/segmentation_models_pytorch/decoders/dpt/model.py b/segmentation_models_pytorch/decoders/dpt/model.py new file mode 100644 index 00000000..1294dd4f --- /dev/null +++ b/segmentation_models_pytorch/decoders/dpt/model.py @@ -0,0 +1,167 @@ +import warnings +from typing import Any, Optional, Union, Callable, Sequence, Literal + +import torch + +from segmentation_models_pytorch.base import ( + ClassificationHead, + SegmentationModel, +) +from segmentation_models_pytorch.encoders.timm_vit import TimmViTEncoder +from segmentation_models_pytorch.base.utils import is_torch_compiling +from segmentation_models_pytorch.base.hub_mixin import supports_config_loading +from .decoder import DPTDecoder, DPTSegmentationHead + + +class DPT(SegmentationModel): + """ + DPT is a dense prediction architecture that leverages vision transformers in place of convolutional networks as + a backbone for dense prediction tasks + + It assembles tokens from various stages of the vision transformer into image-like representations at various resolutions + and progressively combines them into full-resolution predictions using a convolutional decoder. + + The transformer backbone processes representations at a constant and relatively high resolution and has a global receptive + field at every stage. These properties allow the dense vision transformer to provide finer-grained and more globally coherent + predictions when compared to fully-convolutional networks + + Note: + Since this model uses a Vision Transformer backbone, it typically requires a fixed input image size. + To handle variable input sizes, you can set `dynamic_img_size=True` in the model initialization + (if supported by the specific `timm` encoder). You can check if an encoder requires fixed size + using `model.encoder.is_fixed_input_size`, and get the required input dimensions from + `model.encoder.input_size`, however it's no guarantee that information is available. + + Args: + encoder_name: Name of the classification model that will be used as an encoder (a.k.a backbone) + to extract features of different spatial resolution. + encoder_depth: A number of stages used in encoder in range [1,4]. Each stage generate features + smaller by a factor equal to the ViT model patch_size in spatial dimensions. + Default is 4. + encoder_weights: One of **None** (random initialization), or not **None** (pretrained weights would be loaded + with respect to the encoder_name, e.g. for ``"tu-vit_base_patch16_224.augreg_in21k"`` - ``"augreg_in21k"`` + weights would be loaded). + encoder_output_indices: The indices of the encoder output features to use. If **None** will be sampled uniformly + across the number of blocks in encoder, e.g. if number of blocks is 4 and encoder has 20 blocks, then + encoder_output_indices will be (4, 9, 14, 19). If specified the number of indices should be equal to + encoder_depth. Default is **None**. + decoder_readout: The strategy to utilize the prefix tokens (e.g. cls_token) from the encoder. + Can be one of **"cat"**, **"add"**, or **"ignore"**. Default is **"cat"**. + decoder_intermediate_channels: The number of channels for the intermediate decoder layers. Reduce if you + want to reduce the number of parameters in the decoder. Default is (256, 512, 1024, 1024). + decoder_fusion_channels: The latent dimension to which the encoder features will be projected to before fusion. + Default is 256. + in_channels: Number of input channels for the model, default is 3 (RGB images) + classes: Number of classes for output mask (or you can think as a number of channels of output mask) + activation: An activation function to apply after the final convolution layer. + Available options are **"sigmoid"**, **"softmax"**, **"logsoftmax"**, **"tanh"**, **"identity"**, + **callable** and **None**. Default is **None**. + aux_params: Dictionary with parameters of the auxiliary output (classification head). Auxiliary output is build + on top of encoder if **aux_params** is not **None** (default). Supported params: + + - **classes** (*int*): A number of classes; + - **pooling** (*str*): One of "max", "avg". Default is "avg"; + - **dropout** (*float*): Dropout factor in [0, 1); + - **activation** (*str*): An activation function to apply "sigmoid"/"softmax" (could be **None** to return logits). + kwargs: Arguments passed to the encoder class ``__init__()`` function. Applies only to ``timm`` models. Keys with + ``None`` values are pruned before passing. Specify ``dynamic_img_size=True`` to allow the model to handle images of different sizes. + + Returns: + ``torch.nn.Module``: DPT + + """ + + # fails for encoders with prefix tokens + _is_torch_scriptable = False + _is_torch_compilable = True + requires_divisible_input_shape = True + + @supports_config_loading + def __init__( + self, + encoder_name: str = "tu-vit_base_patch16_224.augreg_in21k", + encoder_depth: int = 4, + encoder_weights: Optional[str] = "imagenet", + encoder_output_indices: Optional[list[int]] = None, + decoder_readout: Literal["ignore", "add", "cat"] = "cat", + decoder_intermediate_channels: Sequence[int] = (256, 512, 1024, 1024), + decoder_fusion_channels: int = 256, + in_channels: int = 3, + classes: int = 1, + activation: Optional[Union[str, Callable]] = None, + aux_params: Optional[dict] = None, + **kwargs: dict[str, Any], + ): + super().__init__() + if encoder_name.startswith("tu-"): + encoder_name = encoder_name[3:] + else: + raise ValueError( + f"Only Timm encoders are supported for DPT. Encoder name must start with 'tu-', got {encoder_name}" + ) + + if decoder_readout not in ["ignore", "add", "cat"]: + raise ValueError( + f"Invalid decoder readout mode. Must be one of: 'ignore', 'add', 'cat'. Got: {decoder_readout}" + ) + + self.encoder = TimmViTEncoder( + name=encoder_name, + in_channels=in_channels, + depth=encoder_depth, + pretrained=encoder_weights is not None, + output_indices=encoder_output_indices, + **kwargs, + ) + + if not self.encoder.has_prefix_tokens and decoder_readout != "ignore": + warnings.warn( + f"Encoder does not have prefix tokens (e.g. cls_token), but `decoder_readout` is set to '{decoder_readout}'. " + f"It's recommended to set `decoder_readout='ignore'` when using a encoder without prefix tokens.", + UserWarning, + ) + + self.decoder = DPTDecoder( + encoder_out_channels=self.encoder.out_channels, + encoder_output_strides=self.encoder.output_strides, + encoder_has_prefix_tokens=self.encoder.has_prefix_tokens, + readout=decoder_readout, + intermediate_channels=decoder_intermediate_channels, + fusion_channels=decoder_fusion_channels, + ) + + self.segmentation_head = DPTSegmentationHead( + in_channels=decoder_fusion_channels, + out_channels=classes, + activation=activation, + kernel_size=3, + upsampling=2, + ) + + if aux_params is not None: + self.classification_head = ClassificationHead( + in_channels=self.encoder.out_channels[-1], **aux_params + ) + else: + self.classification_head = None + + self.name = "dpt-{}".format(encoder_name) + self.initialize() + + def forward(self, x): + """Sequentially pass `x` trough model`s encoder, decoder and heads""" + + if not ( + torch.jit.is_scripting() or torch.jit.is_tracing() or is_torch_compiling() + ): + self.check_input_shape(x) + + features, prefix_tokens = self.encoder(x) + decoder_output = self.decoder(features, prefix_tokens) + masks = self.segmentation_head(decoder_output) + + if self.classification_head is not None: + labels = self.classification_head(features[-1]) + return masks, labels + + return masks diff --git a/segmentation_models_pytorch/decoders/fpn/decoder.py b/segmentation_models_pytorch/decoders/fpn/decoder.py index 766190f4..b111843a 100644 --- a/segmentation_models_pytorch/decoders/fpn/decoder.py +++ b/segmentation_models_pytorch/decoders/fpn/decoder.py @@ -2,9 +2,11 @@ import torch.nn as nn import torch.nn.functional as F +from typing import List, Literal + class Conv3x3GNReLU(nn.Module): - def __init__(self, in_channels, out_channels, upsample=False): + def __init__(self, in_channels: int, out_channels: int, upsample: bool = False): super().__init__() self.upsample = upsample self.block = nn.Sequential( @@ -15,27 +17,33 @@ def __init__(self, in_channels, out_channels, upsample=False): nn.ReLU(inplace=True), ) - def forward(self, x): + def forward(self, x: torch.Tensor) -> torch.Tensor: x = self.block(x) if self.upsample: - x = F.interpolate(x, scale_factor=2, mode="bilinear", align_corners=True) + x = F.interpolate(x, scale_factor=2.0, mode="bilinear", align_corners=True) return x class FPNBlock(nn.Module): - def __init__(self, pyramid_channels, skip_channels): + def __init__( + self, + pyramid_channels: int, + skip_channels: int, + interpolation_mode: str = "nearest", + ): super().__init__() self.skip_conv = nn.Conv2d(skip_channels, pyramid_channels, kernel_size=1) + self.interpolation_mode = interpolation_mode - def forward(self, x, skip=None): - x = F.interpolate(x, scale_factor=2, mode="nearest") + def forward(self, x: torch.Tensor, skip: torch.Tensor) -> torch.Tensor: + x = F.interpolate(x, scale_factor=2.0, mode=self.interpolation_mode) skip = self.skip_conv(skip) x = x + skip return x class SegmentationBlock(nn.Module): - def __init__(self, in_channels, out_channels, n_upsamples=0): + def __init__(self, in_channels: int, out_channels: int, n_upsamples: int = 0): super().__init__() blocks = [Conv3x3GNReLU(in_channels, out_channels, upsample=bool(n_upsamples))] @@ -51,7 +59,7 @@ def forward(self, x): class MergeBlock(nn.Module): - def __init__(self, policy): + def __init__(self, policy: Literal["add", "cat"]): super().__init__() if policy not in ["add", "cat"]: raise ValueError( @@ -59,28 +67,30 @@ def __init__(self, policy): ) self.policy = policy - def forward(self, x): + def forward(self, x: List[torch.Tensor]) -> torch.Tensor: if self.policy == "add": - return sum(x) + output = torch.stack(x).sum(dim=0) elif self.policy == "cat": - return torch.cat(x, dim=1) + output = torch.cat(x, dim=1) else: raise ValueError( "`merge_policy` must be one of: ['add', 'cat'], got {}".format( self.policy ) ) + return output class FPNDecoder(nn.Module): def __init__( self, - encoder_channels, - encoder_depth=5, - pyramid_channels=256, - segmentation_channels=128, - dropout=0.2, - merge_policy="add", + encoder_channels: List[int], + encoder_depth: int = 5, + pyramid_channels: int = 256, + segmentation_channels: int = 128, + dropout: float = 0.2, + merge_policy: Literal["add", "cat"] = "add", + interpolation_mode: str = "nearest", ): super().__init__() @@ -100,9 +110,9 @@ def __init__( encoder_channels = encoder_channels[: encoder_depth + 1] self.p5 = nn.Conv2d(encoder_channels[0], pyramid_channels, kernel_size=1) - self.p4 = FPNBlock(pyramid_channels, encoder_channels[1]) - self.p3 = FPNBlock(pyramid_channels, encoder_channels[2]) - self.p2 = FPNBlock(pyramid_channels, encoder_channels[3]) + self.p4 = FPNBlock(pyramid_channels, encoder_channels[1], interpolation_mode) + self.p3 = FPNBlock(pyramid_channels, encoder_channels[2], interpolation_mode) + self.p2 = FPNBlock(pyramid_channels, encoder_channels[3], interpolation_mode) self.seg_blocks = nn.ModuleList( [ @@ -116,7 +126,7 @@ def __init__( self.merge = MergeBlock(merge_policy) self.dropout = nn.Dropout2d(p=dropout, inplace=True) - def forward(self, *features): + def forward(self, features: List[torch.Tensor]) -> torch.Tensor: c2, c3, c4, c5 = features[-4:] p5 = self.p5(c5) @@ -124,9 +134,12 @@ def forward(self, *features): p3 = self.p3(p4, c3) p2 = self.p2(p3, c2) - feature_pyramid = [ - seg_block(p) for seg_block, p in zip(self.seg_blocks, [p5, p4, p3, p2]) - ] + s5 = self.seg_blocks[0](p5) + s4 = self.seg_blocks[1](p4) + s3 = self.seg_blocks[2](p3) + s2 = self.seg_blocks[3](p2) + + feature_pyramid = [s5, s4, s3, s2] x = self.merge(feature_pyramid) x = self.dropout(x) diff --git a/segmentation_models_pytorch/decoders/fpn/model.py b/segmentation_models_pytorch/decoders/fpn/model.py index 7420b289..6e37109a 100644 --- a/segmentation_models_pytorch/decoders/fpn/model.py +++ b/segmentation_models_pytorch/decoders/fpn/model.py @@ -28,12 +28,13 @@ class FPN(SegmentationModel): decoder_merge_policy: Determines how to merge pyramid features inside FPN. Available options are **add** and **cat** decoder_dropout: Spatial dropout rate in range (0, 1) for feature pyramid in FPN_ + decoder_interpolation: Interpolation mode used in decoder of the model. Available options are + **"nearest"**, **"bilinear"**, **"bicubic"**, **"area"**, **"nearest-exact"**. Default is **"nearest"**. in_channels: A number of input channels for the model, default is 3 (RGB images) classes: A number of classes for output mask (or you can think as a number of channels of output mask) activation: An activation function to apply after the final convolution layer. Available options are **"sigmoid"**, **"softmax"**, **"logsoftmax"**, **"tanh"**, **"identity"**, - **callable** and **None**. - Default is **None** + **callable** and **None**. Default is **None**. upsampling: Final upsampling factor. Default is 4 to preserve input-output spatial shape identity aux_params: Dictionary with parameters of the auxiliary output (classification head). Auxiliary output is build on top of encoder if **aux_params** is not **None** (default). Supported params: @@ -62,6 +63,7 @@ def __init__( decoder_segmentation_channels: int = 128, decoder_merge_policy: str = "add", decoder_dropout: float = 0.2, + decoder_interpolation: str = "nearest", in_channels: int = 3, classes: int = 1, activation: Optional[str] = None, @@ -92,6 +94,7 @@ def __init__( segmentation_channels=decoder_segmentation_channels, dropout=decoder_dropout, merge_policy=decoder_merge_policy, + interpolation_mode=decoder_interpolation, ) self.segmentation_head = SegmentationHead( diff --git a/segmentation_models_pytorch/decoders/linknet/decoder.py b/segmentation_models_pytorch/decoders/linknet/decoder.py index e16a32c8..95c7f9f6 100644 --- a/segmentation_models_pytorch/decoders/linknet/decoder.py +++ b/segmentation_models_pytorch/decoders/linknet/decoder.py @@ -1,26 +1,33 @@ +import torch import torch.nn as nn +from typing import Any, Dict, List, Optional, Union from segmentation_models_pytorch.base import modules class TransposeX2(nn.Sequential): - def __init__(self, in_channels, out_channels, use_batchnorm=True): + def __init__( + self, + in_channels: int, + out_channels: int, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + ): super().__init__() - layers = [ - nn.ConvTranspose2d( - in_channels, out_channels, kernel_size=4, stride=2, padding=1 - ), - nn.ReLU(inplace=True), - ] - - if use_batchnorm: - layers.insert(1, nn.BatchNorm2d(out_channels)) - - super().__init__(*layers) + conv = nn.ConvTranspose2d( + in_channels, out_channels, kernel_size=4, stride=2, padding=1 + ) + norm = modules.get_norm_layer(use_norm, out_channels) + activation = nn.ReLU(inplace=True) + super().__init__(conv, norm, activation) class DecoderBlock(nn.Module): - def __init__(self, in_channels, out_channels, use_batchnorm=True): + def __init__( + self, + in_channels: int, + out_channels: int, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + ): super().__init__() self.block = nn.Sequential( @@ -28,20 +35,20 @@ def __init__(self, in_channels, out_channels, use_batchnorm=True): in_channels, in_channels // 4, kernel_size=1, - use_batchnorm=use_batchnorm, - ), - TransposeX2( - in_channels // 4, in_channels // 4, use_batchnorm=use_batchnorm + use_norm=use_norm, ), + TransposeX2(in_channels // 4, in_channels // 4, use_norm=use_norm), modules.Conv2dReLU( in_channels // 4, out_channels, kernel_size=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ), ) - def forward(self, x, skip=None): + def forward( + self, x: torch.Tensor, skip: Optional[torch.Tensor] = None + ) -> torch.Tensor: x = self.block(x) if skip is not None: x = x + skip @@ -50,7 +57,11 @@ def forward(self, x, skip=None): class LinknetDecoder(nn.Module): def __init__( - self, encoder_channels, prefinal_channels=32, n_blocks=5, use_batchnorm=True + self, + encoder_channels: List[int], + prefinal_channels: int = 32, + n_blocks: int = 5, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", ): super().__init__() @@ -63,12 +74,16 @@ def __init__( self.blocks = nn.ModuleList( [ - DecoderBlock(channels[i], channels[i + 1], use_batchnorm=use_batchnorm) + DecoderBlock( + channels[i], + channels[i + 1], + use_norm=use_norm, + ) for i in range(n_blocks) ] ) - def forward(self, *features): + def forward(self, features: List[torch.Tensor]) -> torch.Tensor: features = features[1:] # remove first skip features = features[::-1] # reverse channels to start from head of encoder diff --git a/segmentation_models_pytorch/decoders/linknet/model.py b/segmentation_models_pytorch/decoders/linknet/model.py index 356468ed..38eac4c2 100644 --- a/segmentation_models_pytorch/decoders/linknet/model.py +++ b/segmentation_models_pytorch/decoders/linknet/model.py @@ -1,4 +1,5 @@ -from typing import Any, Optional, Union +import warnings +from typing import Any, Dict, Optional, Union, Callable from segmentation_models_pytorch.base import ( ClassificationHead, @@ -29,15 +30,27 @@ class Linknet(SegmentationModel): Default is 5 encoder_weights: One of **None** (random initialization), **"imagenet"** (pre-training on ImageNet) and other pretrained weights (see table with available weights for each encoder_name) - decoder_use_batchnorm: If **True**, BatchNorm2d layer between Conv2D and Activation layers - is used. If **"inplace"** InplaceABN will be used, allows to decrease memory consumption. - Available options are **True, False, "inplace"** + decoder_use_norm: Specifies normalization between Conv2D and activation. + Accepts the following types: + - **True**: Defaults to `"batchnorm"`. + - **False**: No normalization (`nn.Identity`). + - **str**: Specifies normalization type using default parameters. Available values: + `"batchnorm"`, `"identity"`, `"layernorm"`, `"instancenorm"`, `"inplace"`. + - **dict**: Fully customizable normalization settings. Structure: + ```python + {"type": , **kwargs} + ``` + where `norm_name` corresponds to normalization type (see above), and `kwargs` are passed directly to the normalization layer as defined in PyTorch documentation. + + **Example**: + ```python + decoder_use_norm={"type": "layernorm", "eps": 1e-2} + ``` in_channels: A number of input channels for the model, default is 3 (RGB images) classes: A number of classes for output mask (or you can think as a number of channels of output mask) activation: An activation function to apply after the final convolution layer. Available options are **"sigmoid"**, **"softmax"**, **"logsoftmax"**, **"tanh"**, **"identity"**, - **callable** and **None**. - Default is **None** + **callable** and **None**. Default is **None**. aux_params: Dictionary with parameters of the auxiliary output (classification head). Auxiliary output is build on top of encoder if **aux_params** is not **None** (default). Supported params: - classes (int): A number of classes @@ -60,10 +73,10 @@ def __init__( encoder_name: str = "resnet34", encoder_depth: int = 5, encoder_weights: Optional[str] = "imagenet", - decoder_use_batchnorm: bool = True, + decoder_use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", in_channels: int = 3, classes: int = 1, - activation: Optional[Union[str, callable]] = None, + activation: Optional[Union[str, Callable]] = None, aux_params: Optional[dict] = None, **kwargs: dict[str, Any], ): @@ -74,6 +87,15 @@ def __init__( "Encoder `{}` is not supported for Linknet".format(encoder_name) ) + decoder_use_batchnorm = kwargs.pop("decoder_use_batchnorm", None) + if decoder_use_batchnorm is not None: + warnings.warn( + "The usage of decoder_use_batchnorm is deprecated. Please modify your code for decoder_use_norm", + DeprecationWarning, + stacklevel=2, + ) + decoder_use_norm = decoder_use_batchnorm + self.encoder = get_encoder( encoder_name, in_channels=in_channels, @@ -86,7 +108,7 @@ def __init__( encoder_channels=self.encoder.out_channels, n_blocks=encoder_depth, prefinal_channels=32, - use_batchnorm=decoder_use_batchnorm, + use_norm=decoder_use_norm, ) self.segmentation_head = SegmentationHead( diff --git a/segmentation_models_pytorch/decoders/manet/decoder.py b/segmentation_models_pytorch/decoders/manet/decoder.py index 0f6af18d..39e117bf 100644 --- a/segmentation_models_pytorch/decoders/manet/decoder.py +++ b/segmentation_models_pytorch/decoders/manet/decoder.py @@ -1,3 +1,5 @@ +from typing import Any, Dict, List, Optional, Union + import torch import torch.nn as nn import torch.nn.functional as F @@ -5,9 +7,10 @@ from segmentation_models_pytorch.base import modules as md -class PAB(nn.Module): - def __init__(self, in_channels, out_channels, pab_channels=64): - super(PAB, self).__init__() +class PABBlock(nn.Module): + def __init__(self, in_channels: int, pab_channels: int = 64): + super().__init__() + # Series of 1x1 conv to generate attention feature maps self.pab_channels = pab_channels self.in_channels = in_channels @@ -17,10 +20,9 @@ def __init__(self, in_channels, out_channels, pab_channels=64): self.map_softmax = nn.Softmax(dim=1) self.out_conv = nn.Conv2d(in_channels, in_channels, kernel_size=3, padding=1) - def forward(self, x): - bsize = x.size()[0] - h = x.size()[2] - w = x.size()[3] + def forward(self, x: torch.Tensor) -> torch.Tensor: + batch_size, _, height, width = x.shape + x_top = self.top_conv(x) x_center = self.center_conv(x) x_bottom = self.bottom_conv(x) @@ -30,30 +32,42 @@ def forward(self, x): x_bottom = x_bottom.flatten(2).transpose(1, 2) sp_map = torch.matmul(x_center, x_top) - sp_map = self.map_softmax(sp_map.view(bsize, -1)).view(bsize, h * w, h * w) + sp_map = self.map_softmax(sp_map.view(batch_size, -1)) + sp_map = sp_map.view(batch_size, height * width, height * width) + sp_map = torch.matmul(sp_map, x_bottom) - sp_map = sp_map.reshape(bsize, self.in_channels, h, w) + sp_map = sp_map.reshape(batch_size, self.in_channels, height, width) + x = x + sp_map x = self.out_conv(x) return x -class MFAB(nn.Module): +class MFABBlock(nn.Module): def __init__( - self, in_channels, skip_channels, out_channels, use_batchnorm=True, reduction=16 + self, + in_channels: int, + skip_channels: int, + out_channels: int, + interpolation_mode: str = "nearest", + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + reduction: int = 16, ): - # MFAB is just a modified version of SE-blocks, one for skip, one for input - super(MFAB, self).__init__() + # MFABBlock is just a modified version of SE-blocks, one for skip, one for input + super().__init__() self.hl_conv = nn.Sequential( md.Conv2dReLU( in_channels, in_channels, kernel_size=3, padding=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ), md.Conv2dReLU( - in_channels, skip_channels, kernel_size=1, use_batchnorm=use_batchnorm + in_channels, + skip_channels, + kernel_size=1, + use_norm=use_norm, ), ) reduced_channels = max(1, skip_channels // reduction) @@ -77,19 +91,22 @@ def __init__( out_channels, kernel_size=3, padding=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ) self.conv2 = md.Conv2dReLU( out_channels, out_channels, kernel_size=3, padding=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ) + self.interpolation_mode = interpolation_mode - def forward(self, x, skip=None): + def forward( + self, x: torch.Tensor, skip: Optional[torch.Tensor] = None + ) -> torch.Tensor: x = self.hl_conv(x) - x = F.interpolate(x, scale_factor=2, mode="nearest") + x = F.interpolate(x, scale_factor=2.0, mode=self.interpolation_mode) attention_hl = self.SE_hl(x) if skip is not None: attention_ll = self.SE_ll(skip) @@ -102,25 +119,35 @@ def forward(self, x, skip=None): class DecoderBlock(nn.Module): - def __init__(self, in_channels, skip_channels, out_channels, use_batchnorm=True): + def __init__( + self, + in_channels: int, + skip_channels: int, + out_channels: int, + interpolation_mode: str = "nearest", + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + ): super().__init__() self.conv1 = md.Conv2dReLU( in_channels + skip_channels, out_channels, kernel_size=3, padding=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ) self.conv2 = md.Conv2dReLU( out_channels, out_channels, kernel_size=3, padding=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ) + self.interpolation_mode = interpolation_mode - def forward(self, x, skip=None): - x = F.interpolate(x, scale_factor=2, mode="nearest") + def forward( + self, x: torch.Tensor, skip: Optional[torch.Tensor] = None + ) -> torch.Tensor: + x = F.interpolate(x, scale_factor=2.0, mode=self.interpolation_mode) if skip is not None: x = torch.cat([x, skip], dim=1) x = self.conv1(x) @@ -131,12 +158,13 @@ def forward(self, x, skip=None): class MAnetDecoder(nn.Module): def __init__( self, - encoder_channels, - decoder_channels, - n_blocks=5, - reduction=16, - use_batchnorm=True, - pab_channels=64, + encoder_channels: List[int], + decoder_channels: List[int], + n_blocks: int = 5, + reduction: int = 16, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + pab_channels: int = 64, + interpolation_mode: str = "nearest", ): super().__init__() @@ -159,12 +187,14 @@ def __init__( skip_channels = list(encoder_channels[1:]) + [0] out_channels = decoder_channels - self.center = PAB(head_channels, head_channels, pab_channels=pab_channels) + self.center = PABBlock(head_channels, pab_channels=pab_channels) # combine decoder keyword arguments - kwargs = dict(use_batchnorm=use_batchnorm) # no attention type here + kwargs = dict( + use_norm=use_norm, interpolation_mode=interpolation_mode + ) # no attention type here blocks = [ - MFAB(in_ch, skip_ch, out_ch, reduction=reduction, **kwargs) + MFABBlock(in_ch, skip_ch, out_ch, reduction=reduction, **kwargs) if skip_ch > 0 else DecoderBlock(in_ch, skip_ch, out_ch, **kwargs) for in_ch, skip_ch, out_ch in zip(in_channels, skip_channels, out_channels) @@ -172,7 +202,7 @@ def __init__( # for the last we dont have skip connection -> use simple decoder block self.blocks = nn.ModuleList(blocks) - def forward(self, *features): + def forward(self, features: List[torch.Tensor]) -> torch.Tensor: features = features[1:] # remove first skip with same spatial resolution features = features[::-1] # reverse channels to start from head of encoder diff --git a/segmentation_models_pytorch/decoders/manet/model.py b/segmentation_models_pytorch/decoders/manet/model.py index 6ed59207..568a7f58 100644 --- a/segmentation_models_pytorch/decoders/manet/model.py +++ b/segmentation_models_pytorch/decoders/manet/model.py @@ -1,4 +1,5 @@ -from typing import Any, List, Optional, Union +import warnings +from typing import Any, Dict, Optional, Union, Sequence, Callable from segmentation_models_pytorch.base import ( ClassificationHead, @@ -29,17 +30,31 @@ class MAnet(SegmentationModel): other pretrained weights (see table with available weights for each encoder_name) decoder_channels: List of integers which specify **in_channels** parameter for convolutions used in decoder. Length of the list should be the same as **encoder_depth** - decoder_use_batchnorm: If **True**, BatchNorm2d layer between Conv2D and Activation layers - is used. If **"inplace"** InplaceABN will be used, allows to decrease memory consumption. - Available options are **True, False, "inplace"** + decoder_use_norm: Specifies normalization between Conv2D and activation. + Accepts the following types: + - **True**: Defaults to `"batchnorm"`. + - **False**: No normalization (`nn.Identity`). + - **str**: Specifies normalization type using default parameters. Available values: + `"batchnorm"`, `"identity"`, `"layernorm"`, `"instancenorm"`, `"inplace"`. + - **dict**: Fully customizable normalization settings. Structure: + ```python + {"type": , **kwargs} + ``` + where `norm_name` corresponds to normalization type (see above), and `kwargs` are passed directly to the normalization layer as defined in PyTorch documentation. + + **Example**: + ```python + decoder_use_norm={"type": "layernorm", "eps": 1e-2} + ``` decoder_pab_channels: A number of channels for PAB module in decoder. Default is 64. + decoder_interpolation: Interpolation mode used in decoder of the model. Available options are + **"nearest"**, **"bilinear"**, **"bicubic"**, **"area"**, **"nearest-exact"**. Default is **"nearest"**. in_channels: A number of input channels for the model, default is 3 (RGB images) classes: A number of classes for output mask (or you can think as a number of channels of output mask) activation: An activation function to apply after the final convolution layer. Available options are **"sigmoid"**, **"softmax"**, **"logsoftmax"**, **"tanh"**, **"identity"**, - **callable** and **None**. - Default is **None** + **callable** and **None**. Default is **None**. aux_params: Dictionary with parameters of the auxiliary output (classification head). Auxiliary output is build on top of encoder if **aux_params** is not **None** (default). Supported params: - classes (int): A number of classes @@ -63,17 +78,27 @@ def __init__( encoder_name: str = "resnet34", encoder_depth: int = 5, encoder_weights: Optional[str] = "imagenet", - decoder_use_batchnorm: bool = True, - decoder_channels: List[int] = (256, 128, 64, 32, 16), + decoder_use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + decoder_channels: Sequence[int] = (256, 128, 64, 32, 16), decoder_pab_channels: int = 64, + decoder_interpolation: str = "nearest", in_channels: int = 3, classes: int = 1, - activation: Optional[Union[str, callable]] = None, + activation: Optional[Union[str, Callable]] = None, aux_params: Optional[dict] = None, **kwargs: dict[str, Any], ): super().__init__() + decoder_use_batchnorm = kwargs.pop("decoder_use_batchnorm", None) + if decoder_use_batchnorm is not None: + warnings.warn( + "The usage of decoder_use_batchnorm is deprecated. Please modify your code for decoder_use_norm", + DeprecationWarning, + stacklevel=2, + ) + decoder_use_norm = decoder_use_batchnorm + self.encoder = get_encoder( encoder_name, in_channels=in_channels, @@ -86,8 +111,9 @@ def __init__( encoder_channels=self.encoder.out_channels, decoder_channels=decoder_channels, n_blocks=encoder_depth, - use_batchnorm=decoder_use_batchnorm, + use_norm=decoder_use_norm, pab_channels=decoder_pab_channels, + interpolation_mode=decoder_interpolation, ) self.segmentation_head = SegmentationHead( diff --git a/segmentation_models_pytorch/decoders/pan/decoder.py b/segmentation_models_pytorch/decoders/pan/decoder.py index fa0bb261..729c76ed 100644 --- a/segmentation_models_pytorch/decoders/pan/decoder.py +++ b/segmentation_models_pytorch/decoders/pan/decoder.py @@ -1,5 +1,5 @@ from collections.abc import Sequence -from typing import Literal +from typing import Literal, List import torch import torch.nn as nn @@ -31,18 +31,22 @@ def __init__( bias=bias, groups=groups, ) + self.activation = nn.ReLU(inplace=True) + self.bn = nn.BatchNorm2d(out_channels) + self.add_relu = add_relu self.interpolate = interpolate - self.bn = nn.BatchNorm2d(out_channels) - self.activation = nn.ReLU(inplace=True) - def forward(self, x): + def forward(self, x: torch.Tensor) -> torch.Tensor: x = self.conv(x) x = self.bn(x) + if self.add_relu: x = self.activation(x) + if self.interpolate: - x = F.interpolate(x, scale_factor=2, mode="bilinear", align_corners=True) + x = F.interpolate(x, scale_factor=2.0, mode="bilinear", align_corners=True) + return x @@ -50,7 +54,7 @@ class FPABlock(nn.Module): def __init__( self, in_channels: int, out_channels: int, upscale_mode: str = "bilinear" ): - super(FPABlock, self).__init__() + super().__init__() self.upscale_mode = upscale_mode if self.upscale_mode == "bilinear": @@ -70,7 +74,7 @@ def __init__( ), ) - # midddle branch + # middle branch self.mid = nn.Sequential( ConvBnRelu( in_channels=in_channels, @@ -112,41 +116,64 @@ def __init__( in_channels=1, out_channels=1, kernel_size=7, stride=1, padding=3 ) - def forward(self, x): - h, w = x.size(2), x.size(3) - b1 = self.branch1(x) - upscale_parameters = dict( - mode=self.upscale_mode, align_corners=self.align_corners + def forward(self, x: torch.Tensor) -> torch.Tensor: + _, _, height, width = x.shape + + branch1_output = self.branch1(x) + branch1_output = F.interpolate( + branch1_output, + size=(height, width), + mode=self.upscale_mode, + align_corners=self.align_corners, ) - b1 = F.interpolate(b1, size=(h, w), **upscale_parameters) - mid = self.mid(x) + middle_output = self.mid(x) + x1 = self.down1(x) x2 = self.down2(x1) x3 = self.down3(x2) - x3 = F.interpolate(x3, size=(h // 4, w // 4), **upscale_parameters) + x3 = F.interpolate( + x3, + size=(height // 4, width // 4), + mode=self.upscale_mode, + align_corners=self.align_corners, + ) x2 = self.conv2(x2) x = x2 + x3 - x = F.interpolate(x, size=(h // 2, w // 2), **upscale_parameters) + x = F.interpolate( + x, + size=(height // 2, width // 2), + mode=self.upscale_mode, + align_corners=self.align_corners, + ) x1 = self.conv1(x1) x = x + x1 - x = F.interpolate(x, size=(h, w), **upscale_parameters) + x = F.interpolate( + x, + size=(height, width), + mode=self.upscale_mode, + align_corners=self.align_corners, + ) + + x = torch.mul(x, middle_output) + x = x + branch1_output - x = torch.mul(x, mid) - x = x + b1 return x class GAUBlock(nn.Module): def __init__( - self, in_channels: int, out_channels: int, upscale_mode: str = "bilinear" + self, + in_channels: int, + out_channels: int, + interpolation_mode: str = "bilinear", ): super(GAUBlock, self).__init__() - self.upscale_mode = upscale_mode - self.align_corners = True if upscale_mode == "bilinear" else None + self.interpolation_mode = interpolation_mode + self.align_corners = True if interpolation_mode == "bilinear" else None self.conv1 = nn.Sequential( nn.AdaptiveAvgPool2d(1), @@ -162,15 +189,18 @@ def __init__( in_channels=in_channels, out_channels=out_channels, kernel_size=3, padding=1 ) - def forward(self, x, y): + def forward(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor: """ Args: x: low level feature y: high level feature """ - h, w = x.size(2), x.size(3) + height, width = x.shape[2:] y_up = F.interpolate( - y, size=(h, w), mode=self.upscale_mode, align_corners=self.align_corners + y, + size=(height, width), + mode=self.interpolation_mode, + align_corners=self.align_corners, ) x = self.conv2(x) y = self.conv1(y) @@ -184,7 +214,7 @@ def __init__( encoder_channels: Sequence[int], encoder_depth: Literal[3, 4, 5], decoder_channels: int, - upscale_mode: str = "bilinear", + interpolation_mode: str = "bilinear", ): super().__init__() @@ -205,22 +235,22 @@ def __init__( self.gau3 = GAUBlock( in_channels=encoder_channels[2], out_channels=decoder_channels, - upscale_mode=upscale_mode, + interpolation_mode=interpolation_mode, ) if encoder_depth >= 4: self.gau2 = GAUBlock( in_channels=encoder_channels[1], out_channels=decoder_channels, - upscale_mode=upscale_mode, + interpolation_mode=interpolation_mode, ) if encoder_depth >= 3: self.gau1 = GAUBlock( in_channels=encoder_channels[0], out_channels=decoder_channels, - upscale_mode=upscale_mode, + interpolation_mode=interpolation_mode, ) - def forward(self, *features): + def forward(self, features: List[torch.Tensor]) -> torch.Tensor: features = features[2:] # remove first and second skip out = self.fpa(features[-1]) # 1/16 or 1/32 diff --git a/segmentation_models_pytorch/decoders/pan/model.py b/segmentation_models_pytorch/decoders/pan/model.py index 6d5e78c2..0ea1dfbb 100644 --- a/segmentation_models_pytorch/decoders/pan/model.py +++ b/segmentation_models_pytorch/decoders/pan/model.py @@ -1,4 +1,5 @@ from typing import Any, Callable, Literal, Optional, Union +import warnings from segmentation_models_pytorch.base import ( ClassificationHead, @@ -30,12 +31,13 @@ class PAN(SegmentationModel): encoder_output_stride: 16 or 32, if 16 use dilation in encoder last layer. Doesn't work with ***ception***, **vgg***, **densenet*`** backbones.Default is 16. decoder_channels: A number of convolution layer filters in decoder blocks + decoder_interpolation: Interpolation mode used in decoder of the model. Available options are + **"nearest"**, **"bilinear"**, **"bicubic"**, **"area"**, **"nearest-exact"**. Default is **"bilinear"**. in_channels: A number of input channels for the model, default is 3 (RGB images) classes: A number of classes for output mask (or you can think as a number of channels of output mask) activation: An activation function to apply after the final convolution layer. Available options are **"sigmoid"**, **"softmax"**, **"logsoftmax"**, **"tanh"**, **"identity"**, - **callable** and **None**. - Default is **None** + **callable** and **None**. Default is **None**. upsampling: Final upsampling factor. Default is 4 to preserve input-output spatial shape identity aux_params: Dictionary with parameters of the auxiliary output (classification head). Auxiliary output is build on top of encoder if **aux_params** is not **None** (default). Supported params: @@ -62,6 +64,7 @@ def __init__( encoder_weights: Optional[str] = "imagenet", encoder_output_stride: Literal[16, 32] = 16, decoder_channels: int = 32, + decoder_interpolation: str = "bilinear", in_channels: int = 3, classes: int = 1, activation: Optional[Union[str, Callable]] = None, @@ -78,6 +81,15 @@ def __init__( ) ) + upscale_mode = kwargs.pop("upscale_mode", None) + if upscale_mode is not None: + warnings.warn( + "The usage of upscale_mode is deprecated. Please modify your code for decoder_interpolation", + DeprecationWarning, + stacklevel=2, + ) + decoder_interpolation = upscale_mode + self.encoder = get_encoder( encoder_name, in_channels=in_channels, @@ -91,6 +103,7 @@ def __init__( encoder_channels=self.encoder.out_channels, encoder_depth=encoder_depth, decoder_channels=decoder_channels, + interpolation_mode=decoder_interpolation, ) self.segmentation_head = SegmentationHead( diff --git a/segmentation_models_pytorch/decoders/pspnet/decoder.py b/segmentation_models_pytorch/decoders/pspnet/decoder.py index 40d2e945..80ad289c 100644 --- a/segmentation_models_pytorch/decoders/pspnet/decoder.py +++ b/segmentation_models_pytorch/decoders/pspnet/decoder.py @@ -1,3 +1,5 @@ +from typing import Any, Dict, List, Tuple, Union + import torch import torch.nn as nn import torch.nn.functional as F @@ -6,26 +8,39 @@ class PSPBlock(nn.Module): - def __init__(self, in_channels, out_channels, pool_size, use_bathcnorm=True): + def __init__( + self, + in_channels: int, + out_channels: int, + pool_size: int, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + ): super().__init__() + if pool_size == 1: - use_bathcnorm = False # PyTorch does not support BatchNorm for 1x1 shape + use_norm = "identity" # PyTorch does not support BatchNorm for 1x1 shape + self.pool = nn.Sequential( nn.AdaptiveAvgPool2d(output_size=(pool_size, pool_size)), modules.Conv2dReLU( - in_channels, out_channels, (1, 1), use_batchnorm=use_bathcnorm + in_channels, out_channels, kernel_size=1, use_norm=use_norm ), ) - def forward(self, x): - h, w = x.size(2), x.size(3) + def forward(self, x: torch.Tensor) -> torch.Tensor: + height, width = x.shape[2:] x = self.pool(x) - x = F.interpolate(x, size=(h, w), mode="bilinear", align_corners=True) + x = F.interpolate(x, size=(height, width), mode="bilinear", align_corners=True) return x class PSPModule(nn.Module): - def __init__(self, in_channels, sizes=(1, 2, 3, 6), use_bathcnorm=True): + def __init__( + self, + in_channels: int, + sizes: Tuple[int, ...] = (1, 2, 3, 6), + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + ): super().__init__() self.blocks = nn.ModuleList( @@ -34,7 +49,7 @@ def __init__(self, in_channels, sizes=(1, 2, 3, 6), use_bathcnorm=True): in_channels, in_channels // len(sizes), size, - use_bathcnorm=use_bathcnorm, + use_norm=use_norm, ) for size in sizes ] @@ -48,26 +63,30 @@ def forward(self, x): class PSPDecoder(nn.Module): def __init__( - self, encoder_channels, use_batchnorm=True, out_channels=512, dropout=0.2 + self, + encoder_channels: List[int], + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + out_channels: int = 512, + dropout: float = 0.2, ): super().__init__() self.psp = PSPModule( in_channels=encoder_channels[-1], sizes=(1, 2, 3, 6), - use_bathcnorm=use_batchnorm, + use_norm=use_norm, ) self.conv = modules.Conv2dReLU( in_channels=encoder_channels[-1] * 2, out_channels=out_channels, kernel_size=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ) self.dropout = nn.Dropout2d(p=dropout) - def forward(self, *features): + def forward(self, features: List[torch.Tensor]) -> torch.Tensor: x = features[-1] x = self.psp(x) x = self.conv(x) diff --git a/segmentation_models_pytorch/decoders/pspnet/model.py b/segmentation_models_pytorch/decoders/pspnet/model.py index 8b99b3da..f7740891 100644 --- a/segmentation_models_pytorch/decoders/pspnet/model.py +++ b/segmentation_models_pytorch/decoders/pspnet/model.py @@ -1,4 +1,5 @@ -from typing import Any, Optional, Union +import warnings +from typing import Any, Dict, Optional, Union, Callable from segmentation_models_pytorch.base import ( ClassificationHead, @@ -28,16 +29,28 @@ class PSPNet(SegmentationModel): encoder_weights: One of **None** (random initialization), **"imagenet"** (pre-training on ImageNet) and other pretrained weights (see table with available weights for each encoder_name) psp_out_channels: A number of filters in Spatial Pyramid - psp_use_batchnorm: If **True**, BatchNorm2d layer between Conv2D and Activation layers - is used. If **"inplace"** InplaceABN will be used, allows to decrease memory consumption. - Available options are **True, False, "inplace"** + decoder_use_norm: Specifies normalization between Conv2D and activation. + Accepts the following types: + - **True**: Defaults to `"batchnorm"`. + - **False**: No normalization (`nn.Identity`). + - **str**: Specifies normalization type using default parameters. Available values: + `"batchnorm"`, `"identity"`, `"layernorm"`, `"instancenorm"`, `"inplace"`. + - **dict**: Fully customizable normalization settings. Structure: + ```python + {"type": , **kwargs} + ``` + where `norm_name` corresponds to normalization type (see above), and `kwargs` are passed directly to the normalization layer as defined in PyTorch documentation. + + **Example**: + ```python + decoder_use_norm={"type": "layernorm", "eps": 1e-2} + ``` psp_dropout: Spatial dropout rate in [0, 1) used in Spatial Pyramid in_channels: A number of input channels for the model, default is 3 (RGB images) classes: A number of classes for output mask (or you can think as a number of channels of output mask) activation: An activation function to apply after the final convolution layer. Available options are **"sigmoid"**, **"softmax"**, **"logsoftmax"**, **"tanh"**, **"identity"**, - **callable** and **None**. - Default is **None** + **callable** and **None**. Default is **None**. upsampling: Final upsampling factor. Default is 8 to preserve input-output spatial shape identity aux_params: Dictionary with parameters of the auxiliary output (classification head). Auxiliary output is build on top of encoder if **aux_params** is not **None** (default). Supported params: @@ -62,17 +75,26 @@ def __init__( encoder_weights: Optional[str] = "imagenet", encoder_depth: int = 3, psp_out_channels: int = 512, - psp_use_batchnorm: bool = True, + decoder_use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", psp_dropout: float = 0.2, in_channels: int = 3, classes: int = 1, - activation: Optional[Union[str, callable]] = None, + activation: Optional[Union[str, Callable]] = None, upsampling: int = 8, aux_params: Optional[dict] = None, **kwargs: dict[str, Any], ): super().__init__() + psp_use_batchnorm = kwargs.pop("psp_use_batchnorm", None) + if psp_use_batchnorm is not None: + warnings.warn( + "The usage of psp_use_batchnorm is deprecated. Please modify your code for decoder_use_norm", + DeprecationWarning, + stacklevel=2, + ) + decoder_use_norm = psp_use_batchnorm + self.encoder = get_encoder( encoder_name, in_channels=in_channels, @@ -83,7 +105,7 @@ def __init__( self.decoder = PSPDecoder( encoder_channels=self.encoder.out_channels, - use_batchnorm=psp_use_batchnorm, + use_norm=decoder_use_norm, out_channels=psp_out_channels, dropout=psp_dropout, ) diff --git a/segmentation_models_pytorch/decoders/segformer/decoder.py b/segmentation_models_pytorch/decoders/segformer/decoder.py index daa78b37..2bfadfff 100644 --- a/segmentation_models_pytorch/decoders/segformer/decoder.py +++ b/segmentation_models_pytorch/decoders/segformer/decoder.py @@ -2,11 +2,12 @@ import torch.nn as nn import torch.nn.functional as F +from typing import List from segmentation_models_pytorch.base import modules as md class MLP(nn.Module): - def __init__(self, skip_channels, segmentation_channels): + def __init__(self, skip_channels: int, segmentation_channels: int): super().__init__() self.linear = nn.Linear(skip_channels, segmentation_channels) @@ -22,9 +23,9 @@ def forward(self, x: torch.Tensor): class SegformerDecoder(nn.Module): def __init__( self, - encoder_channels, - encoder_depth=5, - segmentation_channels=256, + encoder_channels: List[int], + encoder_depth: int = 5, + segmentation_channels: int = 256, ): super().__init__() @@ -36,9 +37,9 @@ def __init__( ) if encoder_channels[1] == 0: - encoder_channels = tuple( + encoder_channels = [ channel for index, channel in enumerate(encoder_channels) if index != 1 - ) + ] encoder_channels = encoder_channels[::-1] self.mlp_stage = nn.ModuleList( @@ -49,10 +50,10 @@ def __init__( in_channels=(len(encoder_channels) - 1) * segmentation_channels, out_channels=segmentation_channels, kernel_size=1, - use_batchnorm=True, + use_norm="batchnorm", ) - def forward(self, *features): + def forward(self, features: List[torch.Tensor]) -> torch.Tensor: # Resize all features to the size of the largest feature target_size = [dim // 4 for dim in features[0].shape[2:]] @@ -60,8 +61,8 @@ def forward(self, *features): features = features[::-1] # reverse channels to start from head of encoder resized_features = [] - for feature, stage in zip(features, self.mlp_stage): - feature = stage(feature) + for i, mlp_layer in enumerate(self.mlp_stage): + feature = mlp_layer(features[i]) resized_feature = F.interpolate( feature, size=target_size, mode="bilinear", align_corners=False ) diff --git a/segmentation_models_pytorch/decoders/segformer/model.py b/segmentation_models_pytorch/decoders/segformer/model.py index 45805de7..03deeeef 100644 --- a/segmentation_models_pytorch/decoders/segformer/model.py +++ b/segmentation_models_pytorch/decoders/segformer/model.py @@ -28,8 +28,8 @@ class Segformer(SegmentationModel): classes: A number of classes for output mask (or you can think as a number of channels of output mask) activation: An activation function to apply after the final convolution layer. Available options are **"sigmoid"**, **"softmax"**, **"logsoftmax"**, **"tanh"**, **"identity"**, - **callable** and **None**. - Default is **None** + **callable** and **None**. Default is **None**. + upsampling: A number to upsample the output of the model, default is 4 (same size as input) aux_params: Dictionary with parameters of the auxiliary output (classification head). Auxiliary output is build on top of encoder if **aux_params** is not **None** (default). Supported params: - classes (int): A number of classes @@ -57,6 +57,7 @@ def __init__( in_channels: int = 3, classes: int = 1, activation: Optional[Union[str, Callable]] = None, + upsampling: int = 4, aux_params: Optional[dict] = None, **kwargs: dict[str, Any], ): @@ -81,7 +82,7 @@ def __init__( out_channels=classes, activation=activation, kernel_size=1, - upsampling=4, + upsampling=upsampling, ) if aux_params is not None: diff --git a/segmentation_models_pytorch/decoders/unet/decoder.py b/segmentation_models_pytorch/decoders/unet/decoder.py index 33061542..cfeb267e 100644 --- a/segmentation_models_pytorch/decoders/unet/decoder.py +++ b/segmentation_models_pytorch/decoders/unet/decoder.py @@ -1,3 +1,5 @@ +from typing import Any, Dict, List, Optional, Sequence, Union + import torch import torch.nn as nn import torch.nn.functional as F @@ -5,22 +7,26 @@ from segmentation_models_pytorch.base import modules as md -class DecoderBlock(nn.Module): +class UnetDecoderBlock(nn.Module): + """A decoder block in the U-Net architecture that performs upsampling and feature fusion.""" + def __init__( self, - in_channels, - skip_channels, - out_channels, - use_batchnorm=True, - attention_type=None, + in_channels: int, + skip_channels: int, + out_channels: int, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + attention_type: Optional[str] = None, + interpolation_mode: str = "nearest", ): super().__init__() + self.interpolation_mode = interpolation_mode self.conv1 = md.Conv2dReLU( in_channels + skip_channels, out_channels, kernel_size=3, padding=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ) self.attention1 = md.Attention( attention_type, in_channels=in_channels + skip_channels @@ -30,49 +36,73 @@ def __init__( out_channels, kernel_size=3, padding=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ) self.attention2 = md.Attention(attention_type, in_channels=out_channels) - def forward(self, x, skip=None): - x = F.interpolate(x, scale_factor=2, mode="nearest") - if skip is not None: - x = torch.cat([x, skip], dim=1) - x = self.attention1(x) - x = self.conv1(x) - x = self.conv2(x) - x = self.attention2(x) - return x + def forward( + self, + feature_map: torch.Tensor, + target_height: int, + target_width: int, + skip_connection: Optional[torch.Tensor] = None, + ) -> torch.Tensor: + feature_map = F.interpolate( + feature_map, + size=(target_height, target_width), + mode=self.interpolation_mode, + ) + if skip_connection is not None: + feature_map = torch.cat([feature_map, skip_connection], dim=1) + feature_map = self.attention1(feature_map) + feature_map = self.conv1(feature_map) + feature_map = self.conv2(feature_map) + feature_map = self.attention2(feature_map) + return feature_map + +class UnetCenterBlock(nn.Sequential): + """Center block of the Unet decoder. Applied to the last feature map of the encoder.""" -class CenterBlock(nn.Sequential): - def __init__(self, in_channels, out_channels, use_batchnorm=True): + def __init__( + self, + in_channels: int, + out_channels: int, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + ): conv1 = md.Conv2dReLU( in_channels, out_channels, kernel_size=3, padding=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ) conv2 = md.Conv2dReLU( out_channels, out_channels, kernel_size=3, padding=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ) super().__init__(conv1, conv2) class UnetDecoder(nn.Module): + """The decoder part of the U-Net architecture. + + Takes encoded features from different stages of the encoder and progressively upsamples them while + combining with skip connections. This helps preserve fine-grained details in the final segmentation. + """ + def __init__( self, - encoder_channels, - decoder_channels, - n_blocks=5, - use_batchnorm=True, - attention_type=None, - center=False, + encoder_channels: Sequence[int], + decoder_channels: Sequence[int], + n_blocks: int = 5, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + attention_type: Optional[str] = None, + add_center_block: bool = False, + interpolation_mode: str = "nearest", ): super().__init__() @@ -94,31 +124,47 @@ def __init__( skip_channels = list(encoder_channels[1:]) + [0] out_channels = decoder_channels - if center: - self.center = CenterBlock( - head_channels, head_channels, use_batchnorm=use_batchnorm + if add_center_block: + self.center = UnetCenterBlock( + head_channels, + head_channels, + use_norm=use_norm, ) else: self.center = nn.Identity() # combine decoder keyword arguments - kwargs = dict(use_batchnorm=use_batchnorm, attention_type=attention_type) - blocks = [ - DecoderBlock(in_ch, skip_ch, out_ch, **kwargs) - for in_ch, skip_ch, out_ch in zip(in_channels, skip_channels, out_channels) - ] - self.blocks = nn.ModuleList(blocks) - - def forward(self, *features): + self.blocks = nn.ModuleList() + for block_in_channels, block_skip_channels, block_out_channels in zip( + in_channels, skip_channels, out_channels + ): + block = UnetDecoderBlock( + block_in_channels, + block_skip_channels, + block_out_channels, + use_norm=use_norm, + attention_type=attention_type, + interpolation_mode=interpolation_mode, + ) + self.blocks.append(block) + + def forward(self, features: List[torch.Tensor]) -> torch.Tensor: + # spatial shapes of features: [hw, hw/2, hw/4, hw/8, ...] + spatial_shapes = [feature.shape[2:] for feature in features] + spatial_shapes = spatial_shapes[::-1] + features = features[1:] # remove first skip with same spatial resolution features = features[::-1] # reverse channels to start from head of encoder head = features[0] - skips = features[1:] + skip_connections = features[1:] x = self.center(head) + for i, decoder_block in enumerate(self.blocks): - skip = skips[i] if i < len(skips) else None - x = decoder_block(x, skip) + # upsample to the next spatial shape + height, width = spatial_shapes[i + 1] + skip_connection = skip_connections[i] if i < len(skip_connections) else None + x = decoder_block(x, height, width, skip_connection=skip_connection) return x diff --git a/segmentation_models_pytorch/decoders/unet/model.py b/segmentation_models_pytorch/decoders/unet/model.py index 547581eb..3df36e32 100644 --- a/segmentation_models_pytorch/decoders/unet/model.py +++ b/segmentation_models_pytorch/decoders/unet/model.py @@ -1,4 +1,5 @@ -from typing import Any, Optional, Union, Tuple, Callable +import warnings +from typing import Any, Dict, Optional, Union, Callable, Sequence from segmentation_models_pytorch.base import ( ClassificationHead, @@ -12,10 +13,21 @@ class Unet(SegmentationModel): - """Unet_ is a fully convolution neural network for image semantic segmentation. Consist of *encoder* - and *decoder* parts connected with *skip connections*. Encoder extract features of different spatial - resolution (skip connections) which are used by decoder to define accurate segmentation mask. Use *concatenation* - for fusing decoder blocks with skip connections. + """ + U-Net is a fully convolutional neural network architecture designed for semantic image segmentation. + + It consists of two main parts: + + 1. An encoder (downsampling path) that extracts increasingly abstract features + 2. A decoder (upsampling path) that gradually recovers spatial details + + The key is the use of skip connections between corresponding encoder and decoder layers. + These connections allow the decoder to access fine-grained details from earlier encoder layers, + which helps produce more precise segmentation masks. + + The skip connections work by concatenating feature maps from the encoder directly into the decoder + at corresponding resolutions. This helps preserve important spatial information that would + otherwise be lost during the encoding process. Args: encoder_name: Name of the classification model that will be used as an encoder (a.k.a backbone) @@ -28,17 +40,31 @@ class Unet(SegmentationModel): other pretrained weights (see table with available weights for each encoder_name) decoder_channels: List of integers which specify **in_channels** parameter for convolutions used in decoder. Length of the list should be the same as **encoder_depth** - decoder_use_batchnorm: If **True**, BatchNorm2d layer between Conv2D and Activation layers - is used. If **"inplace"** InplaceABN will be used, allows to decrease memory consumption. - Available options are **True, False, "inplace"** + decoder_use_norm: Specifies normalization between Conv2D and activation. + Accepts the following types: + - **True**: Defaults to `"batchnorm"`. + - **False**: No normalization (`nn.Identity`). + - **str**: Specifies normalization type using default parameters. Available values: + `"batchnorm"`, `"identity"`, `"layernorm"`, `"instancenorm"`, `"inplace"`. + - **dict**: Fully customizable normalization settings. Structure: + ```python + {"type": , **kwargs} + ``` + where `norm_name` corresponds to normalization type (see above), and `kwargs` are passed directly to the normalization layer as defined in PyTorch documentation. + + **Example**: + ```python + decoder_use_norm={"type": "layernorm", "eps": 1e-2} + ``` decoder_attention_type: Attention module used in decoder of the model. Available options are **None** and **scse** (https://arxiv.org/abs/1808.08127). + decoder_interpolation: Interpolation mode used in decoder of the model. Available options are + **"nearest"**, **"bilinear"**, **"bicubic"**, **"area"**, **"nearest-exact"**. Default is **"nearest"**. in_channels: A number of input channels for the model, default is 3 (RGB images) classes: A number of classes for output mask (or you can think as a number of channels of output mask) activation: An activation function to apply after the final convolution layer. Available options are **"sigmoid"**, **"softmax"**, **"logsoftmax"**, **"tanh"**, **"identity"**, - **callable** and **None**. - Default is **None** + **callable** and **None**. Default is **None**. aux_params: Dictionary with parameters of the auxiliary output (classification head). Auxiliary output is build on top of encoder if **aux_params** is not **None** (default). Supported params: - classes (int): A number of classes @@ -51,20 +77,41 @@ class Unet(SegmentationModel): Returns: ``torch.nn.Module``: Unet + Example: + .. code-block:: python + + import torch + import segmentation_models_pytorch as smp + + model = smp.Unet("resnet18", encoder_weights="imagenet", classes=5) + model.eval() + + # generate random images + images = torch.rand(2, 3, 256, 256) + + with torch.inference_mode(): + mask = model(images) + + print(mask.shape) + # torch.Size([2, 5, 256, 256]) + .. _Unet: https://arxiv.org/abs/1505.04597 """ + requires_divisible_input_shape = False + @supports_config_loading def __init__( self, encoder_name: str = "resnet34", encoder_depth: int = 5, encoder_weights: Optional[str] = "imagenet", - decoder_use_batchnorm: bool = True, - decoder_channels: Tuple[int, ...] = (256, 128, 64, 32, 16), + decoder_use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + decoder_channels: Sequence[int] = (256, 128, 64, 32, 16), decoder_attention_type: Optional[str] = None, + decoder_interpolation: str = "nearest", in_channels: int = 3, classes: int = 1, activation: Optional[Union[str, Callable]] = None, @@ -73,6 +120,15 @@ def __init__( ): super().__init__() + decoder_use_batchnorm = kwargs.pop("decoder_use_batchnorm", None) + if decoder_use_batchnorm is not None: + warnings.warn( + "The usage of decoder_use_batchnorm is deprecated. Please modify your code for decoder_use_norm", + DeprecationWarning, + stacklevel=2, + ) + decoder_use_norm = decoder_use_batchnorm + self.encoder = get_encoder( encoder_name, in_channels=in_channels, @@ -81,13 +137,16 @@ def __init__( **kwargs, ) + add_center_block = encoder_name.startswith("vgg") + self.decoder = UnetDecoder( encoder_channels=self.encoder.out_channels, decoder_channels=decoder_channels, n_blocks=encoder_depth, - use_batchnorm=decoder_use_batchnorm, - center=True if encoder_name.startswith("vgg") else False, + use_norm=decoder_use_norm, + add_center_block=add_center_block, attention_type=decoder_attention_type, + interpolation_mode=decoder_interpolation, ) self.segmentation_head = SegmentationHead( diff --git a/segmentation_models_pytorch/decoders/unetplusplus/decoder.py b/segmentation_models_pytorch/decoders/unetplusplus/decoder.py index 54ec7576..b42a73a9 100644 --- a/segmentation_models_pytorch/decoders/unetplusplus/decoder.py +++ b/segmentation_models_pytorch/decoders/unetplusplus/decoder.py @@ -2,17 +2,20 @@ import torch.nn as nn import torch.nn.functional as F +from typing import Any, Dict, List, Optional, Union, Sequence + from segmentation_models_pytorch.base import modules as md class DecoderBlock(nn.Module): def __init__( self, - in_channels, - skip_channels, - out_channels, - use_batchnorm=True, - attention_type=None, + in_channels: int, + skip_channels: int, + out_channels: int, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + attention_type: Optional[str] = None, + interpolation_mode: str = "nearest", ): super().__init__() self.conv1 = md.Conv2dReLU( @@ -20,7 +23,7 @@ def __init__( out_channels, kernel_size=3, padding=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ) self.attention1 = md.Attention( attention_type, in_channels=in_channels + skip_channels @@ -30,12 +33,15 @@ def __init__( out_channels, kernel_size=3, padding=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ) self.attention2 = md.Attention(attention_type, in_channels=out_channels) + self.interpolation_mode = interpolation_mode - def forward(self, x, skip=None): - x = F.interpolate(x, scale_factor=2, mode="nearest") + def forward( + self, x: torch.Tensor, skip: Optional[torch.Tensor] = None + ) -> torch.Tensor: + x = F.interpolate(x, scale_factor=2.0, mode=self.interpolation_mode) if skip is not None: x = torch.cat([x, skip], dim=1) x = self.attention1(x) @@ -46,20 +52,25 @@ def forward(self, x, skip=None): class CenterBlock(nn.Sequential): - def __init__(self, in_channels, out_channels, use_batchnorm=True): + def __init__( + self, + in_channels: int, + out_channels: int, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + ): conv1 = md.Conv2dReLU( in_channels, out_channels, kernel_size=3, padding=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ) conv2 = md.Conv2dReLU( out_channels, out_channels, kernel_size=3, padding=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ) super().__init__(conv1, conv2) @@ -67,20 +78,19 @@ def __init__(self, in_channels, out_channels, use_batchnorm=True): class UnetPlusPlusDecoder(nn.Module): def __init__( self, - encoder_channels, - decoder_channels, - n_blocks=5, - use_batchnorm=True, - attention_type=None, - center=False, + encoder_channels: Sequence[int], + decoder_channels: Sequence[int], + n_blocks: int = 5, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + attention_type: Optional[str] = None, + interpolation_mode: str = "nearest", + center: bool = False, ): super().__init__() if n_blocks != len(decoder_channels): raise ValueError( - "Model depth is {}, but you provide `decoder_channels` for {} blocks.".format( - n_blocks, len(decoder_channels) - ) + f"Model depth is {n_blocks}, but you provide `decoder_channels` for {len(decoder_channels)} blocks." ) # remove first skip with same spatial resolution @@ -95,13 +105,19 @@ def __init__( self.out_channels = decoder_channels if center: self.center = CenterBlock( - head_channels, head_channels, use_batchnorm=use_batchnorm + head_channels, + head_channels, + use_norm=use_norm, ) else: self.center = nn.Identity() # combine decoder keyword arguments - kwargs = dict(use_batchnorm=use_batchnorm, attention_type=attention_type) + kwargs = dict( + use_norm=use_norm, + attention_type=attention_type, + interpolation_mode=interpolation_mode, + ) blocks = {} for layer_idx in range(len(self.in_channels) - 1): @@ -119,15 +135,16 @@ def __init__( blocks[f"x_{depth_idx}_{layer_idx}"] = DecoderBlock( in_ch, skip_ch, out_ch, **kwargs ) - blocks[f"x_{0}_{len(self.in_channels)-1}"] = DecoderBlock( + blocks[f"x_{0}_{len(self.in_channels) - 1}"] = DecoderBlock( self.in_channels[-1], 0, self.out_channels[-1], **kwargs ) self.blocks = nn.ModuleDict(blocks) self.depth = len(self.in_channels) - 1 - def forward(self, *features): + def forward(self, features: List[torch.Tensor]) -> torch.Tensor: features = features[1:] # remove first skip with same spatial resolution features = features[::-1] # reverse channels to start from head of encoder + # start building dense connections dense_x = {} for layer_idx in range(len(self.in_channels) - 1): @@ -148,8 +165,8 @@ def forward(self, *features): ) dense_x[f"x_{depth_idx}_{dense_l_i}"] = self.blocks[ f"x_{depth_idx}_{dense_l_i}" - ](dense_x[f"x_{depth_idx}_{dense_l_i-1}"], cat_features) + ](dense_x[f"x_{depth_idx}_{dense_l_i - 1}"], cat_features) dense_x[f"x_{0}_{self.depth}"] = self.blocks[f"x_{0}_{self.depth}"]( - dense_x[f"x_{0}_{self.depth-1}"] + dense_x[f"x_{0}_{self.depth - 1}"] ) return dense_x[f"x_{0}_{self.depth}"] diff --git a/segmentation_models_pytorch/decoders/unetplusplus/model.py b/segmentation_models_pytorch/decoders/unetplusplus/model.py index 9d4a1e35..5448abcb 100644 --- a/segmentation_models_pytorch/decoders/unetplusplus/model.py +++ b/segmentation_models_pytorch/decoders/unetplusplus/model.py @@ -1,4 +1,5 @@ -from typing import Any, List, Optional, Union +import warnings +from typing import Any, Dict, Sequence, Optional, Union, Callable from segmentation_models_pytorch.base import ( ClassificationHead, @@ -28,17 +29,31 @@ class UnetPlusPlus(SegmentationModel): other pretrained weights (see table with available weights for each encoder_name) decoder_channels: List of integers which specify **in_channels** parameter for convolutions used in decoder. Length of the list should be the same as **encoder_depth** - decoder_use_batchnorm: If **True**, BatchNorm2d layer between Conv2D and Activation layers - is used. If **"inplace"** InplaceABN will be used, allows to decrease memory consumption. - Available options are **True, False, "inplace"** + decoder_use_norm: Specifies normalization between Conv2D and activation. + Accepts the following types: + - **True**: Defaults to `"batchnorm"`. + - **False**: No normalization (`nn.Identity`). + - **str**: Specifies normalization type using default parameters. Available values: + `"batchnorm"`, `"identity"`, `"layernorm"`, `"instancenorm"`, `"inplace"`. + - **dict**: Fully customizable normalization settings. Structure: + ```python + {"type": , **kwargs} + ``` + where `norm_name` corresponds to normalization type (see above), and `kwargs` are passed directly to the normalization layer as defined in PyTorch documentation. + + **Example**: + ```python + decoder_use_norm={"type": "layernorm", "eps": 1e-2} + ``` decoder_attention_type: Attention module used in decoder of the model. Available options are **None** and **scse** (https://arxiv.org/abs/1808.08127). + decoder_interpolation: Interpolation mode used in decoder of the model. Available options are + **"nearest"**, **"bilinear"**, **"bicubic"**, **"area"**, **"nearest-exact"**. Default is **"nearest"**. in_channels: A number of input channels for the model, default is 3 (RGB images) classes: A number of classes for output mask (or you can think as a number of channels of output mask) activation: An activation function to apply after the final convolution layer. Available options are **"sigmoid"**, **"softmax"**, **"logsoftmax"**, **"tanh"**, **"identity"**, - **callable** and **None**. - Default is **None** + **callable** and **None**. Default is **None**. aux_params: Dictionary with parameters of the auxiliary output (classification head). Auxiliary output is build on top of encoder if **aux_params** is not **None** (default). Supported params: - classes (int): A number of classes @@ -56,18 +71,21 @@ class UnetPlusPlus(SegmentationModel): """ + _is_torch_scriptable = False + @supports_config_loading def __init__( self, encoder_name: str = "resnet34", encoder_depth: int = 5, encoder_weights: Optional[str] = "imagenet", - decoder_use_batchnorm: bool = True, - decoder_channels: List[int] = (256, 128, 64, 32, 16), + decoder_use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + decoder_channels: Sequence[int] = (256, 128, 64, 32, 16), decoder_attention_type: Optional[str] = None, + decoder_interpolation: str = "nearest", in_channels: int = 3, classes: int = 1, - activation: Optional[Union[str, callable]] = None, + activation: Optional[Union[str, Callable]] = None, aux_params: Optional[dict] = None, **kwargs: dict[str, Any], ): @@ -78,6 +96,15 @@ def __init__( "UnetPlusPlus is not support encoder_name={}".format(encoder_name) ) + decoder_use_batchnorm = kwargs.pop("decoder_use_batchnorm", None) + if decoder_use_batchnorm is not None: + warnings.warn( + "The usage of decoder_use_batchnorm is deprecated. Please modify your code for decoder_use_norm", + DeprecationWarning, + stacklevel=2, + ) + decoder_use_norm = decoder_use_batchnorm + self.encoder = get_encoder( encoder_name, in_channels=in_channels, @@ -90,9 +117,10 @@ def __init__( encoder_channels=self.encoder.out_channels, decoder_channels=decoder_channels, n_blocks=encoder_depth, - use_batchnorm=decoder_use_batchnorm, + use_norm=decoder_use_norm, center=True if encoder_name.startswith("vgg") else False, attention_type=decoder_attention_type, + interpolation_mode=decoder_interpolation, ) self.segmentation_head = SegmentationHead( diff --git a/segmentation_models_pytorch/decoders/upernet/decoder.py b/segmentation_models_pytorch/decoders/upernet/decoder.py index 092de36a..435927df 100644 --- a/segmentation_models_pytorch/decoders/upernet/decoder.py +++ b/segmentation_models_pytorch/decoders/upernet/decoder.py @@ -1,3 +1,5 @@ +from typing import Any, Dict, Union, Sequence, List + import torch import torch.nn as nn import torch.nn.functional as F @@ -8,10 +10,10 @@ class PSPModule(nn.Module): def __init__( self, - in_channels, - out_channels, - sizes=(1, 2, 3, 6), - use_batchnorm=True, + in_channels: int, + out_channels: int, + sizes: Sequence[int] = (1, 2, 3, 6), + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", ): super().__init__() self.blocks = nn.ModuleList( @@ -20,63 +22,85 @@ def __init__( nn.AdaptiveAvgPool2d(size), md.Conv2dReLU( in_channels, - in_channels // len(sizes), + out_channels, kernel_size=1, - use_batchnorm=use_batchnorm, + use_norm=use_norm, ), ) for size in sizes ] ) self.out_conv = md.Conv2dReLU( - in_channels=in_channels * 2, + in_channels=in_channels + len(sizes) * out_channels, out_channels=out_channels, - kernel_size=1, - use_batchnorm=True, + kernel_size=3, + padding=1, + use_norm="batchnorm", ) - def forward(self, x): - _, _, height, width = x.shape - out = [x] + [ - F.interpolate( - block(x), size=(height, width), mode="bilinear", align_corners=False + def forward(self, feature: torch.Tensor) -> torch.Tensor: + _, _, height, width = feature.shape + pyramid_features = [feature] + for block in self.blocks: + pooled_feature = block(feature) + resized_feature = F.interpolate( + pooled_feature, + size=(height, width), + mode="bilinear", + align_corners=False, ) - for block in self.blocks - ] - out = self.out_conv(torch.cat(out, dim=1)) - return out + pyramid_features.append(resized_feature) + fused_feature = self.out_conv(torch.cat(pyramid_features, dim=1)) + return fused_feature -class FPNBlock(nn.Module): - def __init__(self, skip_channels, pyramid_channels, use_bathcnorm=True): +class LayerNorm2d(nn.LayerNorm): + def forward(self, x: torch.Tensor) -> torch.Tensor: + x = x.permute(0, 2, 3, 1) # to channels_last + normed_x = nn.functional.layer_norm( + x, self.normalized_shape, self.weight, self.bias, self.eps + ) + normed_x = normed_x.permute(0, 3, 1, 2) # to channels_first + return normed_x + + +class FPNLateralBlock(nn.Module): + def __init__( + self, + lateral_channels: int, + out_channels: int, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", + ): super().__init__() - self.skip_conv = ( - md.Conv2dReLU( - skip_channels, - pyramid_channels, - kernel_size=1, - use_batchnorm=use_bathcnorm, - ) - if skip_channels != 0 - else nn.Identity() + self.conv_norm_relu = md.Conv2dReLU( + lateral_channels, + out_channels, + kernel_size=1, + use_norm=use_norm, ) - def forward(self, x, skip): - _, channels, height, width = skip.shape - x = F.interpolate(x, size=(height, width), mode="bilinear", align_corners=False) - if channels != 0: - skip = self.skip_conv(skip) - x = x + skip - return x + def forward( + self, state_feature: torch.Tensor, lateral_feature: torch.Tensor + ) -> torch.Tensor: + # 1. Apply block to encoder feature + lateral_feature = self.conv_norm_relu(lateral_feature) + # 2. Upsample encoder feature to the "state" feature resolution + _, _, height, width = lateral_feature.shape + state_feature = F.interpolate( + state_feature, size=(height, width), mode="bilinear", align_corners=False + ) + # 3. Sum state and encoder features + fused_feature = state_feature + lateral_feature + return fused_feature class UPerNetDecoder(nn.Module): def __init__( self, - encoder_channels, - encoder_depth=5, - pyramid_channels=256, - segmentation_channels=64, + encoder_channels: Sequence[int], + encoder_depth: int = 5, + decoder_channels: int = 256, + use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", ): super().__init__() @@ -87,51 +111,101 @@ def __init__( ) ) - encoder_channels = encoder_channels[::-1] + # Encoder channels for input features starting from the highest resolution + # [1, 1/2, 1/4, 1/8, 1/16, ...] for num_features = encoder_depth + 1, + # but we use only [1/4, 1/8, 1/16, ...] for UPerNet + encoder_channels = encoder_channels[2:] + + self.feature_norms = nn.ModuleList( + [LayerNorm2d(channels, eps=1e-6) for channels in encoder_channels] + ) # PSP Module + lowest_resolution_feature_channels = encoder_channels[-1] self.psp = PSPModule( - in_channels=encoder_channels[0], - out_channels=pyramid_channels, + in_channels=lowest_resolution_feature_channels, + out_channels=decoder_channels, sizes=(1, 2, 3, 6), - use_batchnorm=True, + use_norm=use_norm, ) # FPN Module - self.fpn_stages = nn.ModuleList( - [FPNBlock(ch, pyramid_channels) for ch in encoder_channels[1:]] - ) + # we skip lower resolution feature maps + reverse the order + # [1/4, 1/8, 1/16, 1/32] -> [1/16, 1/8, 1/4] + lateral_channels = encoder_channels[:-1][::-1] + self.fpn_lateral_blocks = nn.ModuleList([]) + self.fpn_conv_blocks = nn.ModuleList([]) + for channels in lateral_channels: + block = FPNLateralBlock( + lateral_channels=channels, + out_channels=decoder_channels, + use_norm=use_norm, + ) + self.fpn_lateral_blocks.append(block) + conv_block = md.Conv2dReLU( + in_channels=decoder_channels, + out_channels=decoder_channels, + kernel_size=3, + padding=1, + use_norm=use_norm, + ) + self.fpn_conv_blocks.append(conv_block) - self.fpn_bottleneck = md.Conv2dReLU( - in_channels=(len(encoder_channels) - 1) * pyramid_channels, - out_channels=segmentation_channels, + num_blocks_to_fuse = len(self.fpn_conv_blocks) + 1 # +1 for the PSP module + self.fusion_block = md.Conv2dReLU( + in_channels=num_blocks_to_fuse * decoder_channels, + out_channels=decoder_channels, kernel_size=3, padding=1, - use_batchnorm=True, + use_norm=use_norm, ) - def forward(self, *features): - output_size = features[0].shape[2:] - target_size = [size // 4 for size in output_size] + def forward(self, features: List[torch.Tensor]) -> torch.Tensor: + """ + Args: + features (List[torch.Tensor]): + features with: [1, 1/2, 1/4, 1/8, 1/16, ...] spatial resolutions, + where the first feature is the highest resolution and the number + of features is equal to encoder_depth + 1. + """ + + # skip 1/1 and 1/2 resolution features + features = features[2:] - features = features[1:] # remove first skip with same spatial resolution - features = features[::-1] # reverse channels to start from head of encoder + # normalize feature maps + for i, norm in enumerate(self.feature_norms): + features[i] = norm(features[i]) - psp_out = self.psp(features[0]) + # pass lowest resolution feature to PSP module + psp_out = self.psp(features[-1]) + # skip lowest features for FPN + reverse the order + # [1/4, 1/8, 1/16, 1/32] -> [1/16, 1/8, 1/4] + fpn_lateral_features = features[:-1][::-1] fpn_features = [psp_out] - for feature, stage in zip(features[1:], self.fpn_stages): - fpn_feature = stage(fpn_features[-1], feature) + for i, block in enumerate(self.fpn_lateral_blocks): + # 1. for each encoder (skip) feature we apply 1x1 ConvNormRelu, + # 2. upsample latest fpn feature to it's resolution + # 3. sum them together + lateral_feature = fpn_lateral_features[i] + state_feature = fpn_features[-1] + fpn_feature = block(state_feature, lateral_feature) fpn_features.append(fpn_feature) + # Apply FPN conv blocks, but skip PSP module + for i, conv_block in enumerate(self.fpn_conv_blocks, start=1): + fpn_features[i] = conv_block(fpn_features[i]) + # Resize all FPN features to 1/4 of the original resolution. resized_fpn_features = [] + target_size = fpn_features[-1].shape[2:] # 1/4 of the original resolution for feature in fpn_features: resized_feature = F.interpolate( feature, size=target_size, mode="bilinear", align_corners=False ) resized_fpn_features.append(resized_feature) - output = self.fpn_bottleneck(torch.cat(resized_fpn_features, dim=1)) - + # reverse and concatenate + stacked_features = torch.cat(resized_fpn_features[::-1], dim=1) + output = self.fusion_block(stacked_features) return output diff --git a/segmentation_models_pytorch/decoders/upernet/model.py b/segmentation_models_pytorch/decoders/upernet/model.py index 076ed2de..54f578b3 100644 --- a/segmentation_models_pytorch/decoders/upernet/model.py +++ b/segmentation_models_pytorch/decoders/upernet/model.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, Union +from typing import Any, Dict, Optional, Union, Callable from segmentation_models_pytorch.base import ( ClassificationHead, @@ -25,12 +25,27 @@ class UPerNet(SegmentationModel): other pretrained weights (see table with available weights for each encoder_name) decoder_pyramid_channels: A number of convolution filters in Feature Pyramid, default is 256 decoder_segmentation_channels: A number of convolution filters in segmentation blocks, default is 64 + decoder_use_norm: Specifies normalization between Conv2D and activation. + Accepts the following types: + - **True**: Defaults to `"batchnorm"`. + - **False**: No normalization (`nn.Identity`). + - **str**: Specifies normalization type using default parameters. Available values: + `"batchnorm"`, `"identity"`, `"layernorm"`, `"instancenorm"`, `"inplace"`. + - **dict**: Fully customizable normalization settings. Structure: + ```python + {"type": , **kwargs} + ``` + where `norm_name` corresponds to normalization type (see above), and `kwargs` are passed directly to the normalization layer as defined in PyTorch documentation. + + **Example**: + ```python + use_norm={"type": "layernorm", "eps": 1e-2} + ``` in_channels: A number of input channels for the model, default is 3 (RGB images) classes: A number of classes for output mask (or you can think as a number of channels of output mask) activation: An activation function to apply after the final convolution layer. Available options are **"sigmoid"**, **"softmax"**, **"logsoftmax"**, **"tanh"**, **"identity"**, - **callable** and **None**. - Default is **None** + **callable** and **None**. Default is **None**. aux_params: Dictionary with parameters of the auxiliary output (classification head). Auxiliary output is build on top of encoder if **aux_params** is not **None** (default). Supported params: - classes (int): A number of classes @@ -54,11 +69,12 @@ def __init__( encoder_name: str = "resnet34", encoder_depth: int = 5, encoder_weights: Optional[str] = "imagenet", - decoder_pyramid_channels: int = 256, - decoder_segmentation_channels: int = 64, + decoder_channels: int = 256, + decoder_use_norm: Union[bool, str, Dict[str, Any]] = "batchnorm", in_channels: int = 3, classes: int = 1, - activation: Optional[Union[str, callable]] = None, + activation: Optional[Union[str, Callable]] = None, + upsampling: int = 4, aux_params: Optional[dict] = None, **kwargs: dict[str, Any], ): @@ -75,16 +91,16 @@ def __init__( self.decoder = UPerNetDecoder( encoder_channels=self.encoder.out_channels, encoder_depth=encoder_depth, - pyramid_channels=decoder_pyramid_channels, - segmentation_channels=decoder_segmentation_channels, + decoder_channels=decoder_channels, + use_norm=decoder_use_norm, ) self.segmentation_head = SegmentationHead( - in_channels=decoder_segmentation_channels, + in_channels=decoder_channels, out_channels=classes, activation=activation, kernel_size=1, - upsampling=4, + upsampling=upsampling, ) if aux_params is not None: diff --git a/segmentation_models_pytorch/encoders/__init__.py b/segmentation_models_pytorch/encoders/__init__.py index c4a4c037..287a921a 100644 --- a/segmentation_models_pytorch/encoders/__init__.py +++ b/segmentation_models_pytorch/encoders/__init__.py @@ -1,6 +1,12 @@ +import json import timm +import copy +import warnings import functools -import torch.utils.model_zoo as model_zoo +from torch.utils.model_zoo import load_url +from huggingface_hub import hf_hub_download +from safetensors.torch import load_file + from .resnet import resnet_encoders from .dpn import dpn_encoders @@ -13,18 +19,23 @@ from .mobilenet import mobilenet_encoders from .xception import xception_encoders from .timm_efficientnet import timm_efficientnet_encoders -from .timm_resnest import timm_resnest_encoders -from .timm_res2net import timm_res2net_encoders -from .timm_regnet import timm_regnet_encoders from .timm_sknet import timm_sknet_encoders -from .timm_mobilenetv3 import timm_mobilenetv3_encoders -from .timm_gernet import timm_gernet_encoders from .mix_transformer import mix_transformer_encoders from .mobileone import mobileone_encoders from .timm_universal import TimmUniversalEncoder +from .timm_vit import TimmViTEncoder # noqa F401 from ._preprocessing import preprocess_input +from ._legacy_pretrained_settings import pretrained_settings + +__all__ = [ + "encoders", + "get_encoder", + "get_encoder_names", + "get_preprocessing_params", + "get_preprocessing_fn", +] encoders = {} encoders.update(resnet_encoders) @@ -38,17 +49,39 @@ encoders.update(mobilenet_encoders) encoders.update(xception_encoders) encoders.update(timm_efficientnet_encoders) -encoders.update(timm_resnest_encoders) -encoders.update(timm_res2net_encoders) -encoders.update(timm_regnet_encoders) encoders.update(timm_sknet_encoders) -encoders.update(timm_mobilenetv3_encoders) -encoders.update(timm_gernet_encoders) encoders.update(mix_transformer_encoders) encoders.update(mobileone_encoders) +def is_equivalent_to_timm_universal(name): + patterns = [ + "timm-regnet", + "timm-res2", + "timm-resnest", + "timm-mobilenetv3", + "timm-gernet", + ] + for pattern in patterns: + if name.startswith(pattern): + return True + return False + + def get_encoder(name, in_channels=3, depth=5, weights=None, output_stride=32, **kwargs): + if name.startswith("timm-"): + warnings.warn( + "`timm-` encoders are deprecated and will be removed in the future. " + "Please use `tu-` equivalent encoders instead (see 'Timm encoders' section in the documentation).", + DeprecationWarning, + ) + + # convert timm- models to tu- models + if is_equivalent_to_timm_universal(name): + name = name.replace("timm-", "tu-") + if "mobilenetv3" in name: + name = name.replace("tu-", "tu-tf_") + if name.startswith("tu-"): name = name[3:] encoder = TimmUniversalEncoder( @@ -61,29 +94,56 @@ def get_encoder(name, in_channels=3, depth=5, weights=None, output_stride=32, ** ) return encoder - try: - Encoder = encoders[name]["encoder"] - except KeyError: + if name not in encoders: raise KeyError( - "Wrong encoder name `{}`, supported encoders: {}".format( - name, list(encoders.keys()) - ) + f"Wrong encoder name `{name}`, supported encoders: {list(encoders.keys())}" ) - params = encoders[name]["params"] - params.update(depth=depth) - encoder = Encoder(**params) + params = copy.deepcopy(encoders[name]["params"]) + params["depth"] = depth + params["output_stride"] = output_stride + + EncoderClass = encoders[name]["encoder"] + encoder = EncoderClass(**params) if weights is not None: - try: - settings = encoders[name]["pretrained_settings"][weights] - except KeyError: + if weights not in encoders[name]["pretrained_settings"]: + available_weights = list(encoders[name]["pretrained_settings"].keys()) raise KeyError( - "Wrong pretrained weights `{}` for encoder `{}`. Available options are: {}".format( - weights, name, list(encoders[name]["pretrained_settings"].keys()) - ) + f"Wrong pretrained weights `{weights}` for encoder `{name}`. " + f"Available options are: {available_weights}" ) - encoder.load_state_dict(model_zoo.load_url(settings["url"])) + + settings = encoders[name]["pretrained_settings"][weights] + repo_id = settings["repo_id"] + revision = settings["revision"] + + # First, try to load from HF-Hub, but as far as I know not all countries have + # access to the Hub (e.g. China), so we try to load from the original url if + # the first attempt fails. + weights_path = None + try: + hf_hub_download(repo_id, filename="config.json", revision=revision) + weights_path = hf_hub_download( + repo_id, filename="model.safetensors", revision=revision + ) + except Exception as e: + if name in pretrained_settings and weights in pretrained_settings[name]: + message = ( + f"Error loading {name} `{weights}` weights from Hugging Face Hub, " + "trying loading from original url..." + ) + warnings.warn(message, UserWarning) + url = pretrained_settings[name][weights]["url"] + state_dict = load_url(url, map_location="cpu") + else: + raise e + + if weights_path is not None: + state_dict = load_file(weights_path, device="cpu") + + # Load model weights + encoder.load_state_dict(state_dict) encoder.set_in_channels(in_channels, pretrained=weights is not None) if output_stride != 32: @@ -110,7 +170,25 @@ def get_preprocessing_params(encoder_name, pretrained="imagenet"): raise ValueError( "Available pretrained options {}".format(all_settings.keys()) ) - settings = all_settings[pretrained] + + repo_id = all_settings[pretrained]["repo_id"] + revision = all_settings[pretrained]["revision"] + + # Load config and model + try: + config_path = hf_hub_download( + repo_id, filename="config.json", revision=revision + ) + with open(config_path, "r") as f: + settings = json.load(f) + except Exception as e: + if ( + encoder_name in pretrained_settings + and pretrained in pretrained_settings[encoder_name] + ): + settings = pretrained_settings[encoder_name][pretrained] + else: + raise e formatted_settings = {} formatted_settings["input_space"] = settings.get("input_space", "RGB") diff --git a/segmentation_models_pytorch/encoders/_base.py b/segmentation_models_pytorch/encoders/_base.py index 3b877075..98c431fb 100644 --- a/segmentation_models_pytorch/encoders/_base.py +++ b/segmentation_models_pytorch/encoders/_base.py @@ -1,3 +1,6 @@ +import torch +from typing import Sequence, Dict + from . import _utils as utils @@ -7,7 +10,14 @@ class EncoderMixin: - patching first convolution for arbitrary input channels """ - _output_stride = 32 + _is_torch_scriptable = True + _is_torch_exportable = True + _is_torch_compilable = True + + def __init__(self): + self._depth = 5 + self._in_channels = 3 + self._output_stride = 32 @property def out_channels(self): @@ -25,34 +35,27 @@ def set_in_channels(self, in_channels, pretrained=True): self._in_channels = in_channels if self._out_channels[0] == 3: - self._out_channels = tuple([in_channels] + list(self._out_channels)[1:]) + self._out_channels = [in_channels] + self._out_channels[1:] utils.patch_first_conv( model=self, new_in_channels=in_channels, pretrained=pretrained ) - def get_stages(self): - """Override it in your implementation""" + def get_stages(self) -> Dict[int, Sequence[torch.nn.Module]]: + """Override it in your implementation, should return a dictionary with keys as + the output stride and values as the list of modules + """ raise NotImplementedError def make_dilated(self, output_stride): - if output_stride == 16: - stage_list = [5] - dilation_list = [2] - - elif output_stride == 8: - stage_list = [4, 5] - dilation_list = [2, 4] - - else: - raise ValueError( - "Output stride should be 16 or 8, got {}.".format(output_stride) - ) - - self._output_stride = output_stride + if output_stride not in [8, 16]: + raise ValueError(f"Output stride should be 16 or 8, got {output_stride}.") stages = self.get_stages() - for stage_indx, dilation_rate in zip(stage_list, dilation_list): - utils.replace_strides_with_dilation( - module=stages[stage_indx], dilation_rate=dilation_rate - ) + for stage_stride, stage_modules in stages.items(): + if stage_stride <= output_stride: + continue + + dilation_rate = stage_stride // output_stride + for module in stage_modules: + utils.replace_strides_with_dilation(module, dilation_rate) diff --git a/segmentation_models_pytorch/encoders/_dpn.py b/segmentation_models_pytorch/encoders/_dpn.py new file mode 100644 index 00000000..e7292615 --- /dev/null +++ b/segmentation_models_pytorch/encoders/_dpn.py @@ -0,0 +1,364 @@ +"""PyTorch implementation of DualPathNetworks +Ported to PyTorch by [Ross Wightman](https://github.com/rwightman/pytorch-dpn-pretrained) + +Based on original MXNet implementation https://github.com/cypw/DPNs with +many ideas from another PyTorch implementation https://github.com/oyam/pytorch-DPNs. + +This implementation is compatible with the pretrained weights +from cypw's MXNet implementation. +""" + +import torch +import torch.nn as nn +import torch.nn.functional as F +from collections import OrderedDict + + +class CatBnAct(nn.Module): + def __init__(self, in_chs, activation_fn=nn.ReLU(inplace=True)): + super(CatBnAct, self).__init__() + self.bn = nn.BatchNorm2d(in_chs, eps=0.001) + self.act = activation_fn + + def forward(self, x): + x = torch.cat(x, dim=1) if isinstance(x, tuple) else x + return self.act(self.bn(x)) + + +class BnActConv2d(nn.Module): + def __init__( + self, + in_chs, + out_chs, + kernel_size, + stride, + padding=0, + groups=1, + activation_fn=nn.ReLU(inplace=True), + ): + super(BnActConv2d, self).__init__() + self.bn = nn.BatchNorm2d(in_chs, eps=0.001) + self.act = activation_fn + self.conv = nn.Conv2d( + in_chs, out_chs, kernel_size, stride, padding, groups=groups, bias=False + ) + + def forward(self, x): + return self.conv(self.act(self.bn(x))) + + +class InputBlock(nn.Module): + def __init__( + self, + num_init_features, + kernel_size=7, + padding=3, + activation_fn=nn.ReLU(inplace=True), + ): + super(InputBlock, self).__init__() + self.conv = nn.Conv2d( + 3, + num_init_features, + kernel_size=kernel_size, + stride=2, + padding=padding, + bias=False, + ) + self.bn = nn.BatchNorm2d(num_init_features, eps=0.001) + self.act = activation_fn + self.pool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + x = self.act(x) + x = self.pool(x) + return x + + +class DualPathBlock(nn.Module): + def __init__( + self, + in_chs, + num_1x1_a, + num_3x3_b, + num_1x1_c, + inc, + groups, + block_type="normal", + b=False, + ): + super(DualPathBlock, self).__init__() + self.num_1x1_c = num_1x1_c + self.inc = inc + self.b = b + if block_type == "proj": + self.key_stride = 1 + self.has_proj = True + elif block_type == "down": + self.key_stride = 2 + self.has_proj = True + else: + assert block_type == "normal" + self.key_stride = 1 + self.has_proj = False + + if self.has_proj: + # Using different member names here to allow easier parameter key matching for conversion + if self.key_stride == 2: + self.c1x1_w_s2 = BnActConv2d( + in_chs=in_chs, out_chs=num_1x1_c + 2 * inc, kernel_size=1, stride=2 + ) + else: + self.c1x1_w_s1 = BnActConv2d( + in_chs=in_chs, out_chs=num_1x1_c + 2 * inc, kernel_size=1, stride=1 + ) + self.c1x1_a = BnActConv2d( + in_chs=in_chs, out_chs=num_1x1_a, kernel_size=1, stride=1 + ) + self.c3x3_b = BnActConv2d( + in_chs=num_1x1_a, + out_chs=num_3x3_b, + kernel_size=3, + stride=self.key_stride, + padding=1, + groups=groups, + ) + if b: + self.c1x1_c = CatBnAct(in_chs=num_3x3_b) + self.c1x1_c1 = nn.Conv2d(num_3x3_b, num_1x1_c, kernel_size=1, bias=False) + self.c1x1_c2 = nn.Conv2d(num_3x3_b, inc, kernel_size=1, bias=False) + else: + self.c1x1_c = BnActConv2d( + in_chs=num_3x3_b, out_chs=num_1x1_c + inc, kernel_size=1, stride=1 + ) + + def forward(self, x): + x_in = torch.cat(x, dim=1) if isinstance(x, tuple) else x + if self.has_proj: + if self.key_stride == 2: + x_s = self.c1x1_w_s2(x_in) + else: + x_s = self.c1x1_w_s1(x_in) + x_s1 = x_s[:, : self.num_1x1_c, :, :] + x_s2 = x_s[:, self.num_1x1_c :, :, :] + else: + x_s1 = x[0] + x_s2 = x[1] + x_in = self.c1x1_a(x_in) + x_in = self.c3x3_b(x_in) + if self.b: + x_in = self.c1x1_c(x_in) + out1 = self.c1x1_c1(x_in) + out2 = self.c1x1_c2(x_in) + else: + x_in = self.c1x1_c(x_in) + out1 = x_in[:, : self.num_1x1_c, :, :] + out2 = x_in[:, self.num_1x1_c :, :, :] + resid = x_s1 + out1 + dense = torch.cat([x_s2, out2], dim=1) + return resid, dense + + +class DPN(nn.Module): + def __init__( + self, + small=False, + num_init_features=64, + k_r=96, + groups=32, + b=False, + k_sec=(3, 4, 20, 3), + inc_sec=(16, 32, 24, 128), + num_classes=1000, + test_time_pool=False, + ): + super(DPN, self).__init__() + self.test_time_pool = test_time_pool + self.b = b + bw_factor = 1 if small else 4 + + blocks = OrderedDict() + + # conv1 + if small: + blocks["conv1_1"] = InputBlock(num_init_features, kernel_size=3, padding=1) + else: + blocks["conv1_1"] = InputBlock(num_init_features, kernel_size=7, padding=3) + + # conv2 + bw = 64 * bw_factor + inc = inc_sec[0] + r = (k_r * bw) // (64 * bw_factor) + blocks["conv2_1"] = DualPathBlock( + num_init_features, r, r, bw, inc, groups, "proj", b + ) + in_chs = bw + 3 * inc + for i in range(2, k_sec[0] + 1): + blocks["conv2_" + str(i)] = DualPathBlock( + in_chs, r, r, bw, inc, groups, "normal", b + ) + in_chs += inc + + # conv3 + bw = 128 * bw_factor + inc = inc_sec[1] + r = (k_r * bw) // (64 * bw_factor) + blocks["conv3_1"] = DualPathBlock(in_chs, r, r, bw, inc, groups, "down", b) + in_chs = bw + 3 * inc + for i in range(2, k_sec[1] + 1): + blocks["conv3_" + str(i)] = DualPathBlock( + in_chs, r, r, bw, inc, groups, "normal", b + ) + in_chs += inc + + # conv4 + bw = 256 * bw_factor + inc = inc_sec[2] + r = (k_r * bw) // (64 * bw_factor) + blocks["conv4_1"] = DualPathBlock(in_chs, r, r, bw, inc, groups, "down", b) + in_chs = bw + 3 * inc + for i in range(2, k_sec[2] + 1): + blocks["conv4_" + str(i)] = DualPathBlock( + in_chs, r, r, bw, inc, groups, "normal", b + ) + in_chs += inc + + # conv5 + bw = 512 * bw_factor + inc = inc_sec[3] + r = (k_r * bw) // (64 * bw_factor) + blocks["conv5_1"] = DualPathBlock(in_chs, r, r, bw, inc, groups, "down", b) + in_chs = bw + 3 * inc + for i in range(2, k_sec[3] + 1): + blocks["conv5_" + str(i)] = DualPathBlock( + in_chs, r, r, bw, inc, groups, "normal", b + ) + in_chs += inc + blocks["conv5_bn_ac"] = CatBnAct(in_chs) + + self.features = nn.Sequential(blocks) + + # Using 1x1 conv for the FC layer to allow the extra pooling scheme + self.last_linear = nn.Conv2d(in_chs, num_classes, kernel_size=1, bias=True) + + def logits(self, features): + if not self.training and self.test_time_pool: + x = F.avg_pool2d(features, kernel_size=7, stride=1) + out = self.last_linear(x) + # The extra test time pool should be pooling an img_size//32 - 6 size patch + out = adaptive_avgmax_pool2d(out, pool_type="avgmax") + else: + x = adaptive_avgmax_pool2d(features, pool_type="avg") + out = self.last_linear(x) + return out.view(out.size(0), -1) + + def forward(self, input): + x = self.features(input) + x = self.logits(x) + return x + + +""" PyTorch selectable adaptive pooling +Adaptive pooling with the ability to select the type of pooling from: + * 'avg' - Average pooling + * 'max' - Max pooling + * 'avgmax' - Sum of average and max pooling re-scaled by 0.5 + * 'avgmaxc' - Concatenation of average and max pooling along feature dim, doubles feature dim + +Both a functional and a nn.Module version of the pooling is provided. + +Author: Ross Wightman (rwightman) +""" + + +def pooling_factor(pool_type="avg"): + return 2 if pool_type == "avgmaxc" else 1 + + +def adaptive_avgmax_pool2d(x, pool_type="avg", padding=0, count_include_pad=False): + """Selectable global pooling function with dynamic input kernel size""" + if pool_type == "avgmaxc": + x = torch.cat( + [ + F.avg_pool2d( + x, + kernel_size=(x.size(2), x.size(3)), + padding=padding, + count_include_pad=count_include_pad, + ), + F.max_pool2d(x, kernel_size=(x.size(2), x.size(3)), padding=padding), + ], + dim=1, + ) + elif pool_type == "avgmax": + x_avg = F.avg_pool2d( + x, + kernel_size=(x.size(2), x.size(3)), + padding=padding, + count_include_pad=count_include_pad, + ) + x_max = F.max_pool2d(x, kernel_size=(x.size(2), x.size(3)), padding=padding) + x = 0.5 * (x_avg + x_max) + elif pool_type == "max": + x = F.max_pool2d(x, kernel_size=(x.size(2), x.size(3)), padding=padding) + else: + if pool_type != "avg": + print( + "Invalid pool type %s specified. Defaulting to average pooling." + % pool_type + ) + x = F.avg_pool2d( + x, + kernel_size=(x.size(2), x.size(3)), + padding=padding, + count_include_pad=count_include_pad, + ) + return x + + +class AdaptiveAvgMaxPool2d(torch.nn.Module): + """Selectable global pooling layer with dynamic input kernel size""" + + def __init__(self, output_size=1, pool_type="avg"): + super(AdaptiveAvgMaxPool2d, self).__init__() + self.output_size = output_size + self.pool_type = pool_type + if pool_type == "avgmaxc" or pool_type == "avgmax": + self.pool = nn.ModuleList( + [nn.AdaptiveAvgPool2d(output_size), nn.AdaptiveMaxPool2d(output_size)] + ) + elif pool_type == "max": + self.pool = nn.AdaptiveMaxPool2d(output_size) + else: + if pool_type != "avg": + print( + "Invalid pool type %s specified. Defaulting to average pooling." + % pool_type + ) + self.pool = nn.AdaptiveAvgPool2d(output_size) + + def forward(self, x): + if self.pool_type == "avgmaxc": + x = torch.cat([p(x) for p in self.pool], dim=1) + elif self.pool_type == "avgmax": + x = 0.5 * torch.sum(torch.stack([p(x) for p in self.pool]), 0).squeeze( + dim=0 + ) + else: + x = self.pool(x) + return x + + def factor(self): + return pooling_factor(self.pool_type) + + def __repr__(self): + return ( + self.__class__.__name__ + + " (" + + "output_size=" + + str(self.output_size) + + ", pool_type=" + + self.pool_type + + ")" + ) diff --git a/segmentation_models_pytorch/encoders/_efficientnet.py b/segmentation_models_pytorch/encoders/_efficientnet.py new file mode 100644 index 00000000..b2847a56 --- /dev/null +++ b/segmentation_models_pytorch/encoders/_efficientnet.py @@ -0,0 +1,883 @@ +"""model.py - Model and module class for EfficientNet. +They are built to mirror those in the official TensorFlow implementation. +""" + +# Author: lukemelas (github username) +# Github repo: https://github.com/lukemelas/EfficientNet-PyTorch +# With adjustments and added comments by workingcoder (github username). + +import torch +from torch import nn +from torch.nn import functional as F +import re +import math +import collections +from functools import partial +from typing import List, Optional + +# Parameters for the entire model (stem, all blocks, and head) +GlobalParams = collections.namedtuple( + "GlobalParams", + [ + "width_coefficient", + "depth_coefficient", + "image_size", + "dropout_rate", + "num_classes", + "batch_norm_momentum", + "batch_norm_epsilon", + "drop_connect_rate", + "depth_divisor", + "min_depth", + "include_top", + ], +) + +# Parameters for an individual model block +BlockArgs = collections.namedtuple( + "BlockArgs", + [ + "num_repeat", + "kernel_size", + "stride", + "expand_ratio", + "input_filters", + "output_filters", + "se_ratio", + "id_skip", + ], +) + +# Set GlobalParams and BlockArgs's defaults +GlobalParams.__new__.__defaults__ = (None,) * len(GlobalParams._fields) +BlockArgs.__new__.__defaults__ = (None,) * len(BlockArgs._fields) + + +class MBConvBlock(nn.Module): + """Mobile Inverted Residual Bottleneck Block. + + Args: + block_args (namedtuple): BlockArgs, defined in utils.py. + global_params (namedtuple): GlobalParam, defined in utils.py. + image_size (tuple or list): [image_height, image_width]. + + References: + [1] https://arxiv.org/abs/1704.04861 (MobileNet v1) + [2] https://arxiv.org/abs/1801.04381 (MobileNet v2) + [3] https://arxiv.org/abs/1905.02244 (MobileNet v3) + """ + + def __init__( + self, block_args: BlockArgs, global_params: GlobalParams, image_size=None + ): + super().__init__() + + self._has_expansion = block_args.expand_ratio != 1 + self._has_se = block_args.se_ratio is not None and 0 < block_args.se_ratio <= 1 + self._has_drop_connect = ( + block_args.id_skip + and block_args.stride == 1 + and block_args.input_filters == block_args.output_filters + ) + + # Pytorch's difference from tensorflow + bn_momentum = 1 - global_params.batch_norm_momentum + bn_eps = global_params.batch_norm_epsilon + + # Expansion phase (Inverted Bottleneck) + input_channels = block_args.input_filters + expanded_channels = input_channels * block_args.expand_ratio + + if self._has_expansion: + Conv2d = get_same_padding_conv2d(image_size=image_size) + self._expand_conv = Conv2d( + input_channels, expanded_channels, kernel_size=1, bias=False + ) + self._bn0 = nn.BatchNorm2d( + expanded_channels, + momentum=bn_momentum, + eps=bn_eps, + ) + else: + # for torchscript compatibility + self._expand_conv = nn.Identity() + self._bn0 = nn.Identity() + + # Depthwise convolution phase + kernel_size = block_args.kernel_size + stride = block_args.stride + Conv2d = get_same_padding_conv2d(image_size=image_size) + self._depthwise_conv = Conv2d( + in_channels=expanded_channels, + out_channels=expanded_channels, + groups=expanded_channels, # groups makes it depthwise + kernel_size=kernel_size, + stride=stride, + bias=False, + ) + self._bn1 = nn.BatchNorm2d( + expanded_channels, + momentum=bn_momentum, + eps=bn_eps, + ) + image_size = calculate_output_image_size(image_size, stride) + + # Squeeze and Excitation layer, if desired + if self._has_se: + squeezed_channels = int(input_channels * block_args.se_ratio) + squeezed_channels = max(1, squeezed_channels) + Conv2d = get_same_padding_conv2d(image_size=(1, 1)) + self._se_reduce = Conv2d( + in_channels=expanded_channels, + out_channels=squeezed_channels, + kernel_size=1, + ) + self._se_expand = Conv2d( + in_channels=squeezed_channels, + out_channels=expanded_channels, + kernel_size=1, + ) + + # Pointwise convolution phase + output_channels = block_args.output_filters + Conv2d = get_same_padding_conv2d(image_size=image_size) + self._project_conv = Conv2d( + in_channels=expanded_channels, + out_channels=output_channels, + kernel_size=1, + bias=False, + ) + self._bn2 = nn.BatchNorm2d( + num_features=output_channels, + momentum=bn_momentum, + eps=bn_eps, + ) + self._swish = nn.SiLU() + + def forward(self, inputs: torch.Tensor, drop_connect_rate: Optional[float] = None): + """MBConvBlock's forward function. + + Args: + inputs (tensor): Input tensor. + drop_connect_rate (bool): Drop connect rate (float, between 0 and 1). + + Returns: + Output of this block after processing. + """ + + # Expansion and Depthwise Convolution + x = inputs + if self._has_expansion: + x = self._expand_conv(inputs) + x = self._bn0(x) + x = self._swish(x) + + x = self._depthwise_conv(x) + x = self._bn1(x) + x = self._swish(x) + + # Squeeze and Excitation + if self._has_se: + x_squeezed = F.adaptive_avg_pool2d(x, 1) + x_squeezed = self._se_reduce(x_squeezed) + x_squeezed = self._swish(x_squeezed) + x_squeezed = self._se_expand(x_squeezed) + x = torch.sigmoid(x_squeezed) * x + + # Pointwise Convolution + x = self._project_conv(x) + x = self._bn2(x) + + # Skip connection and drop connect + if self._has_drop_connect: + # The combination of skip connection and drop connect brings about stochastic depth. + if drop_connect_rate is not None and drop_connect_rate > 0: + x = drop_connect(x, p=drop_connect_rate, training=self.training) + x = x + inputs # skip connection + return x + + +class EfficientNet(nn.Module): + """EfficientNet model. + + Args: + blocks_args (list[namedtuple]): A list of BlockArgs to construct blocks. + global_params (namedtuple): A set of GlobalParams shared between blocks. + + References: + [1] https://arxiv.org/abs/1905.11946 (EfficientNet) + + Example: + >>> import torch + >>> from efficientnet.model import EfficientNet + >>> inputs = torch.rand(1, 3, 224, 224) + >>> model = EfficientNet.from_pretrained('efficientnet-b0') + >>> model.eval() + >>> outputs = model(inputs) + """ + + def __init__(self, blocks_args: List[BlockArgs], global_params: GlobalParams): + super().__init__() + + if not isinstance(blocks_args, list): + raise ValueError("blocks_args should be a list") + if len(blocks_args) == 0: + raise ValueError("block args must be greater than 0") + + self._global_params = global_params + self._blocks_args = blocks_args + + # Batch norm parameters + bn_mom = 1 - self._global_params.batch_norm_momentum + bn_eps = self._global_params.batch_norm_epsilon + + # Get stem static or dynamic convolution depending on image size + image_size = global_params.image_size + Conv2d = get_same_padding_conv2d(image_size=image_size) + + # Stem + in_channels = 3 # rgb + out_channels = round_filters(32, self._global_params) + self._conv_stem = Conv2d( + in_channels, out_channels, kernel_size=3, stride=2, bias=False + ) + self._bn0 = nn.BatchNorm2d(out_channels, momentum=bn_mom, eps=bn_eps) + image_size = calculate_output_image_size(image_size, 2) + + # Build blocks + self._blocks = nn.ModuleList([]) + for block_args in blocks_args: + # Update block input and output filters based on depth multiplier. + block_args = block_args._replace( + input_filters=round_filters( + block_args.input_filters, self._global_params + ), + output_filters=round_filters( + block_args.output_filters, self._global_params + ), + num_repeat=round_repeats(block_args.num_repeat, self._global_params), + ) + + # The first block needs to take care of stride and filter size increase. + self._blocks.append( + MBConvBlock(block_args, self._global_params, image_size=image_size) + ) + image_size = calculate_output_image_size(image_size, block_args.stride) + if block_args.num_repeat > 1: # modify block_args to keep same output size + block_args = block_args._replace( + input_filters=block_args.output_filters, stride=1 + ) + for _ in range(block_args.num_repeat - 1): + self._blocks.append( + MBConvBlock(block_args, self._global_params, image_size=image_size) + ) + # image_size = calculate_output_image_size(image_size, block_args.stride) # stride = 1 + + # Head + in_channels = block_args.output_filters # output of final block + out_channels = round_filters(1280, self._global_params) + Conv2d = get_same_padding_conv2d(image_size=image_size) + self._conv_head = Conv2d(in_channels, out_channels, kernel_size=1, bias=False) + self._bn1 = nn.BatchNorm2d( + num_features=out_channels, momentum=bn_mom, eps=bn_eps + ) + + # Final linear layer + self._avg_pooling = nn.AdaptiveAvgPool2d(1) + if self._global_params.include_top: + self._dropout = nn.Dropout(self._global_params.dropout_rate) + self._fc = nn.Linear(out_channels, self._global_params.num_classes) + + self._swish = nn.SiLU() + + def extract_features(self, inputs): + """Use convolution layer to extract feature. + + Args: + inputs (tensor): Input tensor. + + Returns: + Output of the final convolution + layer in the efficientnet model. + """ + # Stem + x = self._swish(self._bn0(self._conv_stem(inputs))) + + # Blocks + for idx, block in enumerate(self._blocks): + drop_connect_rate = self._global_params.drop_connect_rate + if drop_connect_rate: + # scale drop connect_rate + drop_connect_rate *= float(idx) / len(self._blocks) + x = block(x, drop_connect_rate=drop_connect_rate) + + # Head + x = self._swish(self._bn1(self._conv_head(x))) + + return x + + def forward(self, inputs): + """EfficientNet's forward function. + Calls extract_features to extract features, applies final linear layer, and returns logits. + + Args: + inputs (tensor): Input tensor. + + Returns: + Output of this model after processing. + """ + # Convolution layers + x = self.extract_features(inputs) + + # Pooling and final linear layer + x = self._avg_pooling(x) + if self._global_params.include_top: + x = x.flatten(start_dim=1) + x = self._dropout(x) + x = self._fc(x) + return x + + +################################################################################ +# Help functions for model architecture +################################################################################ + +# GlobalParams and BlockArgs: Two namedtuples +# round_filters and round_repeats: +# Functions to calculate params for scaling model width and depth ! ! ! +# get_width_and_height_from_size and calculate_output_image_size +# drop_connect: A structural design +# get_same_padding_conv2d: +# Conv2dDynamicSamePadding +# Conv2dStaticSamePadding +# get_same_padding_maxPool2d: +# MaxPool2dDynamicSamePadding +# MaxPool2dStaticSamePadding +# It's an additional function, not used in EfficientNet, +# but can be used in other model (such as EfficientDet). + + +def round_filters(filters, global_params): + """Calculate and round number of filters based on width multiplier. + Use width_coefficient, depth_divisor and min_depth of global_params. + + Args: + filters (int): Filters number to be calculated. + global_params (namedtuple): Global params of the model. + + Returns: + new_filters: New filters number after calculating. + """ + multiplier = global_params.width_coefficient + if not multiplier: + return filters + # TODO: modify the params names. + # maybe the names (width_divisor,min_width) + # are more suitable than (depth_divisor,min_depth). + divisor = global_params.depth_divisor + min_depth = global_params.min_depth + filters *= multiplier + min_depth = min_depth or divisor # pay attention to this line when using min_depth + # follow the formula transferred from official TensorFlow implementation + new_filters = max(min_depth, int(filters + divisor / 2) // divisor * divisor) + if new_filters < 0.9 * filters: # prevent rounding by more than 10% + new_filters += divisor + return int(new_filters) + + +def round_repeats(repeats, global_params): + """Calculate module's repeat number of a block based on depth multiplier. + Use depth_coefficient of global_params. + + Args: + repeats (int): num_repeat to be calculated. + global_params (namedtuple): Global params of the model. + + Returns: + new repeat: New repeat number after calculating. + """ + multiplier = global_params.depth_coefficient + if not multiplier: + return repeats + # follow the formula transferred from official TensorFlow implementation + return int(math.ceil(multiplier * repeats)) + + +def drop_connect(inputs: torch.Tensor, p: float, training: bool) -> torch.Tensor: + """Drop connect. + + Args: + input (tensor: BCWH): Input of this structure. + p (float: 0.0~1.0): Probability of drop connection. + training (bool): The running mode. + + Returns: + output: Output after drop connection. + """ + assert 0 <= p <= 1, "p must be in range of [0,1]" + + if not training: + return inputs + + batch_size = inputs.shape[0] + keep_prob = 1 - p + + # generate binary_tensor mask according to probability (p for 0, 1-p for 1) + random_tensor = keep_prob + random_tensor += torch.rand( + [batch_size, 1, 1, 1], dtype=inputs.dtype, device=inputs.device + ) + binary_tensor = torch.floor(random_tensor) + + output = inputs / keep_prob * binary_tensor + return output + + +def get_width_and_height_from_size(x): + """Obtain height and width from x. + + Args: + x (int, tuple or list): Data size. + + Returns: + size: A tuple or list (H,W). + """ + if isinstance(x, int): + return x, x + if isinstance(x, list) or isinstance(x, tuple): + return x + else: + raise TypeError() + + +def calculate_output_image_size(input_image_size, stride): + """Calculates the output image size when using Conv2dSamePadding with a stride. + Necessary for static padding. Thanks to mannatsingh for pointing this out. + + Args: + input_image_size (int, tuple or list): Size of input image. + stride (int, tuple or list): Conv2d operation's stride. + + Returns: + output_image_size: A list [H,W]. + """ + if input_image_size is None: + return None + image_height, image_width = get_width_and_height_from_size(input_image_size) + stride = stride if isinstance(stride, int) else stride[0] + image_height = int(math.ceil(image_height / stride)) + image_width = int(math.ceil(image_width / stride)) + return [image_height, image_width] + + +# Note: +# The following 'SamePadding' functions make output size equal ceil(input size/stride). +# Only when stride equals 1, can the output size be the same as input size. +# Don't be confused by their function names ! ! ! + + +def get_same_padding_conv2d(image_size=None): + """Chooses static padding if you have specified an image size, and dynamic padding otherwise. + Static padding is necessary for ONNX exporting of models. + + Args: + image_size (int or tuple): Size of the image. + + Returns: + Conv2dDynamicSamePadding or Conv2dStaticSamePadding. + """ + if image_size is None: + return Conv2dDynamicSamePadding + else: + return partial(Conv2dStaticSamePadding, image_size=image_size) + + +class Conv2dDynamicSamePadding(nn.Conv2d): + """2D Convolutions like TensorFlow, for a dynamic image size. + The padding is operated in forward function by calculating dynamically. + """ + + # Tips for 'SAME' mode padding. + # Given the following: + # i: width or height + # s: stride + # k: kernel size + # d: dilation + # p: padding + # Output after Conv2d: + # o = floor((i+p-((k-1)*d+1))/s+1) + # If o equals i, i = floor((i+p-((k-1)*d+1))/s+1), + # => p = (i-1)*s+((k-1)*d+1)-i + + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride=1, + dilation=1, + groups=1, + bias=True, + ): + super().__init__( + in_channels, out_channels, kernel_size, stride, 0, dilation, groups, bias + ) + self.stride = self.stride if len(self.stride) == 2 else [self.stride[0]] * 2 + + def forward(self, x): + ih, iw = x.size()[-2:] + kh, kw = self.weight.size()[-2:] + sh, sw = self.stride + oh, ow = ( + math.ceil(ih / sh), + math.ceil(iw / sw), + ) # change the output size according to stride ! ! ! + pad_h = max((oh - 1) * self.stride[0] + (kh - 1) * self.dilation[0] + 1 - ih, 0) + pad_w = max((ow - 1) * self.stride[1] + (kw - 1) * self.dilation[1] + 1 - iw, 0) + if pad_h > 0 or pad_w > 0: + x = F.pad( + x, [pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2] + ) + return F.conv2d( + x, + self.weight, + self.bias, + self.stride, + self.padding, + self.dilation, + self.groups, + ) + + +class Conv2dStaticSamePadding(nn.Conv2d): + """2D Convolutions like TensorFlow's 'SAME' mode, with the given input image size. + The padding mudule is calculated in construction function, then used in forward. + """ + + # With the same calculation as Conv2dDynamicSamePadding + + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride=1, + image_size=None, + **kwargs, + ): + super().__init__(in_channels, out_channels, kernel_size, stride, **kwargs) + self.stride = self.stride if len(self.stride) == 2 else [self.stride[0]] * 2 + + # Calculate padding based on image size and save it + assert image_size is not None + ih, iw = (image_size, image_size) if isinstance(image_size, int) else image_size + kh, kw = self.weight.size()[-2:] + sh, sw = self.stride + oh, ow = math.ceil(ih / sh), math.ceil(iw / sw) + pad_h = max((oh - 1) * self.stride[0] + (kh - 1) * self.dilation[0] + 1 - ih, 0) + pad_w = max((ow - 1) * self.stride[1] + (kw - 1) * self.dilation[1] + 1 - iw, 0) + if pad_h > 0 or pad_w > 0: + self.static_padding = nn.ZeroPad2d( + (pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2) + ) + else: + self.static_padding = nn.Identity() + + def forward(self, x): + x = self.static_padding(x) + x = F.conv2d( + x, + self.weight, + self.bias, + self.stride, + self.padding, + self.dilation, + self.groups, + ) + return x + + +def get_same_padding_maxPool2d(image_size=None): + """Chooses static padding if you have specified an image size, and dynamic padding otherwise. + Static padding is necessary for ONNX exporting of models. + + Args: + image_size (int or tuple): Size of the image. + + Returns: + MaxPool2dDynamicSamePadding or MaxPool2dStaticSamePadding. + """ + if image_size is None: + return MaxPool2dDynamicSamePadding + else: + return partial(MaxPool2dStaticSamePadding, image_size=image_size) + + +class MaxPool2dDynamicSamePadding(nn.MaxPool2d): + """2D MaxPooling like TensorFlow's 'SAME' mode, with a dynamic image size. + The padding is operated in forward function by calculating dynamically. + """ + + def __init__( + self, + kernel_size, + stride, + padding=0, + dilation=1, + return_indices=False, + ceil_mode=False, + ): + super().__init__( + kernel_size, stride, padding, dilation, return_indices, ceil_mode + ) + self.stride = [self.stride] * 2 if isinstance(self.stride, int) else self.stride + self.kernel_size = ( + [self.kernel_size] * 2 + if isinstance(self.kernel_size, int) + else self.kernel_size + ) + self.dilation = ( + [self.dilation] * 2 if isinstance(self.dilation, int) else self.dilation + ) + + def forward(self, x): + ih, iw = x.size()[-2:] + kh, kw = self.kernel_size + sh, sw = self.stride + oh, ow = math.ceil(ih / sh), math.ceil(iw / sw) + pad_h = max((oh - 1) * self.stride[0] + (kh - 1) * self.dilation[0] + 1 - ih, 0) + pad_w = max((ow - 1) * self.stride[1] + (kw - 1) * self.dilation[1] + 1 - iw, 0) + if pad_h > 0 or pad_w > 0: + x = F.pad( + x, [pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2] + ) + return F.max_pool2d( + x, + self.kernel_size, + self.stride, + self.padding, + self.dilation, + self.ceil_mode, + self.return_indices, + ) + + +class MaxPool2dStaticSamePadding(nn.MaxPool2d): + """2D MaxPooling like TensorFlow's 'SAME' mode, with the given input image size. + The padding mudule is calculated in construction function, then used in forward. + """ + + def __init__(self, kernel_size, stride, image_size=None, **kwargs): + super().__init__(kernel_size, stride, **kwargs) + self.stride = [self.stride] * 2 if isinstance(self.stride, int) else self.stride + self.kernel_size = ( + [self.kernel_size] * 2 + if isinstance(self.kernel_size, int) + else self.kernel_size + ) + self.dilation = ( + [self.dilation] * 2 if isinstance(self.dilation, int) else self.dilation + ) + + # Calculate padding based on image size and save it + assert image_size is not None + ih, iw = (image_size, image_size) if isinstance(image_size, int) else image_size + kh, kw = self.kernel_size + sh, sw = self.stride + oh, ow = math.ceil(ih / sh), math.ceil(iw / sw) + pad_h = max((oh - 1) * self.stride[0] + (kh - 1) * self.dilation[0] + 1 - ih, 0) + pad_w = max((ow - 1) * self.stride[1] + (kw - 1) * self.dilation[1] + 1 - iw, 0) + if pad_h > 0 or pad_w > 0: + self.static_padding = nn.ZeroPad2d( + (pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2) + ) + else: + self.static_padding = nn.Identity() + + def forward(self, x): + x = self.static_padding(x) + x = F.max_pool2d( + x, + self.kernel_size, + self.stride, + self.padding, + self.dilation, + self.ceil_mode, + self.return_indices, + ) + return x + + +################################################################################ +# Helper functions for loading model params +################################################################################ + +# BlockDecoder: A Class for encoding and decoding BlockArgs +# efficientnet_params: A function to query compound coefficient +# get_model_params and efficientnet: +# Functions to get BlockArgs and GlobalParams for efficientnet + + +class BlockDecoder(object): + """Block Decoder for readability, + straight from the official TensorFlow repository. + """ + + @staticmethod + def _decode_block_string(block_string): + """Get a block through a string notation of arguments. + + Args: + block_string (str): A string notation of arguments. + Examples: 'r1_k3_s11_e1_i32_o16_se0.25_noskip'. + + Returns: + BlockArgs: The namedtuple defined at the top of this file. + """ + assert isinstance(block_string, str) + + ops = block_string.split("_") + options = {} + for op in ops: + splits = re.split(r"(\d.*)", op) + if len(splits) >= 2: + key, value = splits[:2] + options[key] = value + + # Check stride + assert ("s" in options and len(options["s"]) == 1) or ( + len(options["s"]) == 2 and options["s"][0] == options["s"][1] + ) + + return BlockArgs( + num_repeat=int(options["r"]), + kernel_size=int(options["k"]), + stride=[int(options["s"][0])], + expand_ratio=int(options["e"]), + input_filters=int(options["i"]), + output_filters=int(options["o"]), + se_ratio=float(options["se"]) if "se" in options else None, + id_skip=("noskip" not in block_string), + ) + + @staticmethod + def decode(string_list): + """Decode a list of string notations to specify blocks inside the network. + + Args: + string_list (list[str]): A list of strings, each string is a notation of block. + + Returns: + blocks_args: A list of BlockArgs namedtuples of block args. + """ + assert isinstance(string_list, list) + blocks_args = [] + for block_string in string_list: + blocks_args.append(BlockDecoder._decode_block_string(block_string)) + return blocks_args + + +def efficientnet_params(model_name): + """Map EfficientNet model name to parameter coefficients. + + Args: + model_name (str): Model name to be queried. + + Returns: + params_dict[model_name]: A (width,depth,res,dropout) tuple. + """ + params_dict = { + # Coefficients: width,depth,res,dropout + "efficientnet-b0": (1.0, 1.0, 224, 0.2), + "efficientnet-b1": (1.0, 1.1, 240, 0.2), + "efficientnet-b2": (1.1, 1.2, 260, 0.3), + "efficientnet-b3": (1.2, 1.4, 300, 0.3), + "efficientnet-b4": (1.4, 1.8, 380, 0.4), + "efficientnet-b5": (1.6, 2.2, 456, 0.4), + "efficientnet-b6": (1.8, 2.6, 528, 0.5), + "efficientnet-b7": (2.0, 3.1, 600, 0.5), + "efficientnet-b8": (2.2, 3.6, 672, 0.5), + "efficientnet-l2": (4.3, 5.3, 800, 0.5), + } + return params_dict[model_name] + + +def efficientnet( + width_coefficient=None, + depth_coefficient=None, + image_size=None, + dropout_rate=0.2, + drop_connect_rate=0.2, + num_classes=1000, + include_top=True, +): + """Create BlockArgs and GlobalParams for efficientnet model. + + Args: + width_coefficient (float) + depth_coefficient (float) + image_size (int) + dropout_rate (float) + drop_connect_rate (float) + num_classes (int) + + Meaning as the name suggests. + + Returns: + blocks_args, global_params. + """ + + # Blocks args for the whole model(efficientnet-b0 by default) + # It will be modified in the construction of EfficientNet Class according to model + blocks_args = [ + "r1_k3_s11_e1_i32_o16_se0.25", + "r2_k3_s22_e6_i16_o24_se0.25", + "r2_k5_s22_e6_i24_o40_se0.25", + "r3_k3_s22_e6_i40_o80_se0.25", + "r3_k5_s11_e6_i80_o112_se0.25", + "r4_k5_s22_e6_i112_o192_se0.25", + "r1_k3_s11_e6_i192_o320_se0.25", + ] + blocks_args = BlockDecoder.decode(blocks_args) + + global_params = GlobalParams( + width_coefficient=width_coefficient, + depth_coefficient=depth_coefficient, + image_size=image_size, + dropout_rate=dropout_rate, + num_classes=num_classes, + batch_norm_momentum=0.99, + batch_norm_epsilon=1e-3, + drop_connect_rate=drop_connect_rate, + depth_divisor=8, + min_depth=None, + include_top=include_top, + ) + + return blocks_args, global_params + + +def get_model_params(model_name, override_params): + """Get the block args and global params for a given model name. + + Args: + model_name (str): Model's name. + override_params (dict): A dict to modify global_params. + + Returns: + blocks_args, global_params + """ + if model_name.startswith("efficientnet"): + w, d, s, p = efficientnet_params(model_name) + # note: all models have drop connect rate = 0.2 + blocks_args, global_params = efficientnet( + width_coefficient=w, depth_coefficient=d, dropout_rate=p, image_size=s + ) + else: + raise NotImplementedError( + "model name is not pre-defined: {}".format(model_name) + ) + if override_params: + # ValueError will be raised here if override_params has fields not included in global_params. + global_params = global_params._replace(**override_params) + return blocks_args, global_params diff --git a/segmentation_models_pytorch/encoders/_inceptionresnetv2.py b/segmentation_models_pytorch/encoders/_inceptionresnetv2.py new file mode 100644 index 00000000..50b9b616 --- /dev/null +++ b/segmentation_models_pytorch/encoders/_inceptionresnetv2.py @@ -0,0 +1,301 @@ +import torch +import torch.nn as nn + + +class BasicConv2d(nn.Module): + def __init__(self, in_planes, out_planes, kernel_size, stride, padding=0): + super(BasicConv2d, self).__init__() + self.conv = nn.Conv2d( + in_planes, + out_planes, + kernel_size=kernel_size, + stride=stride, + padding=padding, + bias=False, + ) # verify bias false + self.bn = nn.BatchNorm2d( + out_planes, + eps=0.001, # value found in tensorflow + momentum=0.1, # default pytorch value + affine=True, + ) + self.relu = nn.ReLU(inplace=False) + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + x = self.relu(x) + return x + + +class Mixed_5b(nn.Module): + def __init__(self): + super(Mixed_5b, self).__init__() + + self.branch0 = BasicConv2d(192, 96, kernel_size=1, stride=1) + + self.branch1 = nn.Sequential( + BasicConv2d(192, 48, kernel_size=1, stride=1), + BasicConv2d(48, 64, kernel_size=5, stride=1, padding=2), + ) + + self.branch2 = nn.Sequential( + BasicConv2d(192, 64, kernel_size=1, stride=1), + BasicConv2d(64, 96, kernel_size=3, stride=1, padding=1), + BasicConv2d(96, 96, kernel_size=3, stride=1, padding=1), + ) + + self.branch3 = nn.Sequential( + nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False), + BasicConv2d(192, 64, kernel_size=1, stride=1), + ) + + def forward(self, x): + x0 = self.branch0(x) + x1 = self.branch1(x) + x2 = self.branch2(x) + x3 = self.branch3(x) + out = torch.cat((x0, x1, x2, x3), 1) + return out + + +class Block35(nn.Module): + def __init__(self, scale=1.0): + super(Block35, self).__init__() + + self.scale = scale + + self.branch0 = BasicConv2d(320, 32, kernel_size=1, stride=1) + + self.branch1 = nn.Sequential( + BasicConv2d(320, 32, kernel_size=1, stride=1), + BasicConv2d(32, 32, kernel_size=3, stride=1, padding=1), + ) + + self.branch2 = nn.Sequential( + BasicConv2d(320, 32, kernel_size=1, stride=1), + BasicConv2d(32, 48, kernel_size=3, stride=1, padding=1), + BasicConv2d(48, 64, kernel_size=3, stride=1, padding=1), + ) + + self.conv2d = nn.Conv2d(128, 320, kernel_size=1, stride=1) + self.relu = nn.ReLU(inplace=False) + + def forward(self, x): + x0 = self.branch0(x) + x1 = self.branch1(x) + x2 = self.branch2(x) + out = torch.cat((x0, x1, x2), 1) + out = self.conv2d(out) + out = out * self.scale + x + out = self.relu(out) + return out + + +class Mixed_6a(nn.Module): + def __init__(self): + super(Mixed_6a, self).__init__() + + self.branch0 = BasicConv2d(320, 384, kernel_size=3, stride=2) + + self.branch1 = nn.Sequential( + BasicConv2d(320, 256, kernel_size=1, stride=1), + BasicConv2d(256, 256, kernel_size=3, stride=1, padding=1), + BasicConv2d(256, 384, kernel_size=3, stride=2), + ) + + self.branch2 = nn.MaxPool2d(3, stride=2) + + def forward(self, x): + x0 = self.branch0(x) + x1 = self.branch1(x) + x2 = self.branch2(x) + out = torch.cat((x0, x1, x2), 1) + return out + + +class Block17(nn.Module): + def __init__(self, scale=1.0): + super(Block17, self).__init__() + + self.scale = scale + + self.branch0 = BasicConv2d(1088, 192, kernel_size=1, stride=1) + + self.branch1 = nn.Sequential( + BasicConv2d(1088, 128, kernel_size=1, stride=1), + BasicConv2d(128, 160, kernel_size=(1, 7), stride=1, padding=(0, 3)), + BasicConv2d(160, 192, kernel_size=(7, 1), stride=1, padding=(3, 0)), + ) + + self.conv2d = nn.Conv2d(384, 1088, kernel_size=1, stride=1) + self.relu = nn.ReLU(inplace=False) + + def forward(self, x): + x0 = self.branch0(x) + x1 = self.branch1(x) + out = torch.cat((x0, x1), 1) + out = self.conv2d(out) + out = out * self.scale + x + out = self.relu(out) + return out + + +class Mixed_7a(nn.Module): + def __init__(self): + super(Mixed_7a, self).__init__() + + self.branch0 = nn.Sequential( + BasicConv2d(1088, 256, kernel_size=1, stride=1), + BasicConv2d(256, 384, kernel_size=3, stride=2), + ) + + self.branch1 = nn.Sequential( + BasicConv2d(1088, 256, kernel_size=1, stride=1), + BasicConv2d(256, 288, kernel_size=3, stride=2), + ) + + self.branch2 = nn.Sequential( + BasicConv2d(1088, 256, kernel_size=1, stride=1), + BasicConv2d(256, 288, kernel_size=3, stride=1, padding=1), + BasicConv2d(288, 320, kernel_size=3, stride=2), + ) + + self.branch3 = nn.MaxPool2d(3, stride=2) + + def forward(self, x): + x0 = self.branch0(x) + x1 = self.branch1(x) + x2 = self.branch2(x) + x3 = self.branch3(x) + out = torch.cat((x0, x1, x2, x3), 1) + return out + + +class Block8(nn.Module): + def __init__(self, scale=1.0, noReLU=False): + super(Block8, self).__init__() + + self.scale = scale + self.noReLU = noReLU + + self.branch0 = BasicConv2d(2080, 192, kernel_size=1, stride=1) + + self.branch1 = nn.Sequential( + BasicConv2d(2080, 192, kernel_size=1, stride=1), + BasicConv2d(192, 224, kernel_size=(1, 3), stride=1, padding=(0, 1)), + BasicConv2d(224, 256, kernel_size=(3, 1), stride=1, padding=(1, 0)), + ) + + self.conv2d = nn.Conv2d(448, 2080, kernel_size=1, stride=1) + if not self.noReLU: + self.relu = nn.ReLU(inplace=False) + + def forward(self, x): + x0 = self.branch0(x) + x1 = self.branch1(x) + out = torch.cat((x0, x1), 1) + out = self.conv2d(out) + out = out * self.scale + x + if not self.noReLU: + out = self.relu(out) + return out + + +class InceptionResNetV2(nn.Module): + def __init__(self, num_classes=1001): + super(InceptionResNetV2, self).__init__() + # Special attributs + self.input_space = None + self.input_size = (299, 299, 3) + self.mean = None + self.std = None + # Modules + self.conv2d_1a = BasicConv2d(3, 32, kernel_size=3, stride=2) + self.conv2d_2a = BasicConv2d(32, 32, kernel_size=3, stride=1) + self.conv2d_2b = BasicConv2d(32, 64, kernel_size=3, stride=1, padding=1) + self.maxpool_3a = nn.MaxPool2d(3, stride=2) + self.conv2d_3b = BasicConv2d(64, 80, kernel_size=1, stride=1) + self.conv2d_4a = BasicConv2d(80, 192, kernel_size=3, stride=1) + self.maxpool_5a = nn.MaxPool2d(3, stride=2) + self.mixed_5b = Mixed_5b() + self.repeat = nn.Sequential( + Block35(scale=0.17), + Block35(scale=0.17), + Block35(scale=0.17), + Block35(scale=0.17), + Block35(scale=0.17), + Block35(scale=0.17), + Block35(scale=0.17), + Block35(scale=0.17), + Block35(scale=0.17), + Block35(scale=0.17), + ) + self.mixed_6a = Mixed_6a() + self.repeat_1 = nn.Sequential( + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + Block17(scale=0.10), + ) + self.mixed_7a = Mixed_7a() + self.repeat_2 = nn.Sequential( + Block8(scale=0.20), + Block8(scale=0.20), + Block8(scale=0.20), + Block8(scale=0.20), + Block8(scale=0.20), + Block8(scale=0.20), + Block8(scale=0.20), + Block8(scale=0.20), + Block8(scale=0.20), + ) + self.block8 = Block8(noReLU=True) + self.conv2d_7b = BasicConv2d(2080, 1536, kernel_size=1, stride=1) + self.avgpool_1a = nn.AvgPool2d(8, count_include_pad=False) + self.last_linear = nn.Linear(1536, num_classes) + + def features(self, input): + x = self.conv2d_1a(input) + x = self.conv2d_2a(x) + x = self.conv2d_2b(x) + x = self.maxpool_3a(x) + x = self.conv2d_3b(x) + x = self.conv2d_4a(x) + x = self.maxpool_5a(x) + x = self.mixed_5b(x) + x = self.repeat(x) + x = self.mixed_6a(x) + x = self.repeat_1(x) + x = self.mixed_7a(x) + x = self.repeat_2(x) + x = self.block8(x) + x = self.conv2d_7b(x) + return x + + def logits(self, features): + x = self.avgpool_1a(features) + x = x.view(x.size(0), -1) + x = self.last_linear(x) + return x + + def forward(self, input): + x = self.features(input) + x = self.logits(x) + return x diff --git a/segmentation_models_pytorch/encoders/_inceptionv4.py b/segmentation_models_pytorch/encoders/_inceptionv4.py new file mode 100644 index 00000000..934f74cd --- /dev/null +++ b/segmentation_models_pytorch/encoders/_inceptionv4.py @@ -0,0 +1,291 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + + +class BasicConv2d(nn.Module): + def __init__(self, in_planes, out_planes, kernel_size, stride, padding=0): + super(BasicConv2d, self).__init__() + self.conv = nn.Conv2d( + in_planes, + out_planes, + kernel_size=kernel_size, + stride=stride, + padding=padding, + bias=False, + ) # verify bias false + self.bn = nn.BatchNorm2d( + out_planes, + eps=0.001, # value found in tensorflow + momentum=0.1, # default pytorch value + affine=True, + ) + self.relu = nn.ReLU(inplace=True) + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + x = self.relu(x) + return x + + +class Mixed_3a(nn.Module): + def __init__(self): + super(Mixed_3a, self).__init__() + self.maxpool = nn.MaxPool2d(3, stride=2) + self.conv = BasicConv2d(64, 96, kernel_size=3, stride=2) + + def forward(self, x): + x0 = self.maxpool(x) + x1 = self.conv(x) + out = torch.cat((x0, x1), 1) + return out + + +class Mixed_4a(nn.Module): + def __init__(self): + super(Mixed_4a, self).__init__() + + self.branch0 = nn.Sequential( + BasicConv2d(160, 64, kernel_size=1, stride=1), + BasicConv2d(64, 96, kernel_size=3, stride=1), + ) + + self.branch1 = nn.Sequential( + BasicConv2d(160, 64, kernel_size=1, stride=1), + BasicConv2d(64, 64, kernel_size=(1, 7), stride=1, padding=(0, 3)), + BasicConv2d(64, 64, kernel_size=(7, 1), stride=1, padding=(3, 0)), + BasicConv2d(64, 96, kernel_size=(3, 3), stride=1), + ) + + def forward(self, x): + x0 = self.branch0(x) + x1 = self.branch1(x) + out = torch.cat((x0, x1), 1) + return out + + +class Mixed_5a(nn.Module): + def __init__(self): + super(Mixed_5a, self).__init__() + self.conv = BasicConv2d(192, 192, kernel_size=3, stride=2) + self.maxpool = nn.MaxPool2d(3, stride=2) + + def forward(self, x): + x0 = self.conv(x) + x1 = self.maxpool(x) + out = torch.cat((x0, x1), 1) + return out + + +class Inception_A(nn.Module): + def __init__(self): + super(Inception_A, self).__init__() + self.branch0 = BasicConv2d(384, 96, kernel_size=1, stride=1) + + self.branch1 = nn.Sequential( + BasicConv2d(384, 64, kernel_size=1, stride=1), + BasicConv2d(64, 96, kernel_size=3, stride=1, padding=1), + ) + + self.branch2 = nn.Sequential( + BasicConv2d(384, 64, kernel_size=1, stride=1), + BasicConv2d(64, 96, kernel_size=3, stride=1, padding=1), + BasicConv2d(96, 96, kernel_size=3, stride=1, padding=1), + ) + + self.branch3 = nn.Sequential( + nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False), + BasicConv2d(384, 96, kernel_size=1, stride=1), + ) + + def forward(self, x): + x0 = self.branch0(x) + x1 = self.branch1(x) + x2 = self.branch2(x) + x3 = self.branch3(x) + out = torch.cat((x0, x1, x2, x3), 1) + return out + + +class Reduction_A(nn.Module): + def __init__(self): + super(Reduction_A, self).__init__() + self.branch0 = BasicConv2d(384, 384, kernel_size=3, stride=2) + + self.branch1 = nn.Sequential( + BasicConv2d(384, 192, kernel_size=1, stride=1), + BasicConv2d(192, 224, kernel_size=3, stride=1, padding=1), + BasicConv2d(224, 256, kernel_size=3, stride=2), + ) + + self.branch2 = nn.MaxPool2d(3, stride=2) + + def forward(self, x): + x0 = self.branch0(x) + x1 = self.branch1(x) + x2 = self.branch2(x) + out = torch.cat((x0, x1, x2), 1) + return out + + +class Inception_B(nn.Module): + def __init__(self): + super(Inception_B, self).__init__() + self.branch0 = BasicConv2d(1024, 384, kernel_size=1, stride=1) + + self.branch1 = nn.Sequential( + BasicConv2d(1024, 192, kernel_size=1, stride=1), + BasicConv2d(192, 224, kernel_size=(1, 7), stride=1, padding=(0, 3)), + BasicConv2d(224, 256, kernel_size=(7, 1), stride=1, padding=(3, 0)), + ) + + self.branch2 = nn.Sequential( + BasicConv2d(1024, 192, kernel_size=1, stride=1), + BasicConv2d(192, 192, kernel_size=(7, 1), stride=1, padding=(3, 0)), + BasicConv2d(192, 224, kernel_size=(1, 7), stride=1, padding=(0, 3)), + BasicConv2d(224, 224, kernel_size=(7, 1), stride=1, padding=(3, 0)), + BasicConv2d(224, 256, kernel_size=(1, 7), stride=1, padding=(0, 3)), + ) + + self.branch3 = nn.Sequential( + nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False), + BasicConv2d(1024, 128, kernel_size=1, stride=1), + ) + + def forward(self, x): + x0 = self.branch0(x) + x1 = self.branch1(x) + x2 = self.branch2(x) + x3 = self.branch3(x) + out = torch.cat((x0, x1, x2, x3), 1) + return out + + +class Reduction_B(nn.Module): + def __init__(self): + super(Reduction_B, self).__init__() + + self.branch0 = nn.Sequential( + BasicConv2d(1024, 192, kernel_size=1, stride=1), + BasicConv2d(192, 192, kernel_size=3, stride=2), + ) + + self.branch1 = nn.Sequential( + BasicConv2d(1024, 256, kernel_size=1, stride=1), + BasicConv2d(256, 256, kernel_size=(1, 7), stride=1, padding=(0, 3)), + BasicConv2d(256, 320, kernel_size=(7, 1), stride=1, padding=(3, 0)), + BasicConv2d(320, 320, kernel_size=3, stride=2), + ) + + self.branch2 = nn.MaxPool2d(3, stride=2) + + def forward(self, x): + x0 = self.branch0(x) + x1 = self.branch1(x) + x2 = self.branch2(x) + out = torch.cat((x0, x1, x2), 1) + return out + + +class Inception_C(nn.Module): + def __init__(self): + super(Inception_C, self).__init__() + + self.branch0 = BasicConv2d(1536, 256, kernel_size=1, stride=1) + + self.branch1_0 = BasicConv2d(1536, 384, kernel_size=1, stride=1) + self.branch1_1a = BasicConv2d( + 384, 256, kernel_size=(1, 3), stride=1, padding=(0, 1) + ) + self.branch1_1b = BasicConv2d( + 384, 256, kernel_size=(3, 1), stride=1, padding=(1, 0) + ) + + self.branch2_0 = BasicConv2d(1536, 384, kernel_size=1, stride=1) + self.branch2_1 = BasicConv2d( + 384, 448, kernel_size=(3, 1), stride=1, padding=(1, 0) + ) + self.branch2_2 = BasicConv2d( + 448, 512, kernel_size=(1, 3), stride=1, padding=(0, 1) + ) + self.branch2_3a = BasicConv2d( + 512, 256, kernel_size=(1, 3), stride=1, padding=(0, 1) + ) + self.branch2_3b = BasicConv2d( + 512, 256, kernel_size=(3, 1), stride=1, padding=(1, 0) + ) + + self.branch3 = nn.Sequential( + nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False), + BasicConv2d(1536, 256, kernel_size=1, stride=1), + ) + + def forward(self, x): + x0 = self.branch0(x) + + x1_0 = self.branch1_0(x) + x1_1a = self.branch1_1a(x1_0) + x1_1b = self.branch1_1b(x1_0) + x1 = torch.cat((x1_1a, x1_1b), 1) + + x2_0 = self.branch2_0(x) + x2_1 = self.branch2_1(x2_0) + x2_2 = self.branch2_2(x2_1) + x2_3a = self.branch2_3a(x2_2) + x2_3b = self.branch2_3b(x2_2) + x2 = torch.cat((x2_3a, x2_3b), 1) + + x3 = self.branch3(x) + + out = torch.cat((x0, x1, x2, x3), 1) + return out + + +class InceptionV4(nn.Module): + def __init__(self, num_classes=1001): + super(InceptionV4, self).__init__() + # Special attributs + self.input_space = None + self.input_size = (299, 299, 3) + self.mean = None + self.std = None + # Modules + self.features = nn.Sequential( + BasicConv2d(3, 32, kernel_size=3, stride=2), + BasicConv2d(32, 32, kernel_size=3, stride=1), + BasicConv2d(32, 64, kernel_size=3, stride=1, padding=1), + Mixed_3a(), + Mixed_4a(), + Mixed_5a(), + Inception_A(), + Inception_A(), + Inception_A(), + Inception_A(), + Reduction_A(), # Mixed_6a + Inception_B(), + Inception_B(), + Inception_B(), + Inception_B(), + Inception_B(), + Inception_B(), + Inception_B(), + Reduction_B(), # Mixed_7a + Inception_C(), + Inception_C(), + Inception_C(), + ) + self.last_linear = nn.Linear(1536, num_classes) + + def logits(self, features): + # Allows image of any size to be processed + adaptiveAvgPoolWidth = features.shape[2] + x = F.avg_pool2d(features, kernel_size=adaptiveAvgPoolWidth) + x = x.view(x.size(0), -1) + x = self.last_linear(x) + return x + + def forward(self, input): + x = self.features(input) + x = self.logits(x) + return x diff --git a/segmentation_models_pytorch/encoders/_legacy_pretrained_settings.py b/segmentation_models_pytorch/encoders/_legacy_pretrained_settings.py new file mode 100644 index 00000000..21f5691e --- /dev/null +++ b/segmentation_models_pytorch/encoders/_legacy_pretrained_settings.py @@ -0,0 +1,1062 @@ +pretrained_settings = { + "resnet18": { + "imagenet": { + "url": "https://download.pytorch.org/models/resnet18-5c106cde.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + "ssl": { + "url": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnet18-d92f0530.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + "swsl": { + "url": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnet18-118f1556.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + }, + "resnet34": { + "imagenet": { + "url": "https://download.pytorch.org/models/resnet34-333f7ec4.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "resnet50": { + "imagenet": { + "url": "https://download.pytorch.org/models/resnet50-19c8e357.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + "ssl": { + "url": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnet50-08389792.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + "swsl": { + "url": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnet50-16a12f1b.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + }, + "resnet101": { + "imagenet": { + "url": "https://download.pytorch.org/models/resnet101-5d3b4d8f.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "resnet152": { + "imagenet": { + "url": "https://download.pytorch.org/models/resnet152-b121ed2d.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "resnext50_32x4d": { + "imagenet": { + "url": "https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + "ssl": { + "url": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnext50_32x4-ddb3e555.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + "swsl": { + "url": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnext50_32x4-72679e44.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + }, + "resnext101_32x4d": { + "ssl": { + "url": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnext101_32x4-dc43570a.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + "swsl": { + "url": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnext101_32x4-3f87e46b.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + }, + "resnext101_32x8d": { + "imagenet": { + "url": "https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + "instagram": { + "url": "https://download.pytorch.org/models/ig_resnext101_32x8-c38310e5.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + "ssl": { + "url": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnext101_32x8-2cfe2f8b.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + "swsl": { + "url": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnext101_32x8-b4712904.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + }, + "resnext101_32x16d": { + "instagram": { + "url": "https://download.pytorch.org/models/ig_resnext101_32x16-c6f796b0.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + "ssl": { + "url": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnext101_32x16-15fffa57.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + "swsl": { + "url": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnext101_32x16-f3559a9c.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + }, + }, + "resnext101_32x32d": { + "instagram": { + "url": "https://download.pytorch.org/models/ig_resnext101_32x32-e4b90b00.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "resnext101_32x48d": { + "instagram": { + "url": "https://download.pytorch.org/models/ig_resnext101_32x48-3e41cc8a.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "dpn68": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/dpn68-4af7d88d2.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.48627450980392156, 0.4588235294117647, 0.40784313725490196], + "std": [0.23482446870963955, 0.23482446870963955, 0.23482446870963955], + "num_classes": 1000, + } + }, + "dpn68b": { + "imagenet+5k": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/dpn68b_extra-363ab9c19.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.48627450980392156, 0.4588235294117647, 0.40784313725490196], + "std": [0.23482446870963955, 0.23482446870963955, 0.23482446870963955], + "num_classes": 1000, + } + }, + "dpn92": { + "imagenet+5k": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/dpn92_extra-fda993c95.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.48627450980392156, 0.4588235294117647, 0.40784313725490196], + "std": [0.23482446870963955, 0.23482446870963955, 0.23482446870963955], + "num_classes": 1000, + } + }, + "dpn98": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/dpn98-722954780.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.48627450980392156, 0.4588235294117647, 0.40784313725490196], + "std": [0.23482446870963955, 0.23482446870963955, 0.23482446870963955], + "num_classes": 1000, + } + }, + "dpn107": { + "imagenet+5k": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/dpn107_extra-b7f9f4cc9.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.48627450980392156, 0.4588235294117647, 0.40784313725490196], + "std": [0.23482446870963955, 0.23482446870963955, 0.23482446870963955], + "num_classes": 1000, + } + }, + "dpn131": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/dpn131-7af84be88.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.48627450980392156, 0.4588235294117647, 0.40784313725490196], + "std": [0.23482446870963955, 0.23482446870963955, 0.23482446870963955], + "num_classes": 1000, + } + }, + "vgg11": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg11-bbd30ac9.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg11_bn": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg11_bn-6002323d.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg13": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg13-c768596a.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg13_bn": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg13_bn-abd245e5.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg16": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg16-397923af.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg16_bn": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg16_bn-6c64b313.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg19": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg19-dcbb9e9d.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg19_bn": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg19_bn-c79401a0.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "senet154": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/senet154-c7b49a05.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "se_resnet50": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/se_resnet50-ce0d4300.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "se_resnet101": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/se_resnet101-7e38fcc6.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "se_resnet152": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/se_resnet152-d17c99b7.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "se_resnext50_32x4d": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/se_resnext50_32x4d-a260b3a4.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "se_resnext101_32x4d": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/se_resnext101_32x4d-3b2fe3d8.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "densenet121": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/densenet121-fbdb23505.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "densenet169": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/densenet169-f470b90a4.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "densenet201": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/densenet201-5750cbb1e.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "densenet161": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/densenet161-347e6b360.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "inceptionresnetv2": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/inceptionresnetv2-520b38e4.pth", + "input_space": "RGB", + "input_size": [3, 299, 299], + "input_range": [0, 1], + "mean": [0.5, 0.5, 0.5], + "std": [0.5, 0.5, 0.5], + "num_classes": 1000, + }, + "imagenet+background": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/inceptionresnetv2-520b38e4.pth", + "input_space": "RGB", + "input_size": [3, 299, 299], + "input_range": [0, 1], + "mean": [0.5, 0.5, 0.5], + "std": [0.5, 0.5, 0.5], + "num_classes": 1001, + }, + }, + "inceptionv4": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/inceptionv4-8e4777a0.pth", + "input_space": "RGB", + "input_size": [3, 299, 299], + "input_range": [0, 1], + "mean": [0.5, 0.5, 0.5], + "std": [0.5, 0.5, 0.5], + "num_classes": 1000, + }, + "imagenet+background": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/inceptionv4-8e4777a0.pth", + "input_space": "RGB", + "input_size": [3, 299, 299], + "input_range": [0, 1], + "mean": [0.5, 0.5, 0.5], + "std": [0.5, 0.5, 0.5], + "num_classes": 1001, + }, + }, + "efficientnet-b0": { + "imagenet": { + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b0-355c32eb.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + "advprop": { + "mean": [0.5, 0.5, 0.5], + "std": [0.5, 0.5, 0.5], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b0-b64d5a18.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + }, + "efficientnet-b1": { + "imagenet": { + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b1-f1951068.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + "advprop": { + "mean": [0.5, 0.5, 0.5], + "std": [0.5, 0.5, 0.5], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b1-0f3ce85a.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + }, + "efficientnet-b2": { + "imagenet": { + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b2-8bb594d6.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + "advprop": { + "mean": [0.5, 0.5, 0.5], + "std": [0.5, 0.5, 0.5], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b2-6e9d97e5.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + }, + "efficientnet-b3": { + "imagenet": { + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b3-5fb5a3c3.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + "advprop": { + "mean": [0.5, 0.5, 0.5], + "std": [0.5, 0.5, 0.5], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b3-cdd7c0f4.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + }, + "efficientnet-b4": { + "imagenet": { + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b4-6ed6700e.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + "advprop": { + "mean": [0.5, 0.5, 0.5], + "std": [0.5, 0.5, 0.5], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b4-44fb3a87.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + }, + "efficientnet-b5": { + "imagenet": { + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b5-b6417697.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + "advprop": { + "mean": [0.5, 0.5, 0.5], + "std": [0.5, 0.5, 0.5], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b5-86493f6b.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + }, + "efficientnet-b6": { + "imagenet": { + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b6-c76e70fd.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + "advprop": { + "mean": [0.5, 0.5, 0.5], + "std": [0.5, 0.5, 0.5], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b6-ac80338e.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + }, + "efficientnet-b7": { + "imagenet": { + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b7-dcc49843.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + "advprop": { + "mean": [0.5, 0.5, 0.5], + "std": [0.5, 0.5, 0.5], + "url": "https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b7-4652b6dd.pth", + "input_space": "RGB", + "input_range": [0, 1], + }, + }, + "mobilenet_v2": { + "imagenet": { + "url": "https://download.pytorch.org/models/mobilenet_v2-b0353104.pth", + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "input_space": "RGB", + "input_range": [0, 1], + } + }, + "xception": { + "imagenet": { + "url": "http://data.lip6.fr/cadene/pretrainedmodels/xception-43020ad28.pth", + "input_space": "RGB", + "input_size": [3, 299, 299], + "input_range": [0, 1], + "mean": [0.5, 0.5, 0.5], + "std": [0.5, 0.5, 0.5], + "num_classes": 1000, + "scale": 0.8975, + } + }, + "timm-efficientnet-b0": { + "imagenet": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/huggingface/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b0-0af12548.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "advprop": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b0_ap-f262efe1.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "noisy-student": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b0_ns-c0e6a31c.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + }, + "timm-efficientnet-b1": { + "imagenet": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/huggingface/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b1-5c1377c4.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "advprop": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b1_ap-44ef0a3d.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "noisy-student": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b1_ns-99dd0c41.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + }, + "timm-efficientnet-b2": { + "imagenet": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/huggingface/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b2-e393ef04.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "advprop": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b2_ap-2f8e7636.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "noisy-student": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b2_ns-00306e48.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + }, + "timm-efficientnet-b3": { + "imagenet": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/huggingface/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b3-e3bd6955.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "advprop": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b3_ap-aad25bdd.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "noisy-student": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b3_ns-9d44bf68.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + }, + "timm-efficientnet-b4": { + "imagenet": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/huggingface/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b4-74ee3bed.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "advprop": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b4_ap-dedb23e6.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "noisy-student": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b4_ns-d6313a46.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + }, + "timm-efficientnet-b5": { + "imagenet": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/huggingface/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b5-c6949ce9.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "advprop": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b5_ap-9e82fae8.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "noisy-student": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b5_ns-6f26d0cf.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + }, + "timm-efficientnet-b6": { + "imagenet": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b6_aa-80ba17e4.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "advprop": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b6_ap-4ffb161f.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "noisy-student": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b6_ns-51548356.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + }, + "timm-efficientnet-b7": { + "imagenet": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/huggingface/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b7_aa-076e3472.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "advprop": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b7_ap-ddb28fec.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "noisy-student": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b7_ns-1dbc32de.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + }, + "timm-efficientnet-b8": { + "imagenet": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b8_ra-572d5dd9.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "advprop": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_b8_ap-00e169fa.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + }, + "timm-efficientnet-l2": { + "noisy-student": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_l2_ns-df73bb44.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + "noisy-student-475": { + "mean": (0.485, 0.456, 0.406), + "std": (0.229, 0.224, 0.225), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_l2_ns_475-bebbd00a.pth", + "input_range": (0, 1), + "input_space": "RGB", + }, + }, + "timm-tf_efficientnet_lite0": { + "imagenet": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_lite0-0aa007d2.pth", + "input_range": (0, 1), + "input_space": "RGB", + } + }, + "timm-tf_efficientnet_lite1": { + "imagenet": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_lite1-bde8b488.pth", + "input_range": (0, 1), + "input_space": "RGB", + } + }, + "timm-tf_efficientnet_lite2": { + "imagenet": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_lite2-dcccb7df.pth", + "input_range": (0, 1), + "input_space": "RGB", + } + }, + "timm-tf_efficientnet_lite3": { + "imagenet": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_lite3-b733e338.pth", + "input_range": (0, 1), + "input_space": "RGB", + } + }, + "timm-tf_efficientnet_lite4": { + "imagenet": { + "mean": (0.5, 0.5, 0.5), + "std": (0.5, 0.5, 0.5), + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_efficientnet_lite4-741542c3.pth", + "input_range": (0, 1), + "input_space": "RGB", + } + }, + "timm-skresnet18": { + "imagenet": { + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/skresnet18_ra-4eec2804.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "timm-skresnet34": { + "imagenet": { + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/skresnet34_ra-bdc0ccde.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "timm-skresnext50_32x4d": { + "imagenet": { + "url": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/skresnext50_ra-f40e40bf.pth", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "mit_b0": { + "imagenet": { + "url": "https://github.com/qubvel/segmentation_models.pytorch/releases/download/v0.0.2/mit_b0.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + } + }, + "mit_b1": { + "imagenet": { + "url": "https://github.com/qubvel/segmentation_models.pytorch/releases/download/v0.0.2/mit_b1.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + } + }, + "mit_b2": { + "imagenet": { + "url": "https://github.com/qubvel/segmentation_models.pytorch/releases/download/v0.0.2/mit_b2.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + } + }, + "mit_b3": { + "imagenet": { + "url": "https://github.com/qubvel/segmentation_models.pytorch/releases/download/v0.0.2/mit_b3.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + } + }, + "mit_b4": { + "imagenet": { + "url": "https://github.com/qubvel/segmentation_models.pytorch/releases/download/v0.0.2/mit_b4.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + } + }, + "mit_b5": { + "imagenet": { + "url": "https://github.com/qubvel/segmentation_models.pytorch/releases/download/v0.0.2/mit_b5.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + } + }, + "mobileone_s0": { + "imagenet": { + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "url": "https://docs-assets.developer.apple.com/ml-research/datasets/mobileone/mobileone_s0_unfused.pth.tar", + "input_space": "RGB", + "input_range": [0, 1], + } + }, + "mobileone_s1": { + "imagenet": { + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "url": "https://docs-assets.developer.apple.com/ml-research/datasets/mobileone/mobileone_s1_unfused.pth.tar", + "input_space": "RGB", + "input_range": [0, 1], + } + }, + "mobileone_s2": { + "imagenet": { + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "url": "https://docs-assets.developer.apple.com/ml-research/datasets/mobileone/mobileone_s2_unfused.pth.tar", + "input_space": "RGB", + "input_range": [0, 1], + } + }, + "mobileone_s3": { + "imagenet": { + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "url": "https://docs-assets.developer.apple.com/ml-research/datasets/mobileone/mobileone_s3_unfused.pth.tar", + "input_space": "RGB", + "input_range": [0, 1], + } + }, + "mobileone_s4": { + "imagenet": { + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "url": "https://docs-assets.developer.apple.com/ml-research/datasets/mobileone/mobileone_s4_unfused.pth.tar", + "input_space": "RGB", + "input_range": [0, 1], + } + }, +} diff --git a/segmentation_models_pytorch/encoders/_senet.py b/segmentation_models_pytorch/encoders/_senet.py new file mode 100644 index 00000000..f56c776a --- /dev/null +++ b/segmentation_models_pytorch/encoders/_senet.py @@ -0,0 +1,337 @@ +""" +ResNet code gently borrowed from +https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py +""" + +from collections import OrderedDict +import math + +import torch.nn as nn + + +class SEModule(nn.Module): + def __init__(self, channels, reduction): + super(SEModule, self).__init__() + self.avg_pool = nn.AdaptiveAvgPool2d(1) + self.fc1 = nn.Conv2d(channels, channels // reduction, kernel_size=1, padding=0) + self.relu = nn.ReLU(inplace=True) + self.fc2 = nn.Conv2d(channels // reduction, channels, kernel_size=1, padding=0) + self.sigmoid = nn.Sigmoid() + + def forward(self, x): + module_input = x + x = self.avg_pool(x) + x = self.fc1(x) + x = self.relu(x) + x = self.fc2(x) + x = self.sigmoid(x) + return module_input * x + + +class Bottleneck(nn.Module): + """ + Base class for bottlenecks that implements `forward()` method. + """ + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out = self.se_module(out) + residual + out = self.relu(out) + + return out + + +class SEBottleneck(Bottleneck): + """ + Bottleneck for SENet154. + """ + + expansion = 4 + + def __init__(self, inplanes, planes, groups, reduction, stride=1, downsample=None): + super(SEBottleneck, self).__init__() + self.conv1 = nn.Conv2d(inplanes, planes * 2, kernel_size=1, bias=False) + self.bn1 = nn.BatchNorm2d(planes * 2) + self.conv2 = nn.Conv2d( + planes * 2, + planes * 4, + kernel_size=3, + stride=stride, + padding=1, + groups=groups, + bias=False, + ) + self.bn2 = nn.BatchNorm2d(planes * 4) + self.conv3 = nn.Conv2d(planes * 4, planes * 4, kernel_size=1, bias=False) + self.bn3 = nn.BatchNorm2d(planes * 4) + self.relu = nn.ReLU(inplace=True) + self.se_module = SEModule(planes * 4, reduction=reduction) + self.downsample = downsample + self.stride = stride + + +class SEResNetBottleneck(Bottleneck): + """ + ResNet bottleneck with a Squeeze-and-Excitation module. It follows Caffe + implementation and uses `stride=stride` in `conv1` and not in `conv2` + (the latter is used in the torchvision implementation of ResNet). + """ + + expansion = 4 + + def __init__(self, inplanes, planes, groups, reduction, stride=1, downsample=None): + super(SEResNetBottleneck, self).__init__() + self.conv1 = nn.Conv2d( + inplanes, planes, kernel_size=1, bias=False, stride=stride + ) + self.bn1 = nn.BatchNorm2d(planes) + self.conv2 = nn.Conv2d( + planes, planes, kernel_size=3, padding=1, groups=groups, bias=False + ) + self.bn2 = nn.BatchNorm2d(planes) + self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) + self.bn3 = nn.BatchNorm2d(planes * 4) + self.relu = nn.ReLU(inplace=True) + self.se_module = SEModule(planes * 4, reduction=reduction) + self.downsample = downsample + self.stride = stride + + +class SEResNeXtBottleneck(Bottleneck): + """ + ResNeXt bottleneck type C with a Squeeze-and-Excitation module. + """ + + expansion = 4 + + def __init__( + self, + inplanes, + planes, + groups, + reduction, + stride=1, + downsample=None, + base_width=4, + ): + super(SEResNeXtBottleneck, self).__init__() + width = math.floor(planes * (base_width / 64)) * groups + self.conv1 = nn.Conv2d(inplanes, width, kernel_size=1, bias=False, stride=1) + self.bn1 = nn.BatchNorm2d(width) + self.conv2 = nn.Conv2d( + width, + width, + kernel_size=3, + stride=stride, + padding=1, + groups=groups, + bias=False, + ) + self.bn2 = nn.BatchNorm2d(width) + self.conv3 = nn.Conv2d(width, planes * 4, kernel_size=1, bias=False) + self.bn3 = nn.BatchNorm2d(planes * 4) + self.relu = nn.ReLU(inplace=True) + self.se_module = SEModule(planes * 4, reduction=reduction) + self.downsample = downsample + self.stride = stride + + +class SENet(nn.Module): + def __init__( + self, + block, + layers, + groups, + reduction, + dropout_p=0.2, + inplanes=128, + input_3x3=True, + downsample_kernel_size=3, + downsample_padding=1, + num_classes=1000, + ): + """ + Parameters + ---------- + block (nn.Module): Bottleneck class. + - For SENet154: SEBottleneck + - For SE-ResNet models: SEResNetBottleneck + - For SE-ResNeXt models: SEResNeXtBottleneck + layers (list of ints): Number of residual blocks for 4 layers of the + network (layer1...layer4). + groups (int): Number of groups for the 3x3 convolution in each + bottleneck block. + - For SENet154: 64 + - For SE-ResNet models: 1 + - For SE-ResNeXt models: 32 + reduction (int): Reduction ratio for Squeeze-and-Excitation modules. + - For all models: 16 + dropout_p (float or None): Drop probability for the Dropout layer. + If `None` the Dropout layer is not used. + - For SENet154: 0.2 + - For SE-ResNet models: None + - For SE-ResNeXt models: None + inplanes (int): Number of input channels for layer1. + - For SENet154: 128 + - For SE-ResNet models: 64 + - For SE-ResNeXt models: 64 + input_3x3 (bool): If `True`, use three 3x3 convolutions instead of + a single 7x7 convolution in layer0. + - For SENet154: True + - For SE-ResNet models: False + - For SE-ResNeXt models: False + downsample_kernel_size (int): Kernel size for downsampling convolutions + in layer2, layer3 and layer4. + - For SENet154: 3 + - For SE-ResNet models: 1 + - For SE-ResNeXt models: 1 + downsample_padding (int): Padding for downsampling convolutions in + layer2, layer3 and layer4. + - For SENet154: 1 + - For SE-ResNet models: 0 + - For SE-ResNeXt models: 0 + num_classes (int): Number of outputs in `last_linear` layer. + - For all models: 1000 + """ + super(SENet, self).__init__() + self.inplanes = inplanes + if input_3x3: + layer0_modules = [ + ("conv1", nn.Conv2d(3, 64, 3, stride=2, padding=1, bias=False)), + ("bn1", nn.BatchNorm2d(64)), + ("relu1", nn.ReLU(inplace=True)), + ("conv2", nn.Conv2d(64, 64, 3, stride=1, padding=1, bias=False)), + ("bn2", nn.BatchNorm2d(64)), + ("relu2", nn.ReLU(inplace=True)), + ("conv3", nn.Conv2d(64, inplanes, 3, stride=1, padding=1, bias=False)), + ("bn3", nn.BatchNorm2d(inplanes)), + ("relu3", nn.ReLU(inplace=True)), + ] + else: + layer0_modules = [ + ( + "conv1", + nn.Conv2d( + 3, inplanes, kernel_size=7, stride=2, padding=3, bias=False + ), + ), + ("bn1", nn.BatchNorm2d(inplanes)), + ("relu1", nn.ReLU(inplace=True)), + ] + # To preserve compatibility with Caffe weights `ceil_mode=True` + # is used instead of `padding=1`. + layer0_modules.append(("pool", nn.MaxPool2d(3, stride=2, ceil_mode=True))) + self.layer0 = nn.Sequential(OrderedDict(layer0_modules)) + self.layer1 = self._make_layer( + block, + planes=64, + blocks=layers[0], + groups=groups, + reduction=reduction, + downsample_kernel_size=1, + downsample_padding=0, + ) + self.layer2 = self._make_layer( + block, + planes=128, + blocks=layers[1], + stride=2, + groups=groups, + reduction=reduction, + downsample_kernel_size=downsample_kernel_size, + downsample_padding=downsample_padding, + ) + self.layer3 = self._make_layer( + block, + planes=256, + blocks=layers[2], + stride=2, + groups=groups, + reduction=reduction, + downsample_kernel_size=downsample_kernel_size, + downsample_padding=downsample_padding, + ) + self.layer4 = self._make_layer( + block, + planes=512, + blocks=layers[3], + stride=2, + groups=groups, + reduction=reduction, + downsample_kernel_size=downsample_kernel_size, + downsample_padding=downsample_padding, + ) + self.avg_pool = nn.AvgPool2d(7, stride=1) + self.dropout = nn.Dropout(dropout_p) if dropout_p is not None else None + self.last_linear = nn.Linear(512 * block.expansion, num_classes) + + def _make_layer( + self, + block, + planes, + blocks, + groups, + reduction, + stride=1, + downsample_kernel_size=1, + downsample_padding=0, + ): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2d( + self.inplanes, + planes * block.expansion, + kernel_size=downsample_kernel_size, + stride=stride, + padding=downsample_padding, + bias=False, + ), + nn.BatchNorm2d(planes * block.expansion), + ) + + layers = [] + layers.append( + block(self.inplanes, planes, groups, reduction, stride, downsample) + ) + self.inplanes = planes * block.expansion + for i in range(1, blocks): + layers.append(block(self.inplanes, planes, groups, reduction)) + + return nn.Sequential(*layers) + + def features(self, x): + x = self.layer0(x) + x = self.layer1(x) + x = self.layer2(x) + x = self.layer3(x) + x = self.layer4(x) + return x + + def logits(self, x): + x = self.avg_pool(x) + if self.dropout is not None: + x = self.dropout(x) + x = x.view(x.size(0), -1) + x = self.last_linear(x) + return x + + def forward(self, x): + x = self.features(x) + x = self.logits(x) + return x diff --git a/segmentation_models_pytorch/encoders/_xception.py b/segmentation_models_pytorch/encoders/_xception.py new file mode 100644 index 00000000..4b6f308b --- /dev/null +++ b/segmentation_models_pytorch/encoders/_xception.py @@ -0,0 +1,231 @@ +""" +Ported to pytorch thanks to [tstandley](https://github.com/tstandley/Xception-PyTorch) + +@author: tstandley +Adapted by cadene + +Creates an Xception Model as defined in: + +Francois Chollet +Xception: Deep Learning with Depthwise Separable Convolutions +https://arxiv.org/pdf/1610.02357.pdf + +This weights ported from the Keras implementation. Achieves the following performance on the validation set: + +Loss:0.9173 Prec@1:78.892 Prec@5:94.292 + +REMEMBER to set your image size to 3x299x299 for both test and validation + +normalize = transforms.Normalize(mean=[0.5, 0.5, 0.5], + std=[0.5, 0.5, 0.5]) + +The resize parameter of the validation transform should be 333, and make sure to center crop at 299x299 +""" + +import torch.nn as nn +import torch.nn.functional as F + + +class SeparableConv2d(nn.Module): + def __init__( + self, + in_channels, + out_channels, + kernel_size=1, + stride=1, + padding=0, + dilation=1, + bias=False, + ): + super(SeparableConv2d, self).__init__() + + self.conv1 = nn.Conv2d( + in_channels, + in_channels, + kernel_size, + stride, + padding, + dilation, + groups=in_channels, + bias=bias, + ) + self.pointwise = nn.Conv2d(in_channels, out_channels, 1, 1, 0, 1, 1, bias=bias) + + def forward(self, x): + x = self.conv1(x) + x = self.pointwise(x) + return x + + +class Block(nn.Module): + def __init__( + self, + in_filters, + out_filters, + reps, + strides=1, + start_with_relu=True, + grow_first=True, + ): + super(Block, self).__init__() + + if out_filters != in_filters or strides != 1: + self.skip = nn.Conv2d( + in_filters, out_filters, 1, stride=strides, bias=False + ) + self.skipbn = nn.BatchNorm2d(out_filters) + else: + self.skip = None + + rep = [] + + filters = in_filters + if grow_first: + rep.append(nn.ReLU(inplace=True)) + rep.append( + SeparableConv2d( + in_filters, out_filters, 3, stride=1, padding=1, bias=False + ) + ) + rep.append(nn.BatchNorm2d(out_filters)) + filters = out_filters + + for i in range(reps - 1): + rep.append(nn.ReLU(inplace=True)) + rep.append( + SeparableConv2d(filters, filters, 3, stride=1, padding=1, bias=False) + ) + rep.append(nn.BatchNorm2d(filters)) + + if not grow_first: + rep.append(nn.ReLU(inplace=True)) + rep.append( + SeparableConv2d( + in_filters, out_filters, 3, stride=1, padding=1, bias=False + ) + ) + rep.append(nn.BatchNorm2d(out_filters)) + + if not start_with_relu: + rep = rep[1:] + else: + rep[0] = nn.ReLU(inplace=False) + + if strides != 1: + rep.append(nn.MaxPool2d(3, strides, 1)) + self.rep = nn.Sequential(*rep) + + def forward(self, inp): + x = self.rep(inp) + + if self.skip is not None: + skip = self.skip(inp) + skip = self.skipbn(skip) + else: + skip = inp + + x += skip + return x + + +class Xception(nn.Module): + """ + Xception optimized for the ImageNet dataset, as specified in + https://arxiv.org/pdf/1610.02357.pdf + """ + + def __init__(self, num_classes=1000): + """Constructor + Args: + num_classes: number of classes + """ + super(Xception, self).__init__() + self.num_classes = num_classes + + self.conv1 = nn.Conv2d(3, 32, 3, 2, 0, bias=False) + self.bn1 = nn.BatchNorm2d(32) + self.relu1 = nn.ReLU(inplace=True) + + self.conv2 = nn.Conv2d(32, 64, 3, bias=False) + self.bn2 = nn.BatchNorm2d(64) + self.relu2 = nn.ReLU(inplace=True) + # do relu here + + self.block1 = Block(64, 128, 2, 2, start_with_relu=False, grow_first=True) + self.block2 = Block(128, 256, 2, 2, start_with_relu=True, grow_first=True) + self.block3 = Block(256, 728, 2, 2, start_with_relu=True, grow_first=True) + + self.block4 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) + self.block5 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) + self.block6 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) + self.block7 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) + + self.block8 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) + self.block9 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) + self.block10 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) + self.block11 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True) + + self.block12 = Block(728, 1024, 2, 2, start_with_relu=True, grow_first=False) + + self.conv3 = SeparableConv2d(1024, 1536, 3, 1, 1) + self.bn3 = nn.BatchNorm2d(1536) + self.relu3 = nn.ReLU(inplace=True) + + # do relu here + self.conv4 = SeparableConv2d(1536, 2048, 3, 1, 1) + self.bn4 = nn.BatchNorm2d(2048) + + self.fc = nn.Linear(2048, num_classes) + + # #------- init weights -------- + # for m in self.modules(): + # if isinstance(m, nn.Conv2d): + # n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels + # m.weight.data.normal_(0, math.sqrt(2. / n)) + # elif isinstance(m, nn.BatchNorm2d): + # m.weight.data.fill_(1) + # m.bias.data.zero_() + # #----------------------------- + + def features(self, input): + x = self.conv1(input) + x = self.bn1(x) + x = self.relu1(x) + + x = self.conv2(x) + x = self.bn2(x) + x = self.relu2(x) + + x = self.block1(x) + x = self.block2(x) + x = self.block3(x) + x = self.block4(x) + x = self.block5(x) + x = self.block6(x) + x = self.block7(x) + x = self.block8(x) + x = self.block9(x) + x = self.block10(x) + x = self.block11(x) + x = self.block12(x) + + x = self.conv3(x) + x = self.bn3(x) + x = self.relu3(x) + + x = self.conv4(x) + x = self.bn4(x) + return x + + def logits(self, features): + x = nn.ReLU(inplace=True)(features) + + x = F.adaptive_avg_pool2d(x, (1, 1)) + x = x.view(x.size(0), -1) + x = self.last_linear(x) + return x + + def forward(self, input): + x = self.features(input) + x = self.logits(x) + return x diff --git a/segmentation_models_pytorch/encoders/densenet.py b/segmentation_models_pytorch/encoders/densenet.py index c4bd0ce2..ad0e0c25 100644 --- a/segmentation_models_pytorch/encoders/densenet.py +++ b/segmentation_models_pytorch/encoders/densenet.py @@ -24,33 +24,25 @@ """ import re -import torch.nn as nn -from pretrainedmodels.models.torchvision_models import pretrained_settings from torchvision.models.densenet import DenseNet from ._base import EncoderMixin -class TransitionWithSkip(nn.Module): - def __init__(self, module): - super().__init__() - self.module = module - - def forward(self, x): - for module in self.module: - x = module(x) - if isinstance(module, nn.ReLU): - skip = x - return x, skip - - class DenseNetEncoder(DenseNet, EncoderMixin): - def __init__(self, out_channels, depth=5, **kwargs): + def __init__(self, out_channels, depth=5, output_stride=32, **kwargs): + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) + super().__init__(**kwargs) - self._out_channels = out_channels + self._depth = depth self._in_channels = 3 + self._out_channels = out_channels + self._output_stride = output_stride del self.classifier def make_dilated(self, *args, **kwargs): @@ -59,37 +51,44 @@ def make_dilated(self, *args, **kwargs): "due to pooling operation for downsampling!" ) - def get_stages(self): - return [ - nn.Identity(), - nn.Sequential( - self.features.conv0, self.features.norm0, self.features.relu0 - ), - nn.Sequential( - self.features.pool0, - self.features.denseblock1, - TransitionWithSkip(self.features.transition1), - ), - nn.Sequential( - self.features.denseblock2, TransitionWithSkip(self.features.transition2) - ), - nn.Sequential( - self.features.denseblock3, TransitionWithSkip(self.features.transition3) - ), - nn.Sequential(self.features.denseblock4, self.features.norm5), - ] - def forward(self, x): - stages = self.get_stages() - - features = [] - for i in range(self._depth + 1): - x = stages[i](x) - if isinstance(x, (list, tuple)): - x, skip = x - features.append(skip) - else: - features.append(x) + features = [x] + + if self._depth >= 1: + x = self.features.conv0(x) + x = self.features.norm0(x) + x = self.features.relu0(x) + features.append(x) + + if self._depth >= 2: + x = self.features.pool0(x) + x = self.features.denseblock1(x) + x = self.features.transition1.norm(x) + x = self.features.transition1.relu(x) + features.append(x) + + if self._depth >= 3: + x = self.features.transition1.conv(x) + x = self.features.transition1.pool(x) + x = self.features.denseblock2(x) + x = self.features.transition2.norm(x) + x = self.features.transition2.relu(x) + features.append(x) + + if self._depth >= 4: + x = self.features.transition2.conv(x) + x = self.features.transition2.pool(x) + x = self.features.denseblock3(x) + x = self.features.transition3.norm(x) + x = self.features.transition3.relu(x) + features.append(x) + + if self._depth >= 5: + x = self.features.transition3.conv(x) + x = self.features.transition3.pool(x) + x = self.features.denseblock4(x) + x = self.features.norm5(x) + features.append(x) return features @@ -114,42 +113,62 @@ def load_state_dict(self, state_dict): densenet_encoders = { "densenet121": { "encoder": DenseNetEncoder, - "pretrained_settings": pretrained_settings["densenet121"], "params": { - "out_channels": (3, 64, 256, 512, 1024, 1024), + "out_channels": [3, 64, 256, 512, 1024, 1024], "num_init_features": 64, "growth_rate": 32, "block_config": (6, 12, 24, 16), }, + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/densenet121.imagenet", + "revision": "a17c96896a265b61338f66f61d3887b24f61995a", + } + }, }, "densenet169": { "encoder": DenseNetEncoder, - "pretrained_settings": pretrained_settings["densenet169"], "params": { - "out_channels": (3, 64, 256, 512, 1280, 1664), + "out_channels": [3, 64, 256, 512, 1280, 1664], "num_init_features": 64, "growth_rate": 32, "block_config": (6, 12, 32, 32), }, + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/densenet169.imagenet", + "revision": "8facfba9fc72f7750879dac9ac6ceb3ab990de8d", + } + }, }, "densenet201": { "encoder": DenseNetEncoder, - "pretrained_settings": pretrained_settings["densenet201"], "params": { - "out_channels": (3, 64, 256, 512, 1792, 1920), + "out_channels": [3, 64, 256, 512, 1792, 1920], "num_init_features": 64, "growth_rate": 32, "block_config": (6, 12, 48, 32), }, + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/densenet201.imagenet", + "revision": "ed5deb355d71659391d46fae5e7587460fbb5f84", + } + }, }, "densenet161": { "encoder": DenseNetEncoder, - "pretrained_settings": pretrained_settings["densenet161"], "params": { - "out_channels": (3, 96, 384, 768, 2112, 2208), + "out_channels": [3, 96, 384, 768, 2112, 2208], "num_init_features": 96, "growth_rate": 48, "block_config": (6, 12, 36, 24), }, + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/densenet161.imagenet", + "revision": "9afe0fec51ab2a627141769d97d6f83756d78446", + } + }, }, } diff --git a/segmentation_models_pytorch/encoders/dpn.py b/segmentation_models_pytorch/encoders/dpn.py index 220c66de..527bbc02 100644 --- a/segmentation_models_pytorch/encoders/dpn.py +++ b/segmentation_models_pytorch/encoders/dpn.py @@ -24,49 +24,73 @@ """ import torch -import torch.nn as nn import torch.nn.functional as F - -from pretrainedmodels.models.dpn import DPN -from pretrainedmodels.models.dpn import pretrained_settings +from typing import List, Dict, Sequence from ._base import EncoderMixin +from ._dpn import DPN class DPNEncoder(DPN, EncoderMixin): - def __init__(self, stage_idxs, out_channels, depth=5, **kwargs): + _is_torch_scriptable = False + _is_torch_exportable = True # since torch 2.6.0 + + def __init__( + self, + stage_idxs: List[int], + out_channels: List[int], + depth: int = 5, + output_stride: int = 32, + **kwargs, + ): + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) + super().__init__(**kwargs) self._stage_idxs = stage_idxs self._depth = depth - self._out_channels = out_channels self._in_channels = 3 + self._out_channels = out_channels + self._output_stride = output_stride del self.last_linear - def get_stages(self): - return [ - nn.Identity(), - nn.Sequential( - self.features[0].conv, self.features[0].bn, self.features[0].act - ), - nn.Sequential( - self.features[0].pool, self.features[1 : self._stage_idxs[0]] - ), - self.features[self._stage_idxs[0] : self._stage_idxs[1]], - self.features[self._stage_idxs[1] : self._stage_idxs[2]], - self.features[self._stage_idxs[2] : self._stage_idxs[3]], - ] - - def forward(self, x): - stages = self.get_stages() - - features = [] - for i in range(self._depth + 1): - x = stages[i](x) - if isinstance(x, (list, tuple)): - features.append(F.relu(torch.cat(x, dim=1), inplace=True)) - else: - features.append(x) + def get_stages(self) -> Dict[int, Sequence[torch.nn.Module]]: + return { + 16: [self.features[self._stage_idxs[1] : self._stage_idxs[2]]], + 32: [self.features[self._stage_idxs[2] : self._stage_idxs[3]]], + } + + def forward(self, x: torch.Tensor) -> List[torch.Tensor]: + features = [x] + + if self._depth >= 1: + x = self.features[0].conv(x) + x = self.features[0].bn(x) + x = self.features[0].act(x) + features.append(x) + + if self._depth >= 2: + x = self.features[0].pool(x) + x = self.features[1 : self._stage_idxs[0]](x) + skip = F.relu(torch.cat(x, dim=1), inplace=True) + features.append(skip) + + if self._depth >= 3: + x = self.features[self._stage_idxs[0] : self._stage_idxs[1]](x) + skip = F.relu(torch.cat(x, dim=1), inplace=True) + features.append(skip) + + if self._depth >= 4: + x = self.features[self._stage_idxs[1] : self._stage_idxs[2]](x) + skip = F.relu(torch.cat(x, dim=1), inplace=True) + features.append(skip) + + if self._depth >= 5: + x = self.features[self._stage_idxs[2] : self._stage_idxs[3]](x) + features.append(x) return features @@ -79,10 +103,15 @@ def load_state_dict(self, state_dict, **kwargs): dpn_encoders = { "dpn68": { "encoder": DPNEncoder, - "pretrained_settings": pretrained_settings["dpn68"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/dpn68.imagenet", + "revision": "c209aefdeae6bc93937556629e974b44d4e58535", + } + }, "params": { - "stage_idxs": (4, 8, 20, 24), - "out_channels": (3, 10, 144, 320, 704, 832), + "stage_idxs": [4, 8, 20, 24], + "out_channels": [3, 10, 144, 320, 704, 832], "groups": 32, "inc_sec": (16, 32, 32, 64), "k_r": 128, @@ -95,10 +124,15 @@ def load_state_dict(self, state_dict, **kwargs): }, "dpn68b": { "encoder": DPNEncoder, - "pretrained_settings": pretrained_settings["dpn68b"], + "pretrained_settings": { + "imagenet+5k": { + "repo_id": "smp-hub/dpn68b.imagenet-5k", + "revision": "6c6615e77688e390ae0eaa81e26821fbd83cee4b", + } + }, "params": { - "stage_idxs": (4, 8, 20, 24), - "out_channels": (3, 10, 144, 320, 704, 832), + "stage_idxs": [4, 8, 20, 24], + "out_channels": [3, 10, 144, 320, 704, 832], "b": True, "groups": 32, "inc_sec": (16, 32, 32, 64), @@ -112,10 +146,15 @@ def load_state_dict(self, state_dict, **kwargs): }, "dpn92": { "encoder": DPNEncoder, - "pretrained_settings": pretrained_settings["dpn92"], + "pretrained_settings": { + "imagenet+5k": { + "repo_id": "smp-hub/dpn92.imagenet-5k", + "revision": "d231f51ce4ad2c84ed5fcaf4ef0cfece6814a526", + } + }, "params": { - "stage_idxs": (4, 8, 28, 32), - "out_channels": (3, 64, 336, 704, 1552, 2688), + "stage_idxs": [4, 8, 28, 32], + "out_channels": [3, 64, 336, 704, 1552, 2688], "groups": 32, "inc_sec": (16, 32, 24, 128), "k_r": 96, @@ -127,10 +166,15 @@ def load_state_dict(self, state_dict, **kwargs): }, "dpn98": { "encoder": DPNEncoder, - "pretrained_settings": pretrained_settings["dpn98"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/dpn98.imagenet", + "revision": "b2836c86216c1ddce980d832f7deaa4ca22babd3", + } + }, "params": { - "stage_idxs": (4, 10, 30, 34), - "out_channels": (3, 96, 336, 768, 1728, 2688), + "stage_idxs": [4, 10, 30, 34], + "out_channels": [3, 96, 336, 768, 1728, 2688], "groups": 40, "inc_sec": (16, 32, 32, 128), "k_r": 160, @@ -142,10 +186,15 @@ def load_state_dict(self, state_dict, **kwargs): }, "dpn107": { "encoder": DPNEncoder, - "pretrained_settings": pretrained_settings["dpn107"], + "pretrained_settings": { + "imagenet+5k": { + "repo_id": "smp-hub/dpn107.imagenet-5k", + "revision": "dab4cd6b8b79de3db970f2dbff85359a8847db05", + } + }, "params": { - "stage_idxs": (5, 13, 33, 37), - "out_channels": (3, 128, 376, 1152, 2432, 2688), + "stage_idxs": [5, 13, 33, 37], + "out_channels": [3, 128, 376, 1152, 2432, 2688], "groups": 50, "inc_sec": (20, 64, 64, 128), "k_r": 200, @@ -157,10 +206,15 @@ def load_state_dict(self, state_dict, **kwargs): }, "dpn131": { "encoder": DPNEncoder, - "pretrained_settings": pretrained_settings["dpn131"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/dpn131.imagenet", + "revision": "04bbb9f415ca2bb59f3d8227857967b74698515e", + } + }, "params": { - "stage_idxs": (5, 13, 41, 45), - "out_channels": (3, 128, 352, 832, 1984, 2688), + "stage_idxs": [5, 13, 41, 45], + "out_channels": [3, 128, 352, 832, 1984, 2688], "groups": 40, "inc_sec": (16, 32, 32, 128), "k_r": 160, diff --git a/segmentation_models_pytorch/encoders/efficientnet.py b/segmentation_models_pytorch/encoders/efficientnet.py index 4a7af6b4..70046e44 100644 --- a/segmentation_models_pytorch/encoders/efficientnet.py +++ b/segmentation_models_pytorch/encoders/efficientnet.py @@ -23,56 +23,68 @@ depth = 3 -> number of feature tensors = 4 (one with same resolution as input and 3 downsampled). """ -import torch.nn as nn -from efficientnet_pytorch import EfficientNet -from efficientnet_pytorch.utils import url_map, url_map_advprop, get_model_params +import torch +from typing import List, Dict, Sequence from ._base import EncoderMixin +from ._efficientnet import EfficientNet, get_model_params class EfficientNetEncoder(EfficientNet, EncoderMixin): - def __init__(self, stage_idxs, out_channels, model_name, depth=5): + def __init__( + self, + out_indexes: List[int], + out_channels: List[int], + model_name: str, + depth: int = 5, + output_stride: int = 32, + ): + if depth > 5 or depth < 2: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) + blocks_args, global_params = get_model_params(model_name, override_params=None) super().__init__(blocks_args, global_params) - self._stage_idxs = stage_idxs - self._out_channels = out_channels + self._out_indexes = out_indexes self._depth = depth self._in_channels = 3 + self._out_channels = out_channels + self._output_stride = output_stride + self._drop_connect_rate = self._global_params.drop_connect_rate del self._fc - def get_stages(self): - return [ - nn.Identity(), - nn.Sequential(self._conv_stem, self._bn0, self._swish), - self._blocks[: self._stage_idxs[0]], - self._blocks[self._stage_idxs[0] : self._stage_idxs[1]], - self._blocks[self._stage_idxs[1] : self._stage_idxs[2]], - self._blocks[self._stage_idxs[2] :], - ] - - def forward(self, x): - stages = self.get_stages() - - block_number = 0.0 - drop_connect_rate = self._global_params.drop_connect_rate - - features = [] - for i in range(self._depth + 1): - # Identity and Sequential stages - if i < 2: - x = stages[i](x) - - # Block stages need drop_connect rate - else: - for module in stages[i]: - drop_connect = drop_connect_rate * block_number / len(self._blocks) - block_number += 1.0 - x = module(x, drop_connect) + def get_stages(self) -> Dict[int, Sequence[torch.nn.Module]]: + return { + 16: [self._blocks[self._out_indexes[1] + 1 : self._out_indexes[2] + 1]], + 32: [self._blocks[self._out_indexes[2] + 1 :]], + } + def forward(self, x: torch.Tensor) -> List[torch.Tensor]: + features = [x] + + if self._depth >= 1: + x = self._conv_stem(x) + x = self._bn0(x) + x = self._swish(x) features.append(x) + depth = 1 + for i, block in enumerate(self._blocks): + drop_connect_prob = self._drop_connect_rate * i / len(self._blocks) + x = block(x, drop_connect_prob) + + if i in self._out_indexes: + features.append(x) + depth += 1 + + if not torch.jit.is_scripting() and depth > self._depth: + break + + features = features[: self._depth + 1] + return features def load_state_dict(self, state_dict, **kwargs): @@ -81,96 +93,148 @@ def load_state_dict(self, state_dict, **kwargs): super().load_state_dict(state_dict, **kwargs) -def _get_pretrained_settings(encoder): - pretrained_settings = { - "imagenet": { - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "url": url_map[encoder], - "input_space": "RGB", - "input_range": [0, 1], - }, - "advprop": { - "mean": [0.5, 0.5, 0.5], - "std": [0.5, 0.5, 0.5], - "url": url_map_advprop[encoder], - "input_space": "RGB", - "input_range": [0, 1], - }, - } - return pretrained_settings - - efficient_net_encoders = { "efficientnet-b0": { "encoder": EfficientNetEncoder, - "pretrained_settings": _get_pretrained_settings("efficientnet-b0"), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/efficientnet-b0.imagenet", + "revision": "1bbe7ecc1d5ea1d2058de1a2db063b8701aff314", + }, + "advprop": { + "repo_id": "smp-hub/efficientnet-b0.advprop", + "revision": "29043c08140d9c6ee7de1468d55923f2b06bcec2", + }, + }, "params": { - "out_channels": (3, 32, 24, 40, 112, 320), - "stage_idxs": (3, 5, 9, 16), + "out_channels": [3, 32, 24, 40, 112, 320], + "out_indexes": [2, 4, 8, 15], "model_name": "efficientnet-b0", }, }, "efficientnet-b1": { "encoder": EfficientNetEncoder, - "pretrained_settings": _get_pretrained_settings("efficientnet-b1"), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/efficientnet-b1.imagenet", + "revision": "5d637466a5215de300a8ccb13a39357df2df2bf4", + }, + "advprop": { + "repo_id": "smp-hub/efficientnet-b1.advprop", + "revision": "2e518b8b0955bbab467f50525578dab6b6086afc", + }, + }, "params": { - "out_channels": (3, 32, 24, 40, 112, 320), - "stage_idxs": (5, 8, 16, 23), + "out_channels": [3, 32, 24, 40, 112, 320], + "out_indexes": [4, 7, 15, 22], "model_name": "efficientnet-b1", }, }, "efficientnet-b2": { "encoder": EfficientNetEncoder, - "pretrained_settings": _get_pretrained_settings("efficientnet-b2"), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/efficientnet-b2.imagenet", + "revision": "a96d4f0295ffbae18ebba173bf7f3c0c8f21990e", + }, + "advprop": { + "repo_id": "smp-hub/efficientnet-b2.advprop", + "revision": "be788c20dfb0bbe83b4c439f9cfe0dd937c0783e", + }, + }, "params": { - "out_channels": (3, 32, 24, 48, 120, 352), - "stage_idxs": (5, 8, 16, 23), + "out_channels": [3, 32, 24, 48, 120, 352], + "out_indexes": [4, 7, 15, 22], "model_name": "efficientnet-b2", }, }, "efficientnet-b3": { "encoder": EfficientNetEncoder, - "pretrained_settings": _get_pretrained_settings("efficientnet-b3"), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/efficientnet-b3.imagenet", + "revision": "074c54a6c473e0d294690d49cedb6cf463e7127d", + }, + "advprop": { + "repo_id": "smp-hub/efficientnet-b3.advprop", + "revision": "9ccc166d87bd9c08d6bed4477638c7f4bb3eec78", + }, + }, "params": { - "out_channels": (3, 40, 32, 48, 136, 384), - "stage_idxs": (5, 8, 18, 26), + "out_channels": [3, 40, 32, 48, 136, 384], + "out_indexes": [4, 7, 17, 25], "model_name": "efficientnet-b3", }, }, "efficientnet-b4": { "encoder": EfficientNetEncoder, - "pretrained_settings": _get_pretrained_settings("efficientnet-b4"), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/efficientnet-b4.imagenet", + "revision": "05cd5dde5dab658f00c463f9b9aa0ced76784f40", + }, + "advprop": { + "repo_id": "smp-hub/efficientnet-b4.advprop", + "revision": "f04caa809ea4eb08ee9e7fd555f5514ebe2a9ef5", + }, + }, "params": { - "out_channels": (3, 48, 32, 56, 160, 448), - "stage_idxs": (6, 10, 22, 32), + "out_channels": [3, 48, 32, 56, 160, 448], + "out_indexes": [5, 9, 21, 31], "model_name": "efficientnet-b4", }, }, "efficientnet-b5": { "encoder": EfficientNetEncoder, - "pretrained_settings": _get_pretrained_settings("efficientnet-b5"), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/efficientnet-b5.imagenet", + "revision": "69f4d28460a4e421b7860bc26ee7d832e03e01ca", + }, + "advprop": { + "repo_id": "smp-hub/efficientnet-b5.advprop", + "revision": "dabe78fc8ab7ce93ddc2bb156b01db227caede88", + }, + }, "params": { - "out_channels": (3, 48, 40, 64, 176, 512), - "stage_idxs": (8, 13, 27, 39), + "out_channels": [3, 48, 40, 64, 176, 512], + "out_indexes": [7, 12, 26, 38], "model_name": "efficientnet-b5", }, }, "efficientnet-b6": { "encoder": EfficientNetEncoder, - "pretrained_settings": _get_pretrained_settings("efficientnet-b6"), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/efficientnet-b6.imagenet", + "revision": "8570752016f7c62ae149cffa058550fe44e21c8b", + }, + "advprop": { + "repo_id": "smp-hub/efficientnet-b6.advprop", + "revision": "c2dbb4d1359151165ec7b96cfe54a9cac2142a31", + }, + }, "params": { - "out_channels": (3, 56, 40, 72, 200, 576), - "stage_idxs": (9, 15, 31, 45), + "out_channels": [3, 56, 40, 72, 200, 576], + "out_indexes": [8, 14, 30, 44], "model_name": "efficientnet-b6", }, }, "efficientnet-b7": { "encoder": EfficientNetEncoder, - "pretrained_settings": _get_pretrained_settings("efficientnet-b7"), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/efficientnet-b7.imagenet", + "revision": "5a5dbe687d612ebc3dca248274fd1191111deda6", + }, + "advprop": { + "repo_id": "smp-hub/efficientnet-b7.advprop", + "revision": "ce33edb4e80c0cde268f098ae2299e23f615577d", + }, + }, "params": { - "out_channels": (3, 64, 48, 80, 224, 640), - "stage_idxs": (11, 18, 38, 55), + "out_channels": [3, 64, 48, 80, 224, 640], + "out_indexes": [10, 17, 37, 54], "model_name": "efficientnet-b7", }, }, diff --git a/segmentation_models_pytorch/encoders/inceptionresnetv2.py b/segmentation_models_pytorch/encoders/inceptionresnetv2.py index 5d90c7f4..d7f83f9d 100644 --- a/segmentation_models_pytorch/encoders/inceptionresnetv2.py +++ b/segmentation_models_pytorch/encoders/inceptionresnetv2.py @@ -23,20 +23,33 @@ depth = 3 -> number of feature tensors = 4 (one with same resolution as input and 3 downsampled). """ +import torch import torch.nn as nn -from pretrainedmodels.models.inceptionresnetv2 import InceptionResNetV2 -from pretrainedmodels.models.inceptionresnetv2 import pretrained_settings +from typing import List from ._base import EncoderMixin +from ._inceptionresnetv2 import InceptionResNetV2 class InceptionResNetV2Encoder(InceptionResNetV2, EncoderMixin): - def __init__(self, out_channels, depth=5, **kwargs): + def __init__( + self, + out_channels: List[int], + depth: int = 5, + output_stride: int = 32, + **kwargs, + ): + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) + super().__init__(**kwargs) - self._out_channels = out_channels self._depth = depth self._in_channels = 3 + self._out_channels = out_channels + self._output_stride = output_stride # correct paddings for m in self.modules(): @@ -46,6 +59,9 @@ def __init__(self, out_channels, depth=5, **kwargs): if isinstance(m, nn.MaxPool2d): m.padding = (1, 1) + # for torchscript, block8 does not have relu defined + self.block8.relu = nn.Identity() + # remove linear layers del self.avgpool_1a del self.last_linear @@ -56,22 +72,37 @@ def make_dilated(self, *args, **kwargs): "due to pooling operation for downsampling!" ) - def get_stages(self): - return [ - nn.Identity(), - nn.Sequential(self.conv2d_1a, self.conv2d_2a, self.conv2d_2b), - nn.Sequential(self.maxpool_3a, self.conv2d_3b, self.conv2d_4a), - nn.Sequential(self.maxpool_5a, self.mixed_5b, self.repeat), - nn.Sequential(self.mixed_6a, self.repeat_1), - nn.Sequential(self.mixed_7a, self.repeat_2, self.block8, self.conv2d_7b), - ] - - def forward(self, x): - stages = self.get_stages() - - features = [] - for i in range(self._depth + 1): - x = stages[i](x) + def forward(self, x: torch.Tensor) -> List[torch.Tensor]: + features = [x] + + if self._depth >= 1: + x = self.conv2d_1a(x) + x = self.conv2d_2a(x) + x = self.conv2d_2b(x) + features.append(x) + + if self._depth >= 2: + x = self.maxpool_3a(x) + x = self.conv2d_3b(x) + x = self.conv2d_4a(x) + features.append(x) + + if self._depth >= 3: + x = self.maxpool_5a(x) + x = self.mixed_5b(x) + x = self.repeat(x) + features.append(x) + + if self._depth >= 4: + x = self.mixed_6a(x) + x = self.repeat_1(x) + features.append(x) + + if self._depth >= 5: + x = self.mixed_7a(x) + x = self.repeat_2(x) + x = self.block8(x) + x = self.conv2d_7b(x) features.append(x) return features @@ -85,7 +116,16 @@ def load_state_dict(self, state_dict, **kwargs): inceptionresnetv2_encoders = { "inceptionresnetv2": { "encoder": InceptionResNetV2Encoder, - "pretrained_settings": pretrained_settings["inceptionresnetv2"], - "params": {"out_channels": (3, 64, 192, 320, 1088, 1536), "num_classes": 1000}, + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/inceptionresnetv2.imagenet", + "revision": "120c5afdbb80a1c989db0a7423ebb7a9db9b1e6c", + }, + "imagenet+background": { + "repo_id": "smp-hub/inceptionresnetv2.imagenet-background", + "revision": "3ecf3491658dc0f6a76d69c9d1cb36511b1ee56c", + }, + }, + "params": {"out_channels": [3, 64, 192, 320, 1088, 1536], "num_classes": 1000}, } } diff --git a/segmentation_models_pytorch/encoders/inceptionv4.py b/segmentation_models_pytorch/encoders/inceptionv4.py index 96540f9a..3c335042 100644 --- a/segmentation_models_pytorch/encoders/inceptionv4.py +++ b/segmentation_models_pytorch/encoders/inceptionv4.py @@ -23,20 +23,34 @@ depth = 3 -> number of feature tensors = 4 (one with same resolution as input and 3 downsampled). """ +import torch import torch.nn as nn -from pretrainedmodels.models.inceptionv4 import InceptionV4 -from pretrainedmodels.models.inceptionv4 import pretrained_settings + +from typing import List from ._base import EncoderMixin +from ._inceptionv4 import InceptionV4 class InceptionV4Encoder(InceptionV4, EncoderMixin): - def __init__(self, stage_idxs, out_channels, depth=5, **kwargs): + def __init__( + self, + out_channels: List[int], + depth: int = 5, + output_stride: int = 32, + **kwargs, + ): + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) super().__init__(**kwargs) - self._stage_idxs = stage_idxs - self._out_channels = out_channels + self._depth = depth self._in_channels = 3 + self._out_channels = out_channels + self._output_stride = output_stride + self._out_indexes = [2, 4, 8, 14, len(self.features) - 1] # correct paddings for m in self.modules(): @@ -55,24 +69,23 @@ def make_dilated(self, *args, **kwargs): "due to pooling operation for downsampling!" ) - def get_stages(self): - return [ - nn.Identity(), - self.features[: self._stage_idxs[0]], - self.features[self._stage_idxs[0] : self._stage_idxs[1]], - self.features[self._stage_idxs[1] : self._stage_idxs[2]], - self.features[self._stage_idxs[2] : self._stage_idxs[3]], - self.features[self._stage_idxs[3] :], - ] + def forward(self, x: torch.Tensor) -> List[torch.Tensor]: + depth = 0 + features = [x] + + for i, module in enumerate(self.features): + x = module(x) - def forward(self, x): - stages = self.get_stages() + if i in self._out_indexes: + features.append(x) + depth += 1 - features = [] - for i in range(self._depth + 1): - x = stages[i](x) - features.append(x) + # torchscript does not support break in cycle, so we just + # go over all modules and then slice number of features + if not torch.jit.is_scripting() and depth > self._depth: + break + features = features[: self._depth + 1] return features def load_state_dict(self, state_dict, **kwargs): @@ -84,10 +97,18 @@ def load_state_dict(self, state_dict, **kwargs): inceptionv4_encoders = { "inceptionv4": { "encoder": InceptionV4Encoder, - "pretrained_settings": pretrained_settings["inceptionv4"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/inceptionv4.imagenet", + "revision": "918fb54f07811d82a4ecde3a51156041d0facba9", + }, + "imagenet+background": { + "repo_id": "smp-hub/inceptionv4.imagenet-background", + "revision": "8c2a48e20d2709ee64f8421c61be309f05bfa536", + }, + }, "params": { - "stage_idxs": (3, 5, 9, 15), - "out_channels": (3, 64, 192, 384, 1024, 1536), + "out_channels": [3, 64, 192, 384, 1024, 1536], "num_classes": 1001, }, } diff --git a/segmentation_models_pytorch/encoders/mix_transformer.py b/segmentation_models_pytorch/encoders/mix_transformer.py index 0cc3fb21..d5dca7fd 100644 --- a/segmentation_models_pytorch/encoders/mix_transformer.py +++ b/segmentation_models_pytorch/encoders/mix_transformer.py @@ -11,20 +11,22 @@ import math import torch import torch.nn as nn +import torch.nn.functional as F from functools import partial +from typing import Dict, Sequence, List from timm.layers import DropPath, to_2tuple, trunc_normal_ class LayerNorm(nn.LayerNorm): - def forward(self, x): + def forward(self, x: torch.Tensor) -> torch.Tensor: if x.ndim == 4: - B, C, H, W = x.shape - x = x.view(B, C, -1).transpose(1, 2) - x = super().forward(x) - x = x.transpose(1, 2).view(B, C, H, W) + batch_size, channels, height, width = x.shape + x = x.view(batch_size, channels, -1).transpose(1, 2) + x = F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps) + x = x.transpose(1, 2).view(batch_size, channels, height, width) else: - x = super().forward(x) + x = F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps) return x @@ -60,9 +62,9 @@ def _init_weights(self, m): if m.bias is not None: m.bias.data.zero_() - def forward(self, x, H, W): + def forward(self, x: torch.Tensor, height: int, width: int) -> torch.Tensor: x = self.fc1(x) - x = self.dwconv(x, H, W) + x = self.dwconv(x, height, width) x = self.act(x) x = self.drop(x) x = self.fc2(x) @@ -82,9 +84,9 @@ def __init__( sr_ratio=1, ): super().__init__() - assert ( - dim % num_heads == 0 - ), f"dim {dim} should be divided by num_heads {num_heads}." + assert dim % num_heads == 0, ( + f"dim {dim} should be divided by num_heads {num_heads}." + ) self.dim = dim self.num_heads = num_heads @@ -101,6 +103,10 @@ def __init__( if sr_ratio > 1: self.sr = nn.Conv2d(dim, dim, kernel_size=sr_ratio, stride=sr_ratio) self.norm = LayerNorm(dim) + else: + # for torchscript compatibility + self.sr = nn.Identity() + self.norm = nn.Identity() self.apply(self._init_weights) @@ -119,27 +125,27 @@ def _init_weights(self, m): if m.bias is not None: m.bias.data.zero_() - def forward(self, x, H, W): - B, N, C = x.shape + def forward(self, x: torch.Tensor, height: int, width: int) -> torch.Tensor: + batch_size, N, C = x.shape q = ( self.q(x) - .reshape(B, N, self.num_heads, C // self.num_heads) + .reshape(batch_size, N, self.num_heads, C // self.num_heads) .permute(0, 2, 1, 3) ) if self.sr_ratio > 1: - x_ = x.permute(0, 2, 1).reshape(B, C, H, W) - x_ = self.sr(x_).reshape(B, C, -1).permute(0, 2, 1) + x_ = x.permute(0, 2, 1).reshape(batch_size, C, height, width) + x_ = self.sr(x_).reshape(batch_size, C, -1).permute(0, 2, 1) x_ = self.norm(x_) kv = ( self.kv(x_) - .reshape(B, -1, 2, self.num_heads, C // self.num_heads) + .reshape(batch_size, -1, 2, self.num_heads, C // self.num_heads) .permute(2, 0, 3, 1, 4) ) else: kv = ( self.kv(x) - .reshape(B, -1, 2, self.num_heads, C // self.num_heads) + .reshape(batch_size, -1, 2, self.num_heads, C // self.num_heads) .permute(2, 0, 3, 1, 4) ) k, v = kv[0], kv[1] @@ -148,7 +154,7 @@ def forward(self, x, H, W): attn = attn.softmax(dim=-1) attn = self.attn_drop(attn) - x = (attn @ v).transpose(1, 2).reshape(B, N, C) + x = (attn @ v).transpose(1, 2).reshape(batch_size, N, C) x = self.proj(x) x = self.proj_drop(x) @@ -209,12 +215,12 @@ def _init_weights(self, m): if m.bias is not None: m.bias.data.zero_() - def forward(self, x): - B, _, H, W = x.shape + def forward(self, x: torch.Tensor) -> torch.Tensor: + batch_size, _, height, width = x.shape x = x.flatten(2).transpose(1, 2) - x = x + self.drop_path(self.attn(self.norm1(x), H, W)) - x = x + self.drop_path(self.mlp(self.norm2(x), H, W)) - x = x.transpose(1, 2).view(B, -1, H, W) + x = x + self.drop_path(self.attn(self.norm1(x), height, width)) + x = x + self.drop_path(self.mlp(self.norm2(x), height, width)) + x = x.transpose(1, 2).view(batch_size, -1, height, width) return x @@ -256,7 +262,7 @@ def _init_weights(self, m): if m.bias is not None: m.bias.data.zero_() - def forward(self, x): + def forward(self, x: torch.Tensor) -> torch.Tensor: x = self.proj(x) x = self.norm(x) return x @@ -462,7 +468,7 @@ def reset_classifier(self, num_classes, global_pool=""): nn.Linear(self.embed_dim, num_classes) if num_classes > 0 else nn.Identity() ) - def forward_features(self, x): + def forward_features(self, x: torch.Tensor) -> List[torch.Tensor]: outs = [] # stage 1 @@ -491,11 +497,11 @@ def forward_features(self, x): return outs - def forward(self, x): - x = self.forward_features(x) + def forward(self, x: torch.Tensor) -> List[torch.Tensor]: + features = self.forward_features(x) # x = self.head(x) - return x + return features class DWConv(nn.Module): @@ -503,9 +509,9 @@ def __init__(self, dim=768): super(DWConv, self).__init__() self.dwconv = nn.Conv2d(dim, dim, 3, 1, 1, bias=True, groups=dim) - def forward(self, x, H, W): - B, _, C = x.shape - x = x.transpose(1, 2).view(B, C, H, W) + def forward(self, x: torch.Tensor, height: int, width: int) -> torch.Tensor: + batch_size, _, channels = x.shape + x = x.transpose(1, 2).view(batch_size, channels, height, width) x = self.dwconv(x) x = x.flatten(2).transpose(1, 2) @@ -520,36 +526,63 @@ def forward(self, x, H, W): class MixVisionTransformerEncoder(MixVisionTransformer, EncoderMixin): - def __init__(self, out_channels, depth=5, **kwargs): + def __init__( + self, out_channels: List[int], depth: int = 5, output_stride: int = 32, **kwargs + ): + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) super().__init__(**kwargs) - self._out_channels = out_channels + self._depth = depth self._in_channels = 3 + self._out_channels = out_channels + self._output_stride = output_stride - def get_stages(self): - return [ - nn.Identity(), - nn.Identity(), - nn.Sequential(self.patch_embed1, self.block1, self.norm1), - nn.Sequential(self.patch_embed2, self.block2, self.norm2), - nn.Sequential(self.patch_embed3, self.block3, self.norm3), - nn.Sequential(self.patch_embed4, self.block4, self.norm4), - ] - - def forward(self, x): - stages = self.get_stages() + def get_stages(self) -> Dict[int, Sequence[torch.nn.Module]]: + return { + 16: [self.patch_embed3, self.block3, self.norm3], + 32: [self.patch_embed4, self.block4, self.norm4], + } + def forward(self, x: torch.Tensor) -> List[torch.Tensor]: # create dummy output for the first block - B, _, H, W = x.shape - dummy = torch.empty([B, 0, H // 2, W // 2], dtype=x.dtype, device=x.device) - - features = [] - for i in range(self._depth + 1): - if i == 1: - features.append(dummy) - else: - x = stages[i](x).contiguous() - features.append(x) + batch_size, _, height, width = x.shape + dummy = torch.empty( + [batch_size, 0, height // 2, width // 2], dtype=x.dtype, device=x.device + ) + + features = [x, dummy] + + if self._depth >= 2: + x = self.patch_embed1(x) + x = self.block1(x) + x = self.norm1(x) + x = x.contiguous() + features.append(x) + + if self._depth >= 3: + x = self.patch_embed2(x) + x = self.block2(x) + x = self.norm2(x) + x = x.contiguous() + features.append(x) + + if self._depth >= 4: + x = self.patch_embed3(x) + x = self.block3(x) + x = self.norm3(x) + x = x.contiguous() + features.append(x) + + if self._depth >= 5: + x = self.patch_embed4(x) + x = self.block4(x) + x = self.norm4(x) + x = x.contiguous() + features.append(x) + return features def load_state_dict(self, state_dict): @@ -558,120 +591,137 @@ def load_state_dict(self, state_dict): return super().load_state_dict(state_dict) -def get_pretrained_cfg(name): - return { - "url": "https://github.com/qubvel/segmentation_models.pytorch/releases/download/v0.0.2/{}.pth".format( - name - ), - "input_space": "RGB", - "input_size": [3, 224, 224], - "input_range": [0, 1], - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - } - - mix_transformer_encoders = { "mit_b0": { "encoder": MixVisionTransformerEncoder, - "pretrained_settings": {"imagenet": get_pretrained_cfg("mit_b0")}, - "params": dict( - out_channels=(3, 0, 32, 64, 160, 256), - patch_size=4, - embed_dims=[32, 64, 160, 256], - num_heads=[1, 2, 5, 8], - mlp_ratios=[4, 4, 4, 4], - qkv_bias=True, - norm_layer=partial(LayerNorm, eps=1e-6), - depths=[2, 2, 2, 2], - sr_ratios=[8, 4, 2, 1], - drop_rate=0.0, - drop_path_rate=0.1, - ), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/mit_b0.imagenet", + "revision": "9ce53d104d92d75aabb00aae70677aaab67e7c84", + } + }, + "params": { + "out_channels": [3, 0, 32, 64, 160, 256], + "patch_size": 4, + "embed_dims": [32, 64, 160, 256], + "num_heads": [1, 2, 5, 8], + "mlp_ratios": [4, 4, 4, 4], + "qkv_bias": True, + "norm_layer": partial(LayerNorm, eps=1e-6), + "depths": [2, 2, 2, 2], + "sr_ratios": [8, 4, 2, 1], + "drop_rate": 0.0, + "drop_path_rate": 0.1, + }, }, "mit_b1": { "encoder": MixVisionTransformerEncoder, - "pretrained_settings": {"imagenet": get_pretrained_cfg("mit_b1")}, - "params": dict( - out_channels=(3, 0, 64, 128, 320, 512), - patch_size=4, - embed_dims=[64, 128, 320, 512], - num_heads=[1, 2, 5, 8], - mlp_ratios=[4, 4, 4, 4], - qkv_bias=True, - norm_layer=partial(LayerNorm, eps=1e-6), - depths=[2, 2, 2, 2], - sr_ratios=[8, 4, 2, 1], - drop_rate=0.0, - drop_path_rate=0.1, - ), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/mit_b1.imagenet", + "revision": "a04bf4f13a549bce677cf79b04852e7510782817", + } + }, + "params": { + "out_channels": [3, 0, 64, 128, 320, 512], + "patch_size": 4, + "embed_dims": [64, 128, 320, 512], + "num_heads": [1, 2, 5, 8], + "mlp_ratios": [4, 4, 4, 4], + "qkv_bias": True, + "norm_layer": partial(LayerNorm, eps=1e-6), + "depths": [2, 2, 2, 2], + "sr_ratios": [8, 4, 2, 1], + "drop_rate": 0.0, + "drop_path_rate": 0.1, + }, }, "mit_b2": { "encoder": MixVisionTransformerEncoder, - "pretrained_settings": {"imagenet": get_pretrained_cfg("mit_b2")}, - "params": dict( - out_channels=(3, 0, 64, 128, 320, 512), - patch_size=4, - embed_dims=[64, 128, 320, 512], - num_heads=[1, 2, 5, 8], - mlp_ratios=[4, 4, 4, 4], - qkv_bias=True, - norm_layer=partial(LayerNorm, eps=1e-6), - depths=[3, 4, 6, 3], - sr_ratios=[8, 4, 2, 1], - drop_rate=0.0, - drop_path_rate=0.1, - ), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/mit_b2.imagenet", + "revision": "868ab6f13871dcf8c3d9f90ee4519403475b65ef", + } + }, + "params": { + "out_channels": [3, 0, 64, 128, 320, 512], + "patch_size": 4, + "embed_dims": [64, 128, 320, 512], + "num_heads": [1, 2, 5, 8], + "mlp_ratios": [4, 4, 4, 4], + "qkv_bias": True, + "norm_layer": partial(LayerNorm, eps=1e-6), + "depths": [3, 4, 6, 3], + "sr_ratios": [8, 4, 2, 1], + "drop_rate": 0.0, + "drop_path_rate": 0.1, + }, }, "mit_b3": { "encoder": MixVisionTransformerEncoder, - "pretrained_settings": {"imagenet": get_pretrained_cfg("mit_b3")}, - "params": dict( - out_channels=(3, 0, 64, 128, 320, 512), - patch_size=4, - embed_dims=[64, 128, 320, 512], - num_heads=[1, 2, 5, 8], - mlp_ratios=[4, 4, 4, 4], - qkv_bias=True, - norm_layer=partial(LayerNorm, eps=1e-6), - depths=[3, 4, 18, 3], - sr_ratios=[8, 4, 2, 1], - drop_rate=0.0, - drop_path_rate=0.1, - ), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/mit_b3.imagenet", + "revision": "32558d12a65f1daa0ebcf4f4053c4285e2c1cbda", + } + }, + "params": { + "out_channels": [3, 0, 64, 128, 320, 512], + "patch_size": 4, + "embed_dims": [64, 128, 320, 512], + "num_heads": [1, 2, 5, 8], + "mlp_ratios": [4, 4, 4, 4], + "qkv_bias": True, + "norm_layer": partial(LayerNorm, eps=1e-6), + "depths": [3, 4, 18, 3], + "sr_ratios": [8, 4, 2, 1], + "drop_rate": 0.0, + "drop_path_rate": 0.1, + }, }, "mit_b4": { "encoder": MixVisionTransformerEncoder, - "pretrained_settings": {"imagenet": get_pretrained_cfg("mit_b4")}, - "params": dict( - out_channels=(3, 0, 64, 128, 320, 512), - patch_size=4, - embed_dims=[64, 128, 320, 512], - num_heads=[1, 2, 5, 8], - mlp_ratios=[4, 4, 4, 4], - qkv_bias=True, - norm_layer=partial(LayerNorm, eps=1e-6), - depths=[3, 8, 27, 3], - sr_ratios=[8, 4, 2, 1], - drop_rate=0.0, - drop_path_rate=0.1, - ), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/mit_b4.imagenet", + "revision": "3a3454e900a4b4f11dd60eeb59101a9a1a36b017", + } + }, + "params": { + "out_channels": [3, 0, 64, 128, 320, 512], + "patch_size": 4, + "embed_dims": [64, 128, 320, 512], + "num_heads": [1, 2, 5, 8], + "mlp_ratios": [4, 4, 4, 4], + "qkv_bias": True, + "norm_layer": partial(LayerNorm, eps=1e-6), + "depths": [3, 8, 27, 3], + "sr_ratios": [8, 4, 2, 1], + "drop_rate": 0.0, + "drop_path_rate": 0.1, + }, }, "mit_b5": { "encoder": MixVisionTransformerEncoder, - "pretrained_settings": {"imagenet": get_pretrained_cfg("mit_b5")}, - "params": dict( - out_channels=(3, 0, 64, 128, 320, 512), - patch_size=4, - embed_dims=[64, 128, 320, 512], - num_heads=[1, 2, 5, 8], - mlp_ratios=[4, 4, 4, 4], - qkv_bias=True, - norm_layer=partial(LayerNorm, eps=1e-6), - depths=[3, 6, 40, 3], - sr_ratios=[8, 4, 2, 1], - drop_rate=0.0, - drop_path_rate=0.1, - ), + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/mit_b5.imagenet", + "revision": "ced04d96c586b6297fd59a7a1e244fc78fdb6531", + } + }, + "params": { + "out_channels": [3, 0, 64, 128, 320, 512], + "patch_size": 4, + "embed_dims": [64, 128, 320, 512], + "num_heads": [1, 2, 5, 8], + "mlp_ratios": [4, 4, 4, 4], + "qkv_bias": True, + "norm_layer": partial(LayerNorm, eps=1e-6), + "depths": [3, 6, 40, 3], + "sr_ratios": [8, 4, 2, 1], + "drop_rate": 0.0, + "drop_path_rate": 0.1, + }, }, } diff --git a/segmentation_models_pytorch/encoders/mobilenet.py b/segmentation_models_pytorch/encoders/mobilenet.py index dd30f142..793a9be2 100644 --- a/segmentation_models_pytorch/encoders/mobilenet.py +++ b/segmentation_models_pytorch/encoders/mobilenet.py @@ -23,37 +23,54 @@ depth = 3 -> number of feature tensors = 4 (one with same resolution as input and 3 downsampled). """ +import torch import torchvision -import torch.nn as nn +from typing import Dict, Sequence, List from ._base import EncoderMixin class MobileNetV2Encoder(torchvision.models.MobileNetV2, EncoderMixin): - def __init__(self, out_channels, depth=5, **kwargs): + def __init__( + self, out_channels: List[int], depth: int = 5, output_stride: int = 32, **kwargs + ): + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) super().__init__(**kwargs) + self._depth = depth - self._out_channels = out_channels self._in_channels = 3 + self._out_channels = out_channels + self._output_stride = output_stride + self._out_indexes = [1, 3, 6, 13, len(self.features) - 1] + del self.classifier - def get_stages(self): - return [ - nn.Identity(), - self.features[:2], - self.features[2:4], - self.features[4:7], - self.features[7:14], - self.features[14:], - ] + def get_stages(self) -> Dict[int, Sequence[torch.nn.Module]]: + return { + 16: [self.features[7:14]], + 32: [self.features[14:]], + } + + def forward(self, x: torch.Tensor) -> List[torch.Tensor]: + features = [x] + + depth = 0 + for i, module in enumerate(self.features): + x = module(x) + + if i in self._out_indexes: + features.append(x) + depth += 1 - def forward(self, x): - stages = self.get_stages() + # torchscript does not support break in cycle, so we just + # go over all modules and then slice number of features + if not torch.jit.is_scripting() and depth > self._depth: + break - features = [] - for i in range(self._depth + 1): - x = stages[i](x) - features.append(x) + features = features[: self._depth + 1] return features @@ -68,13 +85,10 @@ def load_state_dict(self, state_dict, **kwargs): "encoder": MobileNetV2Encoder, "pretrained_settings": { "imagenet": { - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "url": "https://download.pytorch.org/models/mobilenet_v2-b0353104.pth", - "input_space": "RGB", - "input_range": [0, 1], + "repo_id": "smp-hub/mobilenet_v2.imagenet", + "revision": "e67aa804e17f7b404b629127eabbd224c4e0690b", } }, - "params": {"out_channels": (3, 16, 24, 32, 96, 1280)}, + "params": {"out_channels": [3, 16, 24, 32, 96, 1280]}, } } diff --git a/segmentation_models_pytorch/encoders/mobileone.py b/segmentation_models_pytorch/encoders/mobileone.py index 76f50053..ba2947d0 100644 --- a/segmentation_models_pytorch/encoders/mobileone.py +++ b/segmentation_models_pytorch/encoders/mobileone.py @@ -3,7 +3,7 @@ # Copyright (C) 2022 Apple Inc. All Rights Reserved. # import copy -from typing import List, Optional, Tuple +from typing import List, Optional, Tuple, Dict, Sequence import torch import torch.nn as nn @@ -120,6 +120,8 @@ def __init__( bias=True, ) else: + self.reparam_conv = nn.Identity() + # Re-parameterizable skip connection self.rbr_skip = ( nn.BatchNorm2d(num_features=in_channels) @@ -157,8 +159,8 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: # Other branches out = scale_out + identity_out - for ix in range(self.num_conv_branches): - out += self.rbr_conv[ix](x) + for module in self.rbr_conv: + out += module(x) return self.activation(self.se(out)) @@ -298,13 +300,14 @@ class MobileOne(nn.Module, EncoderMixin): def __init__( self, - out_channels, + out_channels: List[int], num_blocks_per_stage: List[int] = [2, 8, 10, 1], width_multipliers: Optional[List[float]] = None, inference_mode: bool = False, use_se: bool = False, depth=5, in_channels=3, + output_stride=32, num_conv_branches: int = 1, ) -> None: """Construct MobileOne model. @@ -316,17 +319,23 @@ def __init__( :param use_se: Whether to use SE-ReLU activations. :param num_conv_branches: Number of linear conv branches. """ + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) + super().__init__() assert len(width_multipliers) == 4 self.inference_mode = inference_mode - self._out_channels = out_channels self.in_planes = min(64, int(64 * width_multipliers[0])) self.use_se = use_se self.num_conv_branches = num_conv_branches + self._depth = depth self._in_channels = in_channels - self.set_in_channels(self._in_channels) + self._out_channels = out_channels + self._output_stride = output_stride # Build stages self.stage0 = MobileOneBlock( @@ -355,15 +364,11 @@ def __init__( num_se_blocks=num_blocks_per_stage[3] if use_se else 0, ) - def get_stages(self): - return [ - nn.Identity(), - self.stage0, - self.stage1, - self.stage2, - self.stage3, - self.stage4, - ] + def get_stages(self) -> Dict[int, Sequence[torch.nn.Module]]: + return { + 16: [self.stage3], + 32: [self.stage4], + } def _make_stage( self, planes: int, num_blocks: int, num_se_blocks: int @@ -381,9 +386,7 @@ def _make_stage( for ix, stride in enumerate(strides): use_se = False if num_se_blocks > num_blocks: - raise ValueError( - "Number of SE blocks cannot " "exceed number of layers." - ) + raise ValueError("Number of SE blocks cannot exceed number of layers.") if ix >= (num_blocks - num_se_blocks): use_se = True @@ -419,13 +422,30 @@ def _make_stage( self.cur_layer_idx += 1 return nn.Sequential(*blocks) - def forward(self, x: torch.Tensor) -> torch.Tensor: + def forward(self, x: torch.Tensor) -> List[torch.Tensor]: """Apply forward pass.""" - stages = self.get_stages() - features = [] - for i in range(self._depth + 1): - x = stages[i](x) + features = [x] + + if self._depth >= 1: + x = self.stage0(x) features.append(x) + + if self._depth >= 2: + x = self.stage1(x) + features.append(x) + + if self._depth >= 3: + x = self.stage2(x) + features.append(x) + + if self._depth >= 4: + x = self.stage3(x) + features.append(x) + + if self._depth >= 5: + x = self.stage4(x) + features.append(x) + return features def load_state_dict(self, state_dict, **kwargs): @@ -473,15 +493,12 @@ def reparameterize_model(model: torch.nn.Module) -> nn.Module: "encoder": MobileOne, "pretrained_settings": { "imagenet": { - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "url": "https://docs-assets.developer.apple.com/ml-research/datasets/mobileone/mobileone_s0_unfused.pth.tar", # noqa - "input_space": "RGB", - "input_range": [0, 1], + "repo_id": "smp-hub/mobileone_s0.imagenet", + "revision": "f52815cf0ad29278a9860c9cd5fabf19f904bedf", } }, "params": { - "out_channels": (3, 48, 48, 128, 256, 1024), + "out_channels": [3, 48, 48, 128, 256, 1024], "width_multipliers": (0.75, 1.0, 1.0, 2.0), "num_conv_branches": 4, "inference_mode": False, @@ -491,15 +508,12 @@ def reparameterize_model(model: torch.nn.Module) -> nn.Module: "encoder": MobileOne, "pretrained_settings": { "imagenet": { - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "url": "https://docs-assets.developer.apple.com/ml-research/datasets/mobileone/mobileone_s1_unfused.pth.tar", # noqa - "input_space": "RGB", - "input_range": [0, 1], + "repo_id": "smp-hub/mobileone_s1.imagenet", + "revision": "5707a98852b762cd8e0c43b5c8c729cd28496677", } }, "params": { - "out_channels": (3, 64, 96, 192, 512, 1280), + "out_channels": [3, 64, 96, 192, 512, 1280], "width_multipliers": (1.5, 1.5, 2.0, 2.5), "inference_mode": False, }, @@ -508,15 +522,12 @@ def reparameterize_model(model: torch.nn.Module) -> nn.Module: "encoder": MobileOne, "pretrained_settings": { "imagenet": { - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "url": "https://docs-assets.developer.apple.com/ml-research/datasets/mobileone/mobileone_s2_unfused.pth.tar", # noqa - "input_space": "RGB", - "input_range": [0, 1], + "repo_id": "smp-hub/mobileone_s2.imagenet", + "revision": "ddc3db8fa40d271902c7a8c95cee6691f617d551", } }, "params": { - "out_channels": (3, 64, 96, 256, 640, 2048), + "out_channels": [3, 64, 96, 256, 640, 2048], "width_multipliers": (1.5, 2.0, 2.5, 4.0), "inference_mode": False, }, @@ -525,15 +536,12 @@ def reparameterize_model(model: torch.nn.Module) -> nn.Module: "encoder": MobileOne, "pretrained_settings": { "imagenet": { - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "url": "https://docs-assets.developer.apple.com/ml-research/datasets/mobileone/mobileone_s3_unfused.pth.tar", # noqa - "input_space": "RGB", - "input_range": [0, 1], + "repo_id": "smp-hub/mobileone_s3.imagenet", + "revision": "da89b84a91b7400c366c358bfbf8dd0b2fa4dde2", } }, "params": { - "out_channels": (3, 64, 128, 320, 768, 2048), + "out_channels": [3, 64, 128, 320, 768, 2048], "width_multipliers": (2.0, 2.5, 3.0, 4.0), "inference_mode": False, }, @@ -542,15 +550,12 @@ def reparameterize_model(model: torch.nn.Module) -> nn.Module: "encoder": MobileOne, "pretrained_settings": { "imagenet": { - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "url": "https://docs-assets.developer.apple.com/ml-research/datasets/mobileone/mobileone_s4_unfused.pth.tar", # noqa - "input_space": "RGB", - "input_range": [0, 1], + "repo_id": "smp-hub/mobileone_s4.imagenet", + "revision": "16197c55d599076b6aae67a83d3b3f70c31b097c", } }, "params": { - "out_channels": (3, 64, 192, 448, 896, 2048), + "out_channels": [3, 64, 192, 448, 896, 2048], "width_multipliers": (3.0, 3.5, 3.5, 4.0), "use_se": True, "inference_mode": False, diff --git a/segmentation_models_pytorch/encoders/resnet.py b/segmentation_models_pytorch/encoders/resnet.py index 2040a42c..d4f2db4e 100644 --- a/segmentation_models_pytorch/encoders/resnet.py +++ b/segmentation_models_pytorch/encoders/resnet.py @@ -23,44 +23,65 @@ depth = 3 -> number of feature tensors = 4 (one with same resolution as input and 3 downsampled). """ -from copy import deepcopy - -import torch.nn as nn - +import torch +from typing import Dict, Sequence, List from torchvision.models.resnet import ResNet from torchvision.models.resnet import BasicBlock from torchvision.models.resnet import Bottleneck -from pretrainedmodels.models.torchvision_models import pretrained_settings from ._base import EncoderMixin class ResNetEncoder(ResNet, EncoderMixin): - def __init__(self, out_channels, depth=5, **kwargs): + """ResNet encoder implementation.""" + + def __init__( + self, out_channels: List[int], depth: int = 5, output_stride: int = 32, **kwargs + ): + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) super().__init__(**kwargs) + self._depth = depth - self._out_channels = out_channels self._in_channels = 3 + self._out_channels = out_channels + self._output_stride = output_stride del self.fc del self.avgpool - def get_stages(self): - return [ - nn.Identity(), - nn.Sequential(self.conv1, self.bn1, self.relu), - nn.Sequential(self.maxpool, self.layer1), - self.layer2, - self.layer3, - self.layer4, - ] - - def forward(self, x): - stages = self.get_stages() - - features = [] - for i in range(self._depth + 1): - x = stages[i](x) + def get_stages(self) -> Dict[int, Sequence[torch.nn.Module]]: + return { + 16: [self.layer3], + 32: [self.layer4], + } + + def forward(self, x: torch.Tensor) -> list[torch.Tensor]: + features = [x] + + if self._depth >= 1: + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + features.append(x) + + if self._depth >= 2: + x = self.maxpool(x) + x = self.layer1(x) + features.append(x) + + if self._depth >= 3: + x = self.layer2(x) + features.append(x) + + if self._depth >= 4: + x = self.layer3(x) + features.append(x) + + if self._depth >= 5: + x = self.layer4(x) features.append(x) return features @@ -71,110 +92,111 @@ def load_state_dict(self, state_dict, **kwargs): super().load_state_dict(state_dict, **kwargs) -new_settings = { - "resnet18": { - "ssl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnet18-d92f0530.pth", # noqa - "swsl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnet18-118f1556.pth", # noqa - }, - "resnet50": { - "ssl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnet50-08389792.pth", # noqa - "swsl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnet50-16a12f1b.pth", # noqa - }, - "resnext50_32x4d": { - "imagenet": "https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth", - "ssl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnext50_32x4-ddb3e555.pth", # noqa - "swsl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnext50_32x4-72679e44.pth", # noqa - }, - "resnext101_32x4d": { - "ssl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnext101_32x4-dc43570a.pth", # noqa - "swsl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnext101_32x4-3f87e46b.pth", # noqa - }, - "resnext101_32x8d": { - "imagenet": "https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth", - "instagram": "https://download.pytorch.org/models/ig_resnext101_32x8-c38310e5.pth", - "ssl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnext101_32x8-2cfe2f8b.pth", # noqa - "swsl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnext101_32x8-b4712904.pth", # noqa - }, - "resnext101_32x16d": { - "instagram": "https://download.pytorch.org/models/ig_resnext101_32x16-c6f796b0.pth", - "ssl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnext101_32x16-15fffa57.pth", # noqa - "swsl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnext101_32x16-f3559a9c.pth", # noqa - }, - "resnext101_32x32d": { - "instagram": "https://download.pytorch.org/models/ig_resnext101_32x32-e4b90b00.pth" - }, - "resnext101_32x48d": { - "instagram": "https://download.pytorch.org/models/ig_resnext101_32x48-3e41cc8a.pth" - }, -} - -pretrained_settings = deepcopy(pretrained_settings) -for model_name, sources in new_settings.items(): - if model_name not in pretrained_settings: - pretrained_settings[model_name] = {} - - for source_name, source_url in sources.items(): - pretrained_settings[model_name][source_name] = { - "url": source_url, - "input_size": [3, 224, 224], - "input_range": [0, 1], - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "num_classes": 1000, - } - - resnet_encoders = { "resnet18": { "encoder": ResNetEncoder, - "pretrained_settings": pretrained_settings["resnet18"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/resnet18.imagenet", + "revision": "3f2325ff978283d47aa6a1d6878ca20565622683", + }, + "ssl": { + "repo_id": "smp-hub/resnet18.ssl", + "revision": "d600d5116aac2e6e595f99f40612074c723c00b2", + }, + "swsl": { + "repo_id": "smp-hub/resnet18.swsl", + "revision": "0e3a35d4d8e344088c14a96eee502a88ac70eae1", + }, + }, "params": { - "out_channels": (3, 64, 64, 128, 256, 512), + "out_channels": [3, 64, 64, 128, 256, 512], "block": BasicBlock, "layers": [2, 2, 2, 2], }, }, "resnet34": { "encoder": ResNetEncoder, - "pretrained_settings": pretrained_settings["resnet34"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/resnet34.imagenet", + "revision": "7a57b34f723329ff020b3f8bc41771163c519d0c", + }, + }, "params": { - "out_channels": (3, 64, 64, 128, 256, 512), + "out_channels": [3, 64, 64, 128, 256, 512], "block": BasicBlock, "layers": [3, 4, 6, 3], }, }, "resnet50": { "encoder": ResNetEncoder, - "pretrained_settings": pretrained_settings["resnet50"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/resnet50.imagenet", + "revision": "00cb74e366966d59cd9a35af57e618af9f88efe9", + }, + "ssl": { + "repo_id": "smp-hub/resnet50.ssl", + "revision": "d07daf5b4377f3700c6ac61906b0aafbc4eca46b", + }, + "swsl": { + "repo_id": "smp-hub/resnet50.swsl", + "revision": "b9520cce124f91c6fe7eee45721a2c7954f0d8c0", + }, + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": Bottleneck, "layers": [3, 4, 6, 3], }, }, "resnet101": { "encoder": ResNetEncoder, - "pretrained_settings": pretrained_settings["resnet101"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/resnet101.imagenet", + "revision": "cd7c15e8c51da86ae6a084515fdb962d0c94e7d1", + }, + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": Bottleneck, "layers": [3, 4, 23, 3], }, }, "resnet152": { "encoder": ResNetEncoder, - "pretrained_settings": pretrained_settings["resnet152"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/resnet152.imagenet", + "revision": "951dd835e9d086628e447b484584c8983f9e1dd0", + }, + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": Bottleneck, "layers": [3, 8, 36, 3], }, }, "resnext50_32x4d": { "encoder": ResNetEncoder, - "pretrained_settings": pretrained_settings["resnext50_32x4d"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/resnext50_32x4d.imagenet", + "revision": "329793c85d62fd340ae42ae39fb905a63df872e7", + }, + "ssl": { + "repo_id": "smp-hub/resnext50_32x4d.ssl", + "revision": "9b67cff77d060c7044493a58c24d1007c1eb06c3", + }, + "swsl": { + "repo_id": "smp-hub/resnext50_32x4d.swsl", + "revision": "52e6e49da61b8e26ca691e1aef2cbb952884057d", + }, + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": Bottleneck, "layers": [3, 4, 6, 3], "groups": 32, @@ -183,9 +205,18 @@ def load_state_dict(self, state_dict, **kwargs): }, "resnext101_32x4d": { "encoder": ResNetEncoder, - "pretrained_settings": pretrained_settings["resnext101_32x4d"], + "pretrained_settings": { + "ssl": { + "repo_id": "smp-hub/resnext101_32x4d.ssl", + "revision": "b39796c8459084d13523b7016c3ef13a2e9e472b", + }, + "swsl": { + "repo_id": "smp-hub/resnext101_32x4d.swsl", + "revision": "3f8355b4892a31f001a832b49b2b01484d48516a", + }, + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": Bottleneck, "layers": [3, 4, 23, 3], "groups": 32, @@ -194,9 +225,26 @@ def load_state_dict(self, state_dict, **kwargs): }, "resnext101_32x8d": { "encoder": ResNetEncoder, - "pretrained_settings": pretrained_settings["resnext101_32x8d"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/resnext101_32x8d.imagenet", + "revision": "221af6198d03a4ee88992f78a1ee81b46a52d339", + }, + "instagram": { + "repo_id": "smp-hub/resnext101_32x8d.instagram", + "revision": "44cd927aa6e64673ffe9d31230bad44abc18b823", + }, + "ssl": { + "repo_id": "smp-hub/resnext101_32x8d.ssl", + "revision": "723a95ddeed335c9488c37c6cbef13d779ac8f97", + }, + "swsl": { + "repo_id": "smp-hub/resnext101_32x8d.swsl", + "revision": "58cf0bb65f91365470398080d9588b187d1777c4", + }, + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": Bottleneck, "layers": [3, 4, 23, 3], "groups": 32, @@ -205,9 +253,22 @@ def load_state_dict(self, state_dict, **kwargs): }, "resnext101_32x16d": { "encoder": ResNetEncoder, - "pretrained_settings": pretrained_settings["resnext101_32x16d"], + "pretrained_settings": { + "instagram": { + "repo_id": "smp-hub/resnext101_32x16d.instagram", + "revision": "64e8e320eeae6501185b0627b2429a68e52d050c", + }, + "ssl": { + "repo_id": "smp-hub/resnext101_32x16d.ssl", + "revision": "1283fe03fbb6aa2599b2df24095255acb93c3d5c", + }, + "swsl": { + "repo_id": "smp-hub/resnext101_32x16d.swsl", + "revision": "30ba61bbd4d6af0d955c513dbb4f557b84eb094f", + }, + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": Bottleneck, "layers": [3, 4, 23, 3], "groups": 32, @@ -216,9 +277,14 @@ def load_state_dict(self, state_dict, **kwargs): }, "resnext101_32x32d": { "encoder": ResNetEncoder, - "pretrained_settings": pretrained_settings["resnext101_32x32d"], + "pretrained_settings": { + "instagram": { + "repo_id": "smp-hub/resnext101_32x32d.instagram", + "revision": "c9405de121fdaa275a89de470fb19409e3eeaa86", + }, + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": Bottleneck, "layers": [3, 4, 23, 3], "groups": 32, @@ -227,9 +293,14 @@ def load_state_dict(self, state_dict, **kwargs): }, "resnext101_32x48d": { "encoder": ResNetEncoder, - "pretrained_settings": pretrained_settings["resnext101_32x48d"], + "pretrained_settings": { + "instagram": { + "repo_id": "smp-hub/resnext101_32x48d.instagram", + "revision": "53e61a962b824ad7027409821f9ac3e3336dd024", + }, + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": Bottleneck, "layers": [3, 4, 23, 3], "groups": 32, diff --git a/segmentation_models_pytorch/encoders/senet.py b/segmentation_models_pytorch/encoders/senet.py index 8e0f6fd8..da509f5a 100644 --- a/segmentation_models_pytorch/encoders/senet.py +++ b/segmentation_models_pytorch/encoders/senet.py @@ -23,45 +23,72 @@ depth = 3 -> number of feature tensors = 4 (one with same resolution as input and 3 downsampled). """ -import torch.nn as nn +import torch +from typing import List, Dict, Sequence -from pretrainedmodels.models.senet import ( +from ._base import EncoderMixin +from ._senet import ( SENet, SEBottleneck, SEResNetBottleneck, SEResNeXtBottleneck, - pretrained_settings, ) -from ._base import EncoderMixin class SENetEncoder(SENet, EncoderMixin): - def __init__(self, out_channels, depth=5, **kwargs): + def __init__( + self, + out_channels: List[int], + depth: int = 5, + output_stride: int = 32, + **kwargs, + ): + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) super().__init__(**kwargs) - self._out_channels = out_channels self._depth = depth self._in_channels = 3 + self._out_channels = out_channels + self._output_stride = output_stride + + # for compatibility with torchscript + self.layer0_pool = self.layer0.pool + self.layer0.pool = torch.nn.Identity() del self.last_linear del self.avg_pool - def get_stages(self): - return [ - nn.Identity(), - self.layer0[:-1], - nn.Sequential(self.layer0[-1], self.layer1), - self.layer2, - self.layer3, - self.layer4, - ] - - def forward(self, x): - stages = self.get_stages() - - features = [] - for i in range(self._depth + 1): - x = stages[i](x) + def get_stages(self) -> Dict[int, Sequence[torch.nn.Module]]: + return { + 16: [self.layer3], + 32: [self.layer4], + } + + def forward(self, x: torch.Tensor) -> List[torch.Tensor]: + features = [x] + + if self._depth >= 1: + x = self.layer0(x) + features.append(x) + + if self._depth >= 2: + x = self.layer0_pool(x) + x = self.layer1(x) + features.append(x) + + if self._depth >= 3: + x = self.layer2(x) + features.append(x) + + if self._depth >= 4: + x = self.layer3(x) + features.append(x) + + if self._depth >= 5: + x = self.layer4(x) features.append(x) return features @@ -75,9 +102,14 @@ def load_state_dict(self, state_dict, **kwargs): senet_encoders = { "senet154": { "encoder": SENetEncoder, - "pretrained_settings": pretrained_settings["senet154"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/senet154.imagenet", + "revision": "249f45efc9881ba560a0c480128edbc34ab87e40", + } + }, "params": { - "out_channels": (3, 128, 256, 512, 1024, 2048), + "out_channels": [3, 128, 256, 512, 1024, 2048], "block": SEBottleneck, "dropout_p": 0.2, "groups": 64, @@ -88,9 +120,14 @@ def load_state_dict(self, state_dict, **kwargs): }, "se_resnet50": { "encoder": SENetEncoder, - "pretrained_settings": pretrained_settings["se_resnet50"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/se_resnet50.imagenet", + "revision": "e6b4bc2dc85226c3d3474544410724a485455459", + } + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": SEResNetBottleneck, "layers": [3, 4, 6, 3], "downsample_kernel_size": 1, @@ -105,9 +142,14 @@ def load_state_dict(self, state_dict, **kwargs): }, "se_resnet101": { "encoder": SENetEncoder, - "pretrained_settings": pretrained_settings["se_resnet101"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/se_resnet101.imagenet", + "revision": "71fe95cc0a27f444cf83671f354de02dc741b18b", + } + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": SEResNetBottleneck, "layers": [3, 4, 23, 3], "downsample_kernel_size": 1, @@ -122,9 +164,14 @@ def load_state_dict(self, state_dict, **kwargs): }, "se_resnet152": { "encoder": SENetEncoder, - "pretrained_settings": pretrained_settings["se_resnet152"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/se_resnet152.imagenet", + "revision": "e79fc3d9d76f197bd76a2593c2054edf1083fe32", + } + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": SEResNetBottleneck, "layers": [3, 8, 36, 3], "downsample_kernel_size": 1, @@ -139,9 +186,14 @@ def load_state_dict(self, state_dict, **kwargs): }, "se_resnext50_32x4d": { "encoder": SENetEncoder, - "pretrained_settings": pretrained_settings["se_resnext50_32x4d"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/se_resnext50_32x4d.imagenet", + "revision": "73246406d879a2b0e3fdfe6fddd56347d38f38ae", + } + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": SEResNeXtBottleneck, "layers": [3, 4, 6, 3], "downsample_kernel_size": 1, @@ -156,9 +208,14 @@ def load_state_dict(self, state_dict, **kwargs): }, "se_resnext101_32x4d": { "encoder": SENetEncoder, - "pretrained_settings": pretrained_settings["se_resnext101_32x4d"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/se_resnext101_32x4d.imagenet", + "revision": "18808a4276f46421d358a9de554e0b93c2795df4", + } + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": SEResNeXtBottleneck, "layers": [3, 4, 23, 3], "downsample_kernel_size": 1, diff --git a/segmentation_models_pytorch/encoders/timm_efficientnet.py b/segmentation_models_pytorch/encoders/timm_efficientnet.py index fc248575..a1c36491 100644 --- a/segmentation_models_pytorch/encoders/timm_efficientnet.py +++ b/segmentation_models_pytorch/encoders/timm_efficientnet.py @@ -1,9 +1,11 @@ -from functools import partial - +import torch import torch.nn as nn +from typing import List, Dict, Sequence +from functools import partial + from timm.models.efficientnet import EfficientNet -from timm.models.efficientnet import decode_arch_def, round_channels, default_cfgs +from timm.models.efficientnet import decode_arch_def, round_channels from timm.layers.activations import Swish from ._base import EncoderMixin @@ -95,32 +97,59 @@ def gen_efficientnet_lite_kwargs( class EfficientNetBaseEncoder(EfficientNet, EncoderMixin): - def __init__(self, stage_idxs, out_channels, depth=5, **kwargs): + def __init__( + self, + stage_idxs: List[int], + out_channels: List[int], + depth: int = 5, + output_stride: int = 32, + **kwargs, + ): + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) super().__init__(**kwargs) self._stage_idxs = stage_idxs - self._out_channels = out_channels self._depth = depth self._in_channels = 3 + self._out_channels = out_channels + self._output_stride = output_stride del self.classifier - def get_stages(self): - return [ - nn.Identity(), - nn.Sequential(self.conv_stem, self.bn1), - self.blocks[: self._stage_idxs[0]], - self.blocks[self._stage_idxs[0] : self._stage_idxs[1]], - self.blocks[self._stage_idxs[1] : self._stage_idxs[2]], - self.blocks[self._stage_idxs[2] :], - ] + def get_stages(self) -> Dict[int, Sequence[torch.nn.Module]]: + return { + 16: [self.blocks[self._stage_idxs[1] : self._stage_idxs[2]]], + 32: [self.blocks[self._stage_idxs[2] :]], + } - def forward(self, x): - stages = self.get_stages() + def forward(self, x: torch.Tensor) -> List[torch.Tensor]: + features = [x] - features = [] - for i in range(self._depth + 1): - x = stages[i](x) + if self._depth >= 1: + x = self.conv_stem(x) + x = self.bn1(x) + features.append(x) + + if self._depth >= 2: + x = self.blocks[0](x) + x = self.blocks[1](x) + features.append(x) + + if self._depth >= 3: + x = self.blocks[2](x) + features.append(x) + + if self._depth >= 4: + x = self.blocks[3](x) + x = self.blocks[4](x) + features.append(x) + + if self._depth >= 5: + x = self.blocks[5](x) + x = self.blocks[6](x) features.append(x) return features @@ -134,33 +163,47 @@ def load_state_dict(self, state_dict, **kwargs): class EfficientNetEncoder(EfficientNetBaseEncoder): def __init__( self, - stage_idxs, - out_channels, - depth=5, - channel_multiplier=1.0, - depth_multiplier=1.0, - drop_rate=0.2, + stage_idxs: List[int], + out_channels: List[int], + depth: int = 5, + channel_multiplier: float = 1.0, + depth_multiplier: float = 1.0, + drop_rate: float = 0.2, + output_stride: int = 32, ): kwargs = get_efficientnet_kwargs( channel_multiplier, depth_multiplier, drop_rate ) - super().__init__(stage_idxs, out_channels, depth, **kwargs) + super().__init__( + stage_idxs=stage_idxs, + depth=depth, + out_channels=out_channels, + output_stride=output_stride, + **kwargs, + ) class EfficientNetLiteEncoder(EfficientNetBaseEncoder): def __init__( self, - stage_idxs, - out_channels, - depth=5, - channel_multiplier=1.0, - depth_multiplier=1.0, - drop_rate=0.2, + stage_idxs: List[int], + out_channels: List[int], + depth: int = 5, + channel_multiplier: float = 1.0, + depth_multiplier: float = 1.0, + drop_rate: float = 0.2, + output_stride: int = 32, ): kwargs = gen_efficientnet_lite_kwargs( channel_multiplier, depth_multiplier, drop_rate ) - super().__init__(stage_idxs, out_channels, depth, **kwargs) + super().__init__( + stage_idxs=stage_idxs, + depth=depth, + out_channels=out_channels, + output_stride=output_stride, + **kwargs, + ) def prepare_settings(settings): @@ -177,19 +220,22 @@ def prepare_settings(settings): "timm-efficientnet-b0": { "encoder": EfficientNetEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_b0"].cfgs["in1k"] - ), - "advprop": prepare_settings( - default_cfgs["tf_efficientnet_b0"].cfgs["ap_in1k"] - ), - "noisy-student": prepare_settings( - default_cfgs["tf_efficientnet_b0"].cfgs["ns_jft_in1k"] - ), + "imagenet": { + "repo_id": "smp-hub/timm-efficientnet-b0.imagenet", + "revision": "8419e9cc19da0b68dcd7bb12f19b7c92407ad7c4", + }, + "advprop": { + "repo_id": "smp-hub/timm-efficientnet-b0.advprop", + "revision": "a5870af2d24ce79e0cc7fae2bbd8e0a21fcfa6d8", + }, + "noisy-student": { + "repo_id": "smp-hub/timm-efficientnet-b0.noisy-student", + "revision": "bea8b0ff726a50e48774d2d360c5fb1ac4815836", + }, }, "params": { - "out_channels": (3, 32, 24, 40, 112, 320), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 32, 24, 40, 112, 320], + "stage_idxs": [2, 3, 5], "channel_multiplier": 1.0, "depth_multiplier": 1.0, "drop_rate": 0.2, @@ -198,19 +244,22 @@ def prepare_settings(settings): "timm-efficientnet-b1": { "encoder": EfficientNetEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_b1"].cfgs["in1k"] - ), - "advprop": prepare_settings( - default_cfgs["tf_efficientnet_b1"].cfgs["ap_in1k"] - ), - "noisy-student": prepare_settings( - default_cfgs["tf_efficientnet_b1"].cfgs["ns_jft_in1k"] - ), + "imagenet": { + "repo_id": "smp-hub/timm-efficientnet-b1.imagenet", + "revision": "63bdd65ef6596ef24f1cadc7dd4f46b624442349", + }, + "advprop": { + "repo_id": "smp-hub/timm-efficientnet-b1.advprop", + "revision": "79b3d102080ef679b16c2748e608a871112233d0", + }, + "noisy-student": { + "repo_id": "smp-hub/timm-efficientnet-b1.noisy-student", + "revision": "36856124a699f6032574ceeefab02040daa90a9a", + }, }, "params": { - "out_channels": (3, 32, 24, 40, 112, 320), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 32, 24, 40, 112, 320], + "stage_idxs": [2, 3, 5], "channel_multiplier": 1.0, "depth_multiplier": 1.1, "drop_rate": 0.2, @@ -219,19 +268,22 @@ def prepare_settings(settings): "timm-efficientnet-b2": { "encoder": EfficientNetEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_b2"].cfgs["in1k"] - ), - "advprop": prepare_settings( - default_cfgs["tf_efficientnet_b2"].cfgs["ap_in1k"] - ), - "noisy-student": prepare_settings( - default_cfgs["tf_efficientnet_b2"].cfgs["ns_jft_in1k"] - ), + "imagenet": { + "repo_id": "smp-hub/timm-efficientnet-b2.imagenet", + "revision": "e693adb39d3cb3847e71e3700a0c2aa58072cff1", + }, + "advprop": { + "repo_id": "smp-hub/timm-efficientnet-b2.advprop", + "revision": "b58479bf78007cfbb365091d64eeee369bddfa21", + }, + "noisy-student": { + "repo_id": "smp-hub/timm-efficientnet-b2.noisy-student", + "revision": "67c558827c6d3e0975ff9b4bce8557bc2ca80931", + }, }, "params": { - "out_channels": (3, 32, 24, 48, 120, 352), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 32, 24, 48, 120, 352], + "stage_idxs": [2, 3, 5], "channel_multiplier": 1.1, "depth_multiplier": 1.2, "drop_rate": 0.3, @@ -240,19 +292,22 @@ def prepare_settings(settings): "timm-efficientnet-b3": { "encoder": EfficientNetEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_b3"].cfgs["in1k"] - ), - "advprop": prepare_settings( - default_cfgs["tf_efficientnet_b3"].cfgs["ap_in1k"] - ), - "noisy-student": prepare_settings( - default_cfgs["tf_efficientnet_b3"].cfgs["ns_jft_in1k"] - ), + "imagenet": { + "repo_id": "smp-hub/timm-efficientnet-b3.imagenet", + "revision": "1666b835b5151d6bb2067c7cd67e67ada6c39edf", + }, + "advprop": { + "repo_id": "smp-hub/timm-efficientnet-b3.advprop", + "revision": "70474cdb9f1ff4fcbd7434e66560ead1ab8e506b", + }, + "noisy-student": { + "repo_id": "smp-hub/timm-efficientnet-b3.noisy-student", + "revision": "2367bc9f61e79ee97684169a71a87db280bcf4db", + }, }, "params": { - "out_channels": (3, 40, 32, 48, 136, 384), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 40, 32, 48, 136, 384], + "stage_idxs": [2, 3, 5], "channel_multiplier": 1.2, "depth_multiplier": 1.4, "drop_rate": 0.3, @@ -261,19 +316,22 @@ def prepare_settings(settings): "timm-efficientnet-b4": { "encoder": EfficientNetEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_b4"].cfgs["in1k"] - ), - "advprop": prepare_settings( - default_cfgs["tf_efficientnet_b4"].cfgs["ap_in1k"] - ), - "noisy-student": prepare_settings( - default_cfgs["tf_efficientnet_b4"].cfgs["ns_jft_in1k"] - ), + "imagenet": { + "repo_id": "smp-hub/timm-efficientnet-b4.imagenet", + "revision": "07868c28ab308f4de4cf1e7ec54b33b8b002ccdb", + }, + "advprop": { + "repo_id": "smp-hub/timm-efficientnet-b4.advprop", + "revision": "8ea1772ee9a2a0d18c1b56dce0dfac8dd33d537d", + }, + "noisy-student": { + "repo_id": "smp-hub/timm-efficientnet-b4.noisy-student", + "revision": "faeb77b6e8292a700380c840d39442d7ce4d6443", + }, }, "params": { - "out_channels": (3, 48, 32, 56, 160, 448), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 48, 32, 56, 160, 448], + "stage_idxs": [2, 3, 5], "channel_multiplier": 1.4, "depth_multiplier": 1.8, "drop_rate": 0.4, @@ -282,19 +340,22 @@ def prepare_settings(settings): "timm-efficientnet-b5": { "encoder": EfficientNetEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_b5"].cfgs["in1k"] - ), - "advprop": prepare_settings( - default_cfgs["tf_efficientnet_b5"].cfgs["ap_in1k"] - ), - "noisy-student": prepare_settings( - default_cfgs["tf_efficientnet_b5"].cfgs["ns_jft_in1k"] - ), + "imagenet": { + "repo_id": "smp-hub/timm-efficientnet-b5.imagenet", + "revision": "004153b4ddd93d30afd9bbf34329d7f57396d413", + }, + "advprop": { + "repo_id": "smp-hub/timm-efficientnet-b5.advprop", + "revision": "1d1c5f05aab5ed9a1d5052847ddd4024c06a464d", + }, + "noisy-student": { + "repo_id": "smp-hub/timm-efficientnet-b5.noisy-student", + "revision": "9bc3a1e5490de92b1af061d5c2c474ab3129e38c", + }, }, "params": { - "out_channels": (3, 48, 40, 64, 176, 512), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 48, 40, 64, 176, 512], + "stage_idxs": [2, 3, 5], "channel_multiplier": 1.6, "depth_multiplier": 2.2, "drop_rate": 0.4, @@ -303,19 +364,22 @@ def prepare_settings(settings): "timm-efficientnet-b6": { "encoder": EfficientNetEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_b6"].cfgs["aa_in1k"] - ), - "advprop": prepare_settings( - default_cfgs["tf_efficientnet_b6"].cfgs["ap_in1k"] - ), - "noisy-student": prepare_settings( - default_cfgs["tf_efficientnet_b6"].cfgs["ns_jft_in1k"] - ), + "imagenet": { + "repo_id": "smp-hub/timm-efficientnet-b6.imagenet", + "revision": "dbbf28a5c33f021486db4070de693caad6b56c3d", + }, + "advprop": { + "repo_id": "smp-hub/timm-efficientnet-b6.advprop", + "revision": "3b5d3412047f7711c56ffde997911cfefe79f835", + }, + "noisy-student": { + "repo_id": "smp-hub/timm-efficientnet-b6.noisy-student", + "revision": "9b899ea9e8e0ce2ccada0f34a8cb8b5028e9bb36", + }, }, "params": { - "out_channels": (3, 56, 40, 72, 200, 576), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 56, 40, 72, 200, 576], + "stage_idxs": [2, 3, 5], "channel_multiplier": 1.8, "depth_multiplier": 2.6, "drop_rate": 0.5, @@ -324,19 +388,22 @@ def prepare_settings(settings): "timm-efficientnet-b7": { "encoder": EfficientNetEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_b7"].cfgs["aa_in1k"] - ), - "advprop": prepare_settings( - default_cfgs["tf_efficientnet_b7"].cfgs["ap_in1k"] - ), - "noisy-student": prepare_settings( - default_cfgs["tf_efficientnet_b7"].cfgs["ns_jft_in1k"] - ), + "imagenet": { + "repo_id": "smp-hub/timm-efficientnet-b7.imagenet", + "revision": "8ef7ffccf54dad9baceb21d05b7ef86b6b70f4cc", + }, + "advprop": { + "repo_id": "smp-hub/timm-efficientnet-b7.advprop", + "revision": "fcbc576ffb939c12d5cd8dad523fdae6eb0177ca", + }, + "noisy-student": { + "repo_id": "smp-hub/timm-efficientnet-b7.noisy-student", + "revision": "6b1dd73e61bf934d485d7bd4381dc3e2ab374664", + }, }, "params": { - "out_channels": (3, 64, 48, 80, 224, 640), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 64, 48, 80, 224, 640], + "stage_idxs": [2, 3, 5], "channel_multiplier": 2.0, "depth_multiplier": 3.1, "drop_rate": 0.5, @@ -345,16 +412,18 @@ def prepare_settings(settings): "timm-efficientnet-b8": { "encoder": EfficientNetEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_b8"].cfgs["ra_in1k"] - ), - "advprop": prepare_settings( - default_cfgs["tf_efficientnet_b8"].cfgs["ap_in1k"] - ), + "imagenet": { + "repo_id": "smp-hub/timm-efficientnet-b8.imagenet", + "revision": "b5e9dde35605a3a6d17ea2a727382625f9066a37", + }, + "advprop": { + "repo_id": "smp-hub/timm-efficientnet-b8.advprop", + "revision": "e43f381de72e7467383c2c80bacbb7fcb9572866", + }, }, "params": { - "out_channels": (3, 72, 56, 88, 248, 704), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 72, 56, 88, 248, 704], + "stage_idxs": [2, 3, 5], "channel_multiplier": 2.2, "depth_multiplier": 3.6, "drop_rate": 0.5, @@ -363,16 +432,18 @@ def prepare_settings(settings): "timm-efficientnet-l2": { "encoder": EfficientNetEncoder, "pretrained_settings": { - "noisy-student": prepare_settings( - default_cfgs["tf_efficientnet_l2"].cfgs["ns_jft_in1k"] - ), - "noisy-student-475": prepare_settings( - default_cfgs["tf_efficientnet_l2"].cfgs["ns_jft_in1k_475"] - ), + "noisy-student": { + "repo_id": "smp-hub/timm-efficientnet-l2.noisy-student", + "revision": "cdc711e76d1becdd9197169f1a8bb1b2094e980c", + }, + "noisy-student-475": { + "repo_id": "smp-hub/timm-efficientnet-l2.noisy-student-475", + "revision": "35f5ba667a64bf4f3f0689daf84fc6d0f8e1311b", + }, }, "params": { - "out_channels": (3, 136, 104, 176, 480, 1376), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 136, 104, 176, 480, 1376], + "stage_idxs": [2, 3, 5], "channel_multiplier": 4.3, "depth_multiplier": 5.3, "drop_rate": 0.5, @@ -381,13 +452,14 @@ def prepare_settings(settings): "timm-tf_efficientnet_lite0": { "encoder": EfficientNetLiteEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_lite0"].cfgs["in1k"] - ) + "imagenet": { + "repo_id": "smp-hub/timm-tf_efficientnet_lite0.imagenet", + "revision": "f5729249af07e5d923fb8b16922256ce2865d108", + }, }, "params": { - "out_channels": (3, 32, 24, 40, 112, 320), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 32, 24, 40, 112, 320], + "stage_idxs": [2, 3, 5], "channel_multiplier": 1.0, "depth_multiplier": 1.0, "drop_rate": 0.2, @@ -396,13 +468,14 @@ def prepare_settings(settings): "timm-tf_efficientnet_lite1": { "encoder": EfficientNetLiteEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_lite1"].cfgs["in1k"] - ) + "imagenet": { + "repo_id": "smp-hub/timm-tf_efficientnet_lite1.imagenet", + "revision": "7b5e3f8dbb0c13b74101773584bba7523721be72", + }, }, "params": { - "out_channels": (3, 32, 24, 40, 112, 320), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 32, 24, 40, 112, 320], + "stage_idxs": [2, 3, 5], "channel_multiplier": 1.0, "depth_multiplier": 1.1, "drop_rate": 0.2, @@ -411,13 +484,14 @@ def prepare_settings(settings): "timm-tf_efficientnet_lite2": { "encoder": EfficientNetLiteEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_lite2"].cfgs["in1k"] - ) + "imagenet": { + "repo_id": "smp-hub/timm-tf_efficientnet_lite2.imagenet", + "revision": "cc5f6cd4c7409ebacc13292f09d369ae88547f6a", + }, }, "params": { - "out_channels": (3, 32, 24, 48, 120, 352), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 32, 24, 48, 120, 352], + "stage_idxs": [2, 3, 5], "channel_multiplier": 1.1, "depth_multiplier": 1.2, "drop_rate": 0.3, @@ -426,13 +500,14 @@ def prepare_settings(settings): "timm-tf_efficientnet_lite3": { "encoder": EfficientNetLiteEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_lite3"].cfgs["in1k"] - ) + "imagenet": { + "repo_id": "smp-hub/timm-tf_efficientnet_lite3.imagenet", + "revision": "ab29c8402991591d66f813bbb1f061565d9b0cd0", + }, }, "params": { - "out_channels": (3, 32, 32, 48, 136, 384), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 32, 32, 48, 136, 384], + "stage_idxs": [2, 3, 5], "channel_multiplier": 1.2, "depth_multiplier": 1.4, "drop_rate": 0.3, @@ -441,13 +516,14 @@ def prepare_settings(settings): "timm-tf_efficientnet_lite4": { "encoder": EfficientNetLiteEncoder, "pretrained_settings": { - "imagenet": prepare_settings( - default_cfgs["tf_efficientnet_lite4"].cfgs["in1k"] - ) + "imagenet": { + "repo_id": "smp-hub/timm-tf_efficientnet_lite4.imagenet", + "revision": "91a822e0f03c255b34dfb7846d3858397e50ba39", + }, }, "params": { - "out_channels": (3, 32, 32, 56, 160, 448), - "stage_idxs": (2, 3, 5), + "out_channels": [3, 32, 32, 56, 160, 448], + "stage_idxs": [2, 3, 5], "channel_multiplier": 1.4, "depth_multiplier": 1.8, "drop_rate": 0.4, diff --git a/segmentation_models_pytorch/encoders/timm_gernet.py b/segmentation_models_pytorch/encoders/timm_gernet.py deleted file mode 100644 index e0c3354d..00000000 --- a/segmentation_models_pytorch/encoders/timm_gernet.py +++ /dev/null @@ -1,124 +0,0 @@ -from timm.models import ByoModelCfg, ByoBlockCfg, ByobNet - -from ._base import EncoderMixin -import torch.nn as nn - - -class GERNetEncoder(ByobNet, EncoderMixin): - def __init__(self, out_channels, depth=5, **kwargs): - super().__init__(**kwargs) - self._depth = depth - self._out_channels = out_channels - self._in_channels = 3 - - del self.head - - def get_stages(self): - return [ - nn.Identity(), - self.stem, - self.stages[0], - self.stages[1], - self.stages[2], - nn.Sequential(self.stages[3], self.stages[4], self.final_conv), - ] - - def forward(self, x): - stages = self.get_stages() - - features = [] - for i in range(self._depth + 1): - x = stages[i](x) - features.append(x) - - return features - - def load_state_dict(self, state_dict, **kwargs): - state_dict.pop("head.fc.weight", None) - state_dict.pop("head.fc.bias", None) - super().load_state_dict(state_dict, **kwargs) - - -regnet_weights = { - "timm-gernet_s": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-ger-weights/gernet_s-756b4751.pth" # noqa - }, - "timm-gernet_m": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-ger-weights/gernet_m-0873c53a.pth" # noqa - }, - "timm-gernet_l": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-ger-weights/gernet_l-f31e2e8d.pth" # noqa - }, -} - -pretrained_settings = {} -for model_name, sources in regnet_weights.items(): - pretrained_settings[model_name] = {} - for source_name, source_url in sources.items(): - pretrained_settings[model_name][source_name] = { - "url": source_url, - "input_range": [0, 1], - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "num_classes": 1000, - } - -timm_gernet_encoders = { - "timm-gernet_s": { - "encoder": GERNetEncoder, - "pretrained_settings": pretrained_settings["timm-gernet_s"], - "params": { - "out_channels": (3, 13, 48, 48, 384, 1920), - "cfg": ByoModelCfg( - blocks=( - ByoBlockCfg(type="basic", d=1, c=48, s=2, gs=0, br=1.0), - ByoBlockCfg(type="basic", d=3, c=48, s=2, gs=0, br=1.0), - ByoBlockCfg(type="bottle", d=7, c=384, s=2, gs=0, br=1 / 4), - ByoBlockCfg(type="bottle", d=2, c=560, s=2, gs=1, br=3.0), - ByoBlockCfg(type="bottle", d=1, c=256, s=1, gs=1, br=3.0), - ), - stem_chs=13, - stem_pool=None, - num_features=1920, - ), - }, - }, - "timm-gernet_m": { - "encoder": GERNetEncoder, - "pretrained_settings": pretrained_settings["timm-gernet_m"], - "params": { - "out_channels": (3, 32, 128, 192, 640, 2560), - "cfg": ByoModelCfg( - blocks=( - ByoBlockCfg(type="basic", d=1, c=128, s=2, gs=0, br=1.0), - ByoBlockCfg(type="basic", d=2, c=192, s=2, gs=0, br=1.0), - ByoBlockCfg(type="bottle", d=6, c=640, s=2, gs=0, br=1 / 4), - ByoBlockCfg(type="bottle", d=4, c=640, s=2, gs=1, br=3.0), - ByoBlockCfg(type="bottle", d=1, c=640, s=1, gs=1, br=3.0), - ), - stem_chs=32, - stem_pool=None, - num_features=2560, - ), - }, - }, - "timm-gernet_l": { - "encoder": GERNetEncoder, - "pretrained_settings": pretrained_settings["timm-gernet_l"], - "params": { - "out_channels": (3, 32, 128, 192, 640, 2560), - "cfg": ByoModelCfg( - blocks=( - ByoBlockCfg(type="basic", d=1, c=128, s=2, gs=0, br=1.0), - ByoBlockCfg(type="basic", d=2, c=192, s=2, gs=0, br=1.0), - ByoBlockCfg(type="bottle", d=6, c=640, s=2, gs=0, br=1 / 4), - ByoBlockCfg(type="bottle", d=5, c=640, s=2, gs=1, br=3.0), - ByoBlockCfg(type="bottle", d=4, c=640, s=1, gs=1, br=3.0), - ), - stem_chs=32, - stem_pool=None, - num_features=2560, - ), - }, - }, -} diff --git a/segmentation_models_pytorch/encoders/timm_mobilenetv3.py b/segmentation_models_pytorch/encoders/timm_mobilenetv3.py deleted file mode 100644 index ff733ab9..00000000 --- a/segmentation_models_pytorch/encoders/timm_mobilenetv3.py +++ /dev/null @@ -1,151 +0,0 @@ -import timm -import numpy as np -import torch.nn as nn - -from ._base import EncoderMixin - - -def _make_divisible(x, divisible_by=8): - return int(np.ceil(x * 1.0 / divisible_by) * divisible_by) - - -class MobileNetV3Encoder(nn.Module, EncoderMixin): - def __init__(self, model_name, width_mult, depth=5, **kwargs): - super().__init__() - if "large" not in model_name and "small" not in model_name: - raise ValueError("MobileNetV3 wrong model name {}".format(model_name)) - - self._mode = "small" if "small" in model_name else "large" - self._depth = depth - self._out_channels = self._get_channels(self._mode, width_mult) - self._in_channels = 3 - - # minimal models replace hardswish with relu - self.model = timm.create_model( - model_name=model_name, - scriptable=True, # torch.jit scriptable - exportable=True, # onnx export - features_only=True, - ) - - def _get_channels(self, mode, width_mult): - if mode == "small": - channels = [16, 16, 24, 48, 576] - else: - channels = [16, 24, 40, 112, 960] - channels = [3] + [_make_divisible(x * width_mult) for x in channels] - return tuple(channels) - - def get_stages(self): - if self._mode == "small": - return [ - nn.Identity(), - nn.Sequential(self.model.conv_stem, self.model.bn1, self.model.act1), - self.model.blocks[0], - self.model.blocks[1], - self.model.blocks[2:4], - self.model.blocks[4:], - ] - elif self._mode == "large": - return [ - nn.Identity(), - nn.Sequential( - self.model.conv_stem, - self.model.bn1, - self.model.act1, - self.model.blocks[0], - ), - self.model.blocks[1], - self.model.blocks[2], - self.model.blocks[3:5], - self.model.blocks[5:], - ] - else: - ValueError( - "MobileNetV3 mode should be small or large, got {}".format(self._mode) - ) - - def forward(self, x): - stages = self.get_stages() - - features = [] - for i in range(self._depth + 1): - x = stages[i](x) - features.append(x) - - return features - - def load_state_dict(self, state_dict, **kwargs): - state_dict.pop("conv_head.weight", None) - state_dict.pop("conv_head.bias", None) - state_dict.pop("classifier.weight", None) - state_dict.pop("classifier.bias", None) - self.model.load_state_dict(state_dict, **kwargs) - - -mobilenetv3_weights = { - "tf_mobilenetv3_large_075": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_mobilenetv3_large_075-150ee8b0.pth" # noqa - }, - "tf_mobilenetv3_large_100": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_mobilenetv3_large_100-427764d5.pth" # noqa - }, - "tf_mobilenetv3_large_minimal_100": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_mobilenetv3_large_minimal_100-8596ae28.pth" # noqa - }, - "tf_mobilenetv3_small_075": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_mobilenetv3_small_075-da427f52.pth" # noqa - }, - "tf_mobilenetv3_small_100": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_mobilenetv3_small_100-37f49e2b.pth" # noqa - }, - "tf_mobilenetv3_small_minimal_100": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/tf_mobilenetv3_small_minimal_100-922a7843.pth" # noqa - }, -} - -pretrained_settings = {} -for model_name, sources in mobilenetv3_weights.items(): - pretrained_settings[model_name] = {} - for source_name, source_url in sources.items(): - pretrained_settings[model_name][source_name] = { - "url": source_url, - "input_range": [0, 1], - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "input_space": "RGB", - } - - -timm_mobilenetv3_encoders = { - "timm-mobilenetv3_large_075": { - "encoder": MobileNetV3Encoder, - "pretrained_settings": pretrained_settings["tf_mobilenetv3_large_075"], - "params": {"model_name": "tf_mobilenetv3_large_075", "width_mult": 0.75}, - }, - "timm-mobilenetv3_large_100": { - "encoder": MobileNetV3Encoder, - "pretrained_settings": pretrained_settings["tf_mobilenetv3_large_100"], - "params": {"model_name": "tf_mobilenetv3_large_100", "width_mult": 1.0}, - }, - "timm-mobilenetv3_large_minimal_100": { - "encoder": MobileNetV3Encoder, - "pretrained_settings": pretrained_settings["tf_mobilenetv3_large_minimal_100"], - "params": {"model_name": "tf_mobilenetv3_large_minimal_100", "width_mult": 1.0}, - }, - "timm-mobilenetv3_small_075": { - "encoder": MobileNetV3Encoder, - "pretrained_settings": pretrained_settings["tf_mobilenetv3_small_075"], - "params": {"model_name": "tf_mobilenetv3_small_075", "width_mult": 0.75}, - }, - "timm-mobilenetv3_small_100": { - "encoder": MobileNetV3Encoder, - "pretrained_settings": pretrained_settings["tf_mobilenetv3_small_100"], - "params": {"model_name": "tf_mobilenetv3_small_100", "width_mult": 1.0}, - }, - "timm-mobilenetv3_small_minimal_100": { - "encoder": MobileNetV3Encoder, - "pretrained_settings": pretrained_settings["tf_mobilenetv3_small_minimal_100"], - "params": {"model_name": "tf_mobilenetv3_small_minimal_100", "width_mult": 1.0}, - }, -} diff --git a/segmentation_models_pytorch/encoders/timm_regnet.py b/segmentation_models_pytorch/encoders/timm_regnet.py deleted file mode 100644 index cc60b8ba..00000000 --- a/segmentation_models_pytorch/encoders/timm_regnet.py +++ /dev/null @@ -1,350 +0,0 @@ -from ._base import EncoderMixin -from timm.models.regnet import RegNet, RegNetCfg -import torch.nn as nn - - -class RegNetEncoder(RegNet, EncoderMixin): - def __init__(self, out_channels, depth=5, **kwargs): - kwargs["cfg"] = RegNetCfg(**kwargs["cfg"]) - super().__init__(**kwargs) - self._depth = depth - self._out_channels = out_channels - self._in_channels = 3 - - del self.head - - def get_stages(self): - return [nn.Identity(), self.stem, self.s1, self.s2, self.s3, self.s4] - - def forward(self, x): - stages = self.get_stages() - - features = [] - for i in range(self._depth + 1): - x = stages[i](x) - features.append(x) - - return features - - def load_state_dict(self, state_dict, **kwargs): - state_dict.pop("head.fc.weight", None) - state_dict.pop("head.fc.bias", None) - super().load_state_dict(state_dict, **kwargs) - - -regnet_weights = { - "timm-regnetx_002": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnetx_002-e7e85e5c.pth" # noqa - }, - "timm-regnetx_004": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnetx_004-7d0e9424.pth" # noqa - }, - "timm-regnetx_006": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnetx_006-85ec1baa.pth" # noqa - }, - "timm-regnetx_008": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnetx_008-d8b470eb.pth" # noqa - }, - "timm-regnetx_016": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnetx_016-65ca972a.pth" # noqa - }, - "timm-regnetx_032": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnetx_032-ed0c7f7e.pth" # noqa - }, - "timm-regnetx_040": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnetx_040-73c2a654.pth" # noqa - }, - "timm-regnetx_064": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnetx_064-29278baa.pth" # noqa - }, - "timm-regnetx_080": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnetx_080-7c7fcab1.pth" # noqa - }, - "timm-regnetx_120": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnetx_120-65d5521e.pth" # noqa - }, - "timm-regnetx_160": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnetx_160-c98c4112.pth" # noqa - }, - "timm-regnetx_320": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnetx_320-8ea38b93.pth" # noqa - }, - "timm-regnety_002": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnety_002-e68ca334.pth" # noqa - }, - "timm-regnety_004": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnety_004-0db870e6.pth" # noqa - }, - "timm-regnety_006": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnety_006-c67e57ec.pth" # noqa - }, - "timm-regnety_008": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnety_008-dc900dbe.pth" # noqa - }, - "timm-regnety_016": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnety_016-54367f74.pth" # noqa - }, - "timm-regnety_032": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/regnety_032_ra-7f2439f9.pth" # noqa - }, - "timm-regnety_040": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnety_040-f0d569f9.pth" # noqa - }, - "timm-regnety_064": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnety_064-0a48325c.pth" # noqa - }, - "timm-regnety_080": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnety_080-e7f3eb93.pth" # noqa - }, - "timm-regnety_120": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnety_120-721ba79a.pth" # noqa - }, - "timm-regnety_160": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnety_160-d64013cd.pth" # noqa - }, - "timm-regnety_320": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-regnet/regnety_320-ba464b29.pth" # noqa - }, -} - -pretrained_settings = {} -for model_name, sources in regnet_weights.items(): - pretrained_settings[model_name] = {} - for source_name, source_url in sources.items(): - pretrained_settings[model_name][source_name] = { - "url": source_url, - "input_size": [3, 224, 224], - "input_range": [0, 1], - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "num_classes": 1000, - } - -# at this point I am too lazy to copy configs, so I just used the same configs from timm's repo - - -def _mcfg(**kwargs): - cfg = dict(se_ratio=0.0, bottle_ratio=1.0, stem_width=32) - cfg.update(**kwargs) - return cfg - - -timm_regnet_encoders = { - "timm-regnetx_002": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnetx_002"], - "params": { - "out_channels": (3, 32, 24, 56, 152, 368), - "cfg": _mcfg(w0=24, wa=36.44, wm=2.49, group_size=8, depth=13), - }, - }, - "timm-regnetx_004": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnetx_004"], - "params": { - "out_channels": (3, 32, 32, 64, 160, 384), - "cfg": _mcfg(w0=24, wa=24.48, wm=2.54, group_size=16, depth=22), - }, - }, - "timm-regnetx_006": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnetx_006"], - "params": { - "out_channels": (3, 32, 48, 96, 240, 528), - "cfg": _mcfg(w0=48, wa=36.97, wm=2.24, group_size=24, depth=16), - }, - }, - "timm-regnetx_008": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnetx_008"], - "params": { - "out_channels": (3, 32, 64, 128, 288, 672), - "cfg": _mcfg(w0=56, wa=35.73, wm=2.28, group_size=16, depth=16), - }, - }, - "timm-regnetx_016": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnetx_016"], - "params": { - "out_channels": (3, 32, 72, 168, 408, 912), - "cfg": _mcfg(w0=80, wa=34.01, wm=2.25, group_size=24, depth=18), - }, - }, - "timm-regnetx_032": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnetx_032"], - "params": { - "out_channels": (3, 32, 96, 192, 432, 1008), - "cfg": _mcfg(w0=88, wa=26.31, wm=2.25, group_size=48, depth=25), - }, - }, - "timm-regnetx_040": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnetx_040"], - "params": { - "out_channels": (3, 32, 80, 240, 560, 1360), - "cfg": _mcfg(w0=96, wa=38.65, wm=2.43, group_size=40, depth=23), - }, - }, - "timm-regnetx_064": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnetx_064"], - "params": { - "out_channels": (3, 32, 168, 392, 784, 1624), - "cfg": _mcfg(w0=184, wa=60.83, wm=2.07, group_size=56, depth=17), - }, - }, - "timm-regnetx_080": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnetx_080"], - "params": { - "out_channels": (3, 32, 80, 240, 720, 1920), - "cfg": _mcfg(w0=80, wa=49.56, wm=2.88, group_size=120, depth=23), - }, - }, - "timm-regnetx_120": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnetx_120"], - "params": { - "out_channels": (3, 32, 224, 448, 896, 2240), - "cfg": _mcfg(w0=168, wa=73.36, wm=2.37, group_size=112, depth=19), - }, - }, - "timm-regnetx_160": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnetx_160"], - "params": { - "out_channels": (3, 32, 256, 512, 896, 2048), - "cfg": _mcfg(w0=216, wa=55.59, wm=2.1, group_size=128, depth=22), - }, - }, - "timm-regnetx_320": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnetx_320"], - "params": { - "out_channels": (3, 32, 336, 672, 1344, 2520), - "cfg": _mcfg(w0=320, wa=69.86, wm=2.0, group_size=168, depth=23), - }, - }, - # regnety - "timm-regnety_002": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnety_002"], - "params": { - "out_channels": (3, 32, 24, 56, 152, 368), - "cfg": _mcfg( - w0=24, wa=36.44, wm=2.49, group_size=8, depth=13, se_ratio=0.25 - ), - }, - }, - "timm-regnety_004": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnety_004"], - "params": { - "out_channels": (3, 32, 48, 104, 208, 440), - "cfg": _mcfg( - w0=48, wa=27.89, wm=2.09, group_size=8, depth=16, se_ratio=0.25 - ), - }, - }, - "timm-regnety_006": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnety_006"], - "params": { - "out_channels": (3, 32, 48, 112, 256, 608), - "cfg": _mcfg( - w0=48, wa=32.54, wm=2.32, group_size=16, depth=15, se_ratio=0.25 - ), - }, - }, - "timm-regnety_008": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnety_008"], - "params": { - "out_channels": (3, 32, 64, 128, 320, 768), - "cfg": _mcfg( - w0=56, wa=38.84, wm=2.4, group_size=16, depth=14, se_ratio=0.25 - ), - }, - }, - "timm-regnety_016": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnety_016"], - "params": { - "out_channels": (3, 32, 48, 120, 336, 888), - "cfg": _mcfg( - w0=48, wa=20.71, wm=2.65, group_size=24, depth=27, se_ratio=0.25 - ), - }, - }, - "timm-regnety_032": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnety_032"], - "params": { - "out_channels": (3, 32, 72, 216, 576, 1512), - "cfg": _mcfg( - w0=80, wa=42.63, wm=2.66, group_size=24, depth=21, se_ratio=0.25 - ), - }, - }, - "timm-regnety_040": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnety_040"], - "params": { - "out_channels": (3, 32, 128, 192, 512, 1088), - "cfg": _mcfg( - w0=96, wa=31.41, wm=2.24, group_size=64, depth=22, se_ratio=0.25 - ), - }, - }, - "timm-regnety_064": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnety_064"], - "params": { - "out_channels": (3, 32, 144, 288, 576, 1296), - "cfg": _mcfg( - w0=112, wa=33.22, wm=2.27, group_size=72, depth=25, se_ratio=0.25 - ), - }, - }, - "timm-regnety_080": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnety_080"], - "params": { - "out_channels": (3, 32, 168, 448, 896, 2016), - "cfg": _mcfg( - w0=192, wa=76.82, wm=2.19, group_size=56, depth=17, se_ratio=0.25 - ), - }, - }, - "timm-regnety_120": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnety_120"], - "params": { - "out_channels": (3, 32, 224, 448, 896, 2240), - "cfg": _mcfg( - w0=168, wa=73.36, wm=2.37, group_size=112, depth=19, se_ratio=0.25 - ), - }, - }, - "timm-regnety_160": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnety_160"], - "params": { - "out_channels": (3, 32, 224, 448, 1232, 3024), - "cfg": _mcfg( - w0=200, wa=106.23, wm=2.48, group_size=112, depth=18, se_ratio=0.25 - ), - }, - }, - "timm-regnety_320": { - "encoder": RegNetEncoder, - "pretrained_settings": pretrained_settings["timm-regnety_320"], - "params": { - "out_channels": (3, 32, 232, 696, 1392, 3712), - "cfg": _mcfg( - w0=232, wa=115.89, wm=2.53, group_size=232, depth=20, se_ratio=0.25 - ), - }, - }, -} diff --git a/segmentation_models_pytorch/encoders/timm_res2net.py b/segmentation_models_pytorch/encoders/timm_res2net.py deleted file mode 100644 index e97043e3..00000000 --- a/segmentation_models_pytorch/encoders/timm_res2net.py +++ /dev/null @@ -1,163 +0,0 @@ -from ._base import EncoderMixin -from timm.models.resnet import ResNet -from timm.models.res2net import Bottle2neck -import torch.nn as nn - - -class Res2NetEncoder(ResNet, EncoderMixin): - def __init__(self, out_channels, depth=5, **kwargs): - super().__init__(**kwargs) - self._depth = depth - self._out_channels = out_channels - self._in_channels = 3 - - del self.fc - del self.global_pool - - def get_stages(self): - return [ - nn.Identity(), - nn.Sequential(self.conv1, self.bn1, self.act1), - nn.Sequential(self.maxpool, self.layer1), - self.layer2, - self.layer3, - self.layer4, - ] - - def make_dilated(self, *args, **kwargs): - raise ValueError("Res2Net encoders do not support dilated mode") - - def forward(self, x): - stages = self.get_stages() - - features = [] - for i in range(self._depth + 1): - x = stages[i](x) - features.append(x) - - return features - - def load_state_dict(self, state_dict, **kwargs): - state_dict.pop("fc.bias", None) - state_dict.pop("fc.weight", None) - super().load_state_dict(state_dict, **kwargs) - - -res2net_weights = { - "timm-res2net50_26w_4s": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-res2net/res2net50_26w_4s-06e79181.pth" # noqa - }, - "timm-res2net50_48w_2s": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-res2net/res2net50_48w_2s-afed724a.pth" # noqa - }, - "timm-res2net50_14w_8s": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-res2net/res2net50_14w_8s-6527dddc.pth" # noqa - }, - "timm-res2net50_26w_6s": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-res2net/res2net50_26w_6s-19041792.pth" # noqa - }, - "timm-res2net50_26w_8s": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-res2net/res2net50_26w_8s-2c7c9f12.pth" # noqa - }, - "timm-res2net101_26w_4s": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-res2net/res2net101_26w_4s-02a759a1.pth" # noqa - }, - "timm-res2next50": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-res2net/res2next50_4s-6ef7e7bf.pth" # noqa - }, -} - -pretrained_settings = {} -for model_name, sources in res2net_weights.items(): - pretrained_settings[model_name] = {} - for source_name, source_url in sources.items(): - pretrained_settings[model_name][source_name] = { - "url": source_url, - "input_size": [3, 224, 224], - "input_range": [0, 1], - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "num_classes": 1000, - } - - -timm_res2net_encoders = { - "timm-res2net50_26w_4s": { - "encoder": Res2NetEncoder, - "pretrained_settings": pretrained_settings["timm-res2net50_26w_4s"], - "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), - "block": Bottle2neck, - "layers": [3, 4, 6, 3], - "base_width": 26, - "block_args": {"scale": 4}, - }, - }, - "timm-res2net101_26w_4s": { - "encoder": Res2NetEncoder, - "pretrained_settings": pretrained_settings["timm-res2net101_26w_4s"], - "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), - "block": Bottle2neck, - "layers": [3, 4, 23, 3], - "base_width": 26, - "block_args": {"scale": 4}, - }, - }, - "timm-res2net50_26w_6s": { - "encoder": Res2NetEncoder, - "pretrained_settings": pretrained_settings["timm-res2net50_26w_6s"], - "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), - "block": Bottle2neck, - "layers": [3, 4, 6, 3], - "base_width": 26, - "block_args": {"scale": 6}, - }, - }, - "timm-res2net50_26w_8s": { - "encoder": Res2NetEncoder, - "pretrained_settings": pretrained_settings["timm-res2net50_26w_8s"], - "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), - "block": Bottle2neck, - "layers": [3, 4, 6, 3], - "base_width": 26, - "block_args": {"scale": 8}, - }, - }, - "timm-res2net50_48w_2s": { - "encoder": Res2NetEncoder, - "pretrained_settings": pretrained_settings["timm-res2net50_48w_2s"], - "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), - "block": Bottle2neck, - "layers": [3, 4, 6, 3], - "base_width": 48, - "block_args": {"scale": 2}, - }, - }, - "timm-res2net50_14w_8s": { - "encoder": Res2NetEncoder, - "pretrained_settings": pretrained_settings["timm-res2net50_14w_8s"], - "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), - "block": Bottle2neck, - "layers": [3, 4, 6, 3], - "base_width": 14, - "block_args": {"scale": 8}, - }, - }, - "timm-res2next50": { - "encoder": Res2NetEncoder, - "pretrained_settings": pretrained_settings["timm-res2next50"], - "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), - "block": Bottle2neck, - "layers": [3, 4, 6, 3], - "base_width": 4, - "cardinality": 8, - "block_args": {"scale": 4}, - }, - }, -} diff --git a/segmentation_models_pytorch/encoders/timm_resnest.py b/segmentation_models_pytorch/encoders/timm_resnest.py deleted file mode 100644 index 1599b6c8..00000000 --- a/segmentation_models_pytorch/encoders/timm_resnest.py +++ /dev/null @@ -1,208 +0,0 @@ -from ._base import EncoderMixin -from timm.models.resnet import ResNet -from timm.models.resnest import ResNestBottleneck -import torch.nn as nn - - -class ResNestEncoder(ResNet, EncoderMixin): - def __init__(self, out_channels, depth=5, **kwargs): - super().__init__(**kwargs) - self._depth = depth - self._out_channels = out_channels - self._in_channels = 3 - - del self.fc - del self.global_pool - - def get_stages(self): - return [ - nn.Identity(), - nn.Sequential(self.conv1, self.bn1, self.act1), - nn.Sequential(self.maxpool, self.layer1), - self.layer2, - self.layer3, - self.layer4, - ] - - def make_dilated(self, *args, **kwargs): - raise ValueError("ResNest encoders do not support dilated mode") - - def forward(self, x): - stages = self.get_stages() - - features = [] - for i in range(self._depth + 1): - x = stages[i](x) - features.append(x) - - return features - - def load_state_dict(self, state_dict, **kwargs): - state_dict.pop("fc.bias", None) - state_dict.pop("fc.weight", None) - super().load_state_dict(state_dict, **kwargs) - - -resnest_weights = { - "timm-resnest14d": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/gluon_resnest14-9c8fe254.pth" # noqa - }, - "timm-resnest26d": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/gluon_resnest26-50eb607c.pth" # noqa - }, - "timm-resnest50d": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-resnest/resnest50-528c19ca.pth" # noqa - }, - "timm-resnest101e": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-resnest/resnest101-22405ba7.pth" # noqa - }, - "timm-resnest200e": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-resnest/resnest200-75117900.pth" # noqa - }, - "timm-resnest269e": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-resnest/resnest269-0cc87c48.pth" # noqa - }, - "timm-resnest50d_4s2x40d": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-resnest/resnest50_fast_4s2x40d-41d14ed0.pth" # noqa - }, - "timm-resnest50d_1s4x24d": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-resnest/resnest50_fast_1s4x24d-d4a4f76f.pth" # noqa - }, -} - -pretrained_settings = {} -for model_name, sources in resnest_weights.items(): - pretrained_settings[model_name] = {} - for source_name, source_url in sources.items(): - pretrained_settings[model_name][source_name] = { - "url": source_url, - "input_size": [3, 224, 224], - "input_range": [0, 1], - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "num_classes": 1000, - } - - -timm_resnest_encoders = { - "timm-resnest14d": { - "encoder": ResNestEncoder, - "pretrained_settings": pretrained_settings["timm-resnest14d"], - "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), - "block": ResNestBottleneck, - "layers": [1, 1, 1, 1], - "stem_type": "deep", - "stem_width": 32, - "avg_down": True, - "base_width": 64, - "cardinality": 1, - "block_args": {"radix": 2, "avd": True, "avd_first": False}, - }, - }, - "timm-resnest26d": { - "encoder": ResNestEncoder, - "pretrained_settings": pretrained_settings["timm-resnest26d"], - "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), - "block": ResNestBottleneck, - "layers": [2, 2, 2, 2], - "stem_type": "deep", - "stem_width": 32, - "avg_down": True, - "base_width": 64, - "cardinality": 1, - "block_args": {"radix": 2, "avd": True, "avd_first": False}, - }, - }, - "timm-resnest50d": { - "encoder": ResNestEncoder, - "pretrained_settings": pretrained_settings["timm-resnest50d"], - "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), - "block": ResNestBottleneck, - "layers": [3, 4, 6, 3], - "stem_type": "deep", - "stem_width": 32, - "avg_down": True, - "base_width": 64, - "cardinality": 1, - "block_args": {"radix": 2, "avd": True, "avd_first": False}, - }, - }, - "timm-resnest101e": { - "encoder": ResNestEncoder, - "pretrained_settings": pretrained_settings["timm-resnest101e"], - "params": { - "out_channels": (3, 128, 256, 512, 1024, 2048), - "block": ResNestBottleneck, - "layers": [3, 4, 23, 3], - "stem_type": "deep", - "stem_width": 64, - "avg_down": True, - "base_width": 64, - "cardinality": 1, - "block_args": {"radix": 2, "avd": True, "avd_first": False}, - }, - }, - "timm-resnest200e": { - "encoder": ResNestEncoder, - "pretrained_settings": pretrained_settings["timm-resnest200e"], - "params": { - "out_channels": (3, 128, 256, 512, 1024, 2048), - "block": ResNestBottleneck, - "layers": [3, 24, 36, 3], - "stem_type": "deep", - "stem_width": 64, - "avg_down": True, - "base_width": 64, - "cardinality": 1, - "block_args": {"radix": 2, "avd": True, "avd_first": False}, - }, - }, - "timm-resnest269e": { - "encoder": ResNestEncoder, - "pretrained_settings": pretrained_settings["timm-resnest269e"], - "params": { - "out_channels": (3, 128, 256, 512, 1024, 2048), - "block": ResNestBottleneck, - "layers": [3, 30, 48, 8], - "stem_type": "deep", - "stem_width": 64, - "avg_down": True, - "base_width": 64, - "cardinality": 1, - "block_args": {"radix": 2, "avd": True, "avd_first": False}, - }, - }, - "timm-resnest50d_4s2x40d": { - "encoder": ResNestEncoder, - "pretrained_settings": pretrained_settings["timm-resnest50d_4s2x40d"], - "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), - "block": ResNestBottleneck, - "layers": [3, 4, 6, 3], - "stem_type": "deep", - "stem_width": 32, - "avg_down": True, - "base_width": 40, - "cardinality": 2, - "block_args": {"radix": 4, "avd": True, "avd_first": True}, - }, - }, - "timm-resnest50d_1s4x24d": { - "encoder": ResNestEncoder, - "pretrained_settings": pretrained_settings["timm-resnest50d_1s4x24d"], - "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), - "block": ResNestBottleneck, - "layers": [3, 4, 6, 3], - "stem_type": "deep", - "stem_width": 32, - "avg_down": True, - "base_width": 24, - "cardinality": 4, - "block_args": {"radix": 1, "avd": True, "avd_first": True}, - }, - }, -} diff --git a/segmentation_models_pytorch/encoders/timm_sknet.py b/segmentation_models_pytorch/encoders/timm_sknet.py index 14d6d2b0..49fda0e8 100644 --- a/segmentation_models_pytorch/encoders/timm_sknet.py +++ b/segmentation_models_pytorch/encoders/timm_sknet.py @@ -1,35 +1,63 @@ -from ._base import EncoderMixin +import torch +from typing import Dict, List, Sequence from timm.models.resnet import ResNet from timm.models.sknet import SelectiveKernelBottleneck, SelectiveKernelBasic -import torch.nn as nn + +from ._base import EncoderMixin class SkNetEncoder(ResNet, EncoderMixin): - def __init__(self, out_channels, depth=5, **kwargs): + def __init__( + self, + out_channels: List[int], + depth: int = 5, + output_stride: int = 32, + **kwargs, + ): + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) super().__init__(**kwargs) + self._depth = depth - self._out_channels = out_channels self._in_channels = 3 + self._out_channels = out_channels + self._output_stride = output_stride del self.fc del self.global_pool - def get_stages(self): - return [ - nn.Identity(), - nn.Sequential(self.conv1, self.bn1, self.act1), - nn.Sequential(self.maxpool, self.layer1), - self.layer2, - self.layer3, - self.layer4, - ] - - def forward(self, x): - stages = self.get_stages() - - features = [] - for i in range(self._depth + 1): - x = stages[i](x) + def get_stages(self) -> Dict[int, Sequence[torch.nn.Module]]: + return { + 16: [self.layer3], + 32: [self.layer4], + } + + def forward(self, x: torch.Tensor) -> List[torch.Tensor]: + features = [x] + + if self._depth >= 1: + x = self.conv1(x) + x = self.bn1(x) + x = self.act1(x) + features.append(x) + + if self._depth >= 2: + x = self.maxpool(x) + x = self.layer1(x) + features.append(x) + + if self._depth >= 3: + x = self.layer2(x) + features.append(x) + + if self._depth >= 4: + x = self.layer3(x) + features.append(x) + + if self._depth >= 5: + x = self.layer4(x) features.append(x) return features @@ -40,37 +68,17 @@ def load_state_dict(self, state_dict, **kwargs): super().load_state_dict(state_dict, **kwargs) -sknet_weights = { - "timm-skresnet18": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/skresnet18_ra-4eec2804.pth" # noqa - }, - "timm-skresnet34": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/skresnet34_ra-bdc0ccde.pth" # noqa - }, - "timm-skresnext50_32x4d": { - "imagenet": "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/skresnext50_ra-f40e40bf.pth" # noqa - }, -} - -pretrained_settings = {} -for model_name, sources in sknet_weights.items(): - pretrained_settings[model_name] = {} - for source_name, source_url in sources.items(): - pretrained_settings[model_name][source_name] = { - "url": source_url, - "input_size": [3, 224, 224], - "input_range": [0, 1], - "mean": [0.485, 0.456, 0.406], - "std": [0.229, 0.224, 0.225], - "num_classes": 1000, - } - timm_sknet_encoders = { "timm-skresnet18": { "encoder": SkNetEncoder, - "pretrained_settings": pretrained_settings["timm-skresnet18"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/timm-skresnet18.imagenet", + "revision": "6c97652bb744d89177b68274d2fda3923a7d1f95", + }, + }, "params": { - "out_channels": (3, 64, 64, 128, 256, 512), + "out_channels": [3, 64, 64, 128, 256, 512], "block": SelectiveKernelBasic, "layers": [2, 2, 2, 2], "zero_init_last": False, @@ -79,9 +87,14 @@ def load_state_dict(self, state_dict, **kwargs): }, "timm-skresnet34": { "encoder": SkNetEncoder, - "pretrained_settings": pretrained_settings["timm-skresnet34"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/timm-skresnet34.imagenet", + "revision": "2367796924a8182cc835ef6b5dc303917f923f99", + }, + }, "params": { - "out_channels": (3, 64, 64, 128, 256, 512), + "out_channels": [3, 64, 64, 128, 256, 512], "block": SelectiveKernelBasic, "layers": [3, 4, 6, 3], "zero_init_last": False, @@ -90,9 +103,14 @@ def load_state_dict(self, state_dict, **kwargs): }, "timm-skresnext50_32x4d": { "encoder": SkNetEncoder, - "pretrained_settings": pretrained_settings["timm-skresnext50_32x4d"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/timm-skresnext50_32x4d.imagenet", + "revision": "50207e407cc4c6ea9e6872963db6844ca7b7b9de", + }, + }, "params": { - "out_channels": (3, 64, 256, 512, 1024, 2048), + "out_channels": [3, 64, 256, 512, 1024, 2048], "block": SelectiveKernelBottleneck, "layers": [3, 4, 6, 3], "zero_init_last": False, diff --git a/segmentation_models_pytorch/encoders/timm_universal.py b/segmentation_models_pytorch/encoders/timm_universal.py index 9bdcb188..138b2ef8 100644 --- a/segmentation_models_pytorch/encoders/timm_universal.py +++ b/segmentation_models_pytorch/encoders/timm_universal.py @@ -44,6 +44,10 @@ class TimmUniversalEncoder(nn.Module): - Compatible with convolutional and transformer-like backbones. """ + _is_torch_scriptable = True + _is_torch_exportable = True + _is_torch_compilable = True + def __init__( self, name: str, @@ -64,7 +68,15 @@ def __init__( output_stride (int): Desired output stride (default: 32). **kwargs: Additional arguments passed to `timm.create_model`. """ + # At the moment we do not support models with more than 5 stages, + # but can be reconfigured in the future. + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) + super().__init__() + self.name = name # Default model configuration for feature extraction common_kwargs = dict( @@ -118,9 +130,9 @@ def __init__( # Most transformer-like models use out_indices=(0, 1, 2, 3) for depth=5. common_kwargs["out_indices"] = tuple(range(depth - 1)) - self.model = timm.create_model( - name, **_merge_kwargs_no_duplicates(common_kwargs, kwargs) - ) + timm_model_kwargs = _merge_kwargs_no_duplicates(common_kwargs, kwargs) + self.model = timm.create_model(name, **timm_model_kwargs) + # Add a dummy output channel (0) to align with traditional encoder structures. self._out_channels = ( [in_channels] + [0] + self.model.feature_info.channels() @@ -193,7 +205,28 @@ def output_stride(self) -> int: Returns: int: The effective output stride. """ - return min(self._output_stride, 2**self._depth) + return int(min(self._output_stride, 2**self._depth)) + + def load_state_dict(self, state_dict, **kwargs): + # for compatibility of weights for + # timm- ported encoders with TimmUniversalEncoder + patterns = ["regnet", "res2", "resnest", "mobilenetv3", "gernet"] + + is_deprecated_encoder = any( + self.name.startswith(pattern) for pattern in patterns + ) + + if is_deprecated_encoder: + keys = list(state_dict.keys()) + for key in keys: + new_key = key + if not key.startswith("model."): + new_key = "model." + key + if "gernet" in self.name: + new_key = new_key.replace(".stages.", ".stages_") + state_dict[new_key] = state_dict.pop(key) + + return super().load_state_dict(state_dict, **kwargs) def _merge_kwargs_no_duplicates(a: dict[str, Any], b: dict[str, Any]) -> dict[str, Any]: diff --git a/segmentation_models_pytorch/encoders/timm_vit.py b/segmentation_models_pytorch/encoders/timm_vit.py new file mode 100644 index 00000000..5519d897 --- /dev/null +++ b/segmentation_models_pytorch/encoders/timm_vit.py @@ -0,0 +1,191 @@ +from typing import Any, Optional + +import timm +import torch +import torch.nn as nn + +from .timm_universal import _merge_kwargs_no_duplicates + + +def sample_block_indices_uniformly(n: int, total_num_blocks: int) -> list[int]: + """ + Sample N block indices uniformly from the total number of blocks. + """ + return [ + int(total_num_blocks / n * block_depth) - 1 for block_depth in range(1, n + 1) + ] + + +def validate_output_indices( + output_indices: list[int], model_num_blocks: int, depth: int +): + """ + Validate the output indices are within the valid range of the model and the + length of the output indices is equal to the depth of the encoder. + """ + for output_index in output_indices: + if output_index < -model_num_blocks or output_index >= model_num_blocks: + raise ValueError( + f"Output indices for feature extraction should be in range " + f"[-{model_num_blocks}, {model_num_blocks}), because the model has {model_num_blocks} blocks, " + f"got index = {output_index}." + ) + + +def preprocess_output_indices( + output_indices: Optional[list[int]], model_num_blocks: int, depth: int +) -> list[int]: + """ + Preprocess the output indices for the encoder. + """ + + # Refine encoder output indices + if output_indices is None: + output_indices = sample_block_indices_uniformly(depth, model_num_blocks) + elif not isinstance(output_indices, (list, tuple)): + raise ValueError( + f"`output_indices` for encoder should be a list/tuple/None, got {type(output_indices)}" + ) + validate_output_indices(output_indices, model_num_blocks, depth) + + return output_indices + + +class TimmViTEncoder(nn.Module): + """ + A universal encoder leveraging the `timm` library for feature extraction from + ViT style models + + Features: + - Supports configurable depth. + - Ensures consistent multi-level feature extraction across all ViT models. + """ + + # prefix tokens are not supported for scripting + _is_torch_scriptable = False + _is_torch_exportable = True + _is_torch_compilable = True + + def __init__( + self, + name: str, + pretrained: bool = True, + in_channels: int = 3, + depth: int = 4, + output_indices: Optional[list[int]] = None, + **kwargs: dict[str, Any], + ): + """ + Initialize the encoder. + + Args: + name (str): ViT model name to load from `timm`. + pretrained (bool): Load pretrained weights (default: True). + in_channels (int): Number of input channels (default: 3 for RGB). + depth (int): Number of feature stages to extract (default: 4). + output_indices (Optional[list[int] | int]): Indices of blocks in the model to be used for feature extraction. + **kwargs: Additional arguments passed to `timm.create_model`. + """ + super().__init__() + + if depth < 1: + raise ValueError(f"`encoder_depth` should be greater than 1, got {depth}.") + + # Output stride validation needed for smp encoder test consistency + output_stride = kwargs.pop("output_stride", None) + if output_stride is not None: + raise ValueError("Dilated mode not supported, set output stride to None") + + if isinstance(output_indices, (list, tuple)) and len(output_indices) != depth: + raise ValueError( + f"Length of output indices for feature extraction should be equal to the depth of the encoder " + f"architecture, got output indices length - {len(output_indices)}, encoder depth - {depth}" + ) + + self.name = name + + # Load a timm model + encoder_kwargs = dict(in_chans=in_channels, pretrained=pretrained) + encoder_kwargs = _merge_kwargs_no_duplicates(encoder_kwargs, kwargs) + self.model = timm.create_model(name, **encoder_kwargs) + + if not hasattr(self.model, "forward_intermediates"): + raise ValueError( + f"Encoder `{name}` does not support `forward_intermediates` for feature extraction. " + f"Please update `timm` or use another encoder." + ) + + # Get all the necessary information about the model + feature_info = self.model.feature_info + + # Additional checks + model_num_blocks = len(feature_info) + if depth > model_num_blocks: + raise ValueError( + f"Depth of the encoder cannot exceed the number of blocks in the model " + f"got {depth} depth, model has {model_num_blocks} blocks" + ) + + # Preprocess the output indices, uniformly sample from model_num_blocks if None + output_indices = preprocess_output_indices( + output_indices, model_num_blocks, depth + ) + + # Private attributes for model forward + self._num_prefix_tokens = getattr(self.model, "num_prefix_tokens", 0) + self._has_cls_token = getattr(self.model, "has_cls_token", False) + self._output_indices = output_indices + + # Public attributes + self.output_strides = [feature_info[i]["reduction"] for i in output_indices] + self.output_stride = self.output_strides[-1] + self.out_channels = [feature_info[i]["num_chs"] for i in output_indices] + self.has_prefix_tokens = self._num_prefix_tokens > 0 + self.input_size = self.model.pretrained_cfg.get("input_size", None) + self.is_fixed_input_size = self.model.pretrained_cfg.get( + "fixed_input_size", False + ) + + def _forward_with_prefix_tokens( + self, x: torch.Tensor + ) -> tuple[list[torch.Tensor], list[torch.Tensor]]: + intermediate_outputs = self.model.forward_intermediates( + x, + indices=self._output_indices, + intermediates_only=True, + return_prefix_tokens=True, + ) + + features = [output[0] for output in intermediate_outputs] + prefix_tokens = [output[1] for output in intermediate_outputs] + + return features, prefix_tokens + + def _forward_without_prefix_tokens(self, x: torch.Tensor) -> list[torch.Tensor]: + features = self.model.forward_intermediates( + x, + indices=self._output_indices, + intermediates_only=True, + ) + return features + + def forward( + self, x: torch.Tensor + ) -> tuple[list[torch.Tensor], list[Optional[torch.Tensor]]]: + """ + Forward pass to extract multi-stage features. + + Args: + x (torch.Tensor): Input tensor of shape (B, C, H, W). + + Returns: + tuple[list[torch.Tensor], list[torch.Tensor]]: Tuple of feature maps and cls tokens (if supported) at different scales. + """ + + if self.has_prefix_tokens: + features, prefix_tokens = self._forward_with_prefix_tokens(x) + else: + features = self._forward_without_prefix_tokens(x) + prefix_tokens = [None] * len(features) + + return features, prefix_tokens diff --git a/segmentation_models_pytorch/encoders/vgg.py b/segmentation_models_pytorch/encoders/vgg.py index cbc602c8..1bb577fe 100644 --- a/segmentation_models_pytorch/encoders/vgg.py +++ b/segmentation_models_pytorch/encoders/vgg.py @@ -23,29 +23,53 @@ depth = 3 -> number of feature tensors = 4 (one with same resolution as input and 3 downsampled). """ +import torch import torch.nn as nn + from torchvision.models.vgg import VGG from torchvision.models.vgg import make_layers -from pretrainedmodels.models.torchvision_models import pretrained_settings + +from typing import List, Union from ._base import EncoderMixin # fmt: off cfg = { - 'A': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], - 'B': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], - 'D': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'], - 'E': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'], + "A": [64, "M", 128, "M", 256, 256, "M", 512, 512, "M", 512, 512, "M"], + "B": [64, 64, "M", 128, 128, "M", 256, 256, "M", 512, 512, "M", 512, 512, "M"], + "D": [64, 64, "M", 128, 128, "M", 256, 256, 256, "M", 512, 512, 512, "M", 512, 512, 512, "M"], + "E": [64, 64, "M", 128, 128, "M", 256, 256, 256, 256, "M", 512, 512, 512, 512, "M", 512, 512, 512, 512, "M"], } # fmt: on class VGGEncoder(VGG, EncoderMixin): - def __init__(self, out_channels, config, batch_norm=False, depth=5, **kwargs): + def __init__( + self, + out_channels: List[int], + config: List[Union[int, str]], + batch_norm: bool = False, + depth: int = 5, + output_stride: int = 32, + **kwargs, + ): + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) super().__init__(make_layers(config, batch_norm=batch_norm), **kwargs) - self._out_channels = out_channels + self._depth = depth self._in_channels = 3 + self._out_channels = out_channels + self._output_stride = output_stride + self._out_indexes = [ + i - 1 + for i, module in enumerate(self.features) + if isinstance(module, nn.MaxPool2d) + ] + self._out_indexes.append(len(self.features) - 1) + del self.classifier def make_dilated(self, *args, **kwargs): @@ -54,24 +78,23 @@ def make_dilated(self, *args, **kwargs): " operations for downsampling!" ) - def get_stages(self): - stages = [] - stage_modules = [] - for module in self.features: - if isinstance(module, nn.MaxPool2d): - stages.append(nn.Sequential(*stage_modules)) - stage_modules = [] - stage_modules.append(module) - stages.append(nn.Sequential(*stage_modules)) - return stages + def forward(self, x: torch.Tensor) -> List[torch.Tensor]: + features = [] + depth = 0 + + for i, module in enumerate(self.features): + x = module(x) - def forward(self, x): - stages = self.get_stages() + if i in self._out_indexes: + features.append(x) + depth += 1 - features = [] - for i in range(self._depth + 1): - x = stages[i](x) - features.append(x) + # torchscript does not support break in cycle, so we just + # go over all modules and then slice number of features + if not torch.jit.is_scripting() and depth > self._depth: + break + + features = features[: self._depth + 1] return features @@ -83,75 +106,206 @@ def load_state_dict(self, state_dict, **kwargs): super().load_state_dict(state_dict, **kwargs) +pretrained_settings = { + "vgg11": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg11-bbd30ac9.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg11_bn": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg11_bn-6002323d.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg13": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg13-c768596a.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg13_bn": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg13_bn-abd245e5.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg16": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg16-397923af.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg16_bn": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg16_bn-6c64b313.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg19": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg19-dcbb9e9d.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, + "vgg19_bn": { + "imagenet": { + "url": "https://download.pytorch.org/models/vgg19_bn-c79401a0.pth", + "input_space": "RGB", + "input_size": [3, 224, 224], + "input_range": [0, 1], + "mean": [0.485, 0.456, 0.406], + "std": [0.229, 0.224, 0.225], + "num_classes": 1000, + } + }, +} + vgg_encoders = { "vgg11": { "encoder": VGGEncoder, - "pretrained_settings": pretrained_settings["vgg11"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/vgg11.imagenet", + "revision": "ad8b90e1051c38fdbf399cf5016886a1be357390", + }, + }, "params": { - "out_channels": (64, 128, 256, 512, 512, 512), + "out_channels": [64, 128, 256, 512, 512, 512], "config": cfg["A"], "batch_norm": False, }, }, "vgg11_bn": { "encoder": VGGEncoder, - "pretrained_settings": pretrained_settings["vgg11_bn"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/vgg11_bn.imagenet", + "revision": "59757f9215032c9f092977092d57d26a9df7fd9c", + }, + }, "params": { - "out_channels": (64, 128, 256, 512, 512, 512), + "out_channels": [64, 128, 256, 512, 512, 512], "config": cfg["A"], "batch_norm": True, }, }, "vgg13": { "encoder": VGGEncoder, - "pretrained_settings": pretrained_settings["vgg13"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/vgg13.imagenet", + "revision": "1b70ff2580f101a8007a48b51e2b5d1e5925dc42", + }, + }, "params": { - "out_channels": (64, 128, 256, 512, 512, 512), + "out_channels": [64, 128, 256, 512, 512, 512], "config": cfg["B"], "batch_norm": False, }, }, "vgg13_bn": { "encoder": VGGEncoder, - "pretrained_settings": pretrained_settings["vgg13_bn"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/vgg13_bn.imagenet", + "revision": "9be454515193af6612261b7614fe90607e27b143", + }, + }, "params": { - "out_channels": (64, 128, 256, 512, 512, 512), + "out_channels": [64, 128, 256, 512, 512, 512], "config": cfg["B"], "batch_norm": True, }, }, "vgg16": { "encoder": VGGEncoder, - "pretrained_settings": pretrained_settings["vgg16"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/vgg16.imagenet", + "revision": "49d74b799006ee252b86e25acd6f1fd8ac9a99c1", + }, + }, "params": { - "out_channels": (64, 128, 256, 512, 512, 512), + "out_channels": [64, 128, 256, 512, 512, 512], "config": cfg["D"], "batch_norm": False, }, }, "vgg16_bn": { "encoder": VGGEncoder, - "pretrained_settings": pretrained_settings["vgg16_bn"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/vgg16_bn.imagenet", + "revision": "2c186d02fb519e93219a99a1c2af6295aef0bf0d", + }, + }, "params": { - "out_channels": (64, 128, 256, 512, 512, 512), + "out_channels": [64, 128, 256, 512, 512, 512], "config": cfg["D"], "batch_norm": True, }, }, "vgg19": { "encoder": VGGEncoder, - "pretrained_settings": pretrained_settings["vgg19"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/vgg19.imagenet", + "revision": "2853d00d7bca364dbb98be4d6afa347e5aeec1f6", + }, + }, "params": { - "out_channels": (64, 128, 256, 512, 512, 512), + "out_channels": [64, 128, 256, 512, 512, 512], "config": cfg["E"], "batch_norm": False, }, }, "vgg19_bn": { "encoder": VGGEncoder, - "pretrained_settings": pretrained_settings["vgg19_bn"], + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/vgg19_bn.imagenet", + "revision": "f09a924cb0d201ea6f61601df9559141382271d7", + }, + }, "params": { - "out_channels": (64, 128, 256, 512, 512, 512), + "out_channels": [64, 128, 256, 512, 512, 512], "config": cfg["E"], "batch_norm": True, }, diff --git a/segmentation_models_pytorch/encoders/xception.py b/segmentation_models_pytorch/encoders/xception.py index c8c476ce..af3a26d4 100644 --- a/segmentation_models_pytorch/encoders/xception.py +++ b/segmentation_models_pytorch/encoders/xception.py @@ -1,18 +1,28 @@ -import torch.nn as nn - -from pretrainedmodels.models.xception import pretrained_settings -from pretrainedmodels.models.xception import Xception +from typing import List from ._base import EncoderMixin +from ._xception import Xception class XceptionEncoder(Xception, EncoderMixin): - def __init__(self, out_channels, *args, depth=5, **kwargs): + def __init__( + self, + out_channels: List[int], + *args, + depth: int = 5, + output_stride: int = 32, + **kwargs, + ): + if depth > 5 or depth < 1: + raise ValueError( + f"{self.__class__.__name__} depth should be in range [1, 5], got {depth}" + ) super().__init__(*args, **kwargs) - self._out_channels = out_channels self._depth = depth self._in_channels = 3 + self._out_channels = out_channels + self._output_stride = output_stride # modify padding to maintain output shape self.conv1.padding = (1, 1) @@ -26,36 +36,45 @@ def make_dilated(self, *args, **kwargs): "due to pooling operation for downsampling!" ) - def get_stages(self): - return [ - nn.Identity(), - nn.Sequential( - self.conv1, self.bn1, self.relu, self.conv2, self.bn2, self.relu - ), - self.block1, - self.block2, - nn.Sequential( - self.block3, - self.block4, - self.block5, - self.block6, - self.block7, - self.block8, - self.block9, - self.block10, - self.block11, - ), - nn.Sequential( - self.block12, self.conv3, self.bn3, self.relu, self.conv4, self.bn4 - ), - ] - def forward(self, x): - stages = self.get_stages() + features = [x] + + if self._depth >= 1: + x = self.conv1(x) + x = self.bn1(x) + x = self.relu1(x) + x = self.conv2(x) + x = self.bn2(x) + x = self.relu2(x) + features.append(x) + + if self._depth >= 2: + x = self.block1(x) + features.append(x) + + if self._depth >= 3: + x = self.block2(x) + features.append(x) + + if self._depth >= 4: + x = self.block3(x) + x = self.block4(x) + x = self.block5(x) + x = self.block6(x) + x = self.block7(x) + x = self.block8(x) + x = self.block9(x) + x = self.block10(x) + x = self.block11(x) + features.append(x) - features = [] - for i in range(self._depth + 1): - x = stages[i](x) + if self._depth >= 5: + x = self.block12(x) + x = self.conv3(x) + x = self.bn3(x) + x = self.relu3(x) + x = self.conv4(x) + x = self.bn4(x) features.append(x) return features @@ -71,7 +90,12 @@ def load_state_dict(self, state_dict): xception_encoders = { "xception": { "encoder": XceptionEncoder, - "pretrained_settings": pretrained_settings["xception"], - "params": {"out_channels": (3, 64, 128, 256, 728, 2048)}, + "pretrained_settings": { + "imagenet": { + "repo_id": "smp-hub/xception.imagenet", + "revision": "01cfaf27c11353b1f0c578e7e26d2c000ea91049", + }, + }, + "params": {"out_channels": [3, 64, 128, 256, 728, 2048]}, } } diff --git a/segmentation_models_pytorch/losses/dice.py b/segmentation_models_pytorch/losses/dice.py index d9283161..b8baae98 100644 --- a/segmentation_models_pytorch/losses/dice.py +++ b/segmentation_models_pytorch/losses/dice.py @@ -44,9 +44,9 @@ def __init__( super(DiceLoss, self).__init__() self.mode = mode if classes is not None: - assert ( - mode != BINARY_MODE - ), "Masking classes is not supported with mode=binary" + assert mode != BINARY_MODE, ( + "Masking classes is not supported with mode=binary" + ) classes = to_tensor(classes, dtype=torch.long) self.classes = classes diff --git a/segmentation_models_pytorch/losses/jaccard.py b/segmentation_models_pytorch/losses/jaccard.py index d6aba280..b250cacf 100644 --- a/segmentation_models_pytorch/losses/jaccard.py +++ b/segmentation_models_pytorch/losses/jaccard.py @@ -43,9 +43,9 @@ def __init__( self.mode = mode if classes is not None: - assert ( - mode != BINARY_MODE - ), "Masking classes is not supported with mode=binary" + assert mode != BINARY_MODE, ( + "Masking classes is not supported with mode=binary" + ) classes = to_tensor(classes, dtype=torch.long) self.classes = classes diff --git a/tests/base/test_modules.py b/tests/base/test_modules.py new file mode 100644 index 00000000..5afa8e4f --- /dev/null +++ b/tests/base/test_modules.py @@ -0,0 +1,64 @@ +import pytest +from torch import nn +from segmentation_models_pytorch.base.modules import Conv2dReLU + + +def test_conv2drelu_batchnorm(): + module = Conv2dReLU(3, 16, kernel_size=3, padding=1, use_norm="batchnorm") + + assert isinstance(module[0], nn.Conv2d) + assert isinstance(module[1], nn.BatchNorm2d) + assert isinstance(module[2], nn.ReLU) + + +def test_conv2drelu_batchnorm_with_keywords(): + module = Conv2dReLU( + 3, + 16, + kernel_size=3, + padding=1, + use_norm={"type": "batchnorm", "momentum": 1e-4, "affine": False}, + ) + + assert isinstance(module[0], nn.Conv2d) + assert isinstance(module[1], nn.BatchNorm2d) + assert module[1].momentum == 1e-4 and module[1].affine is False + assert isinstance(module[2], nn.ReLU) + + +def test_conv2drelu_identity(): + module = Conv2dReLU(3, 16, kernel_size=3, padding=1, use_norm="identity") + + assert isinstance(module[0], nn.Conv2d) + assert isinstance(module[1], nn.Identity) + assert isinstance(module[2], nn.ReLU) + + +def test_conv2drelu_layernorm(): + module = Conv2dReLU(3, 16, kernel_size=3, padding=1, use_norm="layernorm") + + assert isinstance(module[0], nn.Conv2d) + assert isinstance(module[1], nn.LayerNorm) + assert isinstance(module[2], nn.ReLU) + + +def test_conv2drelu_instancenorm(): + module = Conv2dReLU(3, 16, kernel_size=3, padding=1, use_norm="instancenorm") + + assert isinstance(module[0], nn.Conv2d) + assert isinstance(module[1], nn.InstanceNorm2d) + assert isinstance(module[2], nn.ReLU) + + +def test_conv2drelu_inplace(): + try: + from inplace_abn import InPlaceABN + except ImportError: + pytest.skip("InPlaceABN is not installed") + + module = Conv2dReLU(3, 16, kernel_size=3, padding=1, use_norm="inplace") + + assert len(module) == 3 + assert isinstance(module[0], nn.Conv2d) + assert isinstance(module[1], InPlaceABN) + assert isinstance(module[2], nn.Identity) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..688fd00b --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,16 @@ +def pytest_addoption(parser): + parser.addoption( + "--non-marked-only", action="store_true", help="Run only non-marked tests" + ) + + +def pytest_collection_modifyitems(config, items): + if config.getoption("--non-marked-only"): + non_marked_items = [] + for item in items: + # Check if the test has no marks + if not item.own_markers: + non_marked_items.append(item) + + # Update the test collection to only include non-marked tests + items[:] = non_marked_items diff --git a/tests/encoders/base.py b/tests/encoders/base.py index 39cd4164..b18be2a9 100644 --- a/tests/encoders/base.py +++ b/tests/encoders/base.py @@ -1,14 +1,22 @@ +import pytest import unittest import torch import segmentation_models_pytorch as smp from functools import lru_cache -from tests.utils import default_device +from tests.utils import ( + default_device, + check_run_test_on_diff_or_main, + requires_torch_greater_or_equal, +) class BaseEncoderTester(unittest.TestCase): encoder_names = [] + # some tests might be slow, running them only on diff + files_for_diff = [] + # standard encoder configuration num_output_features = 6 output_strides = [1, 2, 4, 8, 16, 32] @@ -25,8 +33,15 @@ class BaseEncoderTester(unittest.TestCase): depth_to_test = [3, 4, 5] strides_to_test = [8, 16] # 32 is a default one + def get_tiny_encoder(self): + return smp.encoders.get_encoder(self.encoder_names[0], encoder_weights=None) + @lru_cache - def _get_sample(self, batch_size=1, num_channels=3, height=32, width=32): + def _get_sample(self, batch_size=None, num_channels=None, height=None, width=None): + batch_size = batch_size or self.default_batch_size + num_channels = num_channels or self.default_num_channels + height = height or self.default_height + width = width or self.default_width return torch.rand(batch_size, num_channels, height, width) def get_features_output_strides(self, sample, features): @@ -36,12 +51,7 @@ def get_features_output_strides(self, sample, features): return height_strides, width_strides def test_forward_backward(self): - sample = self._get_sample( - batch_size=self.default_batch_size, - num_channels=self.default_num_channels, - height=self.default_height, - width=self.default_width, - ).to(default_device) + sample = self._get_sample().to(default_device) for encoder_name in self.encoder_names: with self.subTest(encoder_name=encoder_name): # init encoder @@ -68,12 +78,7 @@ def test_in_channels(self): ] for encoder_name, in_channels in cases: - sample = self._get_sample( - batch_size=self.default_batch_size, - num_channels=in_channels, - height=self.default_height, - width=self.default_width, - ).to(default_device) + sample = self._get_sample(num_channels=in_channels).to(default_device) with self.subTest(encoder_name=encoder_name, in_channels=in_channels): encoder = smp.encoders.get_encoder( @@ -82,16 +87,11 @@ def test_in_channels(self): encoder.eval() # forward - with torch.no_grad(): + with torch.inference_mode(): encoder.forward(sample) def test_depth(self): - sample = self._get_sample( - batch_size=self.default_batch_size, - num_channels=self.default_num_channels, - height=self.default_height, - width=self.default_width, - ).to(default_device) + sample = self._get_sample().to(default_device) cases = [ (encoder_name, depth) @@ -110,7 +110,7 @@ def test_depth(self): encoder.eval() # forward - with torch.no_grad(): + with torch.inference_mode(): features = encoder.forward(sample) # check number of features @@ -127,12 +127,12 @@ def test_depth(self): self.assertEqual( height_strides, self.output_strides[: depth + 1], - f"Encoder `{encoder_name}` should have output strides {self.output_strides[:depth + 1]}, but has {height_strides}", + f"Encoder `{encoder_name}` should have output strides {self.output_strides[: depth + 1]}, but has {height_strides}", ) self.assertEqual( width_strides, self.output_strides[: depth + 1], - f"Encoder `{encoder_name}` should have output strides {self.output_strides[:depth + 1]}, but has {width_strides}", + f"Encoder `{encoder_name}` should have output strides {self.output_strides[: depth + 1]}, but has {width_strides}", ) # check encoder output stride property @@ -149,13 +149,14 @@ def test_depth(self): f"Encoder `{encoder_name}` should have {depth + 1} out_channels, but has {len(encoder.out_channels)}", ) + def test_invalid_depth(self): + with self.assertRaises(ValueError): + smp.encoders.get_encoder(self.encoder_names[0], depth=6) + with self.assertRaises(ValueError): + smp.encoders.get_encoder(self.encoder_names[0], depth=0) + def test_dilated(self): - sample = self._get_sample( - batch_size=self.default_batch_size, - num_channels=self.default_num_channels, - height=self.default_height, - width=self.default_width, - ).to(default_device) + sample = self._get_sample().to(default_device) cases = [ (encoder_name, stride) @@ -187,7 +188,7 @@ def test_dilated(self): encoder.eval() # forward - with torch.no_grad(): + with torch.inference_mode(): features = encoder.forward(sample) height_strides, width_strides = self.get_features_output_strides( @@ -206,3 +207,78 @@ def test_dilated(self): expected_width_strides, f"Encoder `{encoder_name}` should have width output strides {expected_width_strides}, but has {width_strides}", ) + + @pytest.mark.compile + def test_compile(self): + if not check_run_test_on_diff_or_main(self.files_for_diff): + self.skipTest("No diff and not on `main`.") + + sample = self._get_sample().to(default_device) + + encoder = self.get_tiny_encoder() + encoder = encoder.eval().to(default_device) + + torch.compiler.reset() + compiled_encoder = torch.compile( + encoder, fullgraph=True, dynamic=True, backend="eager" + ) + + if encoder._is_torch_compilable: + compiled_encoder(sample) + else: + with self.assertRaises(Exception): + compiled_encoder(sample) + + @pytest.mark.torch_export + @requires_torch_greater_or_equal("2.4.0") + def test_torch_export(self): + if not check_run_test_on_diff_or_main(self.files_for_diff): + self.skipTest("No diff and not on `main`.") + + sample = self._get_sample().to(default_device) + + encoder = self.get_tiny_encoder() + encoder = encoder.eval().to(default_device) + + if not encoder._is_torch_exportable: + with self.assertRaises(Exception): + exported_encoder = torch.export.export( + encoder, + args=(sample,), + strict=True, + ) + return + + exported_encoder = torch.export.export( + encoder, + args=(sample,), + strict=True, + ) + + with torch.inference_mode(): + eager_output = encoder(sample) + exported_output = exported_encoder.module().forward(sample) + + for eager_feature, exported_feature in zip(eager_output, exported_output): + torch.testing.assert_close(eager_feature, exported_feature) + + @pytest.mark.torch_script + def test_torch_script(self): + sample = self._get_sample().to(default_device) + + encoder = self.get_tiny_encoder() + encoder = encoder.eval().to(default_device) + + if not encoder._is_torch_scriptable: + with self.assertRaises(RuntimeError, msg="not torch scriptable"): + scripted_encoder = torch.jit.script(encoder) + return + + scripted_encoder = torch.jit.script(encoder) + + with torch.inference_mode(): + eager_output = encoder(sample) + scripted_output = scripted_encoder(sample) + + for eager_feature, scripted_feature in zip(eager_output, scripted_output): + torch.testing.assert_close(eager_feature, scripted_feature) diff --git a/tests/encoders/test_batchnorm_deprecation.py b/tests/encoders/test_batchnorm_deprecation.py new file mode 100644 index 00000000..ff53563f --- /dev/null +++ b/tests/encoders/test_batchnorm_deprecation.py @@ -0,0 +1,54 @@ +import pytest + +import torch + +import segmentation_models_pytorch as smp +from tests.utils import check_two_models_strictly_equal + + +@pytest.mark.parametrize("model_name", ["unet", "unetplusplus", "linknet", "manet"]) +@pytest.mark.parametrize("decoder_option", [True, False, "inplace"]) +def test_seg_models_before_after_use_norm(model_name, decoder_option): + torch.manual_seed(42) + with pytest.warns(DeprecationWarning): + model_decoder_batchnorm = smp.create_model( + model_name, + "mobilenet_v2", + encoder_weights=None, + decoder_use_batchnorm=decoder_option, + ) + model_decoder_norm = smp.create_model( + model_name, + "mobilenet_v2", + encoder_weights=None, + decoder_use_norm=decoder_option, + ) + + model_decoder_norm.load_state_dict(model_decoder_batchnorm.state_dict()) + + check_two_models_strictly_equal( + model_decoder_batchnorm, model_decoder_norm, torch.rand(1, 3, 224, 224) + ) + + +@pytest.mark.parametrize("decoder_option", [True, False, "inplace"]) +def test_pspnet_before_after_use_norm(decoder_option): + torch.manual_seed(42) + with pytest.warns(DeprecationWarning): + model_decoder_batchnorm = smp.create_model( + "pspnet", + "mobilenet_v2", + encoder_weights=None, + psp_use_batchnorm=decoder_option, + ) + model_decoder_norm = smp.create_model( + "pspnet", + "mobilenet_v2", + encoder_weights=None, + decoder_use_norm=decoder_option, + ) + model_decoder_norm.load_state_dict(model_decoder_batchnorm.state_dict()) + + check_two_models_strictly_equal( + model_decoder_batchnorm, model_decoder_norm, torch.rand(1, 3, 224, 224) + ) diff --git a/tests/encoders/test_common.py b/tests/encoders/test_common.py new file mode 100644 index 00000000..f94fd303 --- /dev/null +++ b/tests/encoders/test_common.py @@ -0,0 +1,15 @@ +import pytest +import segmentation_models_pytorch as smp +from tests.utils import slow_test + + +@pytest.mark.parametrize( + "encoder_name_and_weights", + [ + ("resnet18", "imagenet"), + ], +) +@slow_test +def test_load_encoder_from_hub(encoder_name_and_weights): + encoder_name, weights = encoder_name_and_weights + smp.encoders.get_encoder(encoder_name, weights=weights) diff --git a/tests/encoders/test_pretrainedmodels_encoders.py b/tests/encoders/test_pretrainedmodels_encoders.py index bbde576c..2dcc7a52 100644 --- a/tests/encoders/test_pretrainedmodels_encoders.py +++ b/tests/encoders/test_pretrainedmodels_encoders.py @@ -1,54 +1,45 @@ +import segmentation_models_pytorch as smp + from tests.encoders import base from tests.utils import RUN_ALL_ENCODERS -class TestDenseNetEncoder(base.BaseEncoderTester): - supports_dilated = False - encoder_names = ( - ["densenet121"] - if not RUN_ALL_ENCODERS - else ["densenet121", "densenet169", "densenet161"] - ) - - class TestDPNEncoder(base.BaseEncoderTester): encoder_names = ( ["dpn68"] if not RUN_ALL_ENCODERS else ["dpn68", "dpn68b", "dpn92", "dpn98", "dpn107", "dpn131"] ) + files_for_diff = ["encoders/dpn.py"] + + def get_tiny_encoder(self): + params = { + "stage_idxs": [2, 3, 4, 6], + "out_channels": [3, 2, 70, 134, 262, 518], + "groups": 2, + "inc_sec": (2, 2, 2, 2), + "k_r": 2, + "k_sec": (1, 1, 1, 1), + "num_classes": 1000, + "num_init_features": 2, + "small": True, + "test_time_pool": True, + } + return smp.encoders.dpn.DPNEncoder(**params) class TestInceptionResNetV2Encoder(base.BaseEncoderTester): - supports_dilated = False encoder_names = ( ["inceptionresnetv2"] if not RUN_ALL_ENCODERS else ["inceptionresnetv2"] ) + files_for_diff = ["encoders/inceptionresnetv2.py"] + supports_dilated = False class TestInceptionV4Encoder(base.BaseEncoderTester): - supports_dilated = False encoder_names = ["inceptionv4"] if not RUN_ALL_ENCODERS else ["inceptionv4"] - - -class TestResNetEncoder(base.BaseEncoderTester): - encoder_names = ( - ["resnet18"] - if not RUN_ALL_ENCODERS - else [ - "resnet18", - "resnet34", - "resnet50", - "resnet101", - "resnet152", - "resnext50_32x4d", - "resnext101_32x4d", - "resnext101_32x8d", - "resnext101_32x16d", - "resnext101_32x32d", - "resnext101_32x48d", - ] - ) + files_for_diff = ["encoders/inceptionv4.py"] + supports_dilated = False class TestSeNetEncoder(base.BaseEncoderTester): @@ -64,8 +55,26 @@ class TestSeNetEncoder(base.BaseEncoderTester): # "senet154", # extra large model ] ) + files_for_diff = ["encoders/senet.py"] + + def get_tiny_encoder(self): + params = { + "out_channels": [3, 2, 256, 512, 1024, 2048], + "block": smp.encoders.senet.SEResNetBottleneck, + "layers": [1, 1, 1, 1], + "downsample_kernel_size": 1, + "downsample_padding": 0, + "dropout_p": None, + "groups": 1, + "inplanes": 2, + "input_3x3": False, + "num_classes": 1000, + "reduction": 2, + } + return smp.encoders.senet.SENetEncoder(**params) class TestXceptionEncoder(base.BaseEncoderTester): supports_dilated = False encoder_names = ["xception"] if not RUN_ALL_ENCODERS else ["xception"] + files_for_diff = ["encoders/xception.py"] diff --git a/tests/encoders/test_smp_encoders.py b/tests/encoders/test_smp_encoders.py index 863537bf..29e2f416 100644 --- a/tests/encoders/test_smp_encoders.py +++ b/tests/encoders/test_smp_encoders.py @@ -1,3 +1,6 @@ +import segmentation_models_pytorch as smp +from functools import partial + from tests.encoders import base from tests.utils import RUN_ALL_ENCODERS @@ -14,6 +17,7 @@ class TestMobileoneEncoder(base.BaseEncoderTester): "mobileone_s4", ] ) + files_for_diff = ["encoders/mobileone.py"] class TestMixTransformerEncoder(base.BaseEncoderTester): @@ -22,6 +26,24 @@ class TestMixTransformerEncoder(base.BaseEncoderTester): if not RUN_ALL_ENCODERS else ["mit_b0", "mit_b1", "mit_b2", "mit_b3", "mit_b4", "mit_b5"] ) + files_for_diff = ["encoders/mix_transformer.py"] + + def get_tiny_encoder(self): + params = { + "out_channels": [3, 0, 4, 4, 4, 4], + "patch_size": 4, + "embed_dims": [4, 4, 4, 4], + "num_heads": [1, 1, 1, 1], + "mlp_ratios": [1, 1, 1, 1], + "qkv_bias": True, + "norm_layer": partial(smp.encoders.mix_transformer.LayerNorm, eps=1e-6), + "depths": [1, 1, 1, 1], + "sr_ratios": [8, 4, 2, 1], + "drop_rate": 0.0, + "drop_path_rate": 0.1, + } + + return smp.encoders.mix_transformer.MixVisionTransformerEncoder(**params) class TestEfficientNetEncoder(base.BaseEncoderTester): @@ -39,3 +61,4 @@ class TestEfficientNetEncoder(base.BaseEncoderTester): # "efficientnet-b7", # extra large model ] ) + files_for_diff = ["encoders/efficientnet.py"] diff --git a/tests/encoders/test_timm_ported_encoders.py b/tests/encoders/test_timm_ported_encoders.py index b467c968..3793606e 100644 --- a/tests/encoders/test_timm_ported_encoders.py +++ b/tests/encoders/test_timm_ported_encoders.py @@ -24,6 +24,7 @@ class TestTimmEfficientNetEncoder(base.BaseEncoderTester): "timm-tf_efficientnet_lite4", ] ) + files_for_diff = ["encoders/timm_efficientnet.py"] class TestTimmGERNetEncoder(base.BaseEncoderTester): @@ -33,6 +34,9 @@ class TestTimmGERNetEncoder(base.BaseEncoderTester): else ["timm-gernet_s", "timm-gernet_m", "timm-gernet_l"] ) + def test_compile(self): + self.skipTest("Test to be removed") + class TestTimmMobileNetV3Encoder(base.BaseEncoderTester): encoder_names = ( @@ -48,6 +52,9 @@ class TestTimmMobileNetV3Encoder(base.BaseEncoderTester): ] ) + def test_compile(self): + self.skipTest("Test to be removed") + class TestTimmRegNetEncoder(base.BaseEncoderTester): encoder_names = ( @@ -81,9 +88,11 @@ class TestTimmRegNetEncoder(base.BaseEncoderTester): ] ) + def test_compile(self): + self.skipTest("Test to be removed") + class TestTimmRes2NetEncoder(base.BaseEncoderTester): - supports_dilated = False encoder_names = ( ["timm-res2net50_26w_4s"] if not RUN_ALL_ENCODERS @@ -98,10 +107,12 @@ class TestTimmRes2NetEncoder(base.BaseEncoderTester): ] ) + def test_compile(self): + self.skipTest("Test to be removed") + class TestTimmResnestEncoder(base.BaseEncoderTester): default_batch_size = 2 - supports_dilated = False encoder_names = ( ["timm-resnest14d"] if not RUN_ALL_ENCODERS @@ -117,6 +128,9 @@ class TestTimmResnestEncoder(base.BaseEncoderTester): ] ) + def test_compile(self): + self.skipTest("Test to be removed") + class TestTimmSkNetEncoder(base.BaseEncoderTester): default_batch_size = 2 @@ -129,3 +143,4 @@ class TestTimmSkNetEncoder(base.BaseEncoderTester): "timm-skresnext50_32x4d", ] ) + files_for_diff = ["encoders/timm_sknet.py"] diff --git a/tests/encoders/test_timm_universal.py b/tests/encoders/test_timm_universal.py index 753ee4de..99f8990f 100644 --- a/tests/encoders/test_timm_universal.py +++ b/tests/encoders/test_timm_universal.py @@ -9,8 +9,9 @@ ] if has_timm_test_models: - timm_encoders.append("tu-test_resnet.r160_in1k") + timm_encoders.insert(0, "tu-test_resnet.r160_in1k") class TestTimmUniversalEncoder(base.BaseEncoderTester): encoder_names = timm_encoders + files_for_diff = ["encoders/timm_universal.py"] diff --git a/tests/encoders/test_timm_vit_encoders.py b/tests/encoders/test_timm_vit_encoders.py new file mode 100644 index 00000000..260d926f --- /dev/null +++ b/tests/encoders/test_timm_vit_encoders.py @@ -0,0 +1,236 @@ +import timm +import torch +import pytest + +from segmentation_models_pytorch.encoders import TimmViTEncoder +from segmentation_models_pytorch.encoders.timm_vit import sample_block_indices_uniformly + +from tests.encoders import base +from tests.utils import ( + default_device, + check_run_test_on_diff_or_main, + requires_torch_greater_or_equal, + requires_timm_greater_or_equal, +) + +timm_vit_encoders = ["vit_tiny_patch16_224"] + + +@requires_timm_greater_or_equal("1.0.0") +class TestTimmViTEncoders(base.BaseEncoderTester): + encoder_names = timm_vit_encoders + tiny_encoder_patch_size = 224 + default_height = 224 + default_width = 224 + + files_for_diff = ["encoders/dpt.py"] + + num_output_features = 4 + default_depth = 4 + output_strides = None + supports_dilated = False + + depth_to_test = [2, 3, 4] + + def get_tiny_encoder(self) -> TimmViTEncoder: + return TimmViTEncoder( + name=self.encoder_names[0], + pretrained=False, + depth=self.default_depth, + in_channels=3, + ) + + def get_encoder(self, encoder_name: str, **kwargs) -> TimmViTEncoder: + default_kwargs = { + "name": encoder_name, + "pretrained": False, + "depth": self.default_depth, + "in_channels": 3, + } + default_kwargs.update(kwargs) + return TimmViTEncoder(**default_kwargs) + + def test_forward_backward(self): + for encoder_name in self.encoder_names: + sample = self._get_sample().to(default_device) + with self.subTest(encoder_name=encoder_name): + # init encoder + encoder = self.get_encoder(encoder_name).to(default_device) + + # forward + features, prefix_tokens = encoder.forward(sample) + self.assertEqual( + len(features), + self.num_output_features, + f"Encoder `{encoder_name}` should have {self.num_output_features} output feature maps, but has {len(features)}", + ) + if encoder.has_prefix_tokens: + self.assertEqual( + len(prefix_tokens), + self.num_output_features, + f"Encoder `{encoder_name}` should have {self.num_output_features} prefix tokens, but has {len(prefix_tokens)}", + ) + + # backward + features[-1].mean().backward() + + def test_in_channels(self): + cases = [ + (encoder_name, in_channels) + for encoder_name in self.encoder_names + for in_channels in self.in_channels_to_test + ] + + for encoder_name, in_channels in cases: + sample = self._get_sample(num_channels=in_channels).to(default_device) + + with self.subTest(encoder_name=encoder_name, in_channels=in_channels): + encoder = self.get_encoder(encoder_name, in_channels=in_channels).to( + default_device + ) + encoder.eval() + + # forward + with torch.inference_mode(): + encoder.forward(sample) + + def test_depth(self): + cases = [ + (encoder_name, depth) + for encoder_name in self.encoder_names + for depth in self.depth_to_test + ] + + for encoder_name, depth in cases: + sample = self._get_sample().to(default_device) + with self.subTest(encoder_name=encoder_name, depth=depth): + encoder = self.get_encoder(encoder_name, depth=depth).to(default_device) + encoder.eval() + + # forward + with torch.inference_mode(): + features, _ = encoder.forward(sample) + + # check number of features + self.assertEqual( + len(features), + depth, + f"Encoder `{encoder_name}` should have {depth} output feature maps, but has {len(features)}", + ) + + # check feature strides + height_strides, width_strides = self.get_features_output_strides( + sample, features + ) + + encoder_out_indices = sample_block_indices_uniformly(depth, 12) + feature_info = timm.create_model(model_name=encoder_name).feature_info + output_strides = [ + feature_info[i]["reduction"] for i in encoder_out_indices + ] + + self.assertEqual( + height_strides, + output_strides, + f"Encoder `{encoder_name}` should have output strides {output_strides}, but has {height_strides}", + ) + self.assertEqual( + width_strides, + output_strides, + f"Encoder `{encoder_name}` should have output strides {output_strides}, but has {width_strides}", + ) + + # check encoder output stride property + self.assertEqual( + encoder.output_strides, + output_strides, + f"Encoder `{encoder_name}` last feature map should have output stride {output_strides[depth - 1]}, but has {encoder.output_stride}", + ) + + # check out channels also have proper length + self.assertEqual( + len(encoder.out_channels), + depth, + f"Encoder `{encoder_name}` should have {depth} out_channels, but has {len(encoder.out_channels)}", + ) + + def test_invalid_depth(self): + with self.assertRaises(ValueError): + self.get_encoder(self.encoder_names[0], depth=0) + with self.assertRaises(ValueError): + self.get_encoder(self.encoder_names[0], depth=25) + + def test_invalid_out_indices(self): + # out of range + with self.assertRaises(ValueError): + self.get_encoder(self.encoder_names[0], depth=1, output_indices=-25) + with self.assertRaises(ValueError): + self.get_encoder(self.encoder_names[0], depth=3, output_indices=[1, 2, 25]) + + # invalid length + with self.assertRaises(ValueError): + self.get_encoder( + self.encoder_names[0], + depth=2, + output_indices=[ + 2, + ], + ) + + def test_dilated(self): + pytest.skip("Dilation is not supported for ViT encoders") + + @pytest.mark.compile + def test_compile(self): + if not check_run_test_on_diff_or_main(self.files_for_diff): + self.skipTest("No diff and not on `main`.") + + encoder = self.get_tiny_encoder() + encoder = encoder.eval().to(default_device) + + sample = self._get_sample( + height=self.tiny_encoder_patch_size, width=self.tiny_encoder_patch_size + ).to(default_device) + + torch.compiler.reset() + compiled_encoder = torch.compile( + encoder, fullgraph=True, dynamic=True, backend="eager" + ) + + if encoder._is_torch_compilable: + compiled_encoder(sample) + else: + with self.assertRaises(Exception): + compiled_encoder(sample) + + @pytest.mark.torch_export + @requires_torch_greater_or_equal("2.4.0") + def test_torch_export(self): + if not check_run_test_on_diff_or_main(self.files_for_diff): + self.skipTest("No diff and not on `main`.") + + sample = self._get_sample( + height=self.tiny_encoder_patch_size, width=self.tiny_encoder_patch_size + ).to(default_device) + + encoder = self.get_tiny_encoder() + encoder = encoder.eval().to(default_device) + + exported_encoder = torch.export.export( + encoder, + args=(sample,), + strict=True, + ) + + with torch.inference_mode(): + eager_output = encoder(sample) + exported_output = exported_encoder.module().forward(sample) + + for eager_feature, exported_feature in zip(eager_output, exported_output): + torch.testing.assert_close(eager_feature, exported_feature) + + @pytest.mark.torch_script + def test_torch_script(self): + pytest.skip( + "Encoder with prefix tokens are not supported for scripting, due to poor type handling" + ) diff --git a/tests/encoders/test_torchvision_encoders.py b/tests/encoders/test_torchvision_encoders.py index 99b8b9d5..c0d7c64f 100644 --- a/tests/encoders/test_torchvision_encoders.py +++ b/tests/encoders/test_torchvision_encoders.py @@ -1,9 +1,60 @@ +import segmentation_models_pytorch as smp + from tests.encoders import base from tests.utils import RUN_ALL_ENCODERS -class TestMobileoneEncoder(base.BaseEncoderTester): +class TestResNetEncoder(base.BaseEncoderTester): + encoder_names = ( + ["resnet18"] + if not RUN_ALL_ENCODERS + else [ + "resnet18", + "resnet34", + "resnet50", + "resnet101", + "resnet152", + "resnext50_32x4d", + "resnext101_32x4d", + "resnext101_32x8d", + "resnext101_32x16d", + "resnext101_32x32d", + "resnext101_32x48d", + ] + ) + files_for_diff = ["encoders/resnet.py"] + + def get_tiny_encoder(self): + params = { + "out_channels": [3, 64, 64, 128, 256, 512], + "block": smp.encoders.resnet.BasicBlock, + "layers": [1, 1, 1, 1], + } + return smp.encoders.resnet.ResNetEncoder(**params) + + +class TestDenseNetEncoder(base.BaseEncoderTester): + supports_dilated = False + encoder_names = ( + ["densenet121"] + if not RUN_ALL_ENCODERS + else ["densenet121", "densenet169", "densenet161"] + ) + files_for_diff = ["encoders/densenet.py"] + + def get_tiny_encoder(self): + params = { + "out_channels": [3, 2, 3, 2, 2, 2], + "num_init_features": 2, + "growth_rate": 1, + "block_config": (1, 1, 1, 1), + } + return smp.encoders.densenet.DenseNetEncoder(**params) + + +class TestMobileNetEncoder(base.BaseEncoderTester): encoder_names = ["mobilenet_v2"] if not RUN_ALL_ENCODERS else ["mobilenet_v2"] + files_for_diff = ["encoders/mobilenet.py"] class TestVggEncoder(base.BaseEncoderTester): @@ -22,3 +73,12 @@ class TestVggEncoder(base.BaseEncoderTester): "vgg19_bn", ] ) + files_for_diff = ["encoders/vgg.py"] + + def get_tiny_encoder(self): + params = { + "out_channels": [4, 4, 4, 4, 4, 4], + "config": [4, "M", 4, "M", 4, "M", 4, "M", 4, "M"], + "batch_norm": False, + } + return smp.encoders.vgg.VGGEncoder(**params) diff --git a/tests/models/base.py b/tests/models/base.py index 02e17303..2f317348 100644 --- a/tests/models/base.py +++ b/tests/models/base.py @@ -14,6 +14,7 @@ default_device, slow_test, requires_torch_greater_or_equal, + check_run_test_on_diff_or_main, ) @@ -21,6 +22,7 @@ class BaseModelTester(unittest.TestCase): test_encoder_name = ( "tu-test_resnet.r160_in1k" if has_timm_test_models else "resnet18" ) + files_for_diff = [r".*"] # should be overriden test_model_type = None @@ -31,6 +33,8 @@ class BaseModelTester(unittest.TestCase): default_height = 64 default_width = 64 + compile_dynamic = True + @property def model_type(self): if self.test_model_type is None: @@ -54,19 +58,23 @@ def decoder_channels(self): return None @lru_cache - def _get_sample(self, batch_size=1, num_channels=3, height=32, width=32): + def _get_sample(self, batch_size=None, num_channels=None, height=None, width=None): + batch_size = batch_size or self.default_batch_size + num_channels = num_channels or self.default_num_channels + height = height or self.default_height + width = width or self.default_width return torch.rand(batch_size, num_channels, height, width) + @lru_cache + def get_default_model(self): + model = smp.create_model(self.model_type, self.test_encoder_name) + model = model.to(default_device) + return model + def test_forward_backward(self): - sample = self._get_sample( - batch_size=self.default_batch_size, - num_channels=self.default_num_channels, - height=self.default_height, - width=self.default_width, - ).to(default_device) - model = smp.create_model( - arch=self.model_type, encoder_name=self.test_encoder_name - ).to(default_device) + sample = self._get_sample().to(default_device) + + model = self.get_default_model() # check default in_channels=3 output = model(sample) @@ -91,23 +99,26 @@ def test_in_channels_and_depth_and_out_classes( if self.model_type in ["unet", "unetplusplus", "manet"]: kwargs = {"decoder_channels": self.decoder_channels[:depth]} - model = smp.create_model( - arch=self.model_type, - encoder_name=self.test_encoder_name, - encoder_depth=depth, - in_channels=in_channels, - classes=classes, - **kwargs, - ).to(default_device) - sample = self._get_sample( - batch_size=self.default_batch_size, - num_channels=in_channels, - height=self.default_height, - width=self.default_width, - ).to(default_device) + if self.model_type == "dpt": + kwargs = {"decoder_intermediate_channels": self.decoder_channels[:depth]} + + model = ( + smp.create_model( + arch=self.model_type, + encoder_name=self.test_encoder_name, + encoder_depth=depth, + in_channels=in_channels, + classes=classes, + **kwargs, + ) + .to(default_device) + .eval() + ) + + sample = self._get_sample(num_channels=in_channels).to(default_device) # check in channels correctly set - with torch.no_grad(): + with torch.inference_mode(): output = model(sample) self.assertEqual(output.shape[1], classes) @@ -122,7 +133,8 @@ def test_classification_head(self): "dropout": 0.5, "activation": "sigmoid", }, - ).to(default_device) + ) + model = model.to(default_device).eval() self.assertIsNotNone(model.classification_head) self.assertIsInstance(model.classification_head[0], torch.nn.AdaptiveAvgPool2d) @@ -132,24 +144,37 @@ def test_classification_head(self): self.assertIsInstance(model.classification_head[3], torch.nn.Linear) self.assertIsInstance(model.classification_head[4].activation, torch.nn.Sigmoid) - sample = self._get_sample( - batch_size=self.default_batch_size, - num_channels=self.default_num_channels, - height=self.default_height, - width=self.default_width, - ).to(default_device) + sample = self._get_sample().to(default_device) - with torch.no_grad(): + with torch.inference_mode(): _, cls_probs = model(sample) self.assertEqual(cls_probs.shape[1], 10) + def test_any_resolution(self): + model = self.get_default_model() + + sample = self._get_sample( + height=self.default_height + 3, + width=self.default_width + 7, + ).to(default_device) + + if model.requires_divisible_input_shape: + with self.assertRaises(RuntimeError, msg="Wrong input shape"): + output = model(sample) + return + + with torch.inference_mode(): + output = model(sample) + + self.assertEqual(output.shape[2], self.default_height + 3) + self.assertEqual(output.shape[3], self.default_width + 7) + @requires_torch_greater_or_equal("2.0.1") def test_save_load_with_hub_mixin(self): # instantiate model - model = smp.create_model( - arch=self.model_type, encoder_name=self.test_encoder_name - ).to(default_device) + model = self.get_default_model() + model.eval() # save model with tempfile.TemporaryDirectory() as tmpdir: @@ -157,18 +182,15 @@ def test_save_load_with_hub_mixin(self): tmpdir, dataset="test_dataset", metrics={"my_awesome_metric": 0.99} ) restored_model = smp.from_pretrained(tmpdir).to(default_device) + restored_model.eval() + with open(os.path.join(tmpdir, "README.md"), "r") as f: readme = f.read() # check inference is correct - sample = self._get_sample( - batch_size=self.default_batch_size, - num_channels=self.default_num_channels, - height=self.default_height, - width=self.default_width, - ).to(default_device) + sample = self._get_sample().to(default_device) - with torch.no_grad(): + with torch.inference_mode(): output = model(sample) restored_output = restored_model(sample) @@ -197,10 +219,80 @@ def test_preserve_forward_output(self): output_tensor = torch.load(output_tensor_path, weights_only=True) output_tensor = output_tensor.to(default_device) - with torch.no_grad(): + with torch.inference_mode(): output = model(input_tensor) self.assertEqual(output.shape, output_tensor.shape) is_close = torch.allclose(output, output_tensor, atol=5e-2) max_diff = torch.max(torch.abs(output - output_tensor)) self.assertTrue(is_close, f"Max diff: {max_diff}") + + @pytest.mark.compile + def test_compile(self): + if not check_run_test_on_diff_or_main(self.files_for_diff): + self.skipTest("No diff and not on `main`.") + + sample = self._get_sample().to(default_device) + model = self.get_default_model() + model = model.eval().to(default_device) + + if not model._is_torch_compilable: + with self.assertRaises((RuntimeError)): + torch.compiler.reset() + compiled_model = torch.compile( + model, fullgraph=True, dynamic=self.compile_dynamic, backend="eager" + ) + return + + torch.compiler.reset() + compiled_model = torch.compile( + model, fullgraph=True, dynamic=self.compile_dynamic, backend="eager" + ) + with torch.inference_mode(): + compiled_model(sample) + + @pytest.mark.torch_export + def test_torch_export(self, eps=1e-5): + if not check_run_test_on_diff_or_main(self.files_for_diff): + self.skipTest("No diff and not on `main`.") + + torch.manual_seed(42) + sample = self._get_sample().to(default_device) + model = self.get_default_model() + model.eval() + + exported_model = torch.export.export( + model, + args=(sample,), + strict=True, + ) + + with torch.inference_mode(): + eager_output = model(sample) + exported_output = exported_model.module().forward(sample) + + self.assertEqual(eager_output.shape, exported_output.shape) + torch.testing.assert_close(eager_output, exported_output, rtol=eps, atol=eps) + + @pytest.mark.torch_script + def test_torch_script(self): + if not check_run_test_on_diff_or_main(self.files_for_diff): + self.skipTest("No diff and not on `main`.") + + sample = self._get_sample().to(default_device) + model = self.get_default_model() + model.eval() + + if not model._is_torch_scriptable: + with self.assertRaises(RuntimeError): + scripted_model = torch.jit.script(model) + return + + scripted_model = torch.jit.script(model) + + with torch.inference_mode(): + scripted_output = scripted_model(sample) + eager_output = model(sample) + + self.assertEqual(scripted_output.shape, eager_output.shape) + torch.testing.assert_close(scripted_output, eager_output, rtol=1e-3, atol=1e-3) diff --git a/tests/models/test_deeplab.py b/tests/models/test_deeplab.py index d3d350e9..de112633 100644 --- a/tests/models/test_deeplab.py +++ b/tests/models/test_deeplab.py @@ -1,16 +1,15 @@ -import pytest from tests.models import base -@pytest.mark.deeplabv3 class TestDeeplabV3Model(base.BaseModelTester): test_model_type = "deeplabv3" + files_for_diff = [r"decoders/deeplabv3/", r"base/"] default_batch_size = 2 -@pytest.mark.deeplabv3plus class TestDeeplabV3PlusModel(base.BaseModelTester): test_model_type = "deeplabv3plus" + files_for_diff = [r"decoders/deeplabv3plus/", r"base/"] default_batch_size = 2 diff --git a/tests/models/test_dpt.py b/tests/models/test_dpt.py new file mode 100644 index 00000000..40df1e38 --- /dev/null +++ b/tests/models/test_dpt.py @@ -0,0 +1,60 @@ +import pytest +import inspect +import torch +import segmentation_models_pytorch as smp + +from tests.models import base +from tests.utils import ( + slow_test, + default_device, + requires_torch_greater_or_equal, +) + + +class TestDPTModel(base.BaseModelTester): + test_encoder_name = "tu-vit_tiny_patch16_224" + files_for_diff = [r"decoders/dpt/", r"base/"] + + default_height = 224 + default_width = 224 + + # should be overriden + test_model_type = "dpt" + + compile_dynamic = False + + @property + def decoder_channels(self): + signature = inspect.signature(self.model_class) + return signature.parameters["decoder_intermediate_channels"].default + + @property + def hub_checkpoint(self): + return "smp-test-models/dpt-tu-test_vit" + + @slow_test + @requires_torch_greater_or_equal("2.0.1") + @pytest.mark.logits_match + def test_load_pretrained(self): + hub_checkpoint = "smp-hub/dpt-large-ade20k" + + model = smp.from_pretrained(hub_checkpoint) + model = model.eval().to(default_device) + + input_tensor = torch.ones((1, 3, 384, 384)) + input_tensor = input_tensor.to(default_device) + + expected_logits_slice = torch.tensor( + [3.4166, 3.4422, 3.4677, 3.2784, 3.0880, 2.9497] + ) + with torch.inference_mode(): + output = model(input_tensor) + + resulted_logits_slice = output[0, 0, 0, 0:6].cpu() + + self.assertEqual(expected_logits_slice.shape, resulted_logits_slice.shape) + is_close = torch.allclose( + expected_logits_slice, resulted_logits_slice, atol=5e-2 + ) + max_diff = torch.max(torch.abs(expected_logits_slice - resulted_logits_slice)) + self.assertTrue(is_close, f"Max diff: {max_diff}") diff --git a/tests/models/test_fpn.py b/tests/models/test_fpn.py index 15ae1f6a..e0db74bc 100644 --- a/tests/models/test_fpn.py +++ b/tests/models/test_fpn.py @@ -1,7 +1,29 @@ -import pytest +import segmentation_models_pytorch as smp + from tests.models import base -@pytest.mark.fpn class TestFpnModel(base.BaseModelTester): test_model_type = "fpn" + files_for_diff = [r"decoders/fpn/", r"base/"] + + def test_interpolation(self): + # test bilinear + model_1 = smp.create_model( + self.test_model_type, + self.test_encoder_name, + decoder_interpolation="bilinear", + ) + assert model_1.decoder.p2.interpolation_mode == "bilinear" + assert model_1.decoder.p3.interpolation_mode == "bilinear" + assert model_1.decoder.p4.interpolation_mode == "bilinear" + + # test bicubic + model_2 = smp.create_model( + self.test_model_type, + self.test_encoder_name, + decoder_interpolation="bicubic", + ) + assert model_2.decoder.p2.interpolation_mode == "bicubic" + assert model_2.decoder.p3.interpolation_mode == "bicubic" + assert model_2.decoder.p4.interpolation_mode == "bicubic" diff --git a/tests/models/test_linknet.py b/tests/models/test_linknet.py index 1ab5eb4e..6f9490d9 100644 --- a/tests/models/test_linknet.py +++ b/tests/models/test_linknet.py @@ -1,7 +1,6 @@ -import pytest from tests.models import base -@pytest.mark.linknet class TestLinknetModel(base.BaseModelTester): test_model_type = "linknet" + files_for_diff = [r"decoders/linknet/", r"base/"] diff --git a/tests/models/test_manet.py b/tests/models/test_manet.py index 33a8ae3b..0e2dbf9b 100644 --- a/tests/models/test_manet.py +++ b/tests/models/test_manet.py @@ -1,7 +1,27 @@ -import pytest +import segmentation_models_pytorch as smp + from tests.models import base -@pytest.mark.manet class TestManetModel(base.BaseModelTester): test_model_type = "manet" + files_for_diff = [r"decoders/manet/", r"base/"] + + def test_interpolation(self): + # test bilinear + model_1 = smp.create_model( + self.test_model_type, + self.test_encoder_name, + decoder_interpolation="bilinear", + ) + for block in model_1.decoder.blocks: + assert block.interpolation_mode == "bilinear" + + # test bicubic + model_2 = smp.create_model( + self.test_model_type, + self.test_encoder_name, + decoder_interpolation="bicubic", + ) + for block in model_2.decoder.blocks: + assert block.interpolation_mode == "bicubic" diff --git a/tests/models/test_pan.py b/tests/models/test_pan.py index d66fefe0..8edb833a 100644 --- a/tests/models/test_pan.py +++ b/tests/models/test_pan.py @@ -1,11 +1,48 @@ import pytest +import segmentation_models_pytorch as smp + from tests.models import base -@pytest.mark.pan class TestPanModel(base.BaseModelTester): test_model_type = "pan" + files_for_diff = [r"decoders/pan/", r"base/"] default_batch_size = 2 default_height = 128 default_width = 128 + + def test_interpolation(self): + # test bilinear + model_1 = smp.create_model( + self.test_model_type, + self.test_encoder_name, + decoder_interpolation="bilinear", + ) + assert model_1.decoder.gau1.interpolation_mode == "bilinear" + assert model_1.decoder.gau1.align_corners is True + assert model_1.decoder.gau2.interpolation_mode == "bilinear" + assert model_1.decoder.gau2.align_corners is True + assert model_1.decoder.gau3.interpolation_mode == "bilinear" + assert model_1.decoder.gau3.align_corners is True + + # test bicubic + model_2 = smp.create_model( + self.test_model_type, + self.test_encoder_name, + decoder_interpolation="bicubic", + ) + assert model_2.decoder.gau1.interpolation_mode == "bicubic" + assert model_2.decoder.gau1.align_corners is None + assert model_2.decoder.gau2.interpolation_mode == "bicubic" + assert model_2.decoder.gau2.align_corners is None + assert model_2.decoder.gau3.interpolation_mode == "bicubic" + assert model_2.decoder.gau3.align_corners is None + + with pytest.warns(DeprecationWarning): + smp.create_model( + self.test_model_type, + self.test_encoder_name, + upscale_mode="bicubic", + ) + assert model_2.decoder.gau1.interpolation_mode == "bicubic" diff --git a/tests/models/test_psp.py b/tests/models/test_psp.py index 2603cdda..c29b5e99 100644 --- a/tests/models/test_psp.py +++ b/tests/models/test_psp.py @@ -1,9 +1,8 @@ -import pytest from tests.models import base -@pytest.mark.psp class TestPspModel(base.BaseModelTester): test_model_type = "pspnet" + files_for_diff = [r"decoders/pspnet/", r"base/"] default_batch_size = 2 diff --git a/tests/models/test_segformer.py b/tests/models/test_segformer.py index 3ca5016c..b0f288ef 100644 --- a/tests/models/test_segformer.py +++ b/tests/models/test_segformer.py @@ -6,9 +6,9 @@ from tests.utils import slow_test, default_device, requires_torch_greater_or_equal -@pytest.mark.segformer class TestSegformerModel(base.BaseModelTester): test_model_type = "segformer" + files_for_diff = [r"decoders/segformer/", r"base/"] @slow_test @requires_torch_greater_or_equal("2.0.1") @@ -21,7 +21,7 @@ def test_load_pretrained(self): sample = torch.ones([1, 3, 512, 512]).to(default_device) - with torch.no_grad(): + with torch.inference_mode(): output = model(sample) self.assertEqual(output.shape, (1, 150, 512, 512)) diff --git a/tests/models/test_unet.py b/tests/models/test_unet.py index 54c69bf0..98e37206 100644 --- a/tests/models/test_unet.py +++ b/tests/models/test_unet.py @@ -1,7 +1,26 @@ -import pytest +import segmentation_models_pytorch as smp from tests.models import base -@pytest.mark.unet class TestUnetModel(base.BaseModelTester): test_model_type = "unet" + files_for_diff = [r"decoders/unet/", r"base/"] + + def test_interpolation(self): + # test bilinear + model_1 = smp.create_model( + self.test_model_type, + self.test_encoder_name, + decoder_interpolation="bilinear", + ) + for block in model_1.decoder.blocks: + assert block.interpolation_mode == "bilinear" + + # test bicubic + model_2 = smp.create_model( + self.test_model_type, + self.test_encoder_name, + decoder_interpolation="bicubic", + ) + for block in model_2.decoder.blocks: + assert block.interpolation_mode == "bicubic" diff --git a/tests/models/test_unetplusplus.py b/tests/models/test_unetplusplus.py index 9e67f2ed..1d958ae3 100644 --- a/tests/models/test_unetplusplus.py +++ b/tests/models/test_unetplusplus.py @@ -1,7 +1,35 @@ -import pytest +import segmentation_models_pytorch as smp + from tests.models import base -@pytest.mark.unetplusplus class TestUnetPlusPlusModel(base.BaseModelTester): test_model_type = "unetplusplus" + files_for_diff = [r"decoders/unetplusplus/", r"base/"] + + def test_interpolation(self): + # test bilinear + model_1 = smp.create_model( + self.test_model_type, + self.test_encoder_name, + decoder_interpolation="bilinear", + ) + is_tested = False + for module in model_1.decoder.modules(): + if module.__class__.__name__ == "DecoderBlock": + assert module.interpolation_mode == "bilinear" + is_tested = True + assert is_tested + + # test bicubic + model_2 = smp.create_model( + self.test_model_type, + self.test_encoder_name, + decoder_interpolation="bicubic", + ) + is_tested = False + for module in model_2.decoder.modules(): + if module.__class__.__name__ == "DecoderBlock": + assert module.interpolation_mode == "bicubic" + is_tested = True + assert is_tested diff --git a/tests/models/test_upernet.py b/tests/models/test_upernet.py index 71d703f9..a69062ae 100644 --- a/tests/models/test_upernet.py +++ b/tests/models/test_upernet.py @@ -1,8 +1,14 @@ import pytest + from tests.models import base -@pytest.mark.upernet class TestUnetModel(base.BaseModelTester): test_model_type = "upernet" + files_for_diff = [r"decoders/upernet/", r"base/"] + default_batch_size = 2 + + @pytest.mark.torch_export + def test_torch_export(self): + super().test_torch_export(eps=1e-3) diff --git a/tests/test_base.py b/tests/test_base.py new file mode 100644 index 00000000..1078c493 --- /dev/null +++ b/tests/test_base.py @@ -0,0 +1,36 @@ +import torch +import tempfile +import segmentation_models_pytorch as smp + +import pytest + + +def test_from_pretrained_with_mismatched_keys(): + original_model = smp.Unet(classes=1) + + with tempfile.TemporaryDirectory() as temp_dir: + original_model.save_pretrained(temp_dir) + + # we should catch warning here and check if there specific keys there + with pytest.warns(UserWarning): + restored_model = smp.from_pretrained(temp_dir, classes=2, strict=False) + + assert restored_model.segmentation_head[0].out_channels == 2 + + # verify all the weight are the same expect mismatched ones + original_state_dict = original_model.state_dict() + restored_state_dict = restored_model.state_dict() + + expected_mismatched_keys = [ + "segmentation_head.0.weight", + "segmentation_head.0.bias", + ] + mismatched_keys = [] + for key in original_state_dict: + if key not in expected_mismatched_keys: + assert torch.allclose(original_state_dict[key], restored_state_dict[key]) + else: + mismatched_keys.append(key) + + assert len(mismatched_keys) == 2 + assert sorted(mismatched_keys) == sorted(expected_mismatched_keys) diff --git a/tests/test_losses.py b/tests/test_losses.py index 5c3ad75a..94d85d5c 100644 --- a/tests/test_losses.py +++ b/tests/test_losses.py @@ -93,7 +93,7 @@ def test_soft_tversky_score(y_true, y_pred, expected, eps, alpha, beta): assert float(actual) == pytest.approx(expected, eps) -@torch.no_grad() +@torch.inference_mode() def test_dice_loss_binary(): eps = 1e-5 criterion = DiceLoss(mode=smp.losses.BINARY_MODE, from_logits=False) @@ -131,7 +131,7 @@ def test_dice_loss_binary(): assert float(loss) == pytest.approx(1.0, abs=eps) -@torch.no_grad() +@torch.inference_mode() def test_tversky_loss_binary(): eps = 1e-5 # with alpha=0.5; beta=0.5 it is equal to DiceLoss @@ -172,7 +172,7 @@ def test_tversky_loss_binary(): assert float(loss) == pytest.approx(1.0, abs=eps) -@torch.no_grad() +@torch.inference_mode() def test_binary_jaccard_loss(): eps = 1e-5 criterion = JaccardLoss(mode=smp.losses.BINARY_MODE, from_logits=False) @@ -210,7 +210,7 @@ def test_binary_jaccard_loss(): assert float(loss) == pytest.approx(1.0, eps) -@torch.no_grad() +@torch.inference_mode() def test_multiclass_jaccard_loss(): eps = 1e-5 criterion = JaccardLoss(mode=smp.losses.MULTICLASS_MODE, from_logits=False) @@ -237,7 +237,7 @@ def test_multiclass_jaccard_loss(): assert float(loss) == pytest.approx(1.0 - 1.0 / 3.0, abs=eps) -@torch.no_grad() +@torch.inference_mode() def test_multilabel_jaccard_loss(): eps = 1e-5 criterion = JaccardLoss(mode=smp.losses.MULTILABEL_MODE, from_logits=False) @@ -263,7 +263,7 @@ def test_multilabel_jaccard_loss(): assert float(loss) == pytest.approx(1.0 - 1.0 / 3.0, abs=eps) -@torch.no_grad() +@torch.inference_mode() def test_soft_ce_loss(): criterion = SoftCrossEntropyLoss(smooth_factor=0.1, ignore_index=-100) @@ -276,7 +276,7 @@ def test_soft_ce_loss(): assert float(loss) == pytest.approx(1.0125, abs=0.0001) -@torch.no_grad() +@torch.inference_mode() def test_soft_bce_loss(): criterion = SoftBCEWithLogitsLoss(smooth_factor=0.1, ignore_index=-100) @@ -287,7 +287,7 @@ def test_soft_bce_loss(): assert float(loss) == pytest.approx(0.7201, abs=0.0001) -@torch.no_grad() +@torch.inference_mode() def test_binary_mcc_loss(): eps = 1e-5 criterion = MCCLoss(eps=eps) diff --git a/tests/utils.py b/tests/utils.py index e8bce88e..f9e50fc2 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,31 +1,21 @@ import os +import re import timm import torch import unittest +from git import Repo +from typing import List from packaging.version import Version has_timm_test_models = Version(timm.__version__) >= Version("1.0.12") default_device = "cuda" if torch.cuda.is_available() else "cpu" - -def get_commit_message(): - commit_msg = os.getenv("COMMIT_MESSAGE", "") - return commit_msg.lower() - - -# Check both environment variables and commit message -commit_message = get_commit_message() -RUN_ALL_ENCODERS = ( - os.getenv("RUN_ALL_ENCODERS", "false").lower() in ["true", "1", "y", "yes"] - or "run-all-encoders" in commit_message -) - -RUN_SLOW = ( - os.getenv("RUN_SLOW", "false").lower() in ["true", "1", "y", "yes"] - or "run-slow" in commit_message -) +YES_LIST = ["true", "1", "y", "yes"] +RUN_ALL_ENCODERS = os.getenv("RUN_ALL_ENCODERS", "false").lower() in YES_LIST +RUN_SLOW = os.getenv("RUN_SLOW", "false").lower() in YES_LIST +RUN_ALL = os.getenv("RUN_ALL", "false").lower() in YES_LIST def slow_test(test_case): @@ -38,6 +28,15 @@ def slow_test(test_case): return unittest.skipUnless(RUN_SLOW, "test is slow")(test_case) +def requires_timm_greater_or_equal(version: str): + timm_version = Version(timm.__version__) + provided_version = Version(version) + return unittest.skipUnless( + timm_version >= provided_version, + f"timm version {timm_version} is less than {provided_version}", + ) + + def requires_torch_greater_or_equal(version: str): torch_version = Version(torch.__version__) provided_version = Version(version) @@ -45,3 +44,46 @@ def requires_torch_greater_or_equal(version: str): torch_version >= provided_version, f"torch version {torch_version} is less than {provided_version}", ) + + +def check_run_test_on_diff_or_main(filepath_patterns: List[str]): + if RUN_ALL: + return True + + try: + repo = Repo(".") + current_branch = repo.active_branch.name + diff_files = repo.git.diff("main", name_only=True).splitlines() + + except Exception: + return True + + if current_branch == "main": + return True + + for pattern in filepath_patterns: + for file_path in diff_files: + if re.search(pattern, file_path): + return True + + return False + + +def check_two_models_strictly_equal( + model_a: torch.nn.Module, model_b: torch.nn.Module, input_data: torch.Tensor +) -> None: + for (k1, v1), (k2, v2) in zip( + model_a.state_dict().items(), model_b.state_dict().items() + ): + assert k1 == k2, f"Key mismatch: {k1} != {k2}" + torch.testing.assert_close( + v1, v2, msg=f"Tensor mismatch at key '{k1}':\n{v1} !=\n{v2}" + ) + + model_a.eval() + model_b.eval() + with torch.inference_mode(): + output_a = model_a(input_data) + output_b = model_b(input_data) + + torch.testing.assert_close(output_a, output_b)