Duplicate from nvidia/C-RADIOv2-H
Browse filesCo-authored-by: Greg Heinrich <[email protected]>
- .gitattributes +35 -0
- README.md +229 -0
- adaptor_base.py +37 -0
- adaptor_generic.py +69 -0
- adaptor_mlp.py +174 -0
- adaptor_registry.py +37 -0
- cls_token.py +59 -0
- common.py +108 -0
- config.json +228 -0
- dinov2_arch.py +1016 -0
- dual_hybrid_vit.py +213 -0
- enable_cpe_support.py +170 -0
- enable_spectral_reparam.py +277 -0
- eradio_model.py +1392 -0
- extra_models.py +206 -0
- extra_timm_models.py +206 -0
- feature_normalizer.py +111 -0
- forward_intermediates.py +138 -0
- hf_model.py +204 -0
- input_conditioner.py +49 -0
- model.safetensors +3 -0
- open_clip_adaptor.py +41 -0
- preprocessor_config.json +17 -0
- radio_model.py +343 -0
- vit_patch_generator.py +287 -0
- vitdet.py +188 -0
.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: other
|
| 3 |
+
license_name: nvidia-open-model-license
|
| 4 |
+
license_link: https://developer.download.nvidia.com/licenses/nvidia-open-model-license-agreement-june-2024.pdf
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
# Model Overview
|
| 8 |
+
|
| 9 |
+
[[**Github**](https://github.com/NVlabs/RADIO)] [[**CVPR 2025**](https://arxiv.org/abs/2412.07679)] [[**CVPR 2024**](https://arxiv.org/abs/2312.06709)]
|
| 10 |
+
|
| 11 |
+
## Description
|
| 12 |
+
|
| 13 |
+
This model performs visual feature extraction.
|
| 14 |
+
For instance, RADIO generates image embeddings that can be used by a downstream model to classify images.
|
| 15 |
+
|
| 16 |
+
C-RADIOv2 models are available in multiple sizes:
|
| 17 |
+
* Base (90M parameters).
|
| 18 |
+
* Large (320M parameters).
|
| 19 |
+
* Huge (653M parameters).
|
| 20 |
+
* Gigantic (1.1B parameters).
|
| 21 |
+
|
| 22 |
+
C-RADIOv2 was trained for 1M steps (400k more steps than v1), using inverse frequency sampling for data balancing, and [PHI Standardization](https://arxiv.org/abs/2410.01680) for teacher distribution balancing.
|
| 23 |
+
|
| 24 |
+
This model is ready for commercial/non-commercial use.
|
| 25 |
+
|
| 26 |
+
### License/Terms of Use
|
| 27 |
+
|
| 28 |
+
GOVERNING TERMS: Use of this model is governed by the [NVIDIA Open Model License Agreement](https://developer.download.nvidia.com/licenses/nvidia-open-model-license-agreement-june-2024.pdf).
|
| 29 |
+
|
| 30 |
+
## Deployment Geography
|
| 31 |
+
|
| 32 |
+
Global.
|
| 33 |
+
|
| 34 |
+
## Use Case
|
| 35 |
+
|
| 36 |
+
The embeddings generated by this model are expected to be used by a downstream application.
|
| 37 |
+
For example:
|
| 38 |
+
|
| 39 |
+
* Image-level understanding (image classification, curation, etc.).
|
| 40 |
+
* Dense processing (semantic segmentation, depth estimation, etc.).
|
| 41 |
+
* Integration into a Vision-Language Model.
|
| 42 |
+
|
| 43 |
+
## Release Date
|
| 44 |
+
|
| 45 |
+
Huggingface: 03/26/2025 via [RADIO Collection of Models](https://huggingface.co/collections/nvidia/radio-669f77f1dd6b153f007dd1c6).
|
| 46 |
+
|
| 47 |
+
## References
|
| 48 |
+
|
| 49 |
+
* \[CVPR 2025\] [**RADIOv2.5: Improved Baselines for Agglomerative Vision Foundation Models**](https://arxiv.org/abs/2412.07679)
|
| 50 |
+
* \[CVPR 2024\] [**AM-RADIO: Agglomerative Vision Foundation Model - Reduce All Domains Into One**](https://arxiv.org/abs/2312.06709)
|
| 51 |
+
|
| 52 |
+
## Model Architecture
|
| 53 |
+
|
| 54 |
+
**Architecture Type:** Neural Network <br>
|
| 55 |
+
**Network Architecture:** Vision Transformer <br>
|
| 56 |
+
|
| 57 |
+
## Input
|
| 58 |
+
|
| 59 |
+
**Input Type(s):** Image <br>
|
| 60 |
+
**Input Format(s):** Red, Green, Blue (RGB) <br>
|
| 61 |
+
**Input Parameters:** Two Dimensional (2D) <br>
|
| 62 |
+
**Other Properties Related to Input:** Image resolutions up to 2048x2028 in increments of 16 pixels <br>
|
| 63 |
+
|
| 64 |
+
## Output
|
| 65 |
+
|
| 66 |
+
**Output Type(s):** Embeddings <br>
|
| 67 |
+
**Output Format:** Tensor <br>
|
| 68 |
+
**Output Parameters:** 2D <br>
|
| 69 |
+
**Other Properties Related to Output:** Downstream model required to leverage image features <br>
|
| 70 |
+
|
| 71 |
+
## Usage:
|
| 72 |
+
|
| 73 |
+
RADIO will return a tuple with two tensors.
|
| 74 |
+
The `summary` is similar to the `cls_token` in ViT and is meant to represent the general concept of the entire image.
|
| 75 |
+
It has shape `(B,C)` with `B` being the batch dimension, and `C` being some number of channels.
|
| 76 |
+
The `spatial_features` represent more localized content which should be suitable for dense tasks such as semantic segmentation, or for integration into an LLM.
|
| 77 |
+
|
| 78 |
+
```python
|
| 79 |
+
import torch
|
| 80 |
+
from PIL import Image
|
| 81 |
+
from transformers import AutoModel, CLIPImageProcessor
|
| 82 |
+
|
| 83 |
+
hf_repo = "nvidia/C-RADIOv2-H"
|
| 84 |
+
|
| 85 |
+
image_processor = CLIPImageProcessor.from_pretrained(hf_repo)
|
| 86 |
+
model = AutoModel.from_pretrained(hf_repo, trust_remote_code=True)
|
| 87 |
+
model.eval().cuda()
|
| 88 |
+
|
| 89 |
+
image = Image.open('./assets/radio.png').convert('RGB')
|
| 90 |
+
pixel_values = image_processor(images=image, return_tensors='pt', do_resize=True).pixel_values
|
| 91 |
+
pixel_values = pixel_values.cuda()
|
| 92 |
+
|
| 93 |
+
summary, features = model(pixel_values)
|
| 94 |
+
```
|
| 95 |
+
|
| 96 |
+
Spatial features have shape `(B,T,D)` with `T` being the flattened spatial tokens, and `D` being the channels for spatial features. Note that `C!=D` in general.
|
| 97 |
+
Converting to a spatial tensor format can be done using the downsampling size of the model, combined with the input tensor shape. For RADIO, the patch size is 16.
|
| 98 |
+
|
| 99 |
+
```Python
|
| 100 |
+
from einops import rearrange
|
| 101 |
+
spatial_features = rearrange(spatial_features, 'b (h w) d -> b d h w', h=x.shape[-2] // patch_size, w=x.shape[-1] // patch_size)
|
| 102 |
+
```
|
| 103 |
+
|
| 104 |
+
The resulting tensor will have shape `(B,D,H,W)`, as is typically seen with computer vision models.
|
| 105 |
+
|
| 106 |
+
## Software Integration
|
| 107 |
+
|
| 108 |
+
**Runtime Engine(s):**
|
| 109 |
+
* TAO- 24.10 <br>
|
| 110 |
+
|
| 111 |
+
**Supported Hardware Microarchitecture Compatibility:** <br>
|
| 112 |
+
* NVIDIA Ampere <br>
|
| 113 |
+
* NVIDIA Blackwell <br>
|
| 114 |
+
* NVIDIA Jetson <br>
|
| 115 |
+
* NVIDIA Hopper <br>
|
| 116 |
+
* NVIDIA Lovelace <br>
|
| 117 |
+
* NVIDIA Pascal <br>
|
| 118 |
+
* NVIDIA Turing <br>
|
| 119 |
+
* NVIDIA Volta <br>
|
| 120 |
+
|
| 121 |
+
**[Preferred/Supported] Operating System(s):** <br>
|
| 122 |
+
* Linux
|
| 123 |
+
* Linux 4 Tegra
|
| 124 |
+
* QNX
|
| 125 |
+
* Windows
|
| 126 |
+
|
| 127 |
+
## Model Version(s)
|
| 128 |
+
|
| 129 |
+
* C-RADIOv2-B (90M parameters).
|
| 130 |
+
* C-RADIOv2-L (320M parameters).
|
| 131 |
+
* C-RADIOv2-H (653M parameters).
|
| 132 |
+
* C-RADIOv2-G (1.8B parameters).
|
| 133 |
+
|
| 134 |
+
**Links:**
|
| 135 |
+
|
| 136 |
+
* https://huggingface.co/nvidia/C-RADIOv2-B
|
| 137 |
+
* https://huggingface.co/nvidia/C-RADIOv2-L
|
| 138 |
+
* https://huggingface.co/nvidia/C-RADIOv2-H
|
| 139 |
+
* https://huggingface.co/nvidia/C-RADIOv2-g
|
| 140 |
+
|
| 141 |
+
# Training and Evaluation Datasets
|
| 142 |
+
|
| 143 |
+
## Training Dataset
|
| 144 |
+
|
| 145 |
+
NV-CC-Img-Text-Dataset <br>
|
| 146 |
+
|
| 147 |
+
### Data Collection Method by dataset
|
| 148 |
+
|
| 149 |
+
* Automated <br>
|
| 150 |
+
|
| 151 |
+
### Labeling Method by dataset
|
| 152 |
+
|
| 153 |
+
* Not Applicable (no labels are needed)
|
| 154 |
+
|
| 155 |
+
### Properties
|
| 156 |
+
|
| 157 |
+
* 700 Million Images <br>
|
| 158 |
+
|
| 159 |
+
## Evaluation Dataset
|
| 160 |
+
|
| 161 |
+
**Link:** [ImageNet](https://www.image-net.org/) <br>
|
| 162 |
+
|
| 163 |
+
### Data Collection Method by dataset
|
| 164 |
+
|
| 165 |
+
* Automated <br>
|
| 166 |
+
|
| 167 |
+
### Labeling Method by dataset
|
| 168 |
+
|
| 169 |
+
* Human <br>
|
| 170 |
+
|
| 171 |
+
**Properties:** This dataset spans 1000 object classes and contains 1,281,167 training images, 50,000 validation images and 100,000 test images.<br>
|
| 172 |
+
|
| 173 |
+
## Inference
|
| 174 |
+
|
| 175 |
+
**Engine:** PyTorch <br>
|
| 176 |
+
**Test Hardware:** A100 <br>
|
| 177 |
+
|
| 178 |
+
## Ethical Considerations
|
| 179 |
+
|
| 180 |
+
NVIDIA believes Trustworthy AI is a shared responsibility and we have established policies and practices to enable development for a wide array of AI applications. When downloaded or used in accordance with our terms of service, developers should work with their internal model team to ensure this model meets requirements for the relevant industry and use case and addresses unforeseen product misuse.
|
| 181 |
+
|
| 182 |
+
For more detailed information on ethical considerations for this model, please see the Model Card++ Explainability, Bias, Safety & Security, and Privacy Subcards below.
|
| 183 |
+
|
| 184 |
+
Please report security vulnerabilities or NVIDIA AI Concerns [here](https://www.nvidia.com/en-us/support/submit-security-vulnerability/).
|
| 185 |
+
|
| 186 |
+
### Bias
|
| 187 |
+
|
| 188 |
+
Field | Response
|
| 189 |
+
:---------------------------------------------------------------------------------------------------|:---------------
|
| 190 |
+
Participation considerations from adversely impacted groups [protected classes](https://www.senate.ca.gov/content/protected-classes) in model design and testing: | None
|
| 191 |
+
Measures taken to mitigate against unwanted bias: | None
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
### Explainability
|
| 195 |
+
|
| 196 |
+
Field | Response
|
| 197 |
+
:------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------
|
| 198 |
+
Intended Application & Domain: | Visual Feature Extraction
|
| 199 |
+
Model Type: | Vision Transformer
|
| 200 |
+
Intended Users: | Developers of downstream vision applications
|
| 201 |
+
Output: | Image embeddings
|
| 202 |
+
Describe how the model works: | The model takes an image as input, processes the image through multiple transformer blocks, and outputs summary and patch embeddings.
|
| 203 |
+
Name the adversely impacted groups this has been tested to deliver comparable outcomes regardless of: | Not Applicable
|
| 204 |
+
Technical Limitations: | This model generates image embeddings that can be used by a downstream model to, for example, classify images. The downstream model must be trained to leverage the visual embeddings.
|
| 205 |
+
Verified to have met prescribed NVIDIA quality standards: | Yes
|
| 206 |
+
Performance Metrics: | Image classification accuracy, semantic segmentation mean-over-intersection.
|
| 207 |
+
Potential Known Risks: | This model is only tested on input resolutions ranging from 256 to 2048, in increments of 16 pixels. Additionally, the generated embeddings might fail to disambiguate differences that appear evident to humans (e.g. two images showing different breeds of dogs might in fact produce very similar embeddings). Domain-specific evaluation is required for the target application.
|
| 208 |
+
Licensing: | [NVIDIA Open Model License](https://developer.download.nvidia.com/licenses/nvidia-open-model-license-agreement-june-2024.pdf)
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
### Privacy
|
| 212 |
+
|
| 213 |
+
Field | Response
|
| 214 |
+
:----------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------
|
| 215 |
+
Generatable or reverse engineerable personal data? | None
|
| 216 |
+
Personal data used to create this model? | None
|
| 217 |
+
How often is dataset reviewed? | Before Every Release
|
| 218 |
+
Is there provenance for all datasets used in training? | Yes
|
| 219 |
+
Does data labeling (annotation, metadata) comply with privacy laws? | Yes
|
| 220 |
+
Is data compliant with data subject requests for data correction or removal, if such a request was made? | Yes
|
| 221 |
+
|
| 222 |
+
### Safety
|
| 223 |
+
|
| 224 |
+
Field | Response
|
| 225 |
+
:---------------------------------------------------|:----------------------------------
|
| 226 |
+
Model Application(s): | Generation of visual embeddings
|
| 227 |
+
Describe the life critical impact (if present). | Not Applicable
|
| 228 |
+
Use Case Restrictions: | Abide by NVIDIA Open Model License Agreement
|
| 229 |
+
Model and dataset restrictions: | The Principle of least privilege (PoLP) is applied limiting access for dataset generation and model development. Restrictions enforce dataset access during training, and dataset license constraints adhered to.
|
adaptor_base.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
from argparse import Namespace
|
| 9 |
+
from typing import NamedTuple, Optional
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
from torch import nn
|
| 13 |
+
import torch.nn.functional as F
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class AdaptorInput(NamedTuple):
|
| 17 |
+
images: torch.Tensor
|
| 18 |
+
summary: torch.Tensor
|
| 19 |
+
features: torch.Tensor
|
| 20 |
+
feature_fmt: str
|
| 21 |
+
patch_size: int
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class RadioOutput(NamedTuple):
|
| 25 |
+
summary: torch.Tensor
|
| 26 |
+
features: torch.Tensor
|
| 27 |
+
|
| 28 |
+
def to(self, *args, **kwargs):
|
| 29 |
+
return RadioOutput(
|
| 30 |
+
self.summary.to(*args, **kwargs) if self.summary is not None else None,
|
| 31 |
+
self.features.to(*args, **kwargs) if self.features is not None else None,
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class AdaptorBase(nn.Module):
|
| 36 |
+
def forward(self, input: AdaptorInput) -> RadioOutput:
|
| 37 |
+
raise NotImplementedError("Subclasses must implement this!")
|
adaptor_generic.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
from argparse import Namespace
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
from torch import nn
|
| 12 |
+
import torch.nn.functional as F
|
| 13 |
+
|
| 14 |
+
from .adaptor_base import AdaptorBase, AdaptorInput, RadioOutput
|
| 15 |
+
from .adaptor_mlp import create_mlp_from_state, create_mlp_from_config
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class GenericAdaptor(AdaptorBase):
|
| 19 |
+
def __init__(self, main_config: Namespace, adaptor_config, state, mlp_config=None):
|
| 20 |
+
super().__init__()
|
| 21 |
+
|
| 22 |
+
extra_args = dict()
|
| 23 |
+
ups = None
|
| 24 |
+
ups_rank = None
|
| 25 |
+
if adaptor_config is not None:
|
| 26 |
+
ups = adaptor_config.get('fd_upsample_factor', None)
|
| 27 |
+
ups_rank = adaptor_config.get('fd_upsample_rank', None)
|
| 28 |
+
elif mlp_config is not None:
|
| 29 |
+
ups = mlp_config["feature"].get('upsample_factor', None)
|
| 30 |
+
ups_rank = mlp_config["feature"].get('upsample_rank', None)
|
| 31 |
+
if ups is not None:
|
| 32 |
+
extra_args['upsample_factor'] = ups
|
| 33 |
+
extra_args['upsample_rank'] = ups_rank
|
| 34 |
+
|
| 35 |
+
if state is not None:
|
| 36 |
+
spectral_heads = getattr(main_config, 'spectral_heads', False)
|
| 37 |
+
self.head_mlp = create_mlp_from_state(main_config.mlp_version, state, 'summary.', spectral_weights=spectral_heads)
|
| 38 |
+
self.feat_mlp = create_mlp_from_state(main_config.mlp_version, state, 'feature.', spectral_weights=spectral_heads, **extra_args)
|
| 39 |
+
else:
|
| 40 |
+
assert mlp_config is not None, "Config must not be None if state is None"
|
| 41 |
+
|
| 42 |
+
self.head_mlp = create_mlp_from_config(
|
| 43 |
+
main_config.mlp_version,
|
| 44 |
+
mlp_config["summary"]["input_dim"],
|
| 45 |
+
mlp_config["summary"]["hidden_dim"],
|
| 46 |
+
mlp_config["summary"]["output_dim"],
|
| 47 |
+
mlp_config["summary"]["num_inner"],
|
| 48 |
+
)
|
| 49 |
+
self.feat_mlp = create_mlp_from_config(
|
| 50 |
+
main_config.mlp_version,
|
| 51 |
+
mlp_config["feature"]["input_dim"],
|
| 52 |
+
mlp_config["feature"]["hidden_dim"],
|
| 53 |
+
mlp_config["feature"]["output_dim"],
|
| 54 |
+
mlp_config["feature"]["num_inner"],
|
| 55 |
+
**extra_args
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
def forward(self, input: AdaptorInput) -> RadioOutput:
|
| 59 |
+
# Convert input'd type to the type of the first parameter of the adaptor.
|
| 60 |
+
first_param = next(self.parameters())
|
| 61 |
+
summary = self.head_mlp(input.summary.to(dtype=first_param.dtype)).to(dtype=input.summary.dtype)
|
| 62 |
+
feat = self.feat_mlp(input.features.to(dtype=first_param.dtype), images=input.images, patch_size=input.patch_size).to(dtype=input.features.dtype)
|
| 63 |
+
|
| 64 |
+
if input.feature_fmt == 'NCHW':
|
| 65 |
+
feat = (feat.reshape(feat.shape[0], input.images.shape[-2] // input.patch_size * self.feat_mlp.upsample_factor, input.images.shape[-1] // input.patch_size * self.feat_mlp.upsample_factor, feat.shape[2])
|
| 66 |
+
.permute(0, 3, 1, 2)
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
return RadioOutput(summary, feat)
|
adaptor_mlp.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
import math
|
| 9 |
+
from typing import Dict, Optional
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
from torch import nn
|
| 13 |
+
|
| 14 |
+
from einops import rearrange
|
| 15 |
+
from timm.models.vision_transformer import Block
|
| 16 |
+
|
| 17 |
+
from .enable_spectral_reparam import disable_spectral_reparam, enable_spectral_reparam
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class MLP(nn.Module):
|
| 21 |
+
def __init__(self, input_size: int, hidden_size: int, output_size: int,
|
| 22 |
+
num_inner: int = 0, device: torch.device = None, **kwargs):
|
| 23 |
+
super(MLP, self).__init__()
|
| 24 |
+
self.fc1 = nn.Linear(input_size, hidden_size, device=device)
|
| 25 |
+
self.norm = nn.LayerNorm(hidden_size, device=device)
|
| 26 |
+
self.relu = nn.ReLU()
|
| 27 |
+
|
| 28 |
+
inner = []
|
| 29 |
+
for _ in range(num_inner):
|
| 30 |
+
inner.extend([
|
| 31 |
+
nn.Linear(hidden_size, hidden_size, device=device),
|
| 32 |
+
nn.LayerNorm(hidden_size, device=device),
|
| 33 |
+
nn.ReLU(),
|
| 34 |
+
])
|
| 35 |
+
if inner:
|
| 36 |
+
self.inner = nn.Sequential(*inner)
|
| 37 |
+
else:
|
| 38 |
+
self.inner = nn.Identity()
|
| 39 |
+
|
| 40 |
+
self.fc2 = nn.Linear(hidden_size, output_size, device=device)
|
| 41 |
+
|
| 42 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 43 |
+
x = self.fc1(x)
|
| 44 |
+
x = self.norm(x)
|
| 45 |
+
x = self.relu(x)
|
| 46 |
+
x = self.inner(x)
|
| 47 |
+
x = self.fc2(x)
|
| 48 |
+
return x
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
class MLP2(nn.Module):
|
| 52 |
+
def __init__(self, input_size: int, hidden_size: int, output_size: int,
|
| 53 |
+
num_inner: int = 0,
|
| 54 |
+
pre_norm: bool = False, device: torch.device = None,
|
| 55 |
+
upsample_factor: int = 1,
|
| 56 |
+
upsample_rank: int = None,
|
| 57 |
+
from_config: bool = False,
|
| 58 |
+
**kwargs):
|
| 59 |
+
super().__init__()
|
| 60 |
+
|
| 61 |
+
self.pre_norm = nn.Sequential(
|
| 62 |
+
nn.LayerNorm(input_size),
|
| 63 |
+
nn.GELU(),
|
| 64 |
+
) if pre_norm else nn.Identity()
|
| 65 |
+
|
| 66 |
+
self.upsample_factor = upsample_factor
|
| 67 |
+
sq_ups = upsample_factor ** 2
|
| 68 |
+
|
| 69 |
+
self._real_output_dim = output_size // sq_ups
|
| 70 |
+
|
| 71 |
+
# hidden_size *= upsample_factor
|
| 72 |
+
# output_size *= (upsample_factor ** 2)
|
| 73 |
+
|
| 74 |
+
self.fc1 = nn.Linear(input_size, hidden_size, device=device)
|
| 75 |
+
|
| 76 |
+
blocks = []
|
| 77 |
+
for _ in range(num_inner):
|
| 78 |
+
blocks.append(nn.Sequential(
|
| 79 |
+
nn.LayerNorm(hidden_size, device=device),
|
| 80 |
+
nn.GELU(),
|
| 81 |
+
nn.Linear(hidden_size, hidden_size, device=device),
|
| 82 |
+
))
|
| 83 |
+
self.blocks = nn.ModuleList(blocks)
|
| 84 |
+
|
| 85 |
+
self.final = nn.Sequential(
|
| 86 |
+
nn.LayerNorm(hidden_size, device=device),
|
| 87 |
+
nn.GELU(),
|
| 88 |
+
nn.Linear(hidden_size, output_size, device=device),
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
def forward(self, x: torch.Tensor, images: Optional[torch.Tensor] = None, patch_size: Optional[int] = None) -> torch.Tensor:
|
| 92 |
+
x = self.pre_norm(x)
|
| 93 |
+
x = self.fc1(x)
|
| 94 |
+
for block in self.blocks:
|
| 95 |
+
x = x + block(x)
|
| 96 |
+
x = self.final(x)
|
| 97 |
+
|
| 98 |
+
if self.upsample_factor > 1:
|
| 99 |
+
if images is None:
|
| 100 |
+
raise ValueError(f'`images` cannot be `None` when the head\'s `upsample_factor > 1`!')
|
| 101 |
+
if patch_size is None:
|
| 102 |
+
raise ValueError(f'`patch_size` cannot be `None` when the head\'s `upsample_factor > 1`!')
|
| 103 |
+
h, w = tuple(d // patch_size for d in images.shape[-2:])
|
| 104 |
+
x = rearrange(x, 'b (h w) (u1 u2 c) -> b (h u1 w u2) c',
|
| 105 |
+
h=h, w=w, u1=self.upsample_factor, u2=self.upsample_factor,
|
| 106 |
+
c=self._real_output_dim)
|
| 107 |
+
|
| 108 |
+
return x
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
MLP_FACTORY = {
|
| 112 |
+
'v1': MLP,
|
| 113 |
+
'v2': MLP2,
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def strip_prefix(state: Dict[str, torch.Tensor], prefix: str):
|
| 118 |
+
state = {
|
| 119 |
+
k[len(prefix):]: v
|
| 120 |
+
for k, v in state.items()
|
| 121 |
+
if k.startswith(prefix)
|
| 122 |
+
}
|
| 123 |
+
return state
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def get_mlp_info_from_state(version: str, state: Dict[str, torch.Tensor], prefix: str = '', spectral_weights: bool = False):
|
| 127 |
+
state = strip_prefix(state, prefix)
|
| 128 |
+
|
| 129 |
+
weight_suffix = 'weight' if not spectral_weights else 'parametrizations.weight.original'
|
| 130 |
+
|
| 131 |
+
if version == 'v1':
|
| 132 |
+
hidden_dim, input_dim = state[f'fc1.{weight_suffix}'].shape
|
| 133 |
+
output_dim = state[f'fc2.{weight_suffix}'].shape[0]
|
| 134 |
+
|
| 135 |
+
for num_inner in range(1000):
|
| 136 |
+
k = f'inner.{num_inner}.0.weight'
|
| 137 |
+
if k not in state:
|
| 138 |
+
break
|
| 139 |
+
elif version == 'v2':
|
| 140 |
+
hidden_dim, input_dim = state[f'fc1.{weight_suffix}'].shape
|
| 141 |
+
output_dim = state[f'final.2.{weight_suffix}'].shape[0]
|
| 142 |
+
|
| 143 |
+
for num_inner in range(1000):
|
| 144 |
+
k = f'blocks.{num_inner}.0.weight'
|
| 145 |
+
if k not in state:
|
| 146 |
+
break
|
| 147 |
+
else:
|
| 148 |
+
raise ValueError(f'Unsupported MLP version: {version}')
|
| 149 |
+
|
| 150 |
+
return input_dim, hidden_dim, output_dim, num_inner
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def create_mlp_from_config(version: str, input_dim: int, hidden_dim: int, output_dim: int, num_inner: int, **kwargs):
|
| 154 |
+
ret: nn.Module = MLP_FACTORY[version](input_dim, hidden_dim, output_dim, num_inner, from_config=True, **kwargs)
|
| 155 |
+
|
| 156 |
+
return ret
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def create_mlp_from_state(version: str, state: Dict[str, torch.Tensor], prefix: str = '', spectral_weights: bool = False, **kwargs):
|
| 160 |
+
state = strip_prefix(state, prefix)
|
| 161 |
+
|
| 162 |
+
input_dim, hidden_dim, output_dim, num_inner = get_mlp_info_from_state(version, state, spectral_weights=spectral_weights)
|
| 163 |
+
|
| 164 |
+
ret: nn.Module = create_mlp_from_config(version, input_dim, hidden_dim, output_dim, num_inner, **kwargs)
|
| 165 |
+
|
| 166 |
+
if spectral_weights:
|
| 167 |
+
enable_spectral_reparam(ret, init_norm_to_current=False, state_dict_guidance=state)
|
| 168 |
+
|
| 169 |
+
ret.load_state_dict(state)
|
| 170 |
+
|
| 171 |
+
if spectral_weights:
|
| 172 |
+
disable_spectral_reparam(ret)
|
| 173 |
+
|
| 174 |
+
return ret
|
adaptor_registry.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
from argparse import Namespace
|
| 9 |
+
from typing import Dict, Any
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
|
| 13 |
+
from .adaptor_generic import GenericAdaptor, AdaptorBase
|
| 14 |
+
|
| 15 |
+
dict_t = Dict[str, Any]
|
| 16 |
+
state_t = Dict[str, torch.Tensor]
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class AdaptorRegistry:
|
| 20 |
+
def __init__(self):
|
| 21 |
+
self._registry = {}
|
| 22 |
+
|
| 23 |
+
def register_adaptor(self, name):
|
| 24 |
+
def decorator(factory_function):
|
| 25 |
+
if name in self._registry:
|
| 26 |
+
raise ValueError(f"Model '{name}' already registered")
|
| 27 |
+
self._registry[name] = factory_function
|
| 28 |
+
return factory_function
|
| 29 |
+
return decorator
|
| 30 |
+
|
| 31 |
+
def create_adaptor(self, name, main_config: Namespace, adaptor_config: dict_t, state: state_t) -> AdaptorBase:
|
| 32 |
+
if name not in self._registry:
|
| 33 |
+
return GenericAdaptor(main_config, adaptor_config, state)
|
| 34 |
+
return self._registry[name](main_config, adaptor_config, state)
|
| 35 |
+
|
| 36 |
+
# Creating an instance of the registry
|
| 37 |
+
adaptor_registry = AdaptorRegistry()
|
cls_token.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
from typing import Optional
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
from torch import nn
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class ClsToken(nn.Module):
|
| 15 |
+
def __init__(self, ndim: int,
|
| 16 |
+
num_tokens: int = 1,
|
| 17 |
+
enabled: bool = True,
|
| 18 |
+
register_multiple: Optional[int] = None,
|
| 19 |
+
num_registers: Optional[int] = None,
|
| 20 |
+
):
|
| 21 |
+
super().__init__()
|
| 22 |
+
|
| 23 |
+
self.ndim = ndim
|
| 24 |
+
self.enabled = enabled
|
| 25 |
+
self.num_registers = 0
|
| 26 |
+
self.num_tokens = num_tokens
|
| 27 |
+
if enabled:
|
| 28 |
+
if num_registers:
|
| 29 |
+
self.num_registers = num_registers
|
| 30 |
+
elif register_multiple:
|
| 31 |
+
self.num_registers = register_multiple - (num_tokens % register_multiple)
|
| 32 |
+
|
| 33 |
+
scale = ndim ** -0.5
|
| 34 |
+
self.token = nn.Parameter(torch.randn(num_tokens + self.num_registers, ndim) * scale)
|
| 35 |
+
else:
|
| 36 |
+
self.token = None
|
| 37 |
+
|
| 38 |
+
self.num_patches = self.num_tokens + self.num_registers
|
| 39 |
+
|
| 40 |
+
def disable(self):
|
| 41 |
+
self.token = None
|
| 42 |
+
self.enabled = False
|
| 43 |
+
|
| 44 |
+
def forward(self, x: torch.Tensor):
|
| 45 |
+
if self.token is None:
|
| 46 |
+
return x
|
| 47 |
+
|
| 48 |
+
token = self.token.unsqueeze(0).expand(x.shape[0], -1, -1)
|
| 49 |
+
x = torch.cat([
|
| 50 |
+
token,
|
| 51 |
+
x,
|
| 52 |
+
], dim=1)
|
| 53 |
+
|
| 54 |
+
return x
|
| 55 |
+
|
| 56 |
+
def no_weight_decay(self):
|
| 57 |
+
return [
|
| 58 |
+
'token',
|
| 59 |
+
]
|
common.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
|
| 9 |
+
from dataclasses import dataclass
|
| 10 |
+
from typing import Optional
|
| 11 |
+
|
| 12 |
+
from .radio_model import Resolution
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@dataclass
|
| 16 |
+
class RadioResource:
|
| 17 |
+
url: str
|
| 18 |
+
patch_size: int
|
| 19 |
+
max_resolution: int
|
| 20 |
+
preferred_resolution: Resolution
|
| 21 |
+
vitdet_num_windowed: Optional[int] = None
|
| 22 |
+
vitdet_num_global: Optional[int] = None
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
RESOURCE_MAP = {
|
| 26 |
+
# RADIOv2.5
|
| 27 |
+
"radio_v2.5-b": RadioResource(
|
| 28 |
+
"https://huggingface.co/nvidia/RADIO/resolve/main/radio-v2.5-b_half.pth.tar?download=true",
|
| 29 |
+
patch_size=16,
|
| 30 |
+
max_resolution=2048,
|
| 31 |
+
preferred_resolution=(768, 768),
|
| 32 |
+
vitdet_num_global=4,
|
| 33 |
+
),
|
| 34 |
+
"radio_v2.5-l": RadioResource(
|
| 35 |
+
"https://huggingface.co/nvidia/RADIO/resolve/main/radio-v2.5-l_half.pth.tar?download=true",
|
| 36 |
+
patch_size=16,
|
| 37 |
+
max_resolution=2048,
|
| 38 |
+
preferred_resolution=(768, 768),
|
| 39 |
+
vitdet_num_global=4,
|
| 40 |
+
),
|
| 41 |
+
"radio_v2.5-h": RadioResource(
|
| 42 |
+
"https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.5-h.pth.tar?download=true",
|
| 43 |
+
patch_size=16,
|
| 44 |
+
max_resolution=2048,
|
| 45 |
+
preferred_resolution=(768, 768),
|
| 46 |
+
vitdet_num_global=4,
|
| 47 |
+
),
|
| 48 |
+
"radio_v2.5-h-norm": RadioResource(
|
| 49 |
+
"https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.5-h-norm.pth.tar?download=true",
|
| 50 |
+
patch_size=16,
|
| 51 |
+
max_resolution=2048,
|
| 52 |
+
preferred_resolution=(768, 768),
|
| 53 |
+
vitdet_num_global=4,
|
| 54 |
+
),
|
| 55 |
+
"radio_v2.5-g": RadioResource(
|
| 56 |
+
"https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.5-g.pth.tar?download=true",
|
| 57 |
+
patch_size=14,
|
| 58 |
+
max_resolution=1792,
|
| 59 |
+
preferred_resolution=(896, 896),
|
| 60 |
+
vitdet_num_global=8,
|
| 61 |
+
),
|
| 62 |
+
# RADIO
|
| 63 |
+
"radio_v2.1": RadioResource(
|
| 64 |
+
"https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.1_bf16.pth.tar?download=true",
|
| 65 |
+
patch_size=16,
|
| 66 |
+
max_resolution=2048,
|
| 67 |
+
preferred_resolution=Resolution(432, 432),
|
| 68 |
+
vitdet_num_windowed=5,
|
| 69 |
+
),
|
| 70 |
+
"radio_v2": RadioResource(
|
| 71 |
+
"https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.pth.tar?download=true",
|
| 72 |
+
patch_size=16,
|
| 73 |
+
max_resolution=2048,
|
| 74 |
+
preferred_resolution=Resolution(432, 432),
|
| 75 |
+
vitdet_num_windowed=5,
|
| 76 |
+
),
|
| 77 |
+
"radio_v1": RadioResource(
|
| 78 |
+
"https://huggingface.co/nvidia/RADIO/resolve/main/radio_v1.pth.tar?download=true",
|
| 79 |
+
patch_size=14,
|
| 80 |
+
max_resolution=1050,
|
| 81 |
+
preferred_resolution=Resolution(378, 378),
|
| 82 |
+
),
|
| 83 |
+
# E-RADIO
|
| 84 |
+
"e-radio_v2": RadioResource(
|
| 85 |
+
"https://huggingface.co/nvidia/RADIO/resolve/main/eradio_v2.pth.tar?download=true",
|
| 86 |
+
patch_size=16,
|
| 87 |
+
max_resolution=2048,
|
| 88 |
+
preferred_resolution=Resolution(512, 512),
|
| 89 |
+
),
|
| 90 |
+
# C-RADIO
|
| 91 |
+
"c-radio_v2.5-g": RadioResource(
|
| 92 |
+
"https://huggingface.co/nvidia/C-RADIOv2-g/resolve/main/c-radio_v2-g_half.pth.tar",
|
| 93 |
+
patch_size=16,
|
| 94 |
+
max_resolution=2048,
|
| 95 |
+
preferred_resolution=(768, 768),
|
| 96 |
+
vitdet_num_global=8,
|
| 97 |
+
),
|
| 98 |
+
"c-radio_v3-l": RadioResource(
|
| 99 |
+
# NOTE: Currently, this model cannot be loaded via TorchHub. Instead, use the transformers API at https://huggingface.co/nvidia/C-RADIOv3-L
|
| 100 |
+
# and accept the license terms.
|
| 101 |
+
"https://huggingface.co/nvidia/C-RADIOv3-L/resolve/main/c-radio-v3_l_half.pth.tar?download=true",
|
| 102 |
+
patch_size=16,
|
| 103 |
+
max_resolution=2048,
|
| 104 |
+
preferred_resolution=Resolution(512, 512),
|
| 105 |
+
),
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
DEFAULT_VERSION = "radio_v2.5-h"
|
config.json
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"adaptor_configs": {},
|
| 3 |
+
"adaptor_names": null,
|
| 4 |
+
"architectures": [
|
| 5 |
+
"RADIOModel"
|
| 6 |
+
],
|
| 7 |
+
"args": {
|
| 8 |
+
"aa": null,
|
| 9 |
+
"amp": true,
|
| 10 |
+
"amp_dtype": "bfloat16",
|
| 11 |
+
"amp_impl": "native",
|
| 12 |
+
"aug_repeats": 0,
|
| 13 |
+
"aug_splits": 0,
|
| 14 |
+
"bn_eps": null,
|
| 15 |
+
"bn_momentum": null,
|
| 16 |
+
"cache_dir": null,
|
| 17 |
+
"channels_last": false,
|
| 18 |
+
"checkpoint_hist": 10,
|
| 19 |
+
"chk_keep_forever": 100,
|
| 20 |
+
"class_map": "",
|
| 21 |
+
"clip_grad": null,
|
| 22 |
+
"clip_mode": "norm",
|
| 23 |
+
"cls_token_per_teacher": true,
|
| 24 |
+
"coco_annotations_file": "/datasets/coco2017-adlsa/annotations/captions_val2017.json",
|
| 25 |
+
"coco_image_dir": "/datasets/coco2017-adlsa/val2017",
|
| 26 |
+
"color_jitter": 0.4,
|
| 27 |
+
"cooldown_epochs": 0,
|
| 28 |
+
"cpe_max_size": 2048,
|
| 29 |
+
"crd_loss": false,
|
| 30 |
+
"crd_loss_weight": 0.8,
|
| 31 |
+
"crop_pct": null,
|
| 32 |
+
"cutmix": 0.0,
|
| 33 |
+
"cutmix_minmax": null,
|
| 34 |
+
"dataset_download": false,
|
| 35 |
+
"debug_full_knn": false,
|
| 36 |
+
"decay_epochs": 90,
|
| 37 |
+
"decay_milestones": [
|
| 38 |
+
90,
|
| 39 |
+
180,
|
| 40 |
+
270
|
| 41 |
+
],
|
| 42 |
+
"decay_rate": 0.1,
|
| 43 |
+
"depchain": true,
|
| 44 |
+
"dist_bn": "reduce",
|
| 45 |
+
"dist_norm_weight": 0.0,
|
| 46 |
+
"distributed": true,
|
| 47 |
+
"drop": 0.0,
|
| 48 |
+
"drop_block": null,
|
| 49 |
+
"drop_connect": null,
|
| 50 |
+
"drop_path": null,
|
| 51 |
+
"dtype": "float32",
|
| 52 |
+
"epoch_repeats": 0.0,
|
| 53 |
+
"eval": false,
|
| 54 |
+
"eval_metric": "knn_top1",
|
| 55 |
+
"eval_teacher": false,
|
| 56 |
+
"eval_teacher_only": false,
|
| 57 |
+
"eval_throughput": false,
|
| 58 |
+
"fast_norm": false,
|
| 59 |
+
"fd_loss_fn": "MSE",
|
| 60 |
+
"feature_normalization": "SHIP_NORM",
|
| 61 |
+
"feature_summarizer": "cls_token",
|
| 62 |
+
"feature_upscale_factor": null,
|
| 63 |
+
"force_new_wandb_id": false,
|
| 64 |
+
"force_spectral_reparam": true,
|
| 65 |
+
"freeze_bn": false,
|
| 66 |
+
"fsdp": false,
|
| 67 |
+
"fuser": "",
|
| 68 |
+
"gp": null,
|
| 69 |
+
"grad_accum_steps": 1,
|
| 70 |
+
"grad_checkpointing": false,
|
| 71 |
+
"head_init_bias": null,
|
| 72 |
+
"head_init_scale": null,
|
| 73 |
+
"head_warmup": 5,
|
| 74 |
+
"head_weight_decay": 0.001,
|
| 75 |
+
"hflip": 0.5,
|
| 76 |
+
"img_size": null,
|
| 77 |
+
"in_chans": null,
|
| 78 |
+
"initial_checkpoint": null,
|
| 79 |
+
"input_size": null,
|
| 80 |
+
"interpolation": "",
|
| 81 |
+
"layer_decay": null,
|
| 82 |
+
"local_rank": 0,
|
| 83 |
+
"log_interval": 50,
|
| 84 |
+
"log_mlflow": false,
|
| 85 |
+
"log_wandb": true,
|
| 86 |
+
"loss_auto_balance": false,
|
| 87 |
+
"lr_base": 0.1,
|
| 88 |
+
"lr_base_scale": "",
|
| 89 |
+
"lr_base_size": 256,
|
| 90 |
+
"lr_cycle_decay": 0.5,
|
| 91 |
+
"lr_cycle_limit": 1,
|
| 92 |
+
"lr_cycle_mul": 1.0,
|
| 93 |
+
"lr_k_decay": 1.0,
|
| 94 |
+
"lr_noise": null,
|
| 95 |
+
"lr_noise_pct": 0.67,
|
| 96 |
+
"lr_noise_std": 1.0,
|
| 97 |
+
"mean": null,
|
| 98 |
+
"mesa": false,
|
| 99 |
+
"min_lr": 0,
|
| 100 |
+
"mixup": 0.0,
|
| 101 |
+
"mixup_mode": "batch",
|
| 102 |
+
"mixup_off_epoch": 0,
|
| 103 |
+
"mixup_prob": 1.0,
|
| 104 |
+
"mixup_switch_prob": 0.5,
|
| 105 |
+
"mlp_hidden_size": 1520,
|
| 106 |
+
"mlp_num_inner": 3,
|
| 107 |
+
"mlp_version": "v2",
|
| 108 |
+
"model": "vit_huge_patch16_224",
|
| 109 |
+
"model_kwargs": {},
|
| 110 |
+
"model_norm": false,
|
| 111 |
+
"momentum": 0.9,
|
| 112 |
+
"no_aug": false,
|
| 113 |
+
"no_ddp_bb": true,
|
| 114 |
+
"no_prefetcher": false,
|
| 115 |
+
"no_resume_opt": false,
|
| 116 |
+
"num_classes": null,
|
| 117 |
+
"opt_betas": null,
|
| 118 |
+
"opt_eps": null,
|
| 119 |
+
"patience_epochs": 10,
|
| 120 |
+
"pin_mem": false,
|
| 121 |
+
"prefetcher": true,
|
| 122 |
+
"pretrained": false,
|
| 123 |
+
"rank": 0,
|
| 124 |
+
"ratio": [
|
| 125 |
+
0.75,
|
| 126 |
+
1.3333333333333333
|
| 127 |
+
],
|
| 128 |
+
"recount": 1,
|
| 129 |
+
"recovery_interval": 0,
|
| 130 |
+
"register_multiple": 8,
|
| 131 |
+
"remode": "pixel",
|
| 132 |
+
"reprob": 0.0,
|
| 133 |
+
"reset_loss_state": false,
|
| 134 |
+
"resplit": false,
|
| 135 |
+
"save_images": false,
|
| 136 |
+
"scale": [
|
| 137 |
+
0.5,
|
| 138 |
+
1.0
|
| 139 |
+
],
|
| 140 |
+
"sched": "cosine",
|
| 141 |
+
"seed": 42,
|
| 142 |
+
"smoothing": 0.1,
|
| 143 |
+
"spectral_heads": false,
|
| 144 |
+
"spectral_reparam": false,
|
| 145 |
+
"split_bn": false,
|
| 146 |
+
"start_epoch": null,
|
| 147 |
+
"std": null,
|
| 148 |
+
"stream_teachers": true,
|
| 149 |
+
"sync_bn": false,
|
| 150 |
+
"synchronize_step": false,
|
| 151 |
+
"teachers": [
|
| 152 |
+
{
|
| 153 |
+
"fd_normalize": false,
|
| 154 |
+
"feature_distillation": true,
|
| 155 |
+
"input_size": 378,
|
| 156 |
+
"model": "ViT-H-14-378-quickgelu",
|
| 157 |
+
"name": "clip",
|
| 158 |
+
"pretrained": "dfn5b",
|
| 159 |
+
"type": "open_clip",
|
| 160 |
+
"use_summary": true
|
| 161 |
+
},
|
| 162 |
+
{
|
| 163 |
+
"fd_normalize": false,
|
| 164 |
+
"feature_distillation": true,
|
| 165 |
+
"input_size": 378,
|
| 166 |
+
"model": "ViT-SO400M-14-SigLIP-384",
|
| 167 |
+
"name": "siglip",
|
| 168 |
+
"pretrained": "webli",
|
| 169 |
+
"type": "open_clip",
|
| 170 |
+
"use_summary": true
|
| 171 |
+
},
|
| 172 |
+
{
|
| 173 |
+
"fd_normalize": false,
|
| 174 |
+
"feature_distillation": true,
|
| 175 |
+
"input_size": 378,
|
| 176 |
+
"model": "dinov2_vitg14_reg",
|
| 177 |
+
"name": "dino_v2",
|
| 178 |
+
"type": "dino_v2",
|
| 179 |
+
"use_summary": true
|
| 180 |
+
},
|
| 181 |
+
{
|
| 182 |
+
"fd_normalize": false,
|
| 183 |
+
"feature_distillation": true,
|
| 184 |
+
"input_size": 1024,
|
| 185 |
+
"model": "vit-h",
|
| 186 |
+
"name": "sam",
|
| 187 |
+
"type": "sam",
|
| 188 |
+
"use_summary": false
|
| 189 |
+
}
|
| 190 |
+
],
|
| 191 |
+
"torchcompile": null,
|
| 192 |
+
"torchscript": false,
|
| 193 |
+
"train_interpolation": "random",
|
| 194 |
+
"train_split": "train",
|
| 195 |
+
"tta": 0,
|
| 196 |
+
"use_coco": false,
|
| 197 |
+
"use_multi_epochs_loader": false,
|
| 198 |
+
"val_ema_only": false,
|
| 199 |
+
"val_split": "val",
|
| 200 |
+
"vflip": 0.0,
|
| 201 |
+
"vitdet_version": 1,
|
| 202 |
+
"wandb_entity": "",
|
| 203 |
+
"wandb_job_type": "",
|
| 204 |
+
"wandb_name": "",
|
| 205 |
+
"wandb_project": "",
|
| 206 |
+
"warmup_lr": 1e-05,
|
| 207 |
+
"warmup_prefix": false,
|
| 208 |
+
"worker_seeding": "all",
|
| 209 |
+
"workers": 8,
|
| 210 |
+
"world_size": 256
|
| 211 |
+
},
|
| 212 |
+
"auto_map": {
|
| 213 |
+
"AutoConfig": "hf_model.RADIOConfig",
|
| 214 |
+
"AutoModel": "hf_model.RADIOModel"
|
| 215 |
+
},
|
| 216 |
+
"feature_normalizer_config": null,
|
| 217 |
+
"inter_feature_normalizer_config": null,
|
| 218 |
+
"max_resolution": 2048,
|
| 219 |
+
"patch_size": 16,
|
| 220 |
+
"preferred_resolution": [
|
| 221 |
+
768,
|
| 222 |
+
768
|
| 223 |
+
],
|
| 224 |
+
"torch_dtype": "float32",
|
| 225 |
+
"transformers_version": "4.47.0.dev0",
|
| 226 |
+
"version": "radio_v2.5-h",
|
| 227 |
+
"vitdet_window_size": null
|
| 228 |
+
}
|
dinov2_arch.py
ADDED
|
@@ -0,0 +1,1016 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
+
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
# References:
|
| 7 |
+
# https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
|
| 8 |
+
# https://github.com/rwightman/pytorch-image-models/tree/master/timm/models/vision_transformer.py
|
| 9 |
+
|
| 10 |
+
# Nvidia
|
| 11 |
+
# NOTE: We re-define this model architecture primarily so that we don't have to worry about version compatibility breaking,
|
| 12 |
+
# but also because Huggingface does a string replace of `gamma` to something else when loading the model state,
|
| 13 |
+
# and this breaks loading of this model.
|
| 14 |
+
|
| 15 |
+
from enum import Enum
|
| 16 |
+
from functools import partial
|
| 17 |
+
import logging
|
| 18 |
+
import math
|
| 19 |
+
import os
|
| 20 |
+
import sys
|
| 21 |
+
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
|
| 22 |
+
import warnings
|
| 23 |
+
|
| 24 |
+
import torch
|
| 25 |
+
from torch import nn
|
| 26 |
+
from torch.nn import functional as F
|
| 27 |
+
from torch.nn.init import trunc_normal_
|
| 28 |
+
|
| 29 |
+
_torch_has_sdpa = hasattr(F, 'scaled_dot_product_attention')
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
XFORMERS_ENABLED = os.environ.get("XFORMERS_DISABLED") is None
|
| 33 |
+
try:
|
| 34 |
+
if XFORMERS_ENABLED:
|
| 35 |
+
from xformers.ops import fmha, scaled_index_add, index_select_cat, SwiGLU, memory_efficient_attention, unbind
|
| 36 |
+
|
| 37 |
+
XFORMERS_AVAILABLE = True
|
| 38 |
+
else:
|
| 39 |
+
raise ImportError
|
| 40 |
+
except ImportError:
|
| 41 |
+
XFORMERS_AVAILABLE = False
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def make_2tuple(x):
|
| 45 |
+
if isinstance(x, tuple):
|
| 46 |
+
assert len(x) == 2
|
| 47 |
+
return x
|
| 48 |
+
|
| 49 |
+
assert isinstance(x, int)
|
| 50 |
+
return (x, x)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class PatchEmbed(nn.Module):
|
| 54 |
+
"""
|
| 55 |
+
2D image to patch embedding: (B,C,H,W) -> (B,N,D)
|
| 56 |
+
|
| 57 |
+
Args:
|
| 58 |
+
img_size: Image size.
|
| 59 |
+
patch_size: Patch token size.
|
| 60 |
+
in_chans: Number of input image channels.
|
| 61 |
+
embed_dim: Number of linear projection output channels.
|
| 62 |
+
norm_layer: Normalization layer.
|
| 63 |
+
"""
|
| 64 |
+
|
| 65 |
+
def __init__(
|
| 66 |
+
self,
|
| 67 |
+
img_size: Union[int, Tuple[int, int]] = 224,
|
| 68 |
+
patch_size: Union[int, Tuple[int, int]] = 16,
|
| 69 |
+
in_chans: int = 3,
|
| 70 |
+
embed_dim: int = 768,
|
| 71 |
+
norm_layer: Optional[Callable] = None,
|
| 72 |
+
flatten_embedding: bool = True,
|
| 73 |
+
) -> None:
|
| 74 |
+
super().__init__()
|
| 75 |
+
|
| 76 |
+
image_HW = make_2tuple(img_size)
|
| 77 |
+
patch_HW = make_2tuple(patch_size)
|
| 78 |
+
patch_grid_size = (
|
| 79 |
+
image_HW[0] // patch_HW[0],
|
| 80 |
+
image_HW[1] // patch_HW[1],
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
self.img_size = image_HW
|
| 84 |
+
self.patch_size = patch_HW
|
| 85 |
+
self.patches_resolution = patch_grid_size
|
| 86 |
+
self.num_patches = patch_grid_size[0] * patch_grid_size[1]
|
| 87 |
+
|
| 88 |
+
self.in_chans = in_chans
|
| 89 |
+
self.embed_dim = embed_dim
|
| 90 |
+
|
| 91 |
+
self.flatten_embedding = flatten_embedding
|
| 92 |
+
|
| 93 |
+
self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_HW, stride=patch_HW)
|
| 94 |
+
self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity()
|
| 95 |
+
|
| 96 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 97 |
+
_, _, H, W = x.shape
|
| 98 |
+
patch_H, patch_W = self.patch_size
|
| 99 |
+
|
| 100 |
+
assert H % patch_H == 0, f"Input image height {H} is not a multiple of patch height {patch_H}"
|
| 101 |
+
assert W % patch_W == 0, f"Input image width {W} is not a multiple of patch width: {patch_W}"
|
| 102 |
+
|
| 103 |
+
x = self.proj(x) # B C H W
|
| 104 |
+
H, W = x.size(2), x.size(3)
|
| 105 |
+
x = x.flatten(2).transpose(1, 2) # B HW C
|
| 106 |
+
x = self.norm(x)
|
| 107 |
+
if not self.flatten_embedding:
|
| 108 |
+
x = x.reshape(-1, H, W, self.embed_dim) # B H W C
|
| 109 |
+
return x
|
| 110 |
+
|
| 111 |
+
def flops(self) -> float:
|
| 112 |
+
Ho, Wo = self.patches_resolution
|
| 113 |
+
flops = Ho * Wo * self.embed_dim * self.in_chans * (self.patch_size[0] * self.patch_size[1])
|
| 114 |
+
if self.norm is not None:
|
| 115 |
+
flops += Ho * Wo * self.embed_dim
|
| 116 |
+
return flops
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
class Attention(nn.Module):
|
| 120 |
+
def __init__(
|
| 121 |
+
self,
|
| 122 |
+
dim: int,
|
| 123 |
+
num_heads: int = 8,
|
| 124 |
+
qkv_bias: bool = False,
|
| 125 |
+
proj_bias: bool = True,
|
| 126 |
+
attn_drop: float = 0.0,
|
| 127 |
+
proj_drop: float = 0.0,
|
| 128 |
+
) -> None:
|
| 129 |
+
super().__init__()
|
| 130 |
+
self.num_heads = num_heads
|
| 131 |
+
head_dim = dim // num_heads
|
| 132 |
+
self.scale = head_dim**-0.5
|
| 133 |
+
|
| 134 |
+
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
| 135 |
+
self.attn_drop = nn.Dropout(attn_drop)
|
| 136 |
+
self.proj = nn.Linear(dim, dim, bias=proj_bias)
|
| 137 |
+
self.proj_drop = nn.Dropout(proj_drop)
|
| 138 |
+
|
| 139 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 140 |
+
B, N, C = x.shape
|
| 141 |
+
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
|
| 142 |
+
|
| 143 |
+
q, k, v = qkv[0], qkv[1], qkv[2]
|
| 144 |
+
if _torch_has_sdpa:
|
| 145 |
+
x = F.scaled_dot_product_attention(
|
| 146 |
+
q, k, v,
|
| 147 |
+
is_causal=False,
|
| 148 |
+
dropout_p=self.attn_drop.p if self.training else 0.,
|
| 149 |
+
scale=self.scale,
|
| 150 |
+
)
|
| 151 |
+
else:
|
| 152 |
+
q = q * self.scale
|
| 153 |
+
attn = q @ k.transpose(-2, -1)
|
| 154 |
+
|
| 155 |
+
attn = attn.softmax(dim=-1)
|
| 156 |
+
attn = self.attn_drop(attn)
|
| 157 |
+
x = attn @ v
|
| 158 |
+
|
| 159 |
+
x = x.transpose(1, 2).reshape(B, N, C)
|
| 160 |
+
x = self.proj(x)
|
| 161 |
+
x = self.proj_drop(x)
|
| 162 |
+
return x
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
class MemEffAttention(Attention):
|
| 166 |
+
def forward(self, x: torch.Tensor, attn_bias=None) -> torch.Tensor:
|
| 167 |
+
if not XFORMERS_AVAILABLE:
|
| 168 |
+
if attn_bias is not None:
|
| 169 |
+
raise AssertionError("xFormers is required for using nested tensors")
|
| 170 |
+
return super().forward(x)
|
| 171 |
+
|
| 172 |
+
B, N, C = x.shape
|
| 173 |
+
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads)
|
| 174 |
+
|
| 175 |
+
q, k, v = unbind(qkv, 2)
|
| 176 |
+
|
| 177 |
+
x = memory_efficient_attention(q, k, v, attn_bias=attn_bias)
|
| 178 |
+
x = x.reshape([B, N, C])
|
| 179 |
+
|
| 180 |
+
x = self.proj(x)
|
| 181 |
+
x = self.proj_drop(x)
|
| 182 |
+
return x
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
class Mlp(nn.Module):
|
| 186 |
+
def __init__(
|
| 187 |
+
self,
|
| 188 |
+
in_features: int,
|
| 189 |
+
hidden_features: Optional[int] = None,
|
| 190 |
+
out_features: Optional[int] = None,
|
| 191 |
+
act_layer: Callable[..., nn.Module] = nn.GELU,
|
| 192 |
+
drop: float = 0.0,
|
| 193 |
+
bias: bool = True,
|
| 194 |
+
) -> None:
|
| 195 |
+
super().__init__()
|
| 196 |
+
out_features = out_features or in_features
|
| 197 |
+
hidden_features = hidden_features or in_features
|
| 198 |
+
self.fc1 = nn.Linear(in_features, hidden_features, bias=bias)
|
| 199 |
+
self.act = act_layer()
|
| 200 |
+
self.fc2 = nn.Linear(hidden_features, out_features, bias=bias)
|
| 201 |
+
self.drop = nn.Dropout(drop)
|
| 202 |
+
|
| 203 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 204 |
+
x = self.fc1(x)
|
| 205 |
+
x = self.act(x)
|
| 206 |
+
x = self.drop(x)
|
| 207 |
+
x = self.fc2(x)
|
| 208 |
+
x = self.drop(x)
|
| 209 |
+
return x
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
class SwiGLUFFN(nn.Module):
|
| 213 |
+
def __init__(
|
| 214 |
+
self,
|
| 215 |
+
in_features: int,
|
| 216 |
+
hidden_features: Optional[int] = None,
|
| 217 |
+
out_features: Optional[int] = None,
|
| 218 |
+
act_layer: Callable[..., nn.Module] = None,
|
| 219 |
+
drop: float = 0.0,
|
| 220 |
+
bias: bool = True,
|
| 221 |
+
) -> None:
|
| 222 |
+
super().__init__()
|
| 223 |
+
out_features = out_features or in_features
|
| 224 |
+
hidden_features = hidden_features or in_features
|
| 225 |
+
self.w12 = nn.Linear(in_features, 2 * hidden_features, bias=bias)
|
| 226 |
+
self.w3 = nn.Linear(hidden_features, out_features, bias=bias)
|
| 227 |
+
|
| 228 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 229 |
+
x12 = self.w12(x)
|
| 230 |
+
x1, x2 = x12.chunk(2, dim=-1)
|
| 231 |
+
hidden = F.silu(x1) * x2
|
| 232 |
+
return self.w3(hidden)
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
if not XFORMERS_AVAILABLE:
|
| 236 |
+
SwiGLU = SwiGLUFFN
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
class SwiGLUFFNFused(SwiGLU):
|
| 240 |
+
def __init__(
|
| 241 |
+
self,
|
| 242 |
+
in_features: int,
|
| 243 |
+
hidden_features: Optional[int] = None,
|
| 244 |
+
out_features: Optional[int] = None,
|
| 245 |
+
act_layer: Callable[..., nn.Module] = None,
|
| 246 |
+
drop: float = 0.0,
|
| 247 |
+
bias: bool = True,
|
| 248 |
+
) -> None:
|
| 249 |
+
out_features = out_features or in_features
|
| 250 |
+
hidden_features = hidden_features or in_features
|
| 251 |
+
hidden_features = (int(hidden_features * 2 / 3) + 7) // 8 * 8
|
| 252 |
+
super().__init__(
|
| 253 |
+
in_features=in_features,
|
| 254 |
+
hidden_features=hidden_features,
|
| 255 |
+
out_features=out_features,
|
| 256 |
+
bias=bias,
|
| 257 |
+
)
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
def drop_path(x, drop_prob: float = 0.0, training: bool = False):
|
| 261 |
+
if drop_prob == 0.0 or not training:
|
| 262 |
+
return x
|
| 263 |
+
keep_prob = 1 - drop_prob
|
| 264 |
+
shape = (x.shape[0],) + (1,) * (x.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
|
| 265 |
+
random_tensor = x.new_empty(shape).bernoulli_(keep_prob)
|
| 266 |
+
if keep_prob > 0.0:
|
| 267 |
+
random_tensor.div_(keep_prob)
|
| 268 |
+
output = x * random_tensor
|
| 269 |
+
return output
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
class DropPath(nn.Module):
|
| 273 |
+
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks)."""
|
| 274 |
+
|
| 275 |
+
def __init__(self, drop_prob=None):
|
| 276 |
+
super(DropPath, self).__init__()
|
| 277 |
+
self.drop_prob = drop_prob
|
| 278 |
+
|
| 279 |
+
def forward(self, x):
|
| 280 |
+
return drop_path(x, self.drop_prob, self.training)
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
class LayerScale(nn.Module):
|
| 284 |
+
def __init__(
|
| 285 |
+
self,
|
| 286 |
+
dim: int,
|
| 287 |
+
init_values: Union[float, torch.Tensor] = 1e-5,
|
| 288 |
+
inplace: bool = False,
|
| 289 |
+
) -> None:
|
| 290 |
+
super().__init__()
|
| 291 |
+
self.inplace = inplace
|
| 292 |
+
self.grandma = nn.Parameter(init_values * torch.ones(dim))
|
| 293 |
+
|
| 294 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 295 |
+
return x.mul_(self.grandma) if self.inplace else x * self.grandma
|
| 296 |
+
|
| 297 |
+
def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs):
|
| 298 |
+
# Huggingface is absurd and it will rename strings that contain `gamma`, which means that the normal DINO implementation
|
| 299 |
+
# of LayerScale won't work with HFHub. So we rename the variable to 'grandma', and support loading checkpoints in either
|
| 300 |
+
# format
|
| 301 |
+
key_a = f'{prefix}gamma'
|
| 302 |
+
key_b = f'{prefix}grandma'
|
| 303 |
+
if key_a in state_dict:
|
| 304 |
+
gamma = state_dict[key_a]
|
| 305 |
+
elif key_b in state_dict:
|
| 306 |
+
gamma = state_dict[key_b]
|
| 307 |
+
else:
|
| 308 |
+
if strict:
|
| 309 |
+
raise KeyError(f"Couldn't find the key {key_a} nor {key_b} in the state dict!")
|
| 310 |
+
else:
|
| 311 |
+
missing_keys.append(key_a)
|
| 312 |
+
missing_keys.append(key_b)
|
| 313 |
+
unexpected_keys.extend(state_dict.keys())
|
| 314 |
+
gamma = None
|
| 315 |
+
|
| 316 |
+
if gamma is not None:
|
| 317 |
+
self.grandma.data.copy_(gamma)
|
| 318 |
+
|
| 319 |
+
# return super()._load_from_state_dict(state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs)
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
class Block(nn.Module):
|
| 323 |
+
def __init__(
|
| 324 |
+
self,
|
| 325 |
+
dim: int,
|
| 326 |
+
num_heads: int,
|
| 327 |
+
mlp_ratio: float = 4.0,
|
| 328 |
+
qkv_bias: bool = False,
|
| 329 |
+
proj_bias: bool = True,
|
| 330 |
+
ffn_bias: bool = True,
|
| 331 |
+
drop: float = 0.0,
|
| 332 |
+
attn_drop: float = 0.0,
|
| 333 |
+
init_values=None,
|
| 334 |
+
drop_path: float = 0.0,
|
| 335 |
+
act_layer: Callable[..., nn.Module] = nn.GELU,
|
| 336 |
+
norm_layer: Callable[..., nn.Module] = nn.LayerNorm,
|
| 337 |
+
attn_class: Callable[..., nn.Module] = Attention,
|
| 338 |
+
ffn_layer: Callable[..., nn.Module] = Mlp,
|
| 339 |
+
) -> None:
|
| 340 |
+
super().__init__()
|
| 341 |
+
# print(f"biases: qkv: {qkv_bias}, proj: {proj_bias}, ffn: {ffn_bias}")
|
| 342 |
+
self.norm1 = norm_layer(dim)
|
| 343 |
+
self.attn = attn_class(
|
| 344 |
+
dim,
|
| 345 |
+
num_heads=num_heads,
|
| 346 |
+
qkv_bias=qkv_bias,
|
| 347 |
+
proj_bias=proj_bias,
|
| 348 |
+
attn_drop=attn_drop,
|
| 349 |
+
proj_drop=drop,
|
| 350 |
+
)
|
| 351 |
+
self.ls1 = LayerScale(dim, init_values=init_values) if init_values else nn.Identity()
|
| 352 |
+
self.drop_path1 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
|
| 353 |
+
|
| 354 |
+
self.norm2 = norm_layer(dim)
|
| 355 |
+
mlp_hidden_dim = int(dim * mlp_ratio)
|
| 356 |
+
self.mlp = ffn_layer(
|
| 357 |
+
in_features=dim,
|
| 358 |
+
hidden_features=mlp_hidden_dim,
|
| 359 |
+
act_layer=act_layer,
|
| 360 |
+
drop=drop,
|
| 361 |
+
bias=ffn_bias,
|
| 362 |
+
)
|
| 363 |
+
self.ls2 = LayerScale(dim, init_values=init_values) if init_values else nn.Identity()
|
| 364 |
+
self.drop_path2 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
|
| 365 |
+
|
| 366 |
+
self.sample_drop_ratio = drop_path
|
| 367 |
+
|
| 368 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 369 |
+
def attn_residual_func(x: torch.Tensor) -> torch.Tensor:
|
| 370 |
+
return self.ls1(self.attn(self.norm1(x)))
|
| 371 |
+
|
| 372 |
+
def ffn_residual_func(x: torch.Tensor) -> torch.Tensor:
|
| 373 |
+
return self.ls2(self.mlp(self.norm2(x)))
|
| 374 |
+
|
| 375 |
+
if self.training and self.sample_drop_ratio > 0.1:
|
| 376 |
+
# the overhead is compensated only for a drop path rate larger than 0.1
|
| 377 |
+
x = drop_add_residual_stochastic_depth(
|
| 378 |
+
x,
|
| 379 |
+
residual_func=attn_residual_func,
|
| 380 |
+
sample_drop_ratio=self.sample_drop_ratio,
|
| 381 |
+
)
|
| 382 |
+
x = drop_add_residual_stochastic_depth(
|
| 383 |
+
x,
|
| 384 |
+
residual_func=ffn_residual_func,
|
| 385 |
+
sample_drop_ratio=self.sample_drop_ratio,
|
| 386 |
+
)
|
| 387 |
+
elif self.training and self.sample_drop_ratio > 0.0:
|
| 388 |
+
x = x + self.drop_path1(attn_residual_func(x))
|
| 389 |
+
x = x + self.drop_path1(ffn_residual_func(x)) # FIXME: drop_path2
|
| 390 |
+
else:
|
| 391 |
+
x = x + attn_residual_func(x)
|
| 392 |
+
x = x + ffn_residual_func(x)
|
| 393 |
+
return x
|
| 394 |
+
|
| 395 |
+
|
| 396 |
+
class NestedTensorBlock(Block):
|
| 397 |
+
def forward_nested(self, x_list: List[torch.Tensor]) -> List[torch.Tensor]:
|
| 398 |
+
"""
|
| 399 |
+
x_list contains a list of tensors to nest together and run
|
| 400 |
+
"""
|
| 401 |
+
assert isinstance(self.attn, MemEffAttention)
|
| 402 |
+
|
| 403 |
+
if self.training and self.sample_drop_ratio > 0.0:
|
| 404 |
+
|
| 405 |
+
def attn_residual_func(x: torch.Tensor, attn_bias=None) -> torch.Tensor:
|
| 406 |
+
return self.attn(self.norm1(x), attn_bias=attn_bias)
|
| 407 |
+
|
| 408 |
+
def ffn_residual_func(x: torch.Tensor, attn_bias=None) -> torch.Tensor:
|
| 409 |
+
return self.mlp(self.norm2(x))
|
| 410 |
+
|
| 411 |
+
x_list = drop_add_residual_stochastic_depth_list(
|
| 412 |
+
x_list,
|
| 413 |
+
residual_func=attn_residual_func,
|
| 414 |
+
sample_drop_ratio=self.sample_drop_ratio,
|
| 415 |
+
scaling_vector=self.ls1.grandma if isinstance(self.ls1, LayerScale) else None,
|
| 416 |
+
)
|
| 417 |
+
x_list = drop_add_residual_stochastic_depth_list(
|
| 418 |
+
x_list,
|
| 419 |
+
residual_func=ffn_residual_func,
|
| 420 |
+
sample_drop_ratio=self.sample_drop_ratio,
|
| 421 |
+
scaling_vector=self.ls2.grandma if isinstance(self.ls1, LayerScale) else None,
|
| 422 |
+
)
|
| 423 |
+
return x_list
|
| 424 |
+
else:
|
| 425 |
+
|
| 426 |
+
def attn_residual_func(x: torch.Tensor, attn_bias=None) -> torch.Tensor:
|
| 427 |
+
return self.ls1(self.attn(self.norm1(x), attn_bias=attn_bias))
|
| 428 |
+
|
| 429 |
+
def ffn_residual_func(x: torch.Tensor, attn_bias=None) -> torch.Tensor:
|
| 430 |
+
return self.ls2(self.mlp(self.norm2(x)))
|
| 431 |
+
|
| 432 |
+
attn_bias, x = get_attn_bias_and_cat(x_list)
|
| 433 |
+
x = x + attn_residual_func(x, attn_bias=attn_bias)
|
| 434 |
+
x = x + ffn_residual_func(x)
|
| 435 |
+
return attn_bias.split(x)
|
| 436 |
+
|
| 437 |
+
def forward(self, x_or_x_list):
|
| 438 |
+
if isinstance(x_or_x_list, torch.Tensor):
|
| 439 |
+
return super().forward(x_or_x_list)
|
| 440 |
+
elif isinstance(x_or_x_list, list):
|
| 441 |
+
if not XFORMERS_AVAILABLE:
|
| 442 |
+
raise AssertionError("xFormers is required for using nested tensors")
|
| 443 |
+
return self.forward_nested(x_or_x_list)
|
| 444 |
+
else:
|
| 445 |
+
raise AssertionError
|
| 446 |
+
|
| 447 |
+
|
| 448 |
+
def drop_add_residual_stochastic_depth(
|
| 449 |
+
x: torch.Tensor,
|
| 450 |
+
residual_func: Callable[[torch.Tensor], torch.Tensor],
|
| 451 |
+
sample_drop_ratio: float = 0.0,
|
| 452 |
+
) -> torch.Tensor:
|
| 453 |
+
# 1) extract subset using permutation
|
| 454 |
+
b, n, d = x.shape
|
| 455 |
+
sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1)
|
| 456 |
+
brange = (torch.randperm(b, device=x.device))[:sample_subset_size]
|
| 457 |
+
x_subset = x[brange]
|
| 458 |
+
|
| 459 |
+
# 2) apply residual_func to get residual
|
| 460 |
+
residual = residual_func(x_subset)
|
| 461 |
+
|
| 462 |
+
x_flat = x.flatten(1)
|
| 463 |
+
residual = residual.flatten(1)
|
| 464 |
+
|
| 465 |
+
residual_scale_factor = b / sample_subset_size
|
| 466 |
+
|
| 467 |
+
# 3) add the residual
|
| 468 |
+
x_plus_residual = torch.index_add(x_flat, 0, brange, residual.to(dtype=x.dtype), alpha=residual_scale_factor)
|
| 469 |
+
return x_plus_residual.view_as(x)
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
def get_branges_scales(x, sample_drop_ratio=0.0):
|
| 473 |
+
b, n, d = x.shape
|
| 474 |
+
sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1)
|
| 475 |
+
brange = (torch.randperm(b, device=x.device))[:sample_subset_size]
|
| 476 |
+
residual_scale_factor = b / sample_subset_size
|
| 477 |
+
return brange, residual_scale_factor
|
| 478 |
+
|
| 479 |
+
|
| 480 |
+
def add_residual(x, brange, residual, residual_scale_factor, scaling_vector=None):
|
| 481 |
+
if scaling_vector is None:
|
| 482 |
+
x_flat = x.flatten(1)
|
| 483 |
+
residual = residual.flatten(1)
|
| 484 |
+
x_plus_residual = torch.index_add(x_flat, 0, brange, residual.to(dtype=x.dtype), alpha=residual_scale_factor)
|
| 485 |
+
else:
|
| 486 |
+
x_plus_residual = scaled_index_add(
|
| 487 |
+
x, brange, residual.to(dtype=x.dtype), scaling=scaling_vector, alpha=residual_scale_factor
|
| 488 |
+
)
|
| 489 |
+
return x_plus_residual
|
| 490 |
+
|
| 491 |
+
|
| 492 |
+
attn_bias_cache: Dict[Tuple, Any] = {}
|
| 493 |
+
|
| 494 |
+
|
| 495 |
+
def get_attn_bias_and_cat(x_list, branges=None):
|
| 496 |
+
"""
|
| 497 |
+
this will perform the index select, cat the tensors, and provide the attn_bias from cache
|
| 498 |
+
"""
|
| 499 |
+
batch_sizes = [b.shape[0] for b in branges] if branges is not None else [x.shape[0] for x in x_list]
|
| 500 |
+
all_shapes = tuple((b, x.shape[1]) for b, x in zip(batch_sizes, x_list))
|
| 501 |
+
if all_shapes not in attn_bias_cache.keys():
|
| 502 |
+
seqlens = []
|
| 503 |
+
for b, x in zip(batch_sizes, x_list):
|
| 504 |
+
for _ in range(b):
|
| 505 |
+
seqlens.append(x.shape[1])
|
| 506 |
+
attn_bias = fmha.BlockDiagonalMask.from_seqlens(seqlens)
|
| 507 |
+
attn_bias._batch_sizes = batch_sizes
|
| 508 |
+
attn_bias_cache[all_shapes] = attn_bias
|
| 509 |
+
|
| 510 |
+
if branges is not None:
|
| 511 |
+
cat_tensors = index_select_cat([x.flatten(1) for x in x_list], branges).view(1, -1, x_list[0].shape[-1])
|
| 512 |
+
else:
|
| 513 |
+
tensors_bs1 = tuple(x.reshape([1, -1, *x.shape[2:]]) for x in x_list)
|
| 514 |
+
cat_tensors = torch.cat(tensors_bs1, dim=1)
|
| 515 |
+
|
| 516 |
+
return attn_bias_cache[all_shapes], cat_tensors
|
| 517 |
+
|
| 518 |
+
|
| 519 |
+
def drop_add_residual_stochastic_depth_list(
|
| 520 |
+
x_list: List[torch.Tensor],
|
| 521 |
+
residual_func: Callable[[torch.Tensor, Any], torch.Tensor],
|
| 522 |
+
sample_drop_ratio: float = 0.0,
|
| 523 |
+
scaling_vector=None,
|
| 524 |
+
) -> torch.Tensor:
|
| 525 |
+
# 1) generate random set of indices for dropping samples in the batch
|
| 526 |
+
branges_scales = [get_branges_scales(x, sample_drop_ratio=sample_drop_ratio) for x in x_list]
|
| 527 |
+
branges = [s[0] for s in branges_scales]
|
| 528 |
+
residual_scale_factors = [s[1] for s in branges_scales]
|
| 529 |
+
|
| 530 |
+
# 2) get attention bias and index+concat the tensors
|
| 531 |
+
attn_bias, x_cat = get_attn_bias_and_cat(x_list, branges)
|
| 532 |
+
|
| 533 |
+
# 3) apply residual_func to get residual, and split the result
|
| 534 |
+
residual_list = attn_bias.split(residual_func(x_cat, attn_bias=attn_bias)) # type: ignore
|
| 535 |
+
|
| 536 |
+
outputs = []
|
| 537 |
+
for x, brange, residual, residual_scale_factor in zip(x_list, branges, residual_list, residual_scale_factors):
|
| 538 |
+
outputs.append(add_residual(x, brange, residual, residual_scale_factor, scaling_vector).view_as(x))
|
| 539 |
+
return outputs
|
| 540 |
+
|
| 541 |
+
|
| 542 |
+
def named_apply(fn: Callable, module: nn.Module, name="", depth_first=True, include_root=False) -> nn.Module:
|
| 543 |
+
if not depth_first and include_root:
|
| 544 |
+
fn(module=module, name=name)
|
| 545 |
+
for child_name, child_module in module.named_children():
|
| 546 |
+
child_name = ".".join((name, child_name)) if name else child_name
|
| 547 |
+
named_apply(fn=fn, module=child_module, name=child_name, depth_first=depth_first, include_root=True)
|
| 548 |
+
if depth_first and include_root:
|
| 549 |
+
fn(module=module, name=name)
|
| 550 |
+
return module
|
| 551 |
+
|
| 552 |
+
|
| 553 |
+
class BlockChunk(nn.ModuleList):
|
| 554 |
+
def forward(self, x):
|
| 555 |
+
for b in self:
|
| 556 |
+
x = b(x)
|
| 557 |
+
return x
|
| 558 |
+
|
| 559 |
+
|
| 560 |
+
class DinoVisionTransformer(nn.Module):
|
| 561 |
+
def __init__(
|
| 562 |
+
self,
|
| 563 |
+
img_size=224,
|
| 564 |
+
patch_size=16,
|
| 565 |
+
in_chans=3,
|
| 566 |
+
embed_dim=768,
|
| 567 |
+
depth=12,
|
| 568 |
+
num_heads=12,
|
| 569 |
+
mlp_ratio=4.0,
|
| 570 |
+
qkv_bias=True,
|
| 571 |
+
ffn_bias=True,
|
| 572 |
+
proj_bias=True,
|
| 573 |
+
drop_path_rate=0.0,
|
| 574 |
+
drop_path_uniform=False,
|
| 575 |
+
init_values=None, # for layerscale: None or 0 => no layerscale
|
| 576 |
+
embed_layer=PatchEmbed,
|
| 577 |
+
act_layer=nn.GELU,
|
| 578 |
+
block_fn=Block,
|
| 579 |
+
ffn_layer="mlp",
|
| 580 |
+
block_chunks=1,
|
| 581 |
+
num_register_tokens=0,
|
| 582 |
+
interpolate_antialias=False,
|
| 583 |
+
interpolate_offset=0.1,
|
| 584 |
+
):
|
| 585 |
+
"""
|
| 586 |
+
Args:
|
| 587 |
+
img_size (int, tuple): input image size
|
| 588 |
+
patch_size (int, tuple): patch size
|
| 589 |
+
in_chans (int): number of input channels
|
| 590 |
+
embed_dim (int): embedding dimension
|
| 591 |
+
depth (int): depth of transformer
|
| 592 |
+
num_heads (int): number of attention heads
|
| 593 |
+
mlp_ratio (int): ratio of mlp hidden dim to embedding dim
|
| 594 |
+
qkv_bias (bool): enable bias for qkv if True
|
| 595 |
+
proj_bias (bool): enable bias for proj in attn if True
|
| 596 |
+
ffn_bias (bool): enable bias for ffn if True
|
| 597 |
+
drop_path_rate (float): stochastic depth rate
|
| 598 |
+
drop_path_uniform (bool): apply uniform drop rate across blocks
|
| 599 |
+
weight_init (str): weight init scheme
|
| 600 |
+
init_values (float): layer-scale init values
|
| 601 |
+
embed_layer (nn.Module): patch embedding layer
|
| 602 |
+
act_layer (nn.Module): MLP activation layer
|
| 603 |
+
block_fn (nn.Module): transformer block class
|
| 604 |
+
ffn_layer (str): "mlp", "swiglu", "swiglufused" or "identity"
|
| 605 |
+
block_chunks: (int) split block sequence into block_chunks units for FSDP wrap
|
| 606 |
+
num_register_tokens: (int) number of extra cls tokens (so-called "registers")
|
| 607 |
+
interpolate_antialias: (str) flag to apply anti-aliasing when interpolating positional embeddings
|
| 608 |
+
interpolate_offset: (float) work-around offset to apply when interpolating positional embeddings
|
| 609 |
+
"""
|
| 610 |
+
super().__init__()
|
| 611 |
+
norm_layer = partial(nn.LayerNorm, eps=1e-6)
|
| 612 |
+
|
| 613 |
+
self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models
|
| 614 |
+
self.num_tokens = 1
|
| 615 |
+
self.n_blocks = depth
|
| 616 |
+
self.num_heads = num_heads
|
| 617 |
+
self.patch_size = patch_size
|
| 618 |
+
self.num_register_tokens = num_register_tokens
|
| 619 |
+
self.interpolate_antialias = interpolate_antialias
|
| 620 |
+
self.interpolate_offset = interpolate_offset
|
| 621 |
+
|
| 622 |
+
self.patch_embed = embed_layer(img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim)
|
| 623 |
+
num_patches = self.patch_embed.num_patches
|
| 624 |
+
|
| 625 |
+
self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
|
| 626 |
+
self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + self.num_tokens, embed_dim))
|
| 627 |
+
assert num_register_tokens >= 0
|
| 628 |
+
self.register_tokens = (
|
| 629 |
+
nn.Parameter(torch.zeros(1, num_register_tokens, embed_dim)) if num_register_tokens else None
|
| 630 |
+
)
|
| 631 |
+
|
| 632 |
+
if drop_path_uniform is True:
|
| 633 |
+
dpr = [drop_path_rate] * depth
|
| 634 |
+
else:
|
| 635 |
+
dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule
|
| 636 |
+
|
| 637 |
+
if ffn_layer == "mlp":
|
| 638 |
+
ffn_layer = Mlp
|
| 639 |
+
elif ffn_layer == "swiglufused" or ffn_layer == "swiglu":
|
| 640 |
+
ffn_layer = SwiGLUFFNFused
|
| 641 |
+
elif ffn_layer == "identity":
|
| 642 |
+
def f(*args, **kwargs):
|
| 643 |
+
return nn.Identity()
|
| 644 |
+
|
| 645 |
+
ffn_layer = f
|
| 646 |
+
else:
|
| 647 |
+
raise NotImplementedError
|
| 648 |
+
|
| 649 |
+
blocks_list = [
|
| 650 |
+
block_fn(
|
| 651 |
+
dim=embed_dim,
|
| 652 |
+
num_heads=num_heads,
|
| 653 |
+
mlp_ratio=mlp_ratio,
|
| 654 |
+
qkv_bias=qkv_bias,
|
| 655 |
+
proj_bias=proj_bias,
|
| 656 |
+
ffn_bias=ffn_bias,
|
| 657 |
+
drop_path=dpr[i],
|
| 658 |
+
norm_layer=norm_layer,
|
| 659 |
+
act_layer=act_layer,
|
| 660 |
+
ffn_layer=ffn_layer,
|
| 661 |
+
init_values=init_values,
|
| 662 |
+
)
|
| 663 |
+
for i in range(depth)
|
| 664 |
+
]
|
| 665 |
+
if block_chunks > 0:
|
| 666 |
+
self.chunked_blocks = True
|
| 667 |
+
chunked_blocks = []
|
| 668 |
+
chunksize = depth // block_chunks
|
| 669 |
+
for i in range(0, depth, chunksize):
|
| 670 |
+
# this is to keep the block index consistent if we chunk the block list
|
| 671 |
+
chunked_blocks.append([nn.Identity()] * i + blocks_list[i : i + chunksize])
|
| 672 |
+
self.blocks = nn.ModuleList([BlockChunk(p) for p in chunked_blocks])
|
| 673 |
+
else:
|
| 674 |
+
self.chunked_blocks = False
|
| 675 |
+
self.blocks = nn.ModuleList(blocks_list)
|
| 676 |
+
|
| 677 |
+
self.norm = norm_layer(embed_dim)
|
| 678 |
+
self.head = nn.Identity()
|
| 679 |
+
|
| 680 |
+
self.mask_token = nn.Parameter(torch.zeros(1, embed_dim))
|
| 681 |
+
|
| 682 |
+
def interpolate_pos_encoding(self, x, w, h):
|
| 683 |
+
previous_dtype = x.dtype
|
| 684 |
+
npatch = x.shape[1] - 1
|
| 685 |
+
N = self.pos_embed.shape[1] - 1
|
| 686 |
+
if npatch == N and w == h:
|
| 687 |
+
return self.pos_embed
|
| 688 |
+
pos_embed = self.pos_embed.float()
|
| 689 |
+
class_pos_embed = pos_embed[:, 0]
|
| 690 |
+
patch_pos_embed = pos_embed[:, 1:]
|
| 691 |
+
dim = x.shape[-1]
|
| 692 |
+
w0 = w // self.patch_size
|
| 693 |
+
h0 = h // self.patch_size
|
| 694 |
+
M = int(math.sqrt(N)) # Recover the number of patches in each dimension
|
| 695 |
+
assert N == M * M
|
| 696 |
+
kwargs = {}
|
| 697 |
+
if self.interpolate_offset:
|
| 698 |
+
# Historical kludge: add a small number to avoid floating point error in the interpolation, see https://github.com/facebookresearch/dino/issues/8
|
| 699 |
+
# Note: still needed for backward-compatibility, the underlying operators are using both output size and scale factors
|
| 700 |
+
sx = float(w0 + self.interpolate_offset) / M
|
| 701 |
+
sy = float(h0 + self.interpolate_offset) / M
|
| 702 |
+
kwargs["scale_factor"] = (sx, sy)
|
| 703 |
+
else:
|
| 704 |
+
# Simply specify an output size instead of a scale factor
|
| 705 |
+
kwargs["size"] = (w0, h0)
|
| 706 |
+
patch_pos_embed = nn.functional.interpolate(
|
| 707 |
+
patch_pos_embed.reshape(1, M, M, dim).permute(0, 3, 1, 2),
|
| 708 |
+
mode="bicubic",
|
| 709 |
+
antialias=self.interpolate_antialias,
|
| 710 |
+
**kwargs,
|
| 711 |
+
)
|
| 712 |
+
assert (w0, h0) == patch_pos_embed.shape[-2:]
|
| 713 |
+
patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
|
| 714 |
+
return torch.cat((class_pos_embed.unsqueeze(0), patch_pos_embed), dim=1).to(previous_dtype)
|
| 715 |
+
|
| 716 |
+
def prepare_tokens_with_masks(self, x, masks=None):
|
| 717 |
+
B, nc, w, h = x.shape
|
| 718 |
+
x = self.patch_embed(x)
|
| 719 |
+
if masks is not None:
|
| 720 |
+
x = torch.where(masks.unsqueeze(-1), self.mask_token.to(x.dtype).unsqueeze(0), x)
|
| 721 |
+
|
| 722 |
+
x = torch.cat((self.cls_token.expand(x.shape[0], -1, -1), x), dim=1)
|
| 723 |
+
x = x + self.interpolate_pos_encoding(x, w, h)
|
| 724 |
+
|
| 725 |
+
if self.register_tokens is not None:
|
| 726 |
+
x = torch.cat(
|
| 727 |
+
(
|
| 728 |
+
x[:, :1],
|
| 729 |
+
self.register_tokens.expand(x.shape[0], -1, -1),
|
| 730 |
+
x[:, 1:],
|
| 731 |
+
),
|
| 732 |
+
dim=1,
|
| 733 |
+
)
|
| 734 |
+
|
| 735 |
+
return x
|
| 736 |
+
|
| 737 |
+
def forward_features_list(self, x_list, masks_list):
|
| 738 |
+
x = [self.prepare_tokens_with_masks(x, masks) for x, masks in zip(x_list, masks_list)]
|
| 739 |
+
for blk in self.blocks:
|
| 740 |
+
x = blk(x)
|
| 741 |
+
|
| 742 |
+
all_x = x
|
| 743 |
+
output = []
|
| 744 |
+
for x, masks in zip(all_x, masks_list):
|
| 745 |
+
x_norm = self.norm(x)
|
| 746 |
+
output.append(
|
| 747 |
+
{
|
| 748 |
+
"x_norm_clstoken": x_norm[:, 0],
|
| 749 |
+
"x_norm_regtokens": x_norm[:, 1 : self.num_register_tokens + 1],
|
| 750 |
+
"x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :],
|
| 751 |
+
"x_prenorm": x,
|
| 752 |
+
"masks": masks,
|
| 753 |
+
}
|
| 754 |
+
)
|
| 755 |
+
return output
|
| 756 |
+
|
| 757 |
+
def forward_features(self, x, masks=None):
|
| 758 |
+
if isinstance(x, list):
|
| 759 |
+
return self.forward_features_list(x, masks)
|
| 760 |
+
|
| 761 |
+
x = self.prepare_tokens_with_masks(x, masks)
|
| 762 |
+
|
| 763 |
+
for blk in self.blocks:
|
| 764 |
+
x = blk(x)
|
| 765 |
+
|
| 766 |
+
x_norm = self.norm(x)
|
| 767 |
+
return {
|
| 768 |
+
"x_norm_clstoken": x_norm[:, 0],
|
| 769 |
+
"x_norm_regtokens": x_norm[:, 1 : self.num_register_tokens + 1],
|
| 770 |
+
"x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :],
|
| 771 |
+
"x_prenorm": x,
|
| 772 |
+
"masks": masks,
|
| 773 |
+
}
|
| 774 |
+
|
| 775 |
+
def _get_intermediate_layers_not_chunked(self, x, n=1):
|
| 776 |
+
x = self.prepare_tokens_with_masks(x)
|
| 777 |
+
# If n is an int, take the n last blocks. If it's a list, take them
|
| 778 |
+
output, total_block_len = [], len(self.blocks)
|
| 779 |
+
blocks_to_take = range(total_block_len - n, total_block_len) if isinstance(n, int) else n
|
| 780 |
+
for i, blk in enumerate(self.blocks):
|
| 781 |
+
x = blk(x)
|
| 782 |
+
if i in blocks_to_take:
|
| 783 |
+
output.append(x)
|
| 784 |
+
assert len(output) == len(blocks_to_take), f"only {len(output)} / {len(blocks_to_take)} blocks found"
|
| 785 |
+
return output
|
| 786 |
+
|
| 787 |
+
def _get_intermediate_layers_chunked(self, x, n=1):
|
| 788 |
+
x = self.prepare_tokens_with_masks(x)
|
| 789 |
+
output, i, total_block_len = [], 0, len(self.blocks[-1])
|
| 790 |
+
# If n is an int, take the n last blocks. If it's a list, take them
|
| 791 |
+
blocks_to_take = range(total_block_len - n, total_block_len) if isinstance(n, int) else n
|
| 792 |
+
for block_chunk in self.blocks:
|
| 793 |
+
for blk in block_chunk[i:]: # Passing the nn.Identity()
|
| 794 |
+
x = blk(x)
|
| 795 |
+
if i in blocks_to_take:
|
| 796 |
+
output.append(x)
|
| 797 |
+
i += 1
|
| 798 |
+
assert len(output) == len(blocks_to_take), f"only {len(output)} / {len(blocks_to_take)} blocks found"
|
| 799 |
+
return output
|
| 800 |
+
|
| 801 |
+
def get_intermediate_layers(
|
| 802 |
+
self,
|
| 803 |
+
x: torch.Tensor,
|
| 804 |
+
n: Union[int, Sequence] = 1, # Layers or n last layers to take
|
| 805 |
+
reshape: bool = False,
|
| 806 |
+
return_class_token: bool = False,
|
| 807 |
+
norm=True,
|
| 808 |
+
) -> Tuple[Union[torch.Tensor, Tuple[torch.Tensor]]]:
|
| 809 |
+
if self.chunked_blocks:
|
| 810 |
+
outputs = self._get_intermediate_layers_chunked(x, n)
|
| 811 |
+
else:
|
| 812 |
+
outputs = self._get_intermediate_layers_not_chunked(x, n)
|
| 813 |
+
if norm:
|
| 814 |
+
outputs = [self.norm(out) for out in outputs]
|
| 815 |
+
class_tokens = [out[:, 0] for out in outputs]
|
| 816 |
+
outputs = [out[:, 1 + self.num_register_tokens :] for out in outputs]
|
| 817 |
+
if reshape:
|
| 818 |
+
B, _, w, h = x.shape
|
| 819 |
+
outputs = [
|
| 820 |
+
out.reshape(B, w // self.patch_size, h // self.patch_size, -1).permute(0, 3, 1, 2).contiguous()
|
| 821 |
+
for out in outputs
|
| 822 |
+
]
|
| 823 |
+
if return_class_token:
|
| 824 |
+
return tuple(zip(outputs, class_tokens))
|
| 825 |
+
return tuple(outputs)
|
| 826 |
+
|
| 827 |
+
def forward(self, *args, is_training=False, **kwargs):
|
| 828 |
+
ret = self.forward_features(*args, **kwargs)
|
| 829 |
+
if is_training:
|
| 830 |
+
return ret
|
| 831 |
+
else:
|
| 832 |
+
return self.head(ret["x_norm_clstoken"])
|
| 833 |
+
|
| 834 |
+
|
| 835 |
+
def vit_small(patch_size=16, num_register_tokens=0, **kwargs):
|
| 836 |
+
model = DinoVisionTransformer(
|
| 837 |
+
patch_size=patch_size,
|
| 838 |
+
embed_dim=384,
|
| 839 |
+
depth=12,
|
| 840 |
+
num_heads=6,
|
| 841 |
+
mlp_ratio=4,
|
| 842 |
+
block_fn=partial(Block, attn_class=MemEffAttention),
|
| 843 |
+
num_register_tokens=num_register_tokens,
|
| 844 |
+
**kwargs,
|
| 845 |
+
)
|
| 846 |
+
return model
|
| 847 |
+
|
| 848 |
+
|
| 849 |
+
def vit_base(patch_size=16, num_register_tokens=0, **kwargs):
|
| 850 |
+
model = DinoVisionTransformer(
|
| 851 |
+
patch_size=patch_size,
|
| 852 |
+
embed_dim=768,
|
| 853 |
+
depth=12,
|
| 854 |
+
num_heads=12,
|
| 855 |
+
mlp_ratio=4,
|
| 856 |
+
block_fn=partial(Block, attn_class=MemEffAttention),
|
| 857 |
+
num_register_tokens=num_register_tokens,
|
| 858 |
+
**kwargs,
|
| 859 |
+
)
|
| 860 |
+
return model
|
| 861 |
+
|
| 862 |
+
|
| 863 |
+
def vit_large(patch_size=16, num_register_tokens=0, **kwargs):
|
| 864 |
+
model = DinoVisionTransformer(
|
| 865 |
+
patch_size=patch_size,
|
| 866 |
+
embed_dim=1024,
|
| 867 |
+
depth=24,
|
| 868 |
+
num_heads=16,
|
| 869 |
+
mlp_ratio=4,
|
| 870 |
+
block_fn=partial(Block, attn_class=MemEffAttention),
|
| 871 |
+
num_register_tokens=num_register_tokens,
|
| 872 |
+
**kwargs,
|
| 873 |
+
)
|
| 874 |
+
return model
|
| 875 |
+
|
| 876 |
+
|
| 877 |
+
def vit_giant2(patch_size=16, num_register_tokens=0, **kwargs):
|
| 878 |
+
"""
|
| 879 |
+
Close to ViT-giant, with embed-dim 1536 and 24 heads => embed-dim per head 64
|
| 880 |
+
"""
|
| 881 |
+
model = DinoVisionTransformer(
|
| 882 |
+
patch_size=patch_size,
|
| 883 |
+
embed_dim=1536,
|
| 884 |
+
depth=40,
|
| 885 |
+
num_heads=24,
|
| 886 |
+
mlp_ratio=4,
|
| 887 |
+
block_fn=partial(Block, attn_class=MemEffAttention),
|
| 888 |
+
num_register_tokens=num_register_tokens,
|
| 889 |
+
**kwargs,
|
| 890 |
+
)
|
| 891 |
+
return model
|
| 892 |
+
|
| 893 |
+
|
| 894 |
+
class Weights(Enum):
|
| 895 |
+
LVD142M = "LVD142M"
|
| 896 |
+
|
| 897 |
+
|
| 898 |
+
def _make_dinov2_model(
|
| 899 |
+
*,
|
| 900 |
+
arch_name: str = "vit_large",
|
| 901 |
+
img_size: int = 518,
|
| 902 |
+
patch_size: int = 14,
|
| 903 |
+
init_values: float = 1.0,
|
| 904 |
+
ffn_layer: str = "mlp",
|
| 905 |
+
block_chunks: int = 0,
|
| 906 |
+
num_register_tokens: int = 0,
|
| 907 |
+
interpolate_antialias: bool = False,
|
| 908 |
+
interpolate_offset: float = 0.1,
|
| 909 |
+
weights: Union[Weights, str] = Weights.LVD142M,
|
| 910 |
+
**kwargs,
|
| 911 |
+
):
|
| 912 |
+
if isinstance(weights, str):
|
| 913 |
+
try:
|
| 914 |
+
weights = Weights[weights]
|
| 915 |
+
except KeyError:
|
| 916 |
+
raise AssertionError(f"Unsupported weights: {weights}")
|
| 917 |
+
|
| 918 |
+
vit_kwargs = dict(
|
| 919 |
+
img_size=img_size,
|
| 920 |
+
patch_size=patch_size,
|
| 921 |
+
init_values=init_values,
|
| 922 |
+
ffn_layer=ffn_layer,
|
| 923 |
+
block_chunks=block_chunks,
|
| 924 |
+
num_register_tokens=num_register_tokens,
|
| 925 |
+
interpolate_antialias=interpolate_antialias,
|
| 926 |
+
interpolate_offset=interpolate_offset,
|
| 927 |
+
)
|
| 928 |
+
vit_kwargs.update(**kwargs)
|
| 929 |
+
model = sys.modules[__name__].__dict__[arch_name](**vit_kwargs)
|
| 930 |
+
|
| 931 |
+
return model
|
| 932 |
+
|
| 933 |
+
|
| 934 |
+
def dinov2_vits14(**kwargs):
|
| 935 |
+
"""
|
| 936 |
+
DINOv2 ViT-S/14 model (optionally) pretrained on the LVD-142M dataset.
|
| 937 |
+
"""
|
| 938 |
+
return _make_dinov2_model(arch_name="vit_small", **kwargs)
|
| 939 |
+
|
| 940 |
+
|
| 941 |
+
def dinov2_vitb14(**kwargs):
|
| 942 |
+
"""
|
| 943 |
+
DINOv2 ViT-B/14 model (optionally) pretrained on the LVD-142M dataset.
|
| 944 |
+
"""
|
| 945 |
+
return _make_dinov2_model(arch_name="vit_base", **kwargs)
|
| 946 |
+
|
| 947 |
+
|
| 948 |
+
def dinov2_vitl14(**kwargs):
|
| 949 |
+
"""
|
| 950 |
+
DINOv2 ViT-L/14 model (optionally) pretrained on the LVD-142M dataset.
|
| 951 |
+
"""
|
| 952 |
+
return _make_dinov2_model(arch_name="vit_large", **kwargs)
|
| 953 |
+
|
| 954 |
+
|
| 955 |
+
def dinov2_vitg14(**kwargs):
|
| 956 |
+
"""
|
| 957 |
+
DINOv2 ViT-g/14 model (optionally) pretrained on the LVD-142M dataset.
|
| 958 |
+
"""
|
| 959 |
+
return _make_dinov2_model(
|
| 960 |
+
arch_name="vit_giant2",
|
| 961 |
+
ffn_layer="swiglufused",
|
| 962 |
+
**kwargs,
|
| 963 |
+
)
|
| 964 |
+
|
| 965 |
+
|
| 966 |
+
def dinov2_vits14_reg(**kwargs):
|
| 967 |
+
"""
|
| 968 |
+
DINOv2 ViT-S/14 model with registers (optionally) pretrained on the LVD-142M dataset.
|
| 969 |
+
"""
|
| 970 |
+
return _make_dinov2_model(
|
| 971 |
+
arch_name="vit_small",
|
| 972 |
+
num_register_tokens=4,
|
| 973 |
+
interpolate_antialias=True,
|
| 974 |
+
interpolate_offset=0.0,
|
| 975 |
+
**kwargs,
|
| 976 |
+
)
|
| 977 |
+
|
| 978 |
+
|
| 979 |
+
def dinov2_vitb14_reg(**kwargs):
|
| 980 |
+
"""
|
| 981 |
+
DINOv2 ViT-B/14 model with registers (optionally) pretrained on the LVD-142M dataset.
|
| 982 |
+
"""
|
| 983 |
+
return _make_dinov2_model(
|
| 984 |
+
arch_name="vit_base",
|
| 985 |
+
num_register_tokens=4,
|
| 986 |
+
interpolate_antialias=True,
|
| 987 |
+
interpolate_offset=0.0,
|
| 988 |
+
**kwargs,
|
| 989 |
+
)
|
| 990 |
+
|
| 991 |
+
|
| 992 |
+
def dinov2_vitl14_reg(**kwargs):
|
| 993 |
+
"""
|
| 994 |
+
DINOv2 ViT-L/14 model with registers (optionally) pretrained on the LVD-142M dataset.
|
| 995 |
+
"""
|
| 996 |
+
return _make_dinov2_model(
|
| 997 |
+
arch_name="vit_large",
|
| 998 |
+
num_register_tokens=4,
|
| 999 |
+
interpolate_antialias=True,
|
| 1000 |
+
interpolate_offset=0.0,
|
| 1001 |
+
**kwargs,
|
| 1002 |
+
)
|
| 1003 |
+
|
| 1004 |
+
|
| 1005 |
+
def dinov2_vitg14_reg(**kwargs):
|
| 1006 |
+
"""
|
| 1007 |
+
DINOv2 ViT-g/14 model with registers (optionally) pretrained on the LVD-142M dataset.
|
| 1008 |
+
"""
|
| 1009 |
+
return _make_dinov2_model(
|
| 1010 |
+
arch_name="vit_giant2",
|
| 1011 |
+
ffn_layer="swiglufused",
|
| 1012 |
+
num_register_tokens=4,
|
| 1013 |
+
interpolate_antialias=True,
|
| 1014 |
+
interpolate_offset=0.0,
|
| 1015 |
+
**kwargs,
|
| 1016 |
+
)
|
dual_hybrid_vit.py
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from logging import getLogger
|
| 2 |
+
from typing import Tuple
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
from torch import nn
|
| 6 |
+
from torch.nn import functional as F
|
| 7 |
+
|
| 8 |
+
from timm.models import register_model
|
| 9 |
+
from timm.models import vision_transformer as tvit
|
| 10 |
+
from timm.models import convnext as tconv
|
| 11 |
+
|
| 12 |
+
from einops import rearrange
|
| 13 |
+
|
| 14 |
+
from . import extra_timm_models as et
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class Fuser(nn.Module):
|
| 18 |
+
def __init__(self, src_dim: int, tgt_dim: int, gated: bool = True):
|
| 19 |
+
super().__init__()
|
| 20 |
+
self.gated = gated
|
| 21 |
+
|
| 22 |
+
mid_dim = max(src_dim, tgt_dim) * 2
|
| 23 |
+
|
| 24 |
+
self.fwd = nn.Sequential(
|
| 25 |
+
nn.Conv2d(src_dim, mid_dim, kernel_size=3, stride=1, padding=1),
|
| 26 |
+
nn.GELU(),
|
| 27 |
+
nn.Conv2d(mid_dim, tgt_dim * (2 if gated else 1), kernel_size=3, stride=1, padding=1),
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
def forward(self, src: torch.Tensor, tgt: torch.Tensor) -> torch.Tensor:
|
| 31 |
+
if src.ndim == 3:
|
| 32 |
+
shape = tgt.shape[-2:]
|
| 33 |
+
else:
|
| 34 |
+
shape = src.shape[-2:]
|
| 35 |
+
|
| 36 |
+
nd = shape[0] * shape[1]
|
| 37 |
+
|
| 38 |
+
if src.ndim == 3:
|
| 39 |
+
src = src[:, -nd:].reshape(src.shape[0], src.shape[2], *shape)
|
| 40 |
+
|
| 41 |
+
if tgt.ndim == 3:
|
| 42 |
+
tgt_pre = tgt[:, :-nd]
|
| 43 |
+
tgt = tgt[:, -nd:].reshape(tgt.shape[0], tgt.shape[2], *shape)
|
| 44 |
+
else:
|
| 45 |
+
tgt_pre = None
|
| 46 |
+
|
| 47 |
+
pred = self.fwd(src)
|
| 48 |
+
|
| 49 |
+
if self.gated:
|
| 50 |
+
g, pred = torch.chunk(pred, 2, dim=1)
|
| 51 |
+
|
| 52 |
+
g = F.sigmoid(g)
|
| 53 |
+
|
| 54 |
+
pred = g * pred
|
| 55 |
+
|
| 56 |
+
tgt = tgt + pred
|
| 57 |
+
|
| 58 |
+
if tgt_pre is not None:
|
| 59 |
+
tgt = rearrange(tgt, 'b c h w -> b (h w) c')
|
| 60 |
+
tgt = torch.cat([tgt_pre, tgt], dim=1)
|
| 61 |
+
|
| 62 |
+
return tgt
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class AttnDownsample(nn.Module):
|
| 66 |
+
def __init__(self, dim: int, window_size: int, num_heads: int = 16):
|
| 67 |
+
super().__init__()
|
| 68 |
+
self.q = nn.Parameter(torch.randn(1, num_heads, 1, dim // num_heads) * 0.01)
|
| 69 |
+
self.kv = nn.Linear(dim, dim * 2)
|
| 70 |
+
self.proj = nn.Linear(dim, dim)
|
| 71 |
+
self.window_size = window_size
|
| 72 |
+
self.num_heads = num_heads
|
| 73 |
+
self.head_dim = dim // num_heads
|
| 74 |
+
self.scale = self.head_dim ** -0.5
|
| 75 |
+
|
| 76 |
+
def forward(self, x: torch.Tensor, twod_shape: Tuple[int, int]) -> torch.Tensor:
|
| 77 |
+
ntok = twod_shape[0] * twod_shape[1]
|
| 78 |
+
x_pre = x[:, :-ntok]
|
| 79 |
+
|
| 80 |
+
B = x.shape[0]
|
| 81 |
+
ds_hw = tuple(s // self.window_size for s in twod_shape)
|
| 82 |
+
|
| 83 |
+
x_spat = rearrange(
|
| 84 |
+
x[:, -ntok:],
|
| 85 |
+
'b (h d1 w d2) c -> (b h w) (d1 d2) c',
|
| 86 |
+
h=ds_hw[0], w=ds_hw[1],
|
| 87 |
+
d1=self.window_size, d2=self.window_size,
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
B, N, C = x_spat.shape
|
| 91 |
+
|
| 92 |
+
k, v = self.kv(x_spat).reshape(B, N, 2, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4)
|
| 93 |
+
|
| 94 |
+
q = (self.q * self.scale).expand(B, -1, -1, -1)
|
| 95 |
+
attn = q @ k.transpose(-2, -1)
|
| 96 |
+
attn = F.softmax(attn, dim=-1)
|
| 97 |
+
x = attn @ v
|
| 98 |
+
|
| 99 |
+
x = x.transpose(1, 2).reshape(B, C)
|
| 100 |
+
x = self.proj(x)
|
| 101 |
+
|
| 102 |
+
x = rearrange(x, '(b h w) c -> b (h w) c', b=x_pre.shape[0], h=ds_hw[0], w=ds_hw[1])
|
| 103 |
+
|
| 104 |
+
x = torch.cat([x_pre, x], dim=1)
|
| 105 |
+
return x
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
class HybridModel(nn.Module):
|
| 109 |
+
def __init__(self, vit: tvit.VisionTransformer, conv: tconv.ConvNeXt, pretrained: bool = False,
|
| 110 |
+
concatenate: bool = False, **kwargs):
|
| 111 |
+
super().__init__()
|
| 112 |
+
self.conv = conv
|
| 113 |
+
self.vit = vit
|
| 114 |
+
self.concatenate = concatenate
|
| 115 |
+
|
| 116 |
+
conv.stages = nn.ModuleList(conv.stages)
|
| 117 |
+
vit.blocks = nn.ModuleList(vit.blocks)
|
| 118 |
+
|
| 119 |
+
self._half_vit_idx = len(vit.blocks) // 2 + 1
|
| 120 |
+
|
| 121 |
+
self._half_conv_idx = None
|
| 122 |
+
x = torch.empty(1, 3, 256, 256)
|
| 123 |
+
x = self.conv.stem(x)
|
| 124 |
+
for i in range(len(conv.stages)):
|
| 125 |
+
x = conv.stages[i](x)
|
| 126 |
+
if self._half_conv_idx is None and x.shape[-2:] == (16, 16):
|
| 127 |
+
self._half_conv_idx = i + 1
|
| 128 |
+
half_conv_dim = x.shape[1]
|
| 129 |
+
final_conv_dim = x.shape[1]
|
| 130 |
+
|
| 131 |
+
self.vit_to_conv_fusion = Fuser(vit.embed_dim, half_conv_dim)
|
| 132 |
+
self.conv_to_vit_fusion = Fuser(half_conv_dim, vit.embed_dim)
|
| 133 |
+
self.vit_ds = AttnDownsample(vit.embed_dim, window_size=2)
|
| 134 |
+
|
| 135 |
+
embed_dim = vit.embed_dim + (final_conv_dim if concatenate else 0)
|
| 136 |
+
if not concatenate:
|
| 137 |
+
self.final_fuse = Fuser(final_conv_dim, vit.embed_dim, gated=False)
|
| 138 |
+
self.final_block = tvit.Block(embed_dim, num_heads=16)
|
| 139 |
+
|
| 140 |
+
self.embed_dim = embed_dim
|
| 141 |
+
|
| 142 |
+
@property
|
| 143 |
+
def patch_size(self):
|
| 144 |
+
return 32
|
| 145 |
+
|
| 146 |
+
@property
|
| 147 |
+
def no_fsdp_wrap_types(self):
|
| 148 |
+
return {tvit.VisionTransformer, tconv.ConvNeXt}
|
| 149 |
+
|
| 150 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 151 |
+
return self.forward_features(x)
|
| 152 |
+
|
| 153 |
+
def forward_features(self, x: torch.Tensor) -> torch.Tensor:
|
| 154 |
+
y_vit = self.vit.patch_generator(x)
|
| 155 |
+
|
| 156 |
+
for i in range(self._half_vit_idx):
|
| 157 |
+
y_vit = self.vit.blocks[i](y_vit)
|
| 158 |
+
|
| 159 |
+
y_conv = self.conv.stem(x)
|
| 160 |
+
for i in range(self._half_conv_idx):
|
| 161 |
+
y_conv = self.conv.stages[i](y_conv)
|
| 162 |
+
|
| 163 |
+
y_vit, y_conv = self.conv_to_vit_fusion(y_conv, y_vit), self.vit_to_conv_fusion(y_vit, y_conv)
|
| 164 |
+
|
| 165 |
+
y_vit = self.vit_ds(y_vit, y_conv.shape[-2:])
|
| 166 |
+
|
| 167 |
+
for i in range(self._half_vit_idx, len(self.vit.blocks)):
|
| 168 |
+
y_vit = self.vit.blocks[i](y_vit)
|
| 169 |
+
|
| 170 |
+
for i in range(self._half_conv_idx, len(self.conv.stages)):
|
| 171 |
+
y_conv = self.conv.stages[i](y_conv)
|
| 172 |
+
|
| 173 |
+
if self.concatenate:
|
| 174 |
+
y_conv = rearrange(y_conv, 'b c h w -> b (h w) c')
|
| 175 |
+
# Average pool across the board, and replicate for each cls/register token
|
| 176 |
+
conv_summary = y_conv.mean(dim=1, keepdim=True).expand(-1, self.vit.patch_generator.num_cls_patches, -1)
|
| 177 |
+
y_conv = torch.cat([conv_summary, y_conv], dim=1)
|
| 178 |
+
y = torch.cat([y_vit, y_conv], dim=2)
|
| 179 |
+
else:
|
| 180 |
+
y = self.final_fuse(y_conv, y_vit)
|
| 181 |
+
y = self.final_block(y)
|
| 182 |
+
|
| 183 |
+
summary = y[:, :self.vit.patch_generator.num_cls_tokens]
|
| 184 |
+
features = y[:, self.vit.patch_generator.num_cls_patches:]
|
| 185 |
+
|
| 186 |
+
return summary, features
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
@register_model
|
| 190 |
+
def hybrid_base(pretrained=False, concatenate: bool = False, weight_init: str = 'skip', **kwargs):
|
| 191 |
+
cfg = dict(num_classes=0, **kwargs)
|
| 192 |
+
conv = tconv.convnextv2_base(pretrained=pretrained, **cfg)
|
| 193 |
+
vit = tvit.vit_base_patch16_224(pretrained=pretrained, weight_init=weight_init, **cfg)
|
| 194 |
+
|
| 195 |
+
return HybridModel(vit, conv, pretrained, concatenate=concatenate)
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
@register_model
|
| 199 |
+
def hybrid_large(pretrained=False, concatenate: bool = False, weight_init: str = 'skip', **kwargs):
|
| 200 |
+
cfg = dict(num_classes=0, **kwargs)
|
| 201 |
+
conv = tconv.convnextv2_large(pretrained=pretrained, **cfg)
|
| 202 |
+
vit = tvit.vit_large_patch16_224(pretrained=pretrained, weight_init=weight_init, **cfg)
|
| 203 |
+
|
| 204 |
+
return HybridModel(vit, conv, pretrained, concatenate=concatenate)
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
@register_model
|
| 208 |
+
def hybrid_huge(pretrained=False, concatenate: bool = False, weight_init: str = 'skip', **kwargs):
|
| 209 |
+
cfg = dict(num_classes=0, **kwargs)
|
| 210 |
+
conv = tconv.convnextv2_huge(pretrained=pretrained, **cfg)
|
| 211 |
+
vit = et.vit_huge_patch16_224(pretrained=pretrained, weight_init=weight_init, **cfg)
|
| 212 |
+
|
| 213 |
+
return HybridModel(vit, conv, pretrained, concatenate=concatenate)
|
enable_cpe_support.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
|
| 9 |
+
from typing import List, Optional, Set, Tuple, Union
|
| 10 |
+
from types import MethodType
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
from torch import nn
|
| 14 |
+
|
| 15 |
+
from timm.models import VisionTransformer, checkpoint_seq
|
| 16 |
+
|
| 17 |
+
from .feature_normalizer import IntermediateFeatureNormalizerBase, NullIntermediateFeatureNormalizer
|
| 18 |
+
|
| 19 |
+
from .extra_models import DinoWrapper
|
| 20 |
+
from .vit_patch_generator import ViTPatchGenerator
|
| 21 |
+
from .forward_intermediates import forward_intermediates
|
| 22 |
+
from .dual_hybrid_vit import HybridModel
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def _forward_cpe(self: VisionTransformer, x: torch.Tensor) -> torch.Tensor:
|
| 26 |
+
x = self.patch_generator(x)
|
| 27 |
+
if getattr(self, 'grad_checkpointing', False) and not torch.jit.is_scripting():
|
| 28 |
+
x = checkpoint_seq(self.blocks, x)
|
| 29 |
+
else:
|
| 30 |
+
x = self.blocks(x)
|
| 31 |
+
x = self.norm(x)
|
| 32 |
+
return x
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def _take_indices(
|
| 36 |
+
num_blocks: int,
|
| 37 |
+
n: Optional[Union[int, List[int], Tuple[int]]],
|
| 38 |
+
) -> Tuple[Set[int], int]:
|
| 39 |
+
if isinstance(n, int):
|
| 40 |
+
assert n >= 0
|
| 41 |
+
take_indices = {x for x in range(num_blocks - n, num_blocks)}
|
| 42 |
+
else:
|
| 43 |
+
take_indices = {num_blocks + idx if idx < 0 else idx for idx in n}
|
| 44 |
+
return take_indices, max(take_indices)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def _forward_intermediates_cpe(
|
| 48 |
+
self,
|
| 49 |
+
x: torch.Tensor,
|
| 50 |
+
norm: bool = False,
|
| 51 |
+
**kwargs,
|
| 52 |
+
) -> Union[List[torch.Tensor], Tuple[torch.Tensor, List[torch.Tensor]]]:
|
| 53 |
+
return forward_intermediates(
|
| 54 |
+
self,
|
| 55 |
+
patch_extractor=self.patch_generator,
|
| 56 |
+
num_summary_tokens=self.patch_generator.num_skip,
|
| 57 |
+
num_cls_tokens=self.patch_generator.num_cls_tokens,
|
| 58 |
+
norm=self.norm if norm else lambda y: y,
|
| 59 |
+
x=x,
|
| 60 |
+
**kwargs,
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def _forward_cpe_dinov2(self: DinoWrapper, x: torch.Tensor) -> torch.Tensor:
|
| 65 |
+
y = _forward_cpe(self.inner, x)
|
| 66 |
+
|
| 67 |
+
return y[:, 0], y[:, self.num_summary_tokens:]
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def _forward_intermediates_cpe_dinov2(self: DinoWrapper, *args, **kwargs):
|
| 71 |
+
return _forward_intermediates_cpe(self.inner, *args, **kwargs)
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def _enable_cpe_for_timm_vit(model: VisionTransformer,
|
| 75 |
+
max_img_size: Union[int, Tuple[int, int]] = 1024,
|
| 76 |
+
num_cls_tokens: int = 1,
|
| 77 |
+
pos_dropout: float = 0.1,
|
| 78 |
+
register_multiple: int = Optional[None],
|
| 79 |
+
num_registers: int = Optional[None],
|
| 80 |
+
):
|
| 81 |
+
if not isinstance(model, VisionTransformer):
|
| 82 |
+
raise ValueError("CPE only support for VisionTransformer models!")
|
| 83 |
+
|
| 84 |
+
patch_size = model.patch_embed.patch_size[0]
|
| 85 |
+
embed_dim = model.embed_dim
|
| 86 |
+
input_dims = model.patch_embed.img_size
|
| 87 |
+
normalize_patches = not isinstance(model.patch_embed.norm, nn.Identity)
|
| 88 |
+
cls_token = model.cls_token is not None
|
| 89 |
+
|
| 90 |
+
max_img_size = int(round(max_img_size / patch_size) * patch_size)
|
| 91 |
+
|
| 92 |
+
patch_generator = ViTPatchGenerator(
|
| 93 |
+
patch_size=patch_size,
|
| 94 |
+
embed_dim=embed_dim,
|
| 95 |
+
input_dims=input_dims,
|
| 96 |
+
normalize_patches=normalize_patches,
|
| 97 |
+
cls_token=cls_token,
|
| 98 |
+
max_input_dims=max_img_size,
|
| 99 |
+
pos_dropout=pos_dropout,
|
| 100 |
+
num_cls_tokens=num_cls_tokens,
|
| 101 |
+
register_multiple=register_multiple,
|
| 102 |
+
num_registers=num_registers,
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
model.patch_generator = patch_generator
|
| 106 |
+
model.patch_embed = None
|
| 107 |
+
model.cls_token = None
|
| 108 |
+
model.pos_embed = None
|
| 109 |
+
model.pos_drop = None
|
| 110 |
+
model.patch_size = patch_size
|
| 111 |
+
model.num_cls_tokens = num_cls_tokens
|
| 112 |
+
model.num_registers = patch_generator.num_registers
|
| 113 |
+
|
| 114 |
+
model.forward_features = MethodType(_forward_cpe, model)
|
| 115 |
+
model.forward_intermediates = MethodType(_forward_intermediates_cpe, model)
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def _enable_cpe_for_dv2_reg_vit(model: DinoWrapper,
|
| 119 |
+
max_img_size: Union[int, Tuple[int, int]] = 1024,
|
| 120 |
+
num_cls_tokens: int = 1,
|
| 121 |
+
pos_dropout: float = 0.1,
|
| 122 |
+
register_multiple: int = Optional[None],
|
| 123 |
+
num_registers: int = Optional[None],
|
| 124 |
+
):
|
| 125 |
+
patch_size = model.patch_size
|
| 126 |
+
embed_dim = model.embed_dim
|
| 127 |
+
input_dims = model.inner.patch_embed.patches_resolution
|
| 128 |
+
normalize_patches = not isinstance(model.inner.patch_embed.norm, nn.Identity)
|
| 129 |
+
cls_token = True
|
| 130 |
+
|
| 131 |
+
max_img_size = int(round(max_img_size / patch_size) * patch_size)
|
| 132 |
+
|
| 133 |
+
patch_generator = ViTPatchGenerator(
|
| 134 |
+
patch_size=patch_size,
|
| 135 |
+
embed_dim=embed_dim,
|
| 136 |
+
input_dims=input_dims,
|
| 137 |
+
normalize_patches=normalize_patches,
|
| 138 |
+
cls_token=cls_token,
|
| 139 |
+
max_input_dims=max_img_size,
|
| 140 |
+
pos_dropout=pos_dropout,
|
| 141 |
+
num_cls_tokens=num_cls_tokens,
|
| 142 |
+
register_multiple=register_multiple,
|
| 143 |
+
num_registers=num_registers,
|
| 144 |
+
patch_bias=True,
|
| 145 |
+
)
|
| 146 |
+
|
| 147 |
+
inner = model.inner
|
| 148 |
+
inner.patch_generator = patch_generator
|
| 149 |
+
inner.patch_embed = None
|
| 150 |
+
inner.cls_token = None
|
| 151 |
+
inner.pos_embed = None
|
| 152 |
+
inner.register_tokens = None
|
| 153 |
+
inner.patch_size = patch_size
|
| 154 |
+
|
| 155 |
+
model.forward_features = MethodType(_forward_cpe_dinov2, model)
|
| 156 |
+
model.forward_intermediates = MethodType(_forward_intermediates_cpe_dinov2, model)
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def enable_cpe(model: nn.Module,
|
| 160 |
+
*args,
|
| 161 |
+
**kwargs,
|
| 162 |
+
):
|
| 163 |
+
if isinstance(model, VisionTransformer):
|
| 164 |
+
_enable_cpe_for_timm_vit(model, *args, **kwargs)
|
| 165 |
+
elif isinstance(model, DinoWrapper):
|
| 166 |
+
_enable_cpe_for_dv2_reg_vit(model, *args, **kwargs)
|
| 167 |
+
elif isinstance(model, HybridModel):
|
| 168 |
+
_enable_cpe_for_timm_vit(model.vit, *args, **kwargs)
|
| 169 |
+
else:
|
| 170 |
+
raise ValueError(f'CPE not supported for this model type: {type(model)}')
|
enable_spectral_reparam.py
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
|
| 9 |
+
from logging import getLogger
|
| 10 |
+
import math
|
| 11 |
+
import os
|
| 12 |
+
from typing import Dict, List, Optional, Union, Tuple
|
| 13 |
+
from types import MethodType
|
| 14 |
+
|
| 15 |
+
import torch
|
| 16 |
+
from torch import nn
|
| 17 |
+
from torch.nn import functional as F
|
| 18 |
+
from torch.nn.utils import parametrize
|
| 19 |
+
from torch.nn.utils.parametrizations import _SpectralNorm
|
| 20 |
+
|
| 21 |
+
from timm.models.vision_transformer import Attention, Mlp
|
| 22 |
+
|
| 23 |
+
_EPS = 1e-5
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class _SNReweight(_SpectralNorm):
|
| 27 |
+
def __init__(self, weight: torch.Tensor, *args, init_norm_to_current: bool = False, alpha: float = 0.05, version: int = 2, **kwargs):
|
| 28 |
+
super().__init__(weight, *args, **kwargs)
|
| 29 |
+
|
| 30 |
+
self.alpha = alpha
|
| 31 |
+
self.version = version
|
| 32 |
+
self.register_buffer('_sn_version', torch.tensor(version))
|
| 33 |
+
|
| 34 |
+
if init_norm_to_current:
|
| 35 |
+
# This will set the numerator to match the denominator, which should preserve the original values
|
| 36 |
+
init_scale = self._get_sigma(weight, n_power_iterations=20).item()
|
| 37 |
+
else:
|
| 38 |
+
init_scale = 1.0
|
| 39 |
+
|
| 40 |
+
if version == 1:
|
| 41 |
+
init_value = init_scale
|
| 42 |
+
elif version == 2:
|
| 43 |
+
t = init_scale - alpha
|
| 44 |
+
if t < _EPS:
|
| 45 |
+
getLogger("spectral_reparam").warn(f'The initialized spectral norm {init_scale} is too small to be represented. Setting to {_EPS} instead.')
|
| 46 |
+
t = _EPS
|
| 47 |
+
|
| 48 |
+
init_value = math.log(math.exp(t) - 1)
|
| 49 |
+
else:
|
| 50 |
+
raise ValueError(f'Unsupported version: {version}')
|
| 51 |
+
|
| 52 |
+
# Make 2D so that weight decay gets applied
|
| 53 |
+
self.scale = nn.Parameter(torch.tensor([[init_value]], dtype=torch.float32, device=weight.device))
|
| 54 |
+
|
| 55 |
+
# Re-implementing this because we need to make division by sigma safe
|
| 56 |
+
def _get_sigma(self, weight: torch.Tensor, n_power_iterations: int = None) -> torch.Tensor:
|
| 57 |
+
if not n_power_iterations:
|
| 58 |
+
n_power_iterations = self.n_power_iterations
|
| 59 |
+
if weight.ndim == 1:
|
| 60 |
+
# Faster and more exact path, no need to approximate anything
|
| 61 |
+
sigma = weight.norm()
|
| 62 |
+
else:
|
| 63 |
+
weight_mat = self._reshape_weight_to_matrix(weight)
|
| 64 |
+
if self.training:
|
| 65 |
+
self._power_method(weight_mat, n_power_iterations)
|
| 66 |
+
# See above on why we need to clone
|
| 67 |
+
u = self._u.clone(memory_format=torch.contiguous_format)
|
| 68 |
+
v = self._v.clone(memory_format=torch.contiguous_format)
|
| 69 |
+
# The proper way of computing this should be through F.bilinear, but
|
| 70 |
+
# it seems to have some efficiency issues:
|
| 71 |
+
# https://github.com/pytorch/pytorch/issues/58093
|
| 72 |
+
sigma = torch.dot(u, torch.mv(weight_mat, v))
|
| 73 |
+
|
| 74 |
+
return sigma + self.eps
|
| 75 |
+
|
| 76 |
+
def forward(self, weight: torch.Tensor, *args, **kwargs):
|
| 77 |
+
dtype = weight.dtype
|
| 78 |
+
sigma = self._get_sigma(weight, *args, **kwargs)
|
| 79 |
+
|
| 80 |
+
if self.version == 1:
|
| 81 |
+
scale = self.scale
|
| 82 |
+
elif self.version == 2:
|
| 83 |
+
scale = F.softplus(self.scale) + self.alpha
|
| 84 |
+
else:
|
| 85 |
+
raise ValueError(f'Unsupported version: {self.version}')
|
| 86 |
+
|
| 87 |
+
scale = scale.float() / sigma.float()
|
| 88 |
+
|
| 89 |
+
y = weight * scale
|
| 90 |
+
|
| 91 |
+
if dtype in (torch.float16, torch.bfloat16):
|
| 92 |
+
y = y.to(dtype)
|
| 93 |
+
return y
|
| 94 |
+
|
| 95 |
+
def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs):
|
| 96 |
+
version_key = f'{prefix}_sn_version'
|
| 97 |
+
if version_key not in state_dict:
|
| 98 |
+
self.version = 1
|
| 99 |
+
state_dict[version_key] = torch.tensor(1)
|
| 100 |
+
return super()._load_from_state_dict(state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
class _ChunkedSNReweight(nn.Module):
|
| 104 |
+
def __init__(self, weight: torch.Tensor, num_chunks: int, *args, init_norm_to_current: bool = False, **kwargs):
|
| 105 |
+
super().__init__()
|
| 106 |
+
|
| 107 |
+
self.num_chunks = num_chunks
|
| 108 |
+
parts = weight.split(weight.shape[0] // num_chunks, dim=0)
|
| 109 |
+
|
| 110 |
+
self.parts = nn.ModuleList([
|
| 111 |
+
_SNReweight(p, *args, init_norm_to_current=init_norm_to_current, **kwargs)
|
| 112 |
+
for p in parts
|
| 113 |
+
])
|
| 114 |
+
|
| 115 |
+
def forward(self, weight: torch.Tensor, *args, **kwargs):
|
| 116 |
+
parts = weight.split(weight.shape[0] // self.num_chunks, dim=0)
|
| 117 |
+
|
| 118 |
+
parts = [
|
| 119 |
+
fn(p)
|
| 120 |
+
for fn, p in zip(self.parts, parts)
|
| 121 |
+
]
|
| 122 |
+
|
| 123 |
+
return torch.cat(parts, dim=0)
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
class _AttnSNReweight(_ChunkedSNReweight):
|
| 127 |
+
def __init__(self, weight: torch.Tensor, *args, init_norm_to_current: bool = False, renorm_values: bool = False, **kwargs):
|
| 128 |
+
super().__init__(weight, 3, *args, init_norm_to_current=init_norm_to_current, **kwargs)
|
| 129 |
+
|
| 130 |
+
if not renorm_values:
|
| 131 |
+
self.parts[2] = nn.Identity()
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def enable_spectral_reparam(model: Union[nn.Module, List[nn.Module]],
|
| 135 |
+
n_power_iterations: int = 1,
|
| 136 |
+
eps: float = 1e-6,
|
| 137 |
+
init_norm_to_current: bool = False,
|
| 138 |
+
renorm_values: bool = True,
|
| 139 |
+
renorm_mlp: bool = True,
|
| 140 |
+
state_dict_guidance: Optional[Dict[str, torch.Tensor]] = None):
|
| 141 |
+
if isinstance(model, (list, tuple)):
|
| 142 |
+
for i, sub in enumerate(model):
|
| 143 |
+
sub_sd = state_dict_guidance[i] if isinstance(state_dict_guidance, (list, tuple)) else state_dict_guidance
|
| 144 |
+
enable_spectral_reparam(sub, n_power_iterations=n_power_iterations, eps=eps,
|
| 145 |
+
init_norm_to_current=init_norm_to_current, renorm_values=renorm_values,
|
| 146 |
+
renorm_mlp=renorm_mlp, state_dict_guidance=sub_sd)
|
| 147 |
+
return
|
| 148 |
+
|
| 149 |
+
print('Enabling spectral reparametrization')
|
| 150 |
+
args = dict(n_power_iterations=n_power_iterations, dim=0, eps=eps, init_norm_to_current=init_norm_to_current)
|
| 151 |
+
visited_prefixes = set()
|
| 152 |
+
|
| 153 |
+
def is_guidance_parametrized(name: str):
|
| 154 |
+
if state_dict_guidance is None:
|
| 155 |
+
return True
|
| 156 |
+
|
| 157 |
+
p_name = f'{name}.parametrizations'
|
| 158 |
+
is_prm = any(k for k in state_dict_guidance if k.startswith(p_name) and k.endswith('_sn_version'))
|
| 159 |
+
return is_prm
|
| 160 |
+
|
| 161 |
+
def parametrize_linear(linear: nn.Linear):
|
| 162 |
+
parametrize.register_parametrization(
|
| 163 |
+
linear,
|
| 164 |
+
'weight',
|
| 165 |
+
_SNReweight(linear.weight, **args)
|
| 166 |
+
)
|
| 167 |
+
|
| 168 |
+
for name, mod in model.named_modules():
|
| 169 |
+
pref = '.'.join(name.split('.')[:-1])
|
| 170 |
+
if pref in visited_prefixes:
|
| 171 |
+
continue
|
| 172 |
+
|
| 173 |
+
if isinstance(mod, Attention) or name.endswith('.attn'):
|
| 174 |
+
if is_guidance_parametrized(f'{name}.qkv'):
|
| 175 |
+
parametrize.register_parametrization(
|
| 176 |
+
mod.qkv,
|
| 177 |
+
'weight',
|
| 178 |
+
_AttnSNReweight(mod.qkv.weight, renorm_values=renorm_values, **args),
|
| 179 |
+
)
|
| 180 |
+
if hasattr(mod, 'proj') and is_guidance_parametrized(f'{name}.proj'):
|
| 181 |
+
parametrize_linear(mod.proj)
|
| 182 |
+
visited_prefixes.add(name)
|
| 183 |
+
elif name.endswith('mlp') and renorm_mlp and hasattr(mod, 'w12'):
|
| 184 |
+
if is_guidance_parametrized(f'{name}.w12'):
|
| 185 |
+
parametrize.register_parametrization(
|
| 186 |
+
mod.w12,
|
| 187 |
+
'weight',
|
| 188 |
+
_ChunkedSNReweight(mod.w12.weight, num_chunks=2, **args),
|
| 189 |
+
)
|
| 190 |
+
if is_guidance_parametrized(f'{name}.w3'):
|
| 191 |
+
parametrize_linear(mod.w3)
|
| 192 |
+
visited_prefixes.add(name)
|
| 193 |
+
elif isinstance(mod, nn.Linear) and 'patch_generator' not in name and is_guidance_parametrized(name):
|
| 194 |
+
parametrize_linear(mod)
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def configure_spectral_reparam_from_args(model: nn.Module, args, state_dict_guidance: Optional[Dict[str, torch.Tensor]] = None):
|
| 198 |
+
spectral_reparam = getattr(args, 'spectral_reparam', False)
|
| 199 |
+
if isinstance(spectral_reparam, bool) and spectral_reparam:
|
| 200 |
+
enable_spectral_reparam(model, init_norm_to_current=True, state_dict_guidance=state_dict_guidance)
|
| 201 |
+
elif isinstance(spectral_reparam, dict):
|
| 202 |
+
enable_spectral_reparam(
|
| 203 |
+
model,
|
| 204 |
+
n_power_iterations=spectral_reparam.get('n_power_iterations', 1),
|
| 205 |
+
eps=spectral_reparam.get('eps', 1e-12),
|
| 206 |
+
init_norm_to_current=True,
|
| 207 |
+
state_dict_guidance=state_dict_guidance,
|
| 208 |
+
)
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
def disable_spectral_reparam(model: nn.Module):
|
| 212 |
+
print('Disabling spectral reparametrization')
|
| 213 |
+
for name, mod in model.named_modules():
|
| 214 |
+
if parametrize.is_parametrized(mod):
|
| 215 |
+
parametrize.remove_parametrizations(mod, 'weight')
|
| 216 |
+
pass
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
if __name__ == '__main__':
|
| 221 |
+
import argparse
|
| 222 |
+
from . import radio_model as create_model
|
| 223 |
+
|
| 224 |
+
parser = argparse.ArgumentParser(description='Remove parametrization from state dict')
|
| 225 |
+
parser.add_argument('--checkpoint', type=str, required=True, help='The checkpoint to load')
|
| 226 |
+
parser.add_argument('--output', type=str, default='', help='Where to store the checkpoint')
|
| 227 |
+
parser.add_argument('--release', default=False, action='store_true', help='Prune extraneous checkpoint fields')
|
| 228 |
+
parser.add_argument('--strict', default=False, action='store_true', help='Strictly load the state dict')
|
| 229 |
+
|
| 230 |
+
args = parser.parse_args()
|
| 231 |
+
|
| 232 |
+
if not args.output:
|
| 233 |
+
chk_dir, chk_name = os.path.split(args.checkpoint)
|
| 234 |
+
args.output = os.path.join(chk_dir, f'clean_{chk_name}')
|
| 235 |
+
print(f'Set output to "{args.output}"')
|
| 236 |
+
|
| 237 |
+
chk = torch.load(args.checkpoint, map_location='cpu', mmap=True)
|
| 238 |
+
|
| 239 |
+
model = create_model.create_model_from_args(chk['args'])
|
| 240 |
+
|
| 241 |
+
key = 'base_model.'
|
| 242 |
+
mod_state = dict()
|
| 243 |
+
extra_state = dict()
|
| 244 |
+
for k, v in chk['state_dict'].items():
|
| 245 |
+
if k.startswith(key):
|
| 246 |
+
mod_state[k[len(key):]] = v
|
| 247 |
+
else:
|
| 248 |
+
extra_state[k] = v
|
| 249 |
+
|
| 250 |
+
chk_load_info = model.load_state_dict(mod_state, strict=args.strict)
|
| 251 |
+
if chk_load_info.unexpected_keys or chk_load_info.missing_keys:
|
| 252 |
+
print(chk_load_info)
|
| 253 |
+
|
| 254 |
+
if chk['args'].spectral_reparam:
|
| 255 |
+
disable_spectral_reparam(model)
|
| 256 |
+
|
| 257 |
+
if hasattr(chk['args'], 'dtype'):
|
| 258 |
+
model.to(dtype=chk['args'].dtype)
|
| 259 |
+
|
| 260 |
+
mod_state = model.state_dict()
|
| 261 |
+
final_state = dict()
|
| 262 |
+
final_state.update({f'{key}{k}': v for k, v in mod_state.items()})
|
| 263 |
+
final_state.update(extra_state)
|
| 264 |
+
|
| 265 |
+
chk['state_dict'] = final_state
|
| 266 |
+
chk['args'].spectral_reparam = False
|
| 267 |
+
|
| 268 |
+
if args.release:
|
| 269 |
+
chk = {
|
| 270 |
+
'arch': chk['arch'],
|
| 271 |
+
'epoch': chk['epoch'],
|
| 272 |
+
'state_dict': chk['state_dict'],
|
| 273 |
+
'args': chk['args'],
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
torch.save(chk, args.output)
|
| 277 |
+
pass
|
eradio_model.py
ADDED
|
@@ -0,0 +1,1392 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
|
| 3 |
+
# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
|
| 4 |
+
#
|
| 5 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 6 |
+
# and proprietary rights in and to this software, related documentation
|
| 7 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 8 |
+
# distribution of this software and related documentation without an express
|
| 9 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 10 |
+
|
| 11 |
+
# E-RADIO model from
|
| 12 |
+
# Mike Ranzinger, Greg Heinrich, Jan Kautz, and Pavlo Molchanov. "AM-RADIO: Agglomerative Model--Reduce All Domains Into One." arXiv preprint arXiv:2312.06709 (2023).
|
| 13 |
+
|
| 14 |
+
# based on FasterViT, Swin Transformer, YOLOv8
|
| 15 |
+
|
| 16 |
+
# FasterViT:
|
| 17 |
+
# Ali Hatamizadeh, Greg Heinrich, Hongxu Yin, Andrew Tao, Jose M. Alvarez, Jan Kautz, and Pavlo Molchanov. "FasterViT: Fast Vision Transformers with Hierarchical Attention." arXiv preprint arXiv:2306.06189 (2023).
|
| 18 |
+
|
| 19 |
+
import timm
|
| 20 |
+
import torch
|
| 21 |
+
import torch.nn as nn
|
| 22 |
+
from timm.models.registry import register_model
|
| 23 |
+
|
| 24 |
+
from timm.models.layers import trunc_normal_, DropPath, LayerNorm2d
|
| 25 |
+
import numpy as np
|
| 26 |
+
import torch.nn.functional as F
|
| 27 |
+
import math
|
| 28 |
+
import warnings
|
| 29 |
+
|
| 30 |
+
#######################
|
| 31 |
+
## Codebase from YOLOv8
|
| 32 |
+
## BEGINNING
|
| 33 |
+
#######################
|
| 34 |
+
|
| 35 |
+
class C2f(nn.Module):
|
| 36 |
+
"""Faster Implementation of CSP Bottleneck with 2 convolutions."""
|
| 37 |
+
"""From YOLOv8 codebase"""
|
| 38 |
+
def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5, drop_path=None): # ch_in, ch_out, number, shortcut, groups, expansion
|
| 39 |
+
super().__init__()
|
| 40 |
+
if drop_path is None:
|
| 41 |
+
drop_path = [0.0] * n
|
| 42 |
+
|
| 43 |
+
self.c = int(c2 * e) # hidden channels
|
| 44 |
+
self.cv1 = Conv(c1, 2 * self.c, 1, 1)
|
| 45 |
+
self.cv2 = Conv((2 + n) * self.c, c2, 1) # optional act=FReLU(c2)
|
| 46 |
+
self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0, drop_path=drop_path[i]) for i in range(n))
|
| 47 |
+
|
| 48 |
+
def forward(self, x):
|
| 49 |
+
"""Forward pass through C2f layer."""
|
| 50 |
+
y = list(self.cv1(x).chunk(2, 1))
|
| 51 |
+
y.extend(m(y[-1]) for m in self.m)
|
| 52 |
+
return self.cv2(torch.cat(y, 1))
|
| 53 |
+
|
| 54 |
+
def forward_split(self, x):
|
| 55 |
+
"""Forward pass using split() instead of chunk()."""
|
| 56 |
+
y = list(self.cv1(x).split((self.c, self.c), 1))
|
| 57 |
+
y.extend(m(y[-1]) for m in self.m)
|
| 58 |
+
return self.cv2(torch.cat(y, 1))
|
| 59 |
+
|
| 60 |
+
class Bottleneck(nn.Module):
|
| 61 |
+
"""Standard bottleneck."""
|
| 62 |
+
|
| 63 |
+
def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5, drop_path=0.0): # ch_in, ch_out, shortcut, groups, kernels, expand
|
| 64 |
+
super().__init__()
|
| 65 |
+
c_ = int(c2 * e) # hidden channels
|
| 66 |
+
self.cv1 = Conv(c1, c_, k[0], 1)
|
| 67 |
+
self.cv2 = Conv(c_, c2, k[1], 1, g=g)
|
| 68 |
+
self.add = shortcut and c1 == c2
|
| 69 |
+
self.drop_path1 = DropPath(drop_path) if drop_path > 0. else nn.Identity()
|
| 70 |
+
|
| 71 |
+
def forward(self, x):
|
| 72 |
+
"""'forward()' applies the YOLOv5 FPN to input data."""
|
| 73 |
+
return x + self.drop_path1(self.cv2(self.cv1(x))) if self.add else self.cv2(self.cv1(x))
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
class Conv(nn.Module):
|
| 77 |
+
"""Modified to support layer fusion"""
|
| 78 |
+
default_act = nn.SiLU() # default activation
|
| 79 |
+
|
| 80 |
+
def __init__(self, a, b, kernel_size=1, stride=1, padding=None, g=1, dilation=1, bn_weight_init=1, bias=False, act=True):
|
| 81 |
+
super().__init__()
|
| 82 |
+
|
| 83 |
+
self.conv = torch.nn.Conv2d(a, b, kernel_size, stride, autopad(kernel_size, padding, dilation), dilation, g, bias=False)
|
| 84 |
+
if 1:
|
| 85 |
+
self.bn = torch.nn.BatchNorm2d(b)
|
| 86 |
+
torch.nn.init.constant_(self.bn.weight, bn_weight_init)
|
| 87 |
+
torch.nn.init.constant_(self.bn.bias, 0)
|
| 88 |
+
self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def forward(self,x):
|
| 92 |
+
x = self.conv(x)
|
| 93 |
+
x = self.bn(x)
|
| 94 |
+
x = self.act(x)
|
| 95 |
+
return x
|
| 96 |
+
|
| 97 |
+
@torch.no_grad()
|
| 98 |
+
def switch_to_deploy(self):
|
| 99 |
+
# return 1
|
| 100 |
+
if not isinstance(self.bn, nn.Identity):
|
| 101 |
+
c, bn = self.conv, self.bn
|
| 102 |
+
w = bn.weight / (bn.running_var + bn.eps) ** 0.5
|
| 103 |
+
w = c.weight * w[:, None, None, None]
|
| 104 |
+
b = bn.bias - bn.running_mean * bn.weight / \
|
| 105 |
+
(bn.running_var + bn.eps)**0.5
|
| 106 |
+
|
| 107 |
+
self.conv.weight.data.copy_(w)
|
| 108 |
+
self.conv.bias = nn.Parameter(b)
|
| 109 |
+
|
| 110 |
+
self.bn = nn.Identity()
|
| 111 |
+
|
| 112 |
+
def autopad(k, p=None, d=1): # kernel, padding, dilation
|
| 113 |
+
"""Pad to 'same' shape outputs."""
|
| 114 |
+
if d > 1:
|
| 115 |
+
k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k] # actual kernel-size
|
| 116 |
+
if p is None:
|
| 117 |
+
p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad
|
| 118 |
+
return p
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
#######################
|
| 122 |
+
## Codebase from YOLOv8
|
| 123 |
+
## END
|
| 124 |
+
#######################
|
| 125 |
+
|
| 126 |
+
def pixel_unshuffle(data, factor=2):
|
| 127 |
+
# performs nn.PixelShuffle(factor) in reverse, torch has some bug for ONNX and TRT, so doing it manually
|
| 128 |
+
B, C, H, W = data.shape
|
| 129 |
+
return data.view(B, C, factor, H//factor, factor, W//factor).permute(0,1,2,4,3,5).reshape(B, -1, H//factor, W//factor)
|
| 130 |
+
|
| 131 |
+
class SwiGLU(nn.Module):
|
| 132 |
+
# should be more advanced, but doesnt improve results so far
|
| 133 |
+
def forward(self, x):
|
| 134 |
+
x, gate = x.chunk(2, dim=-1)
|
| 135 |
+
return F.silu(gate) * x
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def window_partition(x, window_size):
|
| 139 |
+
"""
|
| 140 |
+
Function for partitioning image into windows and later do windowed attention
|
| 141 |
+
Args:
|
| 142 |
+
x: (B, C, H, W)
|
| 143 |
+
window_size: window size
|
| 144 |
+
Returns:
|
| 145 |
+
windows - local window features (num_windows*B, window_size*window_size, C)
|
| 146 |
+
(Hp, Wp) - the size of the padded image
|
| 147 |
+
"""
|
| 148 |
+
B, C, H, W = x.shape
|
| 149 |
+
|
| 150 |
+
if window_size == 0 or (window_size==H and window_size==W):
|
| 151 |
+
windows = x.flatten(2).transpose(1, 2)
|
| 152 |
+
Hp, Wp = H, W
|
| 153 |
+
else:
|
| 154 |
+
pad_h = (window_size - H % window_size) % window_size
|
| 155 |
+
pad_w = (window_size - W % window_size) % window_size
|
| 156 |
+
if pad_h > 0 or pad_w > 0:
|
| 157 |
+
x = F.pad(x, (0, pad_w, 0, pad_h), mode="reflect")
|
| 158 |
+
Hp, Wp = H + pad_h, W + pad_w
|
| 159 |
+
|
| 160 |
+
x = x.view(B, C, Hp // window_size, window_size, Wp // window_size, window_size)
|
| 161 |
+
windows = x.permute(0, 2, 4, 3, 5, 1).reshape(-1, window_size*window_size, C)
|
| 162 |
+
|
| 163 |
+
return windows, (Hp, Wp)
|
| 164 |
+
|
| 165 |
+
class Conv2d_BN(nn.Module):
|
| 166 |
+
'''
|
| 167 |
+
Conv2d + BN layer with folding capability to speed up inference
|
| 168 |
+
Can be merged with Conv() function with additional arguments
|
| 169 |
+
'''
|
| 170 |
+
def __init__(self, a, b, kernel_size=1, stride=1, padding=0, dilation=1, groups=1, bn_weight_init=1, bias=False):
|
| 171 |
+
super().__init__()
|
| 172 |
+
self.conv = torch.nn.Conv2d(a, b, kernel_size, stride, padding, dilation, groups, bias=False)
|
| 173 |
+
if 1:
|
| 174 |
+
self.bn = torch.nn.BatchNorm2d(b)
|
| 175 |
+
torch.nn.init.constant_(self.bn.weight, bn_weight_init)
|
| 176 |
+
torch.nn.init.constant_(self.bn.bias, 0)
|
| 177 |
+
|
| 178 |
+
def forward(self,x):
|
| 179 |
+
x = self.conv(x)
|
| 180 |
+
x = self.bn(x)
|
| 181 |
+
return x
|
| 182 |
+
|
| 183 |
+
@torch.no_grad()
|
| 184 |
+
def switch_to_deploy(self):
|
| 185 |
+
if not isinstance(self.bn, nn.Identity):
|
| 186 |
+
c, bn = self.conv, self.bn
|
| 187 |
+
w = bn.weight / (bn.running_var + bn.eps) ** 0.5
|
| 188 |
+
w = c.weight * w[:, None, None, None]
|
| 189 |
+
b = bn.bias - bn.running_mean * bn.weight / \
|
| 190 |
+
(bn.running_var + bn.eps)**0.5
|
| 191 |
+
self.conv.weight.data.copy_(w)
|
| 192 |
+
self.conv.bias = nn.Parameter(b)
|
| 193 |
+
self.bn = nn.Identity()
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def window_reverse(windows, window_size, H, W, pad_hw):
|
| 198 |
+
"""
|
| 199 |
+
Windows to the full feature map
|
| 200 |
+
Args:
|
| 201 |
+
windows: local window features (num_windows*B, window_size, window_size, C)
|
| 202 |
+
window_size: Window size
|
| 203 |
+
H: Height of image
|
| 204 |
+
W: Width of image
|
| 205 |
+
pad_w - a tuple of image passing used in windowing step
|
| 206 |
+
Returns:
|
| 207 |
+
x: (B, C, H, W)
|
| 208 |
+
|
| 209 |
+
"""
|
| 210 |
+
# print(f"window_reverse, windows.shape {windows.shape}")
|
| 211 |
+
Hp, Wp = pad_hw
|
| 212 |
+
if window_size == 0 or (window_size==H and window_size==W):
|
| 213 |
+
B = int(windows.shape[0] / (Hp * Wp / window_size / window_size))
|
| 214 |
+
x = windows.transpose(1, 2).view(B, -1, H, W)
|
| 215 |
+
else:
|
| 216 |
+
B = int(windows.shape[0] / (Hp * Wp / window_size / window_size))
|
| 217 |
+
x = windows.view(B, Hp // window_size, Wp // window_size, window_size, window_size, -1)
|
| 218 |
+
x = x.permute(0, 5, 1, 3, 2, 4).reshape(B,windows.shape[2], Hp, Wp)
|
| 219 |
+
|
| 220 |
+
if Hp > H or Wp > W:
|
| 221 |
+
x = x[:, :, :H, :W, ].contiguous()
|
| 222 |
+
|
| 223 |
+
return x
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
class PosEmbMLPSwinv2D(nn.Module):
|
| 228 |
+
"""
|
| 229 |
+
2D positional embedding from Swin Transformer v2
|
| 230 |
+
Added functionality to store the positional embedding in the model and not recompute it every time
|
| 231 |
+
"""
|
| 232 |
+
def __init__(
|
| 233 |
+
self, window_size, pretrained_window_size, num_heads, seq_length, no_log=False, cpb_mlp_hidden=512,
|
| 234 |
+
):
|
| 235 |
+
super().__init__()
|
| 236 |
+
self.window_size = window_size
|
| 237 |
+
self.num_heads = num_heads
|
| 238 |
+
# mlp to generate continuous relative position bias
|
| 239 |
+
self.cpb_mlp = nn.Sequential(
|
| 240 |
+
nn.Linear(2, cpb_mlp_hidden, bias=True),
|
| 241 |
+
nn.ReLU(inplace=True),
|
| 242 |
+
nn.Linear(cpb_mlp_hidden, num_heads, bias=False),
|
| 243 |
+
)
|
| 244 |
+
|
| 245 |
+
self.grid_exists = False
|
| 246 |
+
self.seq_length = seq_length
|
| 247 |
+
self.deploy = False
|
| 248 |
+
self.num_heads = num_heads
|
| 249 |
+
self.no_log = no_log
|
| 250 |
+
self.pretrained_window_size = pretrained_window_size
|
| 251 |
+
self.relative_bias_window_size = window_size
|
| 252 |
+
|
| 253 |
+
relative_coords_table, relative_position_index, relative_bias = self.relative_bias_initialization(window_size, num_heads,
|
| 254 |
+
pretrained_window_size, seq_length,
|
| 255 |
+
no_log)
|
| 256 |
+
|
| 257 |
+
self.register_buffer("relative_coords_table", relative_coords_table)
|
| 258 |
+
self.register_buffer("relative_position_index", relative_position_index)
|
| 259 |
+
self.register_buffer("relative_bias", relative_bias) # for EMA
|
| 260 |
+
|
| 261 |
+
def relative_bias_initialization(self, window_size, num_heads, pretrained_window_size, seq_length, no_log):
|
| 262 |
+
# as in separate function to support window size chage after model weights loading
|
| 263 |
+
relative_coords_h = torch.arange(
|
| 264 |
+
-(window_size[0] - 1), window_size[0], dtype=torch.float32
|
| 265 |
+
)
|
| 266 |
+
relative_coords_w = torch.arange(
|
| 267 |
+
-(window_size[1] - 1), window_size[1], dtype=torch.float32
|
| 268 |
+
)
|
| 269 |
+
relative_coords_table = (
|
| 270 |
+
torch.stack(torch.meshgrid([relative_coords_h, relative_coords_w]))
|
| 271 |
+
.permute(1, 2, 0)
|
| 272 |
+
.contiguous()
|
| 273 |
+
.unsqueeze(0)
|
| 274 |
+
) # 1, 2*Wh-1, 2*Ww-1, 2
|
| 275 |
+
if pretrained_window_size[0] > 0:
|
| 276 |
+
relative_coords_table[:, :, :, 0] /= pretrained_window_size[0] - 1
|
| 277 |
+
relative_coords_table[:, :, :, 1] /= pretrained_window_size[1] - 1
|
| 278 |
+
else:
|
| 279 |
+
relative_coords_table[:, :, :, 0] /= self.window_size[0] - 1
|
| 280 |
+
relative_coords_table[:, :, :, 1] /= self.window_size[1] - 1
|
| 281 |
+
|
| 282 |
+
if not no_log:
|
| 283 |
+
relative_coords_table *= 8 # normalize to -8, 8
|
| 284 |
+
relative_coords_table = (
|
| 285 |
+
torch.sign(relative_coords_table)
|
| 286 |
+
* torch.log2(torch.abs(relative_coords_table) + 1.0)
|
| 287 |
+
/ np.log2(8)
|
| 288 |
+
)
|
| 289 |
+
|
| 290 |
+
# get pair-wise relative position index for each token inside the window
|
| 291 |
+
coords_h = torch.arange(self.window_size[0])
|
| 292 |
+
coords_w = torch.arange(self.window_size[1])
|
| 293 |
+
coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww
|
| 294 |
+
coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww
|
| 295 |
+
relative_coords = (
|
| 296 |
+
coords_flatten[:, :, None] - coords_flatten[:, None, :]
|
| 297 |
+
) # 2, Wh*Ww, Wh*Ww
|
| 298 |
+
relative_coords = relative_coords.permute(
|
| 299 |
+
1, 2, 0
|
| 300 |
+
).contiguous() # Wh*Ww, Wh*Ww, 2
|
| 301 |
+
relative_coords[:, :, 0] += self.window_size[0] - 1 # shift to start from 0
|
| 302 |
+
relative_coords[:, :, 1] += self.window_size[1] - 1
|
| 303 |
+
relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1
|
| 304 |
+
relative_position_index = relative_coords.sum(-1) # Wh*Ww, Wh*Ww
|
| 305 |
+
|
| 306 |
+
relative_bias = torch.zeros(1, num_heads, seq_length, seq_length)
|
| 307 |
+
|
| 308 |
+
self.relative_bias_window_size = window_size
|
| 309 |
+
|
| 310 |
+
return relative_coords_table, relative_position_index, relative_bias
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
def switch_to_deploy(self):
|
| 314 |
+
self.deploy = True
|
| 315 |
+
self.grid_exists = True
|
| 316 |
+
|
| 317 |
+
def forward(self, input_tensor):
|
| 318 |
+
# for efficiency, we want this forward to be folded into a single operation (sum)
|
| 319 |
+
# if resolution stays the same, then we dont need to recompute MLP layers
|
| 320 |
+
|
| 321 |
+
if not self.deploy or self.training:
|
| 322 |
+
self.grid_exists = False
|
| 323 |
+
|
| 324 |
+
#compare if all elements in self.window_size list match those in self.relative_bias_window_size
|
| 325 |
+
if not all([self.window_size[i] == self.relative_bias_window_size[i] for i in range(len(self.window_size))]):
|
| 326 |
+
relative_coords_table, relative_position_index, relative_bias = self.relative_bias_initialization(self.window_size, self.num_heads,
|
| 327 |
+
self.pretrained_window_size, self.seq_length,
|
| 328 |
+
self.no_log)
|
| 329 |
+
|
| 330 |
+
self.relative_coords_table = relative_coords_table.to(self.relative_coords_table.device)
|
| 331 |
+
self.relative_position_index = relative_position_index.to(self.relative_position_index.device)
|
| 332 |
+
self.relative_bias = relative_bias.to(self.relative_bias.device)
|
| 333 |
+
|
| 334 |
+
if self.deploy and self.grid_exists:
|
| 335 |
+
input_tensor = input_tensor + self.relative_bias
|
| 336 |
+
return input_tensor
|
| 337 |
+
|
| 338 |
+
if 1:
|
| 339 |
+
self.grid_exists = True
|
| 340 |
+
|
| 341 |
+
relative_position_bias_table = self.cpb_mlp(
|
| 342 |
+
self.relative_coords_table
|
| 343 |
+
).view(-1, self.num_heads)
|
| 344 |
+
relative_position_bias = relative_position_bias_table[
|
| 345 |
+
self.relative_position_index.view(-1)
|
| 346 |
+
].view(
|
| 347 |
+
self.window_size[0] * self.window_size[1],
|
| 348 |
+
self.window_size[0] * self.window_size[1],
|
| 349 |
+
-1,
|
| 350 |
+
) # Wh*Ww,Wh*Ww,nH
|
| 351 |
+
|
| 352 |
+
relative_position_bias = relative_position_bias.permute(
|
| 353 |
+
2, 0, 1
|
| 354 |
+
).contiguous() # nH, Wh*Ww, Wh*Ww
|
| 355 |
+
relative_position_bias = 16 * torch.sigmoid(relative_position_bias)
|
| 356 |
+
|
| 357 |
+
self.relative_bias = relative_position_bias.unsqueeze(0)
|
| 358 |
+
|
| 359 |
+
input_tensor = input_tensor + self.relative_bias
|
| 360 |
+
return input_tensor
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
class GRAAttentionBlock(nn.Module):
|
| 364 |
+
def __init__(self, window_size, dim_in, dim_out,
|
| 365 |
+
num_heads, drop_path=0., qk_scale=None, qkv_bias=False,
|
| 366 |
+
norm_layer=nn.LayerNorm, layer_scale=None,
|
| 367 |
+
use_swiglu=True,
|
| 368 |
+
subsample_ratio=1, dim_ratio=1, conv_base=False,
|
| 369 |
+
do_windowing=True, multi_query=False, use_shift=0,
|
| 370 |
+
cpb_mlp_hidden=512, conv_groups_ratio=0):
|
| 371 |
+
'''
|
| 372 |
+
Global Resolution Attention Block , see README for details
|
| 373 |
+
Attention with subsampling to get a bigger receptive field for attention
|
| 374 |
+
conv_base - use conv2d instead of avgpool2d for downsample / upsample
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
'''
|
| 378 |
+
super().__init__()
|
| 379 |
+
|
| 380 |
+
self.shift_size=window_size//2 if use_shift else 0
|
| 381 |
+
|
| 382 |
+
self.do_windowing = do_windowing
|
| 383 |
+
self.subsample_ratio = subsample_ratio
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
|
| 387 |
+
if do_windowing:
|
| 388 |
+
if conv_base:
|
| 389 |
+
self.downsample_op = nn.Conv2d(dim_in, dim_out, kernel_size=subsample_ratio, stride=subsample_ratio) if subsample_ratio > 1 else nn.Identity()
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
self.downsample_mixer = nn.Identity()
|
| 393 |
+
self.upsample_mixer = nn.Identity()
|
| 394 |
+
self.upsample_op = nn.ConvTranspose2d(dim_in, dim_out, kernel_size=subsample_ratio, stride=subsample_ratio) if subsample_ratio > 1 else nn.Identity()
|
| 395 |
+
else:
|
| 396 |
+
self.downsample_op = nn.AvgPool2d(kernel_size=subsample_ratio, stride=subsample_ratio) if subsample_ratio > 1 else nn.Identity()
|
| 397 |
+
self.downsample_mixer = Conv2d_BN(dim_in, dim_out, kernel_size=1, stride=1) if subsample_ratio > 1 else nn.Identity()
|
| 398 |
+
self.upsample_mixer = nn.Upsample(scale_factor=subsample_ratio, mode='nearest') if subsample_ratio > 1 else nn.Identity()
|
| 399 |
+
self.upsample_op = Conv2d_BN(dim_in, dim_out, kernel_size=1, stride=1, padding=0, bias=False) if subsample_ratio > 1 else nn.Identity()
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
# in case there is no downsampling conv we want to have it separately
|
| 403 |
+
# will help with information propagation between windows
|
| 404 |
+
if subsample_ratio == 1:
|
| 405 |
+
# conv_groups_ratio=0
|
| 406 |
+
self.pre_conv = Conv2d_BN(dim_in, dim_in, kernel_size=3, stride=1, padding=1, groups=max(1,int(conv_groups_ratio*dim_in)), bias=False)
|
| 407 |
+
# self.pre_conv = nn.Conv2d(dim_in, dim_in, kernel_size=3, stride=1, padding=1, groups=max(1,int(conv_groups_ratio*dim_in)), bias=False)
|
| 408 |
+
# self.pre_conv_act = nn.ReLU6()
|
| 409 |
+
#for simplicity:
|
| 410 |
+
self.pre_conv_act = nn.Identity()
|
| 411 |
+
if conv_groups_ratio == -1:
|
| 412 |
+
self.pre_conv = nn.Identity()
|
| 413 |
+
self.pre_conv_act = nn.Identity()
|
| 414 |
+
|
| 415 |
+
self.window_size = window_size
|
| 416 |
+
|
| 417 |
+
self.norm1 = norm_layer(dim_in)
|
| 418 |
+
|
| 419 |
+
self.attn = WindowAttention(
|
| 420 |
+
dim_in,
|
| 421 |
+
num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale,
|
| 422 |
+
resolution=window_size,
|
| 423 |
+
seq_length=window_size**2, dim_out=dim_in, multi_query=multi_query,
|
| 424 |
+
shift_size=self.shift_size, cpb_mlp_hidden=cpb_mlp_hidden)
|
| 425 |
+
|
| 426 |
+
self.drop_path1 = DropPath(drop_path) if drop_path > 0. else nn.Identity()
|
| 427 |
+
|
| 428 |
+
use_layer_scale = layer_scale is not None and type(layer_scale) in [int, float]
|
| 429 |
+
self.gamma1 = nn.Parameter(layer_scale * torch.ones(dim_in)) if use_layer_scale else 1
|
| 430 |
+
|
| 431 |
+
### mlp layer
|
| 432 |
+
mlp_ratio = 4
|
| 433 |
+
self.norm2 = norm_layer(dim_in)
|
| 434 |
+
mlp_hidden_dim = int(dim_in * mlp_ratio)
|
| 435 |
+
|
| 436 |
+
activation = nn.GELU if not use_swiglu else SwiGLU
|
| 437 |
+
mlp_hidden_dim = int((4 * dim_in * 1 / 2) / 64) * 64 if use_swiglu else mlp_hidden_dim
|
| 438 |
+
|
| 439 |
+
self.mlp = Mlp(in_features=dim_in, hidden_features=mlp_hidden_dim, act_layer=activation, use_swiglu=use_swiglu)
|
| 440 |
+
|
| 441 |
+
self.gamma2 = nn.Parameter(layer_scale * torch.ones(dim_in)) if layer_scale else 1
|
| 442 |
+
self.drop_path2=DropPath(drop_path) if drop_path > 0. else nn.Identity()
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
def forward(self, x):
|
| 446 |
+
skip_connection = x
|
| 447 |
+
attn_mask = None
|
| 448 |
+
|
| 449 |
+
# in case there is no downsampling conv we want to have it separately
|
| 450 |
+
# will help with information propagation
|
| 451 |
+
if self.subsample_ratio == 1:
|
| 452 |
+
x = self.pre_conv_act(self.pre_conv(x)) + skip_connection
|
| 453 |
+
|
| 454 |
+
if self.do_windowing:
|
| 455 |
+
# performing windowing if required
|
| 456 |
+
x = self.downsample_op(x)
|
| 457 |
+
x = self.downsample_mixer(x)
|
| 458 |
+
|
| 459 |
+
if self.window_size>0:
|
| 460 |
+
H, W = x.shape[2], x.shape[3]
|
| 461 |
+
|
| 462 |
+
if self.shift_size > 0 and H>self.window_size and W>self.window_size:
|
| 463 |
+
# @swin like cyclic shift, doesnt show better performance
|
| 464 |
+
x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(2, 3))
|
| 465 |
+
|
| 466 |
+
x, pad_hw = window_partition(x, self.window_size)
|
| 467 |
+
|
| 468 |
+
if self.shift_size > 0 and H>self.window_size and W>self.window_size:
|
| 469 |
+
# set atten matrix to have -100 and the top right square
|
| 470 |
+
# attn[:, :, :-self.shift_size, -self.shift_size:] = -100.0
|
| 471 |
+
# calculate attention mask for SW-MSA
|
| 472 |
+
# not used in final version, can be useful for some cases especially for high res
|
| 473 |
+
H, W = pad_hw
|
| 474 |
+
img_mask = torch.zeros((1, H, W, 1), device=x.device) # 1 H W 1
|
| 475 |
+
h_slices = (slice(0, -self.window_size),
|
| 476 |
+
slice(-self.window_size, -self.shift_size),
|
| 477 |
+
slice(-self.shift_size, None))
|
| 478 |
+
w_slices = (slice(0, -self.window_size),
|
| 479 |
+
slice(-self.window_size, -self.shift_size),
|
| 480 |
+
slice(-self.shift_size, None))
|
| 481 |
+
cnt = 0
|
| 482 |
+
for h in h_slices:
|
| 483 |
+
for w in w_slices:
|
| 484 |
+
img_mask[:, h, w, :] = cnt
|
| 485 |
+
cnt += 1
|
| 486 |
+
img_mask = img_mask.transpose(1,2).transpose(1,3)
|
| 487 |
+
mask_windows = window_partition(img_mask, self.window_size) # nW, window_size, window_size, 1
|
| 488 |
+
|
| 489 |
+
mask_windows = mask_windows[0].view(-1, self.window_size * self.window_size)
|
| 490 |
+
attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2)
|
| 491 |
+
attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0))
|
| 492 |
+
|
| 493 |
+
# window attention
|
| 494 |
+
x = x + self.drop_path1(self.gamma1*self.attn(self.norm1(x), attn_mask=attn_mask)) # or pass H,W
|
| 495 |
+
# mlp layer
|
| 496 |
+
x = x + self.drop_path2(self.gamma2*self.mlp(self.norm2(x)))
|
| 497 |
+
|
| 498 |
+
if self.do_windowing:
|
| 499 |
+
if self.window_size > 0:
|
| 500 |
+
x = window_reverse(x, self.window_size, H, W, pad_hw)
|
| 501 |
+
|
| 502 |
+
# reverse cyclic shift
|
| 503 |
+
if self.shift_size > 0 and H>self.window_size and W>self.window_size:
|
| 504 |
+
# @swin like cyclic shift, not tested
|
| 505 |
+
x = torch.roll(x, shifts=(self.shift_size, self.shift_size), dims=(2, 3))
|
| 506 |
+
|
| 507 |
+
x = self.upsample_mixer(x)
|
| 508 |
+
x = self.upsample_op(x)
|
| 509 |
+
|
| 510 |
+
|
| 511 |
+
if x.shape[2] != skip_connection.shape[2] or x.shape[3] != skip_connection.shape[3]:
|
| 512 |
+
x = torch.nn.functional.pad(x, ( 0, -x.shape[3] + skip_connection.shape[3], 0, -x.shape[2] + skip_connection.shape[2]), mode="reflect")
|
| 513 |
+
# need to add skip connection because downsampling and upsampling will break residual connection
|
| 514 |
+
# 0.5 is needed to make sure that the skip connection is not too strong
|
| 515 |
+
# in case of no downsample / upsample we can show that 0.5 compensates for the residual connection
|
| 516 |
+
x = 0.5 * x + 0.5 * skip_connection
|
| 517 |
+
return x
|
| 518 |
+
|
| 519 |
+
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
class MultiResolutionAttention(nn.Module):
|
| 523 |
+
"""
|
| 524 |
+
MultiResolutionAttention (MRA) module
|
| 525 |
+
The idea is to use multiple attention blocks with different resolution
|
| 526 |
+
Feature maps are downsampled / upsampled for each attention block on different blocks
|
| 527 |
+
Every attention block supports windowing
|
| 528 |
+
"""
|
| 529 |
+
|
| 530 |
+
def __init__(self, window_size, sr_ratio,
|
| 531 |
+
dim, dim_ratio, num_heads,
|
| 532 |
+
do_windowing=True,
|
| 533 |
+
layer_scale=1e-5, norm_layer=nn.LayerNorm,
|
| 534 |
+
drop_path = 0, qkv_bias=False, qk_scale=1.0,
|
| 535 |
+
use_swiglu=True, multi_query=False, conv_base=False,
|
| 536 |
+
use_shift=0, cpb_mlp_hidden=512, conv_groups_ratio=0) -> None:
|
| 537 |
+
"""
|
| 538 |
+
Args:
|
| 539 |
+
input_resolution: input image resolution
|
| 540 |
+
window_size: window size
|
| 541 |
+
compression_ratio: compression ratio
|
| 542 |
+
max_depth: maximum depth of the GRA module
|
| 543 |
+
use_shift: do window shifting
|
| 544 |
+
"""
|
| 545 |
+
super().__init__()
|
| 546 |
+
|
| 547 |
+
depth = len(sr_ratio)
|
| 548 |
+
|
| 549 |
+
self.attention_blocks = nn.ModuleList()
|
| 550 |
+
|
| 551 |
+
|
| 552 |
+
for i in range(depth):
|
| 553 |
+
subsample_ratio = sr_ratio[i]
|
| 554 |
+
if len(window_size) > i:
|
| 555 |
+
window_size_local = window_size[i]
|
| 556 |
+
else:
|
| 557 |
+
window_size_local = window_size[0]
|
| 558 |
+
|
| 559 |
+
self.attention_blocks.append(GRAAttentionBlock(window_size=window_size_local,
|
| 560 |
+
dim_in=dim, dim_out=dim, num_heads=num_heads,
|
| 561 |
+
qkv_bias=qkv_bias, qk_scale=qk_scale, norm_layer=norm_layer,
|
| 562 |
+
layer_scale=layer_scale, drop_path=drop_path,
|
| 563 |
+
use_swiglu=use_swiglu, subsample_ratio=subsample_ratio, dim_ratio=dim_ratio,
|
| 564 |
+
do_windowing=do_windowing, multi_query=multi_query, conv_base=conv_base,
|
| 565 |
+
use_shift=use_shift, cpb_mlp_hidden=cpb_mlp_hidden, conv_groups_ratio=conv_groups_ratio),
|
| 566 |
+
)
|
| 567 |
+
|
| 568 |
+
def forward(self, x):
|
| 569 |
+
|
| 570 |
+
for attention_block in self.attention_blocks:
|
| 571 |
+
x = attention_block(x)
|
| 572 |
+
|
| 573 |
+
return x
|
| 574 |
+
|
| 575 |
+
|
| 576 |
+
|
| 577 |
+
class Mlp(nn.Module):
|
| 578 |
+
"""
|
| 579 |
+
Multi-Layer Perceptron (MLP) block
|
| 580 |
+
"""
|
| 581 |
+
|
| 582 |
+
def __init__(self,
|
| 583 |
+
in_features,
|
| 584 |
+
hidden_features=None,
|
| 585 |
+
out_features=None,
|
| 586 |
+
act_layer=nn.GELU,
|
| 587 |
+
use_swiglu=True,
|
| 588 |
+
drop=0.):
|
| 589 |
+
"""
|
| 590 |
+
Args:
|
| 591 |
+
in_features: input features dimension.
|
| 592 |
+
hidden_features: hidden features dimension.
|
| 593 |
+
out_features: output features dimension.
|
| 594 |
+
act_layer: activation function.
|
| 595 |
+
drop: dropout rate.
|
| 596 |
+
"""
|
| 597 |
+
|
| 598 |
+
super().__init__()
|
| 599 |
+
out_features = out_features or in_features
|
| 600 |
+
hidden_features = hidden_features or in_features
|
| 601 |
+
self.fc1 = nn.Linear(in_features, hidden_features * (2 if use_swiglu else 1), bias=False)
|
| 602 |
+
self.act = act_layer()
|
| 603 |
+
self.fc2 = nn.Linear(hidden_features, out_features, bias=False)
|
| 604 |
+
|
| 605 |
+
def forward(self, x):
|
| 606 |
+
x_size = x.size()
|
| 607 |
+
x = x.view(-1, x_size[-1])
|
| 608 |
+
x = self.fc1(x)
|
| 609 |
+
x = self.act(x)
|
| 610 |
+
x = self.fc2(x)
|
| 611 |
+
x = x.view(x_size)
|
| 612 |
+
return x
|
| 613 |
+
|
| 614 |
+
class Downsample(nn.Module):
|
| 615 |
+
"""
|
| 616 |
+
Down-sampling block
|
| 617 |
+
Pixel Unshuffle is used for down-sampling, works great accuracy - wise but takes 10% more TRT time
|
| 618 |
+
"""
|
| 619 |
+
|
| 620 |
+
def __init__(self,
|
| 621 |
+
dim,
|
| 622 |
+
shuffle = False,
|
| 623 |
+
):
|
| 624 |
+
"""
|
| 625 |
+
Args:
|
| 626 |
+
dim: feature size dimension.
|
| 627 |
+
shuffle: idea with
|
| 628 |
+
keep_dim: bool argument for maintaining the resolution.
|
| 629 |
+
"""
|
| 630 |
+
|
| 631 |
+
super().__init__()
|
| 632 |
+
dim_out = 2 * dim
|
| 633 |
+
|
| 634 |
+
if shuffle:
|
| 635 |
+
self.norm = lambda x: pixel_unshuffle(x, factor=2)
|
| 636 |
+
self.reduction = Conv2d_BN(dim*4, dim_out, 1, 1, 0, bias=False)
|
| 637 |
+
# pixel unshuffleging works well but doesnt provide any speedup
|
| 638 |
+
else:
|
| 639 |
+
# removed layer norm for better, in this formulation we are getting 10% better speed
|
| 640 |
+
# LayerNorm for high resolution inputs will be a pain as it pools over the entire spatial dimension
|
| 641 |
+
# therefore we remove it compared to the original implementation in FasterViT
|
| 642 |
+
self.norm = nn.Identity()
|
| 643 |
+
self.reduction = Conv2d_BN(dim, dim_out, 3, 2, 1, bias=False)
|
| 644 |
+
|
| 645 |
+
|
| 646 |
+
def forward(self, x):
|
| 647 |
+
x = self.norm(x)
|
| 648 |
+
x = self.reduction(x)
|
| 649 |
+
return x
|
| 650 |
+
|
| 651 |
+
|
| 652 |
+
class PatchEmbed(nn.Module):
|
| 653 |
+
"""
|
| 654 |
+
Patch embedding block
|
| 655 |
+
Used to convert image into an initial set of feature maps with lower resolution
|
| 656 |
+
"""
|
| 657 |
+
|
| 658 |
+
def __init__(self, in_chans=3, in_dim=64, dim=96, shuffle_down=False):
|
| 659 |
+
"""
|
| 660 |
+
Args:
|
| 661 |
+
in_chans: number of input channels.
|
| 662 |
+
in_dim: intermediate feature size dimension to speed up stem.
|
| 663 |
+
dim: final stem channel number
|
| 664 |
+
shuffle_down: use PixelUnshuffle for down-sampling, effectively increases the receptive field
|
| 665 |
+
"""
|
| 666 |
+
|
| 667 |
+
super().__init__()
|
| 668 |
+
# shuffle_down = False
|
| 669 |
+
if not shuffle_down:
|
| 670 |
+
self.proj = nn.Identity()
|
| 671 |
+
self.conv_down = nn.Sequential(
|
| 672 |
+
Conv2d_BN(in_chans, in_dim, 3, 2, 1, bias=False),
|
| 673 |
+
nn.ReLU(),
|
| 674 |
+
Conv2d_BN(in_dim, dim, 3, 2, 1, bias=False),
|
| 675 |
+
nn.ReLU()
|
| 676 |
+
)
|
| 677 |
+
else:
|
| 678 |
+
self.proj = lambda x: pixel_unshuffle(x, factor=4)
|
| 679 |
+
self.conv_down = nn.Sequential(Conv2d_BN(in_chans*16, dim, 3, 1, 1),
|
| 680 |
+
nn.ReLU(),
|
| 681 |
+
)
|
| 682 |
+
|
| 683 |
+
def forward(self, x):
|
| 684 |
+
x = self.proj(x)
|
| 685 |
+
x = self.conv_down(x)
|
| 686 |
+
return x
|
| 687 |
+
|
| 688 |
+
|
| 689 |
+
|
| 690 |
+
class ConvBlock(nn.Module):
|
| 691 |
+
"""
|
| 692 |
+
Convolutional block, used in first couple of stages
|
| 693 |
+
Experimented with plan resnet-18 like modules, they are the best in terms of throughput
|
| 694 |
+
Finally, YOLOv8 idea seem to work fine (resnet-18 like block with squeezed feature dimension, and feature concatendation at the end)
|
| 695 |
+
"""
|
| 696 |
+
def __init__(self, dim,
|
| 697 |
+
drop_path=0.,
|
| 698 |
+
layer_scale=None,
|
| 699 |
+
kernel_size=3,
|
| 700 |
+
):
|
| 701 |
+
super().__init__()
|
| 702 |
+
|
| 703 |
+
self.conv1 = Conv2d_BN(dim, dim, kernel_size=kernel_size, stride=1, padding=1)
|
| 704 |
+
self.act1 = nn.GELU()
|
| 705 |
+
|
| 706 |
+
self.conv2 = Conv2d_BN(dim, dim, kernel_size=kernel_size, stride=1, padding=1)
|
| 707 |
+
|
| 708 |
+
self.layer_scale = layer_scale
|
| 709 |
+
if layer_scale is not None and type(layer_scale) in [int, float]:
|
| 710 |
+
self.gamma = nn.Parameter(layer_scale * torch.ones(dim))
|
| 711 |
+
self.layer_scale = True
|
| 712 |
+
else:
|
| 713 |
+
self.layer_scale = False
|
| 714 |
+
self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
|
| 715 |
+
|
| 716 |
+
def forward(self, x):
|
| 717 |
+
input = x
|
| 718 |
+
|
| 719 |
+
x = self.conv1(x)
|
| 720 |
+
x = self.act1(x)
|
| 721 |
+
x = self.conv2(x)
|
| 722 |
+
|
| 723 |
+
if self.layer_scale:
|
| 724 |
+
x = x * self.gamma.view(1, -1, 1, 1)
|
| 725 |
+
x = input + self.drop_path(x)
|
| 726 |
+
return x
|
| 727 |
+
|
| 728 |
+
|
| 729 |
+
class WindowAttention(nn.Module):
|
| 730 |
+
# Windowed Attention from SwinV2
|
| 731 |
+
# use a MLP trick to deal with various input image resolutions, then fold it to improve speed
|
| 732 |
+
|
| 733 |
+
def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, resolution=0,
|
| 734 |
+
seq_length=0, dim_out=None, multi_query=False, shift_size=0, cpb_mlp_hidden=512):
|
| 735 |
+
# taken from EdgeViT and tweaked with attention bias.
|
| 736 |
+
super().__init__()
|
| 737 |
+
if not dim_out: dim_out = dim
|
| 738 |
+
self.shift_size = shift_size
|
| 739 |
+
self.multi_query = multi_query
|
| 740 |
+
self.num_heads = num_heads
|
| 741 |
+
head_dim = dim // num_heads
|
| 742 |
+
self.head_dim = dim // num_heads
|
| 743 |
+
|
| 744 |
+
self.dim_internal = dim
|
| 745 |
+
|
| 746 |
+
self.scale = qk_scale or head_dim ** -0.5
|
| 747 |
+
if not multi_query:
|
| 748 |
+
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
| 749 |
+
else:
|
| 750 |
+
self.qkv = nn.Linear(dim, dim + 2*self.head_dim, bias=qkv_bias)
|
| 751 |
+
|
| 752 |
+
self.proj = nn.Linear(dim, dim_out, bias=False)
|
| 753 |
+
# attention positional bias
|
| 754 |
+
self.pos_emb_funct = PosEmbMLPSwinv2D(window_size=[resolution, resolution],
|
| 755 |
+
pretrained_window_size=[resolution, resolution],
|
| 756 |
+
num_heads=num_heads,
|
| 757 |
+
seq_length=seq_length,
|
| 758 |
+
cpb_mlp_hidden=cpb_mlp_hidden)
|
| 759 |
+
|
| 760 |
+
self.resolution = resolution
|
| 761 |
+
|
| 762 |
+
def forward(self, x, attn_mask = None):
|
| 763 |
+
B, N, C = x.shape
|
| 764 |
+
|
| 765 |
+
if not self.multi_query:
|
| 766 |
+
qkv = self.qkv(x).reshape(B, -1, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
|
| 767 |
+
q, k, v = qkv[0], qkv[1], qkv[2]
|
| 768 |
+
else:
|
| 769 |
+
qkv = self.qkv(x)
|
| 770 |
+
(q, k, v) = qkv.split([self.dim_internal, self.head_dim, self.head_dim], dim=2)
|
| 771 |
+
|
| 772 |
+
q = q.reshape(B, -1, self.num_heads, C // self.num_heads).permute(0, 2, 1, 3)
|
| 773 |
+
k = k.reshape(B, -1, 1, C // self.num_heads).permute(0, 2, 1, 3)
|
| 774 |
+
v = v.reshape(B, -1, 1, C // self.num_heads).permute(0, 2, 1, 3)
|
| 775 |
+
|
| 776 |
+
attn = (q @ k.transpose(-2, -1)) * self.scale
|
| 777 |
+
|
| 778 |
+
attn = self.pos_emb_funct(attn)
|
| 779 |
+
|
| 780 |
+
#add window shift
|
| 781 |
+
if attn_mask is not None:
|
| 782 |
+
nW = attn_mask.shape[0]
|
| 783 |
+
attn = attn.view(B // nW, nW, self.num_heads, N, N) + attn_mask.unsqueeze(1).unsqueeze(0)
|
| 784 |
+
attn = attn.view(-1, self.num_heads, N, N)
|
| 785 |
+
|
| 786 |
+
attn = attn.softmax(dim=-1)
|
| 787 |
+
x = (attn @ v).transpose(1, 2).reshape(B, -1, C)
|
| 788 |
+
x = self.proj(x)
|
| 789 |
+
return x
|
| 790 |
+
|
| 791 |
+
|
| 792 |
+
|
| 793 |
+
class ERADIOLayer(nn.Module):
|
| 794 |
+
"""
|
| 795 |
+
E-RADIO Layer
|
| 796 |
+
"""
|
| 797 |
+
|
| 798 |
+
def __init__(self,
|
| 799 |
+
dim,
|
| 800 |
+
depth,
|
| 801 |
+
num_heads,
|
| 802 |
+
window_size,
|
| 803 |
+
conv=False,
|
| 804 |
+
downsample=True,
|
| 805 |
+
mlp_ratio=4.,
|
| 806 |
+
qkv_bias=False,
|
| 807 |
+
qk_scale=None,
|
| 808 |
+
norm_layer=nn.LayerNorm,
|
| 809 |
+
drop_path=0.,
|
| 810 |
+
layer_scale=None,
|
| 811 |
+
layer_scale_conv=None,
|
| 812 |
+
sr_dim_ratio=1,
|
| 813 |
+
sr_ratio=1,
|
| 814 |
+
multi_query=False,
|
| 815 |
+
use_swiglu=True,
|
| 816 |
+
yolo_arch=False,
|
| 817 |
+
downsample_shuffle=False,
|
| 818 |
+
conv_base=False,
|
| 819 |
+
use_shift=False,
|
| 820 |
+
cpb_mlp_hidden=512,
|
| 821 |
+
conv_groups_ratio=0,
|
| 822 |
+
verbose: bool = True,
|
| 823 |
+
|
| 824 |
+
):
|
| 825 |
+
"""
|
| 826 |
+
Args:
|
| 827 |
+
dim: feature size dimension.
|
| 828 |
+
depth: number of layers in each stage.
|
| 829 |
+
input_resolution: input image resolution.
|
| 830 |
+
window_size: window size in each stage.
|
| 831 |
+
downsample: bool argument for down-sampling.
|
| 832 |
+
mlp_ratio: MLP ratio.
|
| 833 |
+
num_heads: number of heads in each stage.
|
| 834 |
+
qkv_bias: bool argument for query, key, value learnable bias.
|
| 835 |
+
qk_scale: bool argument to scaling query, key.
|
| 836 |
+
drop: dropout rate.
|
| 837 |
+
attn_drop: attention dropout rate.
|
| 838 |
+
drop_path: drop path rate.
|
| 839 |
+
norm_layer: normalization layer.
|
| 840 |
+
layer_scale: layer scaling coefficient.
|
| 841 |
+
use_shift: SWIN like window shifting for half the window size for every alternating layer (considering multi-resolution)
|
| 842 |
+
conv_groups_ratio: group ratio for conv when no subsampling in multi-res attention
|
| 843 |
+
"""
|
| 844 |
+
|
| 845 |
+
super().__init__()
|
| 846 |
+
self.conv = conv
|
| 847 |
+
self.yolo_arch=False
|
| 848 |
+
self.verbose = verbose
|
| 849 |
+
if conv:
|
| 850 |
+
if not yolo_arch:
|
| 851 |
+
self.blocks = nn.ModuleList([
|
| 852 |
+
ConvBlock(dim=dim,
|
| 853 |
+
drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path,
|
| 854 |
+
layer_scale=layer_scale_conv)
|
| 855 |
+
for i in range(depth)])
|
| 856 |
+
self.blocks = nn.Sequential(*self.blocks)
|
| 857 |
+
else:
|
| 858 |
+
self.blocks = C2f(dim,dim,n=depth,shortcut=True,e=0.5)
|
| 859 |
+
self.yolo_arch=True
|
| 860 |
+
else:
|
| 861 |
+
if not isinstance(window_size, list): window_size = [window_size]
|
| 862 |
+
self.window_size = window_size[0]
|
| 863 |
+
self.do_single_windowing = True
|
| 864 |
+
if not isinstance(sr_ratio, list): sr_ratio = [sr_ratio]
|
| 865 |
+
self.sr_ratio = sr_ratio
|
| 866 |
+
if any([sr!=1 for sr in sr_ratio]) or len(set(window_size))>1:
|
| 867 |
+
self.do_single_windowing = False
|
| 868 |
+
do_windowing = True
|
| 869 |
+
else:
|
| 870 |
+
self.do_single_windowing = True
|
| 871 |
+
do_windowing = False
|
| 872 |
+
|
| 873 |
+
#for v2_2
|
| 874 |
+
if conv_groups_ratio != -1:
|
| 875 |
+
self.do_single_windowing = False
|
| 876 |
+
do_windowing = True
|
| 877 |
+
|
| 878 |
+
self.blocks = nn.ModuleList()
|
| 879 |
+
for i in range(depth):
|
| 880 |
+
self.blocks.append(
|
| 881 |
+
MultiResolutionAttention(window_size=window_size,
|
| 882 |
+
sr_ratio=sr_ratio,
|
| 883 |
+
dim=dim,
|
| 884 |
+
dim_ratio = sr_dim_ratio,
|
| 885 |
+
num_heads=num_heads,
|
| 886 |
+
norm_layer=norm_layer,
|
| 887 |
+
drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path,
|
| 888 |
+
layer_scale=layer_scale,
|
| 889 |
+
qkv_bias=qkv_bias,
|
| 890 |
+
qk_scale=qk_scale,
|
| 891 |
+
use_swiglu=use_swiglu,
|
| 892 |
+
do_windowing=do_windowing,
|
| 893 |
+
multi_query=multi_query,
|
| 894 |
+
conv_base=conv_base,
|
| 895 |
+
cpb_mlp_hidden=cpb_mlp_hidden,
|
| 896 |
+
use_shift =0 if ((not use_shift) or ((i) % 2 == 0)) else True ,
|
| 897 |
+
conv_groups_ratio=conv_groups_ratio,
|
| 898 |
+
))
|
| 899 |
+
self.blocks = nn.Sequential(*self.blocks)
|
| 900 |
+
|
| 901 |
+
self.transformer = not conv
|
| 902 |
+
self.downsample = None if not downsample else Downsample(dim=dim, shuffle=downsample_shuffle)
|
| 903 |
+
|
| 904 |
+
|
| 905 |
+
def forward(self, x):
|
| 906 |
+
B, C, H, W = x.shape
|
| 907 |
+
|
| 908 |
+
# do padding for transforemr
|
| 909 |
+
interpolate = True
|
| 910 |
+
if self.transformer and interpolate:
|
| 911 |
+
# Windowed Attention will split feature map into windows with the size of window_size x window_size
|
| 912 |
+
# if the resolution is not divisible by window_size, we need to interpolate the feature map
|
| 913 |
+
# can be done via padding, but doing so after training hurts the model performance.
|
| 914 |
+
# interpolation affects the performance as well, but not as much as padding
|
| 915 |
+
if isinstance(self.window_size, list) or isinstance(self.window_size, tuple):
|
| 916 |
+
current_max_window_size = max(self.window_size)
|
| 917 |
+
else:
|
| 918 |
+
current_max_window_size = self.window_size
|
| 919 |
+
|
| 920 |
+
max_window_size = max([res_upsample*current_max_window_size for res_upsample in self.sr_ratio])
|
| 921 |
+
if H % max_window_size != 0 or W % max_window_size != 0:
|
| 922 |
+
new_h = int(np.ceil(H/max_window_size)*max_window_size)
|
| 923 |
+
new_w = int(np.ceil(W/max_window_size)*max_window_size)
|
| 924 |
+
x = F.interpolate(x, size=(new_h, new_w), mode='nearest')
|
| 925 |
+
if self.verbose:
|
| 926 |
+
warnings.warn(f"Choosen window size is not optimal for given resolution. Interpolation of features maps will be done and it can affect the performance. Max window size is {max_window_size}, feature map size is {H}x{W}, interpolated feature map size is {new_h}x{new_w}.")
|
| 927 |
+
|
| 928 |
+
|
| 929 |
+
if self.transformer and self.do_single_windowing:
|
| 930 |
+
H, W = x.shape[2], x.shape[3]
|
| 931 |
+
x, pad_hw = window_partition(x, self.window_size)
|
| 932 |
+
|
| 933 |
+
#run main blocks
|
| 934 |
+
x = self.blocks(x)
|
| 935 |
+
|
| 936 |
+
if self.transformer and self.do_single_windowing:
|
| 937 |
+
x = window_reverse(x, self.window_size, H, W, pad_hw)
|
| 938 |
+
|
| 939 |
+
if self.transformer and interpolate:
|
| 940 |
+
#lets keep original resolution, might be not ideal, but for the upsampling tower we need to keep the expected resolution.
|
| 941 |
+
x = F.interpolate(x, size=(H, W), mode='nearest')
|
| 942 |
+
|
| 943 |
+
if self.downsample is None:
|
| 944 |
+
return x, x
|
| 945 |
+
|
| 946 |
+
return self.downsample(x), x # changing to output pre downsampled features
|
| 947 |
+
|
| 948 |
+
|
| 949 |
+
class InterpolateLayer(nn.Module):
|
| 950 |
+
def __init__(self, size=None, scale_factor=None, mode='nearest'):
|
| 951 |
+
super(InterpolateLayer, self).__init__()
|
| 952 |
+
self.size = size
|
| 953 |
+
self.scale_factor = scale_factor
|
| 954 |
+
self.mode = mode
|
| 955 |
+
|
| 956 |
+
def forward(self, x):
|
| 957 |
+
return F.interpolate(x, size=self.size, scale_factor=self.scale_factor, mode=self.mode)
|
| 958 |
+
|
| 959 |
+
|
| 960 |
+
class HiResNeck(nn.Module):
|
| 961 |
+
"""
|
| 962 |
+
The block is used to output dense features from all stages
|
| 963 |
+
Otherwise, by default, only the last stage features are returned with E-RADIO
|
| 964 |
+
"""
|
| 965 |
+
def __init__(self, dim, depths, neck_start_stage, full_features_head_dim, downsample_enabled):
|
| 966 |
+
|
| 967 |
+
'''
|
| 968 |
+
Hi Resolution neck to support output of high res features that are useful for dense tasks.
|
| 969 |
+
depths - total number of layers in the base model
|
| 970 |
+
neck_start_stage - when to start the neck, 0 - start from the first stage, 1 - start from the second stage etc.
|
| 971 |
+
earlier layers result in higher resolution features at the cost of compute
|
| 972 |
+
full_features_head_dim - number of channels in the dense features head
|
| 973 |
+
'''
|
| 974 |
+
super().__init__()
|
| 975 |
+
# create feature projection layers for segmentation output
|
| 976 |
+
self.neck_features_proj = nn.ModuleList()
|
| 977 |
+
self.neck_start_stage = neck_start_stage
|
| 978 |
+
upsample_ratio = 1
|
| 979 |
+
for i in range(len(depths)):
|
| 980 |
+
level_n_features_output = int(dim * 2 ** i)
|
| 981 |
+
|
| 982 |
+
if self.neck_start_stage > i: continue
|
| 983 |
+
|
| 984 |
+
if (upsample_ratio > 1) or full_features_head_dim!=level_n_features_output:
|
| 985 |
+
feature_projection = nn.Sequential()
|
| 986 |
+
if False:
|
| 987 |
+
feature_projection.add_module("norm",nn.BatchNorm2d(level_n_features_output)) #fast, but worse
|
| 988 |
+
feature_projection.add_module("dconv", nn.ConvTranspose2d(level_n_features_output,
|
| 989 |
+
full_features_head_dim, kernel_size=upsample_ratio, stride=upsample_ratio))
|
| 990 |
+
else:
|
| 991 |
+
# B, in_channels, H, W -> B, in_channels, H*upsample_ratio, W*upsample_ratio
|
| 992 |
+
# print("upsample ratio", upsample_ratio, level_n_features_output, level_n_features_output)
|
| 993 |
+
feature_projection.add_module("upsample", InterpolateLayer(scale_factor=upsample_ratio, mode='nearest'))
|
| 994 |
+
feature_projection.add_module("conv1", nn.Conv2d(level_n_features_output, level_n_features_output, kernel_size=3, stride=1, padding=1, groups=level_n_features_output))
|
| 995 |
+
feature_projection.add_module("norm",nn.BatchNorm2d(level_n_features_output))
|
| 996 |
+
# B, in_channels, H*upsample_ratio, W*upsample_ratio -> B, full_features_head_dim, H*upsample_ratio, W*upsample_ratio
|
| 997 |
+
feature_projection.add_module("conv2", nn.Conv2d(level_n_features_output, full_features_head_dim, kernel_size=1, stride=1, padding=0))
|
| 998 |
+
else:
|
| 999 |
+
feature_projection = nn.Sequential()
|
| 1000 |
+
|
| 1001 |
+
self.neck_features_proj.append(feature_projection)
|
| 1002 |
+
|
| 1003 |
+
if i>0 and downsample_enabled[i]:
|
| 1004 |
+
upsample_ratio *= 2
|
| 1005 |
+
|
| 1006 |
+
def forward(self, x, il_level=-1, full_features=None):
|
| 1007 |
+
if self.neck_start_stage > il_level:
|
| 1008 |
+
return full_features
|
| 1009 |
+
|
| 1010 |
+
if full_features is None:
|
| 1011 |
+
full_features = self.neck_features_proj[il_level - self.neck_start_stage](x)
|
| 1012 |
+
else:
|
| 1013 |
+
#upsample torch tensor x to match full_features size, and add to full_features
|
| 1014 |
+
feature_projection = self.neck_features_proj[il_level - self.neck_start_stage](x)
|
| 1015 |
+
if feature_projection.shape[2] != full_features.shape[2] or feature_projection.shape[3] != full_features.shape[3]:
|
| 1016 |
+
feature_projection = torch.nn.functional.pad(feature_projection, ( 0, -feature_projection.shape[3] + full_features.shape[3], 0, -feature_projection.shape[2] + full_features.shape[2]))
|
| 1017 |
+
full_features = full_features + feature_projection
|
| 1018 |
+
return full_features
|
| 1019 |
+
|
| 1020 |
+
class ERADIO(nn.Module):
|
| 1021 |
+
"""
|
| 1022 |
+
Efficient RADIO
|
| 1023 |
+
"""
|
| 1024 |
+
|
| 1025 |
+
def __init__(self,
|
| 1026 |
+
dim,
|
| 1027 |
+
in_dim,
|
| 1028 |
+
depths,
|
| 1029 |
+
window_size,
|
| 1030 |
+
mlp_ratio,
|
| 1031 |
+
num_heads,
|
| 1032 |
+
drop_path_rate=0.2,
|
| 1033 |
+
in_chans=3,
|
| 1034 |
+
num_classes=1000,
|
| 1035 |
+
qkv_bias=False,
|
| 1036 |
+
qk_scale=None,
|
| 1037 |
+
layer_scale=None,
|
| 1038 |
+
layer_scale_conv=None,
|
| 1039 |
+
layer_norm_last=False,
|
| 1040 |
+
sr_ratio = [1, 1, 1, 1],
|
| 1041 |
+
max_depth = -1,
|
| 1042 |
+
conv_base=False,
|
| 1043 |
+
use_swiglu=False,
|
| 1044 |
+
multi_query=False,
|
| 1045 |
+
norm_layer=nn.LayerNorm,
|
| 1046 |
+
drop_uniform=False,
|
| 1047 |
+
yolo_arch=False,
|
| 1048 |
+
shuffle_down=False,
|
| 1049 |
+
downsample_shuffle=False,
|
| 1050 |
+
return_full_features=False,
|
| 1051 |
+
full_features_head_dim=128,
|
| 1052 |
+
neck_start_stage=1,
|
| 1053 |
+
use_neck=False,
|
| 1054 |
+
use_shift=False,
|
| 1055 |
+
cpb_mlp_hidden=512,
|
| 1056 |
+
conv_groups_ratio=0,
|
| 1057 |
+
verbose: bool = False,
|
| 1058 |
+
**kwargs):
|
| 1059 |
+
"""
|
| 1060 |
+
Args:
|
| 1061 |
+
dim: feature size dimension.
|
| 1062 |
+
depths: number of layers in each stage.
|
| 1063 |
+
window_size: window size in each stage.
|
| 1064 |
+
mlp_ratio: MLP ratio.
|
| 1065 |
+
num_heads: number of heads in each stage.
|
| 1066 |
+
drop_path_rate: drop path rate.
|
| 1067 |
+
in_chans: number of input channels.
|
| 1068 |
+
num_classes: number of classes.
|
| 1069 |
+
qkv_bias: bool argument for query, key, value learnable bias.
|
| 1070 |
+
qk_scale: bool argument to scaling query, key.
|
| 1071 |
+
drop_rate: dropout rate.
|
| 1072 |
+
attn_drop_rate: attention dropout rate.
|
| 1073 |
+
norm_layer: normalization layer.
|
| 1074 |
+
layer_scale: layer scaling coefficient.
|
| 1075 |
+
return_full_features: output dense features as well as logits
|
| 1076 |
+
full_features_head_dim: number of channels in the dense features head
|
| 1077 |
+
neck_start_stage: a stage id to start full feature neck. Model has 4 stages, indix starts with 0
|
| 1078 |
+
for 224 resolution, the output of the stage before downsample:
|
| 1079 |
+
stage 0: 56x56, stage 1: 28x28, stage 2: 14x14, stage 3: 7x7
|
| 1080 |
+
use_neck: even for summarization embedding use neck
|
| 1081 |
+
use_shift: SWIN like window shifting but without masking attention
|
| 1082 |
+
conv_groups_ratio: will be used for conv blocks where there is no multires attention,
|
| 1083 |
+
if 0 then normal conv,
|
| 1084 |
+
if 1 then channels are independent,
|
| 1085 |
+
if -1 then no conv at all
|
| 1086 |
+
|
| 1087 |
+
"""
|
| 1088 |
+
super().__init__()
|
| 1089 |
+
|
| 1090 |
+
num_features = int(dim * 2 ** (len(depths) - 1))
|
| 1091 |
+
self.num_classes = num_classes
|
| 1092 |
+
self.patch_embed = PatchEmbed(in_chans=in_chans, in_dim=in_dim, dim=dim, shuffle_down=shuffle_down)
|
| 1093 |
+
# set return_full_features true if we want to return full features from all stages
|
| 1094 |
+
self.return_full_features = return_full_features
|
| 1095 |
+
self.use_neck = use_neck
|
| 1096 |
+
|
| 1097 |
+
dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))]
|
| 1098 |
+
if drop_uniform:
|
| 1099 |
+
dpr = [drop_path_rate for x in range(sum(depths))]
|
| 1100 |
+
|
| 1101 |
+
if not isinstance(max_depth, list): max_depth = [max_depth] * len(depths)
|
| 1102 |
+
|
| 1103 |
+
self.levels = nn.ModuleList()
|
| 1104 |
+
for i in range(len(depths)):
|
| 1105 |
+
conv = True if (i == 0 or i == 1) else False
|
| 1106 |
+
|
| 1107 |
+
level = ERADIOLayer(dim=int(dim * 2 ** i),
|
| 1108 |
+
depth=depths[i],
|
| 1109 |
+
num_heads=num_heads[i],
|
| 1110 |
+
window_size=window_size[i],
|
| 1111 |
+
mlp_ratio=mlp_ratio,
|
| 1112 |
+
qkv_bias=qkv_bias,
|
| 1113 |
+
qk_scale=qk_scale,
|
| 1114 |
+
conv=conv,
|
| 1115 |
+
drop_path=dpr[sum(depths[:i]):sum(depths[:i + 1])],
|
| 1116 |
+
downsample=(i < len(depths) - 1),
|
| 1117 |
+
layer_scale=layer_scale,
|
| 1118 |
+
layer_scale_conv=layer_scale_conv,
|
| 1119 |
+
sr_ratio=sr_ratio[i],
|
| 1120 |
+
use_swiglu=use_swiglu,
|
| 1121 |
+
multi_query=multi_query,
|
| 1122 |
+
norm_layer=norm_layer,
|
| 1123 |
+
yolo_arch=yolo_arch,
|
| 1124 |
+
downsample_shuffle=downsample_shuffle,
|
| 1125 |
+
conv_base=conv_base,
|
| 1126 |
+
cpb_mlp_hidden=cpb_mlp_hidden,
|
| 1127 |
+
use_shift=use_shift,
|
| 1128 |
+
conv_groups_ratio=conv_groups_ratio,
|
| 1129 |
+
verbose=verbose)
|
| 1130 |
+
|
| 1131 |
+
self.levels.append(level)
|
| 1132 |
+
|
| 1133 |
+
if self.return_full_features or self.use_neck:
|
| 1134 |
+
#num_heads
|
| 1135 |
+
downsample_enabled = [self.levels[i-1].downsample is not None for i in range(len(self.levels))]
|
| 1136 |
+
self.high_res_neck = HiResNeck(dim, depths, neck_start_stage, full_features_head_dim, downsample_enabled)
|
| 1137 |
+
|
| 1138 |
+
self.switched_to_deploy = False
|
| 1139 |
+
|
| 1140 |
+
self.norm = LayerNorm2d(num_features) if layer_norm_last else nn.BatchNorm2d(num_features)
|
| 1141 |
+
self.avgpool = nn.AdaptiveAvgPool2d(1)
|
| 1142 |
+
self.head = nn.Linear(num_features, num_classes) if num_classes > 0 else nn.Identity()
|
| 1143 |
+
self.apply(self._init_weights)
|
| 1144 |
+
|
| 1145 |
+
def _init_weights(self, m):
|
| 1146 |
+
if isinstance(m, nn.Linear):
|
| 1147 |
+
trunc_normal_(m.weight, std=.02)
|
| 1148 |
+
if isinstance(m, nn.Linear) and m.bias is not None:
|
| 1149 |
+
nn.init.constant_(m.bias, 0)
|
| 1150 |
+
elif isinstance(m, nn.LayerNorm):
|
| 1151 |
+
nn.init.constant_(m.bias, 0)
|
| 1152 |
+
nn.init.constant_(m.weight, 1.0)
|
| 1153 |
+
elif isinstance(m, LayerNorm2d):
|
| 1154 |
+
nn.init.constant_(m.bias, 0)
|
| 1155 |
+
nn.init.constant_(m.weight, 1.0)
|
| 1156 |
+
elif isinstance(m, nn.BatchNorm2d):
|
| 1157 |
+
nn.init.ones_(m.weight)
|
| 1158 |
+
nn.init.zeros_(m.bias)
|
| 1159 |
+
|
| 1160 |
+
@torch.jit.ignore
|
| 1161 |
+
def no_weight_decay_keywords(self):
|
| 1162 |
+
return {'rpb'}
|
| 1163 |
+
|
| 1164 |
+
def forward_features(self, x):
|
| 1165 |
+
_, _, H, W = x.shape
|
| 1166 |
+
if H % 32 != 0 or W % 32 != 0:
|
| 1167 |
+
raise ValueError(f"E-RADIO requires input dimensions to be divisible by 32 but got H x W: {H} x {W}")
|
| 1168 |
+
x = self.patch_embed(x)
|
| 1169 |
+
full_features = None
|
| 1170 |
+
for il, level in enumerate(self.levels):
|
| 1171 |
+
x, pre_downsample_x = level(x)
|
| 1172 |
+
|
| 1173 |
+
if self.return_full_features or self.use_neck:
|
| 1174 |
+
full_features = self.high_res_neck(pre_downsample_x, il, full_features)
|
| 1175 |
+
|
| 1176 |
+
# x = self.norm(full_features if (self.return_full_features or self.use_neck) else x)
|
| 1177 |
+
x = self.norm(x) # new version for
|
| 1178 |
+
|
| 1179 |
+
if not self.return_full_features:
|
| 1180 |
+
return x, None
|
| 1181 |
+
|
| 1182 |
+
return x, full_features
|
| 1183 |
+
|
| 1184 |
+
def forward(self, x):
|
| 1185 |
+
x, full_features = self.forward_features(x)
|
| 1186 |
+
|
| 1187 |
+
x = self.avgpool(x)
|
| 1188 |
+
x = torch.flatten(x, 1)
|
| 1189 |
+
|
| 1190 |
+
x = self.head(x)
|
| 1191 |
+
if full_features is not None:
|
| 1192 |
+
return x, full_features
|
| 1193 |
+
return x
|
| 1194 |
+
|
| 1195 |
+
def switch_to_deploy(self):
|
| 1196 |
+
'''
|
| 1197 |
+
A method to perform model self-compression
|
| 1198 |
+
merges BN into conv layers
|
| 1199 |
+
converts MLP relative positional bias into precomputed buffers
|
| 1200 |
+
'''
|
| 1201 |
+
if not self.switched_to_deploy:
|
| 1202 |
+
for level in [self.patch_embed, self.levels, self.head]:
|
| 1203 |
+
for module in level.modules():
|
| 1204 |
+
if hasattr(module, 'switch_to_deploy'):
|
| 1205 |
+
module.switch_to_deploy()
|
| 1206 |
+
self.switched_to_deploy = True
|
| 1207 |
+
|
| 1208 |
+
|
| 1209 |
+
def change_window_size(self, new_window_size):
|
| 1210 |
+
"""
|
| 1211 |
+
E-RADIO employs windowed attention, which may be sensitive to the choice of this parameter,
|
| 1212 |
+
especially in cases of uneven partitioning of the feature maps.
|
| 1213 |
+
E-RADIO allows for the adjustment of the window size after training,
|
| 1214 |
+
making it adaptable to different input image resolutions.
|
| 1215 |
+
The recommended values for window size based on input resolution are as follows:
|
| 1216 |
+
|
| 1217 |
+
Input Resolution | Window Size
|
| 1218 |
+
224 | 7
|
| 1219 |
+
256 | 8
|
| 1220 |
+
386 | 12
|
| 1221 |
+
512 | 16
|
| 1222 |
+
Ideally, the window size should be a factor of the input resolution. In the third stage, we divide the resolution by 16, so the window size should be
|
| 1223 |
+
img_res/16/2
|
| 1224 |
+
for the third stage and img_res/32 for the last stage. While this can be applied in a brute-force manner, a better way is to do model.change_window_size.
|
| 1225 |
+
Manual way to change resolution -> model.change_window_size(resolution)
|
| 1226 |
+
"""
|
| 1227 |
+
window_size = new_window_size
|
| 1228 |
+
print(f"Setting window size to {window_size}")
|
| 1229 |
+
for module in self.modules():
|
| 1230 |
+
if hasattr(module, "window_size"):
|
| 1231 |
+
# check if tuple or a number
|
| 1232 |
+
if isinstance(module.window_size, tuple):
|
| 1233 |
+
if module.window_size[0] != window_size:
|
| 1234 |
+
module.window_size = (window_size, window_size)
|
| 1235 |
+
elif isinstance(module.window_size, list):
|
| 1236 |
+
if module.window_size[0] != window_size:
|
| 1237 |
+
module.window_size = [window_size, window_size]
|
| 1238 |
+
else:
|
| 1239 |
+
module.window_size = window_size
|
| 1240 |
+
|
| 1241 |
+
|
| 1242 |
+
def set_optimal_window_size(self, image_dim, max_window_size = 16):
|
| 1243 |
+
"""
|
| 1244 |
+
Using hand picked window size for various resolutions.
|
| 1245 |
+
|
| 1246 |
+
E-RADIO employs windowed attention, which may be sensitive to the choice of this parameter,
|
| 1247 |
+
especially in cases of uneven partitioning of the feature maps.
|
| 1248 |
+
E-RADIO allows for the adjustment of the window size after training,
|
| 1249 |
+
making it adaptable to different input image resolutions.
|
| 1250 |
+
The recommended values for window size based on input resolution are as follows:
|
| 1251 |
+
|
| 1252 |
+
Input Resolution | Window Size
|
| 1253 |
+
224 | 7
|
| 1254 |
+
256 | 8
|
| 1255 |
+
386 | 12
|
| 1256 |
+
512 | 16
|
| 1257 |
+
Ideally, the window size should be a factor of the input resolution. In the third stage, we divide the resolution by 16, so the window size should be
|
| 1258 |
+
img_res/16/2
|
| 1259 |
+
for the third stage and img_res/32 for the last stage. While this can be applied in a brute-force manner, a better way is to do model.change_window_size.
|
| 1260 |
+
Manual way to change resolution -> model.change_window_size(resolution)
|
| 1261 |
+
|
| 1262 |
+
"""
|
| 1263 |
+
# import math
|
| 1264 |
+
|
| 1265 |
+
def divisorGenerator(n):
|
| 1266 |
+
large_divisors = []
|
| 1267 |
+
for i in range(1, int(math.sqrt(n) + 1)):
|
| 1268 |
+
if n % i == 0:
|
| 1269 |
+
yield i
|
| 1270 |
+
if i*i != n:
|
| 1271 |
+
large_divisors.append(n / i)
|
| 1272 |
+
for divisor in reversed(large_divisors):
|
| 1273 |
+
yield divisor
|
| 1274 |
+
|
| 1275 |
+
if isinstance(image_dim, list) or isinstance(image_dim, tuple):
|
| 1276 |
+
image_dim = min(image_dim)
|
| 1277 |
+
|
| 1278 |
+
# we do windowed attention in the 3rd stage for the first time, therefore //16,
|
| 1279 |
+
# we do subsampled attention with downsample by 2 so need to get //32 actually
|
| 1280 |
+
# ideally we should rewrite this to be dependent on the structure of the model like what if subsampled is removed etc
|
| 1281 |
+
all_divisors = np.array(list(divisorGenerator(image_dim//32)))
|
| 1282 |
+
new_window_size = int(min(all_divisors[all_divisors <= max_window_size][-1], max_window_size))
|
| 1283 |
+
|
| 1284 |
+
# for image_dim in [128, 224, 256, 384, 512, 768, 1024]:
|
| 1285 |
+
# all_divisors = np.array(list(divisorGenerator(image_dim//32)))
|
| 1286 |
+
# new_window_size = int(min(all_divisors[all_divisors <= max_window_size][-1], max_window_size))
|
| 1287 |
+
# print(f"Setting window size to {new_window_size} for image resolution {image_dim}")
|
| 1288 |
+
|
| 1289 |
+
self.change_window_size(new_window_size = new_window_size)
|
| 1290 |
+
|
| 1291 |
+
|
| 1292 |
+
@register_model
|
| 1293 |
+
def eradio_large_fullres_ws16(pretrained=False, **kwargs):
|
| 1294 |
+
model = ERADIO(
|
| 1295 |
+
depths=[3, 3, 5, 5],
|
| 1296 |
+
num_heads=[2, 4, 8, 16],
|
| 1297 |
+
window_size=[None, None, [16, 16], 16],
|
| 1298 |
+
dim=192,
|
| 1299 |
+
in_dim=64,
|
| 1300 |
+
mlp_ratio=4,
|
| 1301 |
+
drop_path_rate=0.0,
|
| 1302 |
+
sr_ratio=[1, 1, [2, 1], 1],
|
| 1303 |
+
use_swiglu=False,
|
| 1304 |
+
yolo_arch=True,
|
| 1305 |
+
shuffle_down=False,
|
| 1306 |
+
conv_base=True,
|
| 1307 |
+
use_neck=True,
|
| 1308 |
+
full_features_head_dim=1536,
|
| 1309 |
+
neck_start_stage=2,
|
| 1310 |
+
**kwargs,
|
| 1311 |
+
)
|
| 1312 |
+
if pretrained:
|
| 1313 |
+
model.load_state_dict(torch.load(pretrained)["state_dict"])
|
| 1314 |
+
return model
|
| 1315 |
+
|
| 1316 |
+
|
| 1317 |
+
@register_model
|
| 1318 |
+
def eradio_xxxtiny(pretrained=False, **kwargs): # ,
|
| 1319 |
+
model = ERADIO(
|
| 1320 |
+
depths=[1, 3, 4, 5],
|
| 1321 |
+
num_heads=[2, 4, 8, 16],
|
| 1322 |
+
window_size=[None, None, [16, 16], 16],
|
| 1323 |
+
dim=32,
|
| 1324 |
+
in_dim=32,
|
| 1325 |
+
mlp_ratio=4,
|
| 1326 |
+
drop_path_rate=0.0,
|
| 1327 |
+
sr_ratio=[1, 1, [2, 1], 1],
|
| 1328 |
+
use_swiglu=False,
|
| 1329 |
+
yolo_arch=True,
|
| 1330 |
+
shuffle_down=False,
|
| 1331 |
+
conv_base=True,
|
| 1332 |
+
use_neck=True,
|
| 1333 |
+
full_features_head_dim=256,
|
| 1334 |
+
neck_start_stage=2,
|
| 1335 |
+
**kwargs,
|
| 1336 |
+
)
|
| 1337 |
+
if pretrained:
|
| 1338 |
+
model.load_state_dict(torch.load(pretrained))
|
| 1339 |
+
return model
|
| 1340 |
+
|
| 1341 |
+
@register_model
|
| 1342 |
+
def eradio_xxxtiny_8x_ws12(pretrained=False, **kwargs):
|
| 1343 |
+
model = ERADIO(depths=[1, 3, 4, 5],
|
| 1344 |
+
num_heads=[2, 4, 8, 16],
|
| 1345 |
+
window_size=[None, None, [12, 12], 12],
|
| 1346 |
+
dim=32,
|
| 1347 |
+
in_dim=32,
|
| 1348 |
+
mlp_ratio=4,
|
| 1349 |
+
drop_path_rate=0.0,
|
| 1350 |
+
sr_ratio=[1, 1, [2, 1], 1],
|
| 1351 |
+
use_swiglu=False,
|
| 1352 |
+
downsample_shuffle=False,
|
| 1353 |
+
yolo_arch=True,
|
| 1354 |
+
shuffle_down=False,
|
| 1355 |
+
cpb_mlp_hidden=64,
|
| 1356 |
+
use_neck=True,
|
| 1357 |
+
full_features_head_dim=256,
|
| 1358 |
+
neck_start_stage=2,
|
| 1359 |
+
conv_groups_ratio = 1,
|
| 1360 |
+
**kwargs)
|
| 1361 |
+
if pretrained:
|
| 1362 |
+
model.load_state_dict(torch.load(pretrained)["state_dict"])
|
| 1363 |
+
return model
|
| 1364 |
+
|
| 1365 |
+
|
| 1366 |
+
@register_model
|
| 1367 |
+
def eradio_xxxtiny_8x_ws16(pretrained=False, **kwargs):
|
| 1368 |
+
model = ERADIO(depths=[1, 3, 4, 5],
|
| 1369 |
+
num_heads=[2, 4, 8, 16],
|
| 1370 |
+
window_size=[None, None, [16, 16], 16],
|
| 1371 |
+
dim=32,
|
| 1372 |
+
in_dim=32,
|
| 1373 |
+
mlp_ratio=4,
|
| 1374 |
+
drop_path_rate=0.0,
|
| 1375 |
+
sr_ratio=[1, 1, [2, 1], 1],
|
| 1376 |
+
use_swiglu=False,
|
| 1377 |
+
downsample_shuffle=False,
|
| 1378 |
+
yolo_arch=True,
|
| 1379 |
+
shuffle_down=False,
|
| 1380 |
+
cpb_mlp_hidden=64,
|
| 1381 |
+
use_neck=True,
|
| 1382 |
+
full_features_head_dim=256,
|
| 1383 |
+
neck_start_stage=1,
|
| 1384 |
+
conv_groups_ratio = 1,
|
| 1385 |
+
**kwargs)
|
| 1386 |
+
if pretrained:
|
| 1387 |
+
model.load_state_dict(torch.load(pretrained)["state_dict"])
|
| 1388 |
+
return model
|
| 1389 |
+
|
| 1390 |
+
@register_model
|
| 1391 |
+
def eradio(pretrained=False, **kwargs):
|
| 1392 |
+
return eradio_large_fullres_ws16(pretrained=pretrained, **kwargs)
|
extra_models.py
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from distutils.version import LooseVersion
|
| 2 |
+
from types import MethodType
|
| 3 |
+
from typing import List, Optional, Tuple, Union
|
| 4 |
+
import warnings
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
from torch import nn
|
| 8 |
+
import torch.nn.functional as F
|
| 9 |
+
|
| 10 |
+
from timm.models.registry import register_model
|
| 11 |
+
from timm.data.constants import IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD
|
| 12 |
+
|
| 13 |
+
from .forward_intermediates import forward_intermediates
|
| 14 |
+
from .input_conditioner import InputConditioner
|
| 15 |
+
|
| 16 |
+
_has_torch_sdpa = hasattr(F, 'scaled_dot_product_attention')
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class PaliGemmaWrapper(nn.Module):
|
| 20 |
+
def __init__(self, vis_model: nn.Module, embed_dim: int):
|
| 21 |
+
super().__init__()
|
| 22 |
+
|
| 23 |
+
self.vis_model = vis_model
|
| 24 |
+
self.embed_dim = embed_dim
|
| 25 |
+
|
| 26 |
+
@property
|
| 27 |
+
def patch_size(self):
|
| 28 |
+
return self.vis_model.embeddings.patch_size
|
| 29 |
+
|
| 30 |
+
@property
|
| 31 |
+
def blocks(self):
|
| 32 |
+
return self.vis_model.encoder.layers
|
| 33 |
+
|
| 34 |
+
@property
|
| 35 |
+
def embed_dim(self):
|
| 36 |
+
return self.vis_model.embeddings.embed_dim
|
| 37 |
+
|
| 38 |
+
def forward(self, x: torch.Tensor):
|
| 39 |
+
outputs = self.vis_model(
|
| 40 |
+
x,
|
| 41 |
+
return_dict=False,
|
| 42 |
+
interpolate_pos_encoding=True,
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
features = outputs[0].to(torch.float32)
|
| 46 |
+
|
| 47 |
+
summary = features.mean(dim=1)
|
| 48 |
+
|
| 49 |
+
return summary, features
|
| 50 |
+
|
| 51 |
+
def forward_features(self, x: torch.Tensor):
|
| 52 |
+
return self(x)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def _get_paligemma_model(repo: str, embed_dim: int = None, dtype: torch.dtype = torch.bfloat16):
|
| 56 |
+
from transformers import PaliGemmaForConditionalGeneration, __version__ as tx_version
|
| 57 |
+
|
| 58 |
+
if LooseVersion(tx_version) > LooseVersion('4.44.2'):
|
| 59 |
+
warnings.warn(f'Your transformers version "{tx_version}" is higher than 4.44.2, and for whatever reason, PaliGemma might be broken.')
|
| 60 |
+
|
| 61 |
+
extra_args = dict()
|
| 62 |
+
|
| 63 |
+
if dtype is not None:
|
| 64 |
+
extra_args['torch_dtype'] = dtype
|
| 65 |
+
rev = str(dtype).split('.')[-1]
|
| 66 |
+
extra_args['revision'] = rev
|
| 67 |
+
|
| 68 |
+
model = PaliGemmaForConditionalGeneration.from_pretrained(repo, **extra_args)
|
| 69 |
+
|
| 70 |
+
vis_model = model.vision_tower.vision_model
|
| 71 |
+
|
| 72 |
+
vis_model = PaliGemmaWrapper(vis_model, embed_dim)
|
| 73 |
+
|
| 74 |
+
return vis_model
|
| 75 |
+
|
| 76 |
+
@register_model
|
| 77 |
+
def paligemma_896_student(**kwargs):
|
| 78 |
+
model = _get_paligemma_model('google/paligemma-3b-pt-896', embed_dim=1152, dtype=None)
|
| 79 |
+
|
| 80 |
+
return model
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def dv2_sdpa(self, x: torch.Tensor) -> torch.Tensor:
|
| 84 |
+
B, N, C = x.shape
|
| 85 |
+
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
|
| 86 |
+
|
| 87 |
+
q, k, v = qkv[0], qkv[1], qkv[2]
|
| 88 |
+
x = F.scaled_dot_product_attention(
|
| 89 |
+
q, k, v,
|
| 90 |
+
is_causal=False,
|
| 91 |
+
dropout_p=self.attn_drop.p if self.training else 0.,
|
| 92 |
+
scale=self.scale,
|
| 93 |
+
)
|
| 94 |
+
x = x.transpose(1, 2).reshape(B, N, C)
|
| 95 |
+
x = self.proj(x)
|
| 96 |
+
x = self.proj_drop(x)
|
| 97 |
+
return x
|
| 98 |
+
|
| 99 |
+
def _load_dino_v2(dino_v2_model, cache_dir: Optional[str] = None, pretrained=True, **kwargs):
|
| 100 |
+
if cache_dir:
|
| 101 |
+
torch.hub.set_dir(cache_dir)
|
| 102 |
+
model: nn.Module = torch.hub.load(
|
| 103 |
+
'facebookresearch/dinov2',
|
| 104 |
+
dino_v2_model,
|
| 105 |
+
pretrained=pretrained,
|
| 106 |
+
# **kwargs,
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
if _has_torch_sdpa:
|
| 110 |
+
for n, m in model.named_modules():
|
| 111 |
+
if n.endswith('.attn'):
|
| 112 |
+
m.forward = MethodType(dv2_sdpa, m)
|
| 113 |
+
|
| 114 |
+
return model
|
| 115 |
+
|
| 116 |
+
class DinoWrapper(nn.Module):
|
| 117 |
+
def __init__(self, dino_model: nn.Module):
|
| 118 |
+
super().__init__()
|
| 119 |
+
|
| 120 |
+
self.inner = dino_model
|
| 121 |
+
dino_model.blocks = nn.Sequential(*dino_model.blocks)
|
| 122 |
+
|
| 123 |
+
@property
|
| 124 |
+
def embed_dim(self):
|
| 125 |
+
return self.inner.embed_dim
|
| 126 |
+
|
| 127 |
+
@property
|
| 128 |
+
def patch_size(self):
|
| 129 |
+
return self.inner.patch_size
|
| 130 |
+
|
| 131 |
+
@property
|
| 132 |
+
def num_cls_tokens(self):
|
| 133 |
+
return getattr(self.inner, 'num_tokens', 1)
|
| 134 |
+
|
| 135 |
+
@property
|
| 136 |
+
def num_registers(self):
|
| 137 |
+
return getattr(self.inner, 'num_register_tokens', 0)
|
| 138 |
+
|
| 139 |
+
@property
|
| 140 |
+
def num_summary_tokens(self):
|
| 141 |
+
return self.num_cls_tokens + self.num_registers
|
| 142 |
+
|
| 143 |
+
@property
|
| 144 |
+
def blocks(self):
|
| 145 |
+
return self.inner.blocks
|
| 146 |
+
|
| 147 |
+
def forward(self, *args, **kwargs) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 148 |
+
parts = self.inner.forward_features(*args, **kwargs)
|
| 149 |
+
|
| 150 |
+
cls_token = parts['x_norm_clstoken']
|
| 151 |
+
features = parts['x_norm_patchtokens']
|
| 152 |
+
|
| 153 |
+
return cls_token, features
|
| 154 |
+
|
| 155 |
+
def forward_features(self, x: torch.Tensor):
|
| 156 |
+
x = self.inner.prepare_tokens_with_masks(x)
|
| 157 |
+
x = self.inner.blocks(x)
|
| 158 |
+
x_norm = self.inner.norm(x)
|
| 159 |
+
|
| 160 |
+
return x_norm[:, 0], x_norm[:, self.num_summary_tokens:]
|
| 161 |
+
|
| 162 |
+
def patchify(self, x: torch.Tensor) -> torch.Tensor:
|
| 163 |
+
return self.inner.prepare_tokens_with_masks(x)
|
| 164 |
+
|
| 165 |
+
def forward_intermediates(self,
|
| 166 |
+
x: torch.Tensor,
|
| 167 |
+
norm: bool = False,
|
| 168 |
+
**kwargs,
|
| 169 |
+
) -> Union[List[torch.Tensor], Tuple[torch.Tensor, List[torch.Tensor]]]:
|
| 170 |
+
return forward_intermediates(
|
| 171 |
+
self,
|
| 172 |
+
patch_extractor=self.inner.prepare_tokens_with_masks,
|
| 173 |
+
num_summary_tokens=self.num_summary_tokens,
|
| 174 |
+
num_cls_tokens=self.num_cls_tokens,
|
| 175 |
+
norm=self.inner.norm if norm else lambda y: y,
|
| 176 |
+
x=x,
|
| 177 |
+
**kwargs,
|
| 178 |
+
)
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def _dino_student(arch: str, **kwargs):
|
| 182 |
+
from . import dinov2_arch
|
| 183 |
+
|
| 184 |
+
factory = getattr(dinov2_arch, arch)
|
| 185 |
+
model = factory()
|
| 186 |
+
|
| 187 |
+
model = DinoWrapper(model)
|
| 188 |
+
|
| 189 |
+
conditioner = InputConditioner(
|
| 190 |
+
input_scale=1.0,
|
| 191 |
+
norm_mean=IMAGENET_DEFAULT_MEAN,
|
| 192 |
+
norm_std=IMAGENET_DEFAULT_STD,
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
model.input_conditioner = conditioner
|
| 196 |
+
|
| 197 |
+
return model
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
@register_model
|
| 201 |
+
def dino_v2_l_student(**kwargs):
|
| 202 |
+
return _dino_student('dinov2_vitl14_reg', **kwargs)
|
| 203 |
+
|
| 204 |
+
@register_model
|
| 205 |
+
def dino_v2_g_student(**kwargs):
|
| 206 |
+
return _dino_student('dinov2_vitg14_reg', **kwargs)
|
extra_timm_models.py
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
|
| 9 |
+
import math
|
| 10 |
+
import warnings
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
from torch import nn
|
| 14 |
+
from torch.nn import functional as F
|
| 15 |
+
|
| 16 |
+
from timm.models import register_model
|
| 17 |
+
from timm.models.vision_transformer import (
|
| 18 |
+
VisionTransformer,
|
| 19 |
+
_create_vision_transformer as _timm_create_vision_transformer,
|
| 20 |
+
Mlp,
|
| 21 |
+
Block,
|
| 22 |
+
LayerScale as TIMMLayerScale,
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
# Import these to also register them
|
| 26 |
+
from . import dinov2_arch
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@register_model
|
| 30 |
+
def vit_tiny_patch14_224(pretrained=False, **kwargs) -> VisionTransformer:
|
| 31 |
+
""" ViT-Tiny (Vit-Ti/16)
|
| 32 |
+
"""
|
| 33 |
+
model_args = dict(patch_size=14, embed_dim=192, depth=12, num_heads=3)
|
| 34 |
+
model = _create_vision_transformer('vit_tiny_patch14_224', pretrained=pretrained, **dict(model_args, **kwargs))
|
| 35 |
+
return model
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
@register_model
|
| 39 |
+
def vit_small_patch14_224(pretrained=False, **kwargs) -> VisionTransformer:
|
| 40 |
+
""" ViT-Small (ViT-S/16)
|
| 41 |
+
"""
|
| 42 |
+
model_args = dict(patch_size=14, embed_dim=384, depth=12, num_heads=6)
|
| 43 |
+
model = _create_vision_transformer('vit_small_patch16_224', pretrained=pretrained, **dict(model_args, **kwargs))
|
| 44 |
+
return model
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
@register_model
|
| 48 |
+
def vit_base_patch14_224(pretrained=False, **kwargs) -> VisionTransformer:
|
| 49 |
+
""" ViT-Base (ViT-B/14) from original paper (https://arxiv.org/abs/2010.11929).
|
| 50 |
+
ImageNet-1k weights fine-tuned from in21k @ 224x224, source https://github.com/google-research/vision_transformer.
|
| 51 |
+
"""
|
| 52 |
+
model_args = dict(patch_size=14, embed_dim=768, depth=12, num_heads=12)
|
| 53 |
+
model = _create_vision_transformer('vit_base_patch14_224', pretrained=pretrained, **dict(model_args, **kwargs))
|
| 54 |
+
return model
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
@register_model
|
| 58 |
+
def vit_base_patch16_v2_224(pretrained=False, **kwargs) -> VisionTransformer:
|
| 59 |
+
""" ViT-Base (ViT-B/16) from original paper (https://arxiv.org/abs/2010.11929).
|
| 60 |
+
ImageNet-1k weights fine-tuned from in21k @ 224x224, source https://github.com/google-research/vision_transformer.
|
| 61 |
+
"""
|
| 62 |
+
model_args = dict(
|
| 63 |
+
patch_size=16, embed_dim=768, depth=12, num_heads=12, init_values=1e-5,
|
| 64 |
+
reg_tokens=4, no_embed_class=True, img_size=518 * 16 // 14
|
| 65 |
+
)
|
| 66 |
+
model = _create_vision_transformer(
|
| 67 |
+
'vit_base_patch14_reg4_dinov2', pretrained=pretrained, **dict(model_args, **kwargs))
|
| 68 |
+
return model
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
@register_model
|
| 72 |
+
def vit_large_patch16_v2_224(pretrained: bool = False, **kwargs) -> VisionTransformer:
|
| 73 |
+
""" ViT-Large model (ViT-L/16) from original paper (https://arxiv.org/abs/2010.11929).
|
| 74 |
+
ImageNet-1k weights fine-tuned from in21k @ 224x224, source https://github.com/google-research/vision_transformer.
|
| 75 |
+
"""
|
| 76 |
+
name = 'vit_large_patch14_reg4_dinov2'
|
| 77 |
+
model_args = dict(
|
| 78 |
+
patch_size=16, embed_dim=1024, depth=24, num_heads=16, init_values=1e-5,
|
| 79 |
+
reg_tokens=4, no_embed_class=True, img_size=518 * 16 // 14
|
| 80 |
+
)
|
| 81 |
+
model = _create_vision_transformer(name, pretrained=pretrained, **dict(model_args, **kwargs))
|
| 82 |
+
|
| 83 |
+
return model
|
| 84 |
+
|
| 85 |
+
@register_model
|
| 86 |
+
def vit_huge_patch16_224(pretrained=False, **kwargs) -> VisionTransformer:
|
| 87 |
+
""" ViT-Huge model (ViT-H/16) from original paper (https://arxiv.org/abs/2010.11929).
|
| 88 |
+
"""
|
| 89 |
+
model_args = dict(patch_size=16, embed_dim=1280, depth=32, num_heads=16)
|
| 90 |
+
if pretrained:
|
| 91 |
+
# There is no pretrained version of ViT-H/16, but we can adapt a ViT-H/14 for this purpose
|
| 92 |
+
model = _create_vision_transformer('vit_huge_patch14_224', pretrained=True, **dict(model_args, **kwargs))
|
| 93 |
+
else:
|
| 94 |
+
model = _create_vision_transformer('vit_huge_patch16_224', pretrained=False, **dict(model_args, **kwargs))
|
| 95 |
+
return model
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
@register_model
|
| 99 |
+
def vit_huge_patch16_224_mlpnorm(pretrained=False, **kwargs) -> VisionTransformer:
|
| 100 |
+
""" ViT-Huge model (ViT-H/16) from original paper (https://arxiv.org/abs/2010.11929).
|
| 101 |
+
"""
|
| 102 |
+
model = vit_huge_patch16_224(pretrained=pretrained, **kwargs)
|
| 103 |
+
|
| 104 |
+
for m in model.modules():
|
| 105 |
+
if isinstance(m, Mlp) and not isinstance(m.norm, nn.LayerNorm):
|
| 106 |
+
m.norm = nn.LayerNorm(m.fc1.out_features)
|
| 107 |
+
|
| 108 |
+
return model
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
@register_model
|
| 112 |
+
def vit_giant_patch16_224(pretrained=False, scaled_ln: bool = False, **kwargs) -> VisionTransformer:
|
| 113 |
+
""" ViT-giant model (ViT-g/16) from original paper (https://arxiv.org/abs/2010.11929).
|
| 114 |
+
"""
|
| 115 |
+
model_args = dict(patch_size=16, embed_dim=1536, depth=40, num_heads=24)
|
| 116 |
+
model = _create_vision_transformer('vit_giant_patch16_224', pretrained=False, **dict(model_args, **kwargs))
|
| 117 |
+
if scaled_ln:
|
| 118 |
+
_apply_scaled_ln(model)
|
| 119 |
+
return model
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
@register_model
|
| 123 |
+
def vit_bigG_patch14_224(pretrained=False, **kwargs) -> VisionTransformer:
|
| 124 |
+
model_args = dict(patch_size=14, embed_dim=1664, depth=48, num_heads=16, init_values=1e-6)
|
| 125 |
+
model = _create_vision_transformer('vit_bigG_patch14', pretrained=False, **dict(model_args, **kwargs))
|
| 126 |
+
return model
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def _create_vision_transformer(*args, **kwargs):
|
| 130 |
+
model = _timm_create_vision_transformer(*args, **kwargs)
|
| 131 |
+
_patch_layer_scale(model)
|
| 132 |
+
return model
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def _patch_layer_scale(model: VisionTransformer):
|
| 136 |
+
def replace_ls(old_ls: TIMMLayerScale):
|
| 137 |
+
new_ls = dinov2_arch.LayerScale(old_ls.gamma.shape[0], inplace=old_ls.inplace)
|
| 138 |
+
new_ls.load_state_dict(old_ls.state_dict())
|
| 139 |
+
return new_ls
|
| 140 |
+
|
| 141 |
+
# Monkey patch: Replace TIMM's LayerScale with our modified DINOv2 one, that uses a param name
|
| 142 |
+
# other than gamma, so that HFHub doesn't mess with it!
|
| 143 |
+
for mod in model.modules():
|
| 144 |
+
if isinstance(mod, Block):
|
| 145 |
+
if isinstance(mod.ls1, TIMMLayerScale):
|
| 146 |
+
mod.ls1 = replace_ls(mod.ls1)
|
| 147 |
+
if isinstance(mod.ls2, TIMMLayerScale):
|
| 148 |
+
mod.ls2 = replace_ls(mod.ls2)
|
| 149 |
+
pass
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
class ScaledLayerNorm(nn.LayerNorm):
|
| 153 |
+
'''
|
| 154 |
+
https://arxiv.org/pdf/2502.05795v1
|
| 155 |
+
'''
|
| 156 |
+
def __init__(self, ln_base: nn.LayerNorm, depth: int = 0):
|
| 157 |
+
super().__init__(ln_base.normalized_shape, eps=ln_base.eps, elementwise_affine=ln_base.elementwise_affine)
|
| 158 |
+
self.load_state_dict(ln_base.state_dict())
|
| 159 |
+
self.register_buffer('ln_scale', torch.tensor(1.0 / math.sqrt(depth)), persistent=False)
|
| 160 |
+
|
| 161 |
+
def forward(self, x):
|
| 162 |
+
y = super().forward(x)
|
| 163 |
+
y = y * self.ln_scale
|
| 164 |
+
return y
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
class DyT(nn.Module):
|
| 168 |
+
def __init__(self, C: int, init_alpha: float):
|
| 169 |
+
super().__init__()
|
| 170 |
+
self.alpha = nn.Parameter(torch.full((1,), init_alpha))
|
| 171 |
+
self.gamma = nn.Parameter(torch.ones(C))
|
| 172 |
+
self.beta = nn.Parameter(torch.zeros(C))
|
| 173 |
+
|
| 174 |
+
def forward(self, x: torch.Tensor):
|
| 175 |
+
x = F.tanh(self.alpha * x)
|
| 176 |
+
return self.gamma * x + self.beta
|
| 177 |
+
|
| 178 |
+
@register_model
|
| 179 |
+
def vit_large_dyt_patch16_224(pretrained: bool = False, **kwargs) -> VisionTransformer:
|
| 180 |
+
""" ViT-Large model (ViT-L/16) from original paper (https://arxiv.org/abs/2010.11929).
|
| 181 |
+
ImageNet-1k weights fine-tuned from in21k @ 224x224, source https://github.com/google-research/vision_transformer.
|
| 182 |
+
"""
|
| 183 |
+
model_args = dict(patch_size=16, embed_dim=1024, depth=24, num_heads=16)
|
| 184 |
+
model = _create_vision_transformer('vit_large_dyt_patch16_224', pretrained=pretrained, **dict(model_args, **kwargs))
|
| 185 |
+
|
| 186 |
+
def _replace_ln_with_dyt(ln: nn.LayerNorm, depth: int):
|
| 187 |
+
return DyT(ln.normalized_shape[0], init_alpha=0.9)
|
| 188 |
+
_replace_ln(model, _replace_ln_with_dyt)
|
| 189 |
+
|
| 190 |
+
return model
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def _apply_scaled_ln(model: VisionTransformer):
|
| 194 |
+
warnings.warn('Post-LayerNorm scaling activated!')
|
| 195 |
+
|
| 196 |
+
_replace_ln(model, lambda ln, depth: ScaledLayerNorm(ln, depth=depth))
|
| 197 |
+
|
| 198 |
+
def _replace_ln(model: VisionTransformer, fn):
|
| 199 |
+
def _inner_replace_ln(block: Block, depth: int, key: str):
|
| 200 |
+
prev = getattr(block, key)
|
| 201 |
+
if isinstance(prev, nn.LayerNorm):
|
| 202 |
+
setattr(block, key, fn(prev, depth=depth))
|
| 203 |
+
|
| 204 |
+
for i, block in enumerate(model.blocks):
|
| 205 |
+
_inner_replace_ln(block, i + 1, 'norm1')
|
| 206 |
+
_inner_replace_ln(block, i + 1, 'norm2')
|
feature_normalizer.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
from collections import namedtuple
|
| 9 |
+
from typing import NamedTuple, Optional, Tuple
|
| 10 |
+
import torch
|
| 11 |
+
from torch import nn
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def _run_kernel(x: torch.Tensor, mean: torch.Tensor, tx: torch.Tensor):
|
| 15 |
+
if x.ndim <= 3:
|
| 16 |
+
x = x - mean
|
| 17 |
+
x = x @ tx.T
|
| 18 |
+
elif x.ndim == 4:
|
| 19 |
+
x = x - mean.reshape(1, -1, 1, 1)
|
| 20 |
+
kernel = tx.reshape(*tx.shape, 1, 1)
|
| 21 |
+
x = torch.nn.functional.conv2d(x, weight=kernel, bias=None, stride=1, padding=0)
|
| 22 |
+
else:
|
| 23 |
+
raise ValueError(f'Unsupported input dimension: {x.ndim}, shape: {x.shape}')
|
| 24 |
+
return x
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class FeatureNormalizer(nn.Module):
|
| 28 |
+
def __init__(self, embed_dim: int, dtype: torch.dtype = torch.float32):
|
| 29 |
+
super().__init__()
|
| 30 |
+
|
| 31 |
+
self.register_buffer('mean', torch.zeros(embed_dim, dtype=dtype))
|
| 32 |
+
self.register_buffer('tx', torch.eye(embed_dim, dtype=dtype))
|
| 33 |
+
|
| 34 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 35 |
+
x = _run_kernel(x, self.mean, self.tx)
|
| 36 |
+
return x
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class InterFeatState(NamedTuple):
|
| 40 |
+
y: torch.Tensor
|
| 41 |
+
alpha: torch.Tensor
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class IntermediateFeatureNormalizerBase(nn.Module):
|
| 45 |
+
def forward(self, x: torch.Tensor, index: int, rot_index: int = None, skip: Optional[int] = None) -> InterFeatState:
|
| 46 |
+
raise NotImplementedError()
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class IntermediateFeatureNormalizer(IntermediateFeatureNormalizerBase):
|
| 50 |
+
def __init__(self, num_intermediates: int, embed_dim: int, rot_per_layer: bool = False, dtype: torch.dtype = torch.float32):
|
| 51 |
+
super().__init__()
|
| 52 |
+
self.register_buffer('alphas', torch.ones(num_intermediates, dtype=dtype))
|
| 53 |
+
|
| 54 |
+
rot = torch.eye(embed_dim, dtype=dtype)
|
| 55 |
+
if rot_per_layer:
|
| 56 |
+
rot = rot.unsqueeze(0).repeat(num_intermediates, 1, 1)
|
| 57 |
+
|
| 58 |
+
self.register_buffer('rotation', rot.contiguous())
|
| 59 |
+
self.register_buffer('means', torch.zeros(num_intermediates, embed_dim, dtype=dtype))
|
| 60 |
+
|
| 61 |
+
def forward(self, x: torch.Tensor, index: int, rot_index: int = None, skip: Optional[int] = None) -> InterFeatState:
|
| 62 |
+
if rot_index is None:
|
| 63 |
+
rot_index = index
|
| 64 |
+
|
| 65 |
+
if skip:
|
| 66 |
+
assert x.ndim == 3, f'Cannot use the `skip` parameter when the `x` tensor isn\'t 3-dimensional.'
|
| 67 |
+
prefix, x = x[:, :skip], x[:, skip:]
|
| 68 |
+
|
| 69 |
+
rotation = self._get_rotation(rot_index)
|
| 70 |
+
y = _run_kernel(x, self.means[index], rotation)
|
| 71 |
+
|
| 72 |
+
alpha = self.alphas[index]
|
| 73 |
+
if skip:
|
| 74 |
+
alpha = torch.cat([
|
| 75 |
+
torch.ones(skip, dtype=alpha.dtype, device=alpha.device),
|
| 76 |
+
alpha[None].expand(y.shape[1]),
|
| 77 |
+
]).reshape(1, -1, 1)
|
| 78 |
+
y = torch.cat([prefix, y], dim=1)
|
| 79 |
+
else:
|
| 80 |
+
if x.ndim == 3:
|
| 81 |
+
alpha = alpha.reshape(1, 1, 1).expand(1, y.shape[1], 1)
|
| 82 |
+
elif x.ndim == 4:
|
| 83 |
+
alpha = alpha.reshape(1, 1, 1, 1).expand(1, 1, *y.shape[2:])
|
| 84 |
+
else:
|
| 85 |
+
raise ValueError(f'Unsupported input dimension: {x.ndim}')
|
| 86 |
+
|
| 87 |
+
return InterFeatState(y, alpha)
|
| 88 |
+
|
| 89 |
+
def _get_rotation(self, rot_index: int) -> torch.Tensor:
|
| 90 |
+
if self.rotation.ndim == 2:
|
| 91 |
+
return self.rotation
|
| 92 |
+
return self.rotation[rot_index]
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
class NullIntermediateFeatureNormalizer(IntermediateFeatureNormalizerBase):
|
| 96 |
+
instances = dict()
|
| 97 |
+
|
| 98 |
+
def __init__(self, dtype: torch.dtype, device: torch.device):
|
| 99 |
+
super().__init__()
|
| 100 |
+
self.register_buffer('alpha', torch.tensor(1, dtype=dtype, device=device))
|
| 101 |
+
|
| 102 |
+
@staticmethod
|
| 103 |
+
def get_instance(dtype: torch.dtype, device: torch.device):
|
| 104 |
+
instance = NullIntermediateFeatureNormalizer.instances.get((dtype, device), None)
|
| 105 |
+
if instance is None:
|
| 106 |
+
instance = NullIntermediateFeatureNormalizer(dtype, device)
|
| 107 |
+
NullIntermediateFeatureNormalizer.instances[(dtype, device)] = instance
|
| 108 |
+
return instance
|
| 109 |
+
|
| 110 |
+
def forward(self, x: torch.Tensor, index: int, rot_index: int = None, skip: Optional[int] = None) -> InterFeatState:
|
| 111 |
+
return InterFeatState(x, self.alpha)
|
forward_intermediates.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
|
| 9 |
+
from typing import Callable, Dict, List, Optional, Set, Tuple, Union, Any, Iterable
|
| 10 |
+
from types import MethodType
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
from torch import nn
|
| 14 |
+
|
| 15 |
+
from .feature_normalizer import IntermediateFeatureNormalizerBase, NullIntermediateFeatureNormalizer
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def _take_indices(
|
| 19 |
+
num_blocks: int,
|
| 20 |
+
n: Optional[Union[int, List[int], Tuple[int]]],
|
| 21 |
+
) -> Tuple[Set[int], int]:
|
| 22 |
+
if isinstance(n, int):
|
| 23 |
+
assert n >= 0
|
| 24 |
+
take_indices = {x for x in range(num_blocks - n, num_blocks)}
|
| 25 |
+
else:
|
| 26 |
+
take_indices = {num_blocks + idx if idx < 0 else idx for idx in n}
|
| 27 |
+
return take_indices, max(take_indices)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def forward_intermediates(
|
| 31 |
+
model: nn.Module,
|
| 32 |
+
patch_extractor: Callable[[torch.Tensor], torch.Tensor],
|
| 33 |
+
norm: nn.Module,
|
| 34 |
+
num_summary_tokens: int,
|
| 35 |
+
num_cls_tokens: int,
|
| 36 |
+
x: torch.Tensor,
|
| 37 |
+
indices: Optional[Union[int, List[int], Tuple[int]]] = None,
|
| 38 |
+
return_prefix_tokens: bool = False,
|
| 39 |
+
stop_early: bool = False,
|
| 40 |
+
output_fmt: str = 'NCHW',
|
| 41 |
+
intermediates_only: bool = False,
|
| 42 |
+
aggregation: Optional[str] = "sparse",
|
| 43 |
+
inter_feature_normalizer: Optional[IntermediateFeatureNormalizerBase] = None,
|
| 44 |
+
norm_alpha_scheme = "post-alpha",
|
| 45 |
+
block_kwargs: Dict = None,
|
| 46 |
+
) -> Union[List[torch.Tensor], Tuple[torch.Tensor, List[torch.Tensor]]]:
|
| 47 |
+
""" Forward features that returns intermediates.
|
| 48 |
+
|
| 49 |
+
The Dense layer aggregation method is inspired from the paper: "Dense Connector for MLLMs"
|
| 50 |
+
by Yao, Huanjin et al. (2024). arXiv preprint arXiv:2405.13800}
|
| 51 |
+
|
| 52 |
+
Args:
|
| 53 |
+
x: Input image tensor
|
| 54 |
+
indices: Take last n blocks if int, select matching indices if sequence
|
| 55 |
+
return_prefix_tokens: Return both prefix and spatial intermediate tokens
|
| 56 |
+
norm: Apply norm layer to all intermediates
|
| 57 |
+
stop_early: Stop iterating over blocks when last desired intermediate hit
|
| 58 |
+
output_fmt: Shape of intermediate feature outputs
|
| 59 |
+
intermediates_only: Only return intermediate features
|
| 60 |
+
aggregation: intermediate layer aggregation method (sparse or dense)
|
| 61 |
+
norm_alpha_scheme: apply alpha before ("pre-alpha") or after accumulation ("post-alpha")
|
| 62 |
+
Returns:
|
| 63 |
+
"""
|
| 64 |
+
assert output_fmt in ('NCHW', 'NLC'), 'Output format must be one of NCHW or NLC.'
|
| 65 |
+
assert aggregation in ('sparse', 'dense'), 'Aggregation must be one of sparse or dense.'
|
| 66 |
+
reshape = output_fmt == 'NCHW'
|
| 67 |
+
intermediates = []
|
| 68 |
+
|
| 69 |
+
block_kwargs = block_kwargs or dict()
|
| 70 |
+
|
| 71 |
+
blocks = model.blocks
|
| 72 |
+
|
| 73 |
+
take_indices, max_index = _take_indices(len(blocks), indices)
|
| 74 |
+
take_indices = sorted(take_indices)
|
| 75 |
+
# forward pass
|
| 76 |
+
B, _, height, width = x.shape
|
| 77 |
+
|
| 78 |
+
x = patch_extractor(x)
|
| 79 |
+
|
| 80 |
+
if stop_early:
|
| 81 |
+
blocks = blocks[:max_index + 1]
|
| 82 |
+
|
| 83 |
+
if inter_feature_normalizer is None or norm_alpha_scheme == 'none':
|
| 84 |
+
inter_feature_normalizer = NullIntermediateFeatureNormalizer.get_instance(x.dtype, x.device)
|
| 85 |
+
|
| 86 |
+
assert norm_alpha_scheme in ('none', 'pre-alpha', 'post-alpha'), f'Unsupported alpha scheme: {norm_alpha_scheme}'
|
| 87 |
+
post_alpha_scheme = norm_alpha_scheme == 'post-alpha'
|
| 88 |
+
|
| 89 |
+
accumulator = 0
|
| 90 |
+
alpha_sum = 0
|
| 91 |
+
num_accumulated = 0
|
| 92 |
+
|
| 93 |
+
take_off = 0
|
| 94 |
+
|
| 95 |
+
for i, blk in enumerate(blocks):
|
| 96 |
+
x = blk(x, **block_kwargs)
|
| 97 |
+
if aggregation == "dense":
|
| 98 |
+
# Arbitrarily use the rotation matrix from the final layer in the dense group
|
| 99 |
+
y, alpha = inter_feature_normalizer(x, i, rot_index=take_indices[take_off], skip=num_summary_tokens)
|
| 100 |
+
if post_alpha_scheme:
|
| 101 |
+
accumulator = accumulator + y
|
| 102 |
+
alpha_sum = alpha_sum + alpha
|
| 103 |
+
else:
|
| 104 |
+
accumulator = accumulator + (alpha * y)
|
| 105 |
+
alpha_sum += 1
|
| 106 |
+
num_accumulated += 1
|
| 107 |
+
if i == take_indices[take_off]:
|
| 108 |
+
if aggregation == "dense":
|
| 109 |
+
alpha = alpha_sum / num_accumulated
|
| 110 |
+
x_ = alpha * accumulator / num_accumulated
|
| 111 |
+
num_accumulated = 0
|
| 112 |
+
accumulator = 0
|
| 113 |
+
alpha_sum = 0
|
| 114 |
+
else:
|
| 115 |
+
y, alpha = inter_feature_normalizer(x, i, skip=num_summary_tokens)
|
| 116 |
+
x_ = alpha * y
|
| 117 |
+
# normalize intermediates with final norm layer if enabled
|
| 118 |
+
intermediates.append(norm(x_))
|
| 119 |
+
take_off = min(take_off + 1, len(take_indices) - 1)
|
| 120 |
+
|
| 121 |
+
# process intermediates
|
| 122 |
+
|
| 123 |
+
# split prefix (e.g. class, distill) and spatial feature tokens
|
| 124 |
+
prefix_tokens = [y[:, :num_cls_tokens] for y in intermediates]
|
| 125 |
+
intermediates = [y[:, num_summary_tokens:] for y in intermediates]
|
| 126 |
+
|
| 127 |
+
if reshape:
|
| 128 |
+
# reshape to BCHW output format
|
| 129 |
+
H = height // model.patch_size
|
| 130 |
+
W = width // model.patch_size
|
| 131 |
+
intermediates = [y.reshape(B, H, W, -1).permute(0, 3, 1, 2).contiguous() for y in intermediates]
|
| 132 |
+
if not torch.jit.is_scripting() and return_prefix_tokens:
|
| 133 |
+
# return_prefix not support in torchscript due to poor type handling
|
| 134 |
+
intermediates = list(zip(prefix_tokens, intermediates))
|
| 135 |
+
if intermediates_only:
|
| 136 |
+
return intermediates
|
| 137 |
+
x = norm(x)
|
| 138 |
+
return x, intermediates
|
hf_model.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
from collections import namedtuple
|
| 15 |
+
from typing import Callable, Dict, Optional, List, Union
|
| 16 |
+
|
| 17 |
+
from timm.models import VisionTransformer
|
| 18 |
+
import torch
|
| 19 |
+
from torch import nn
|
| 20 |
+
from transformers import PretrainedConfig, PreTrainedModel
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
from .common import RESOURCE_MAP, DEFAULT_VERSION
|
| 24 |
+
|
| 25 |
+
# Import all required modules.
|
| 26 |
+
from .adaptor_base import AdaptorBase, RadioOutput, AdaptorInput
|
| 27 |
+
from .adaptor_generic import GenericAdaptor, AdaptorBase
|
| 28 |
+
from .adaptor_mlp import create_mlp_from_config
|
| 29 |
+
from .adaptor_registry import adaptor_registry
|
| 30 |
+
from .cls_token import ClsToken
|
| 31 |
+
from .dinov2_arch import dinov2_vitg14_reg
|
| 32 |
+
from .enable_cpe_support import enable_cpe
|
| 33 |
+
from .enable_spectral_reparam import configure_spectral_reparam_from_args
|
| 34 |
+
from .eradio_model import eradio
|
| 35 |
+
from .feature_normalizer import FeatureNormalizer, IntermediateFeatureNormalizer
|
| 36 |
+
from .forward_intermediates import forward_intermediates
|
| 37 |
+
from .radio_model import create_model_from_args
|
| 38 |
+
from .radio_model import RADIOModel as RADIOModelBase, Resolution
|
| 39 |
+
from .input_conditioner import get_default_conditioner, InputConditioner
|
| 40 |
+
from .open_clip_adaptor import OpenCLIP_RADIO
|
| 41 |
+
from .vit_patch_generator import ViTPatchGenerator
|
| 42 |
+
from .vitdet import apply_vitdet_arch, VitDetArgs
|
| 43 |
+
|
| 44 |
+
# Register extra models
|
| 45 |
+
from .extra_timm_models import *
|
| 46 |
+
from .extra_models import *
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class RADIOConfig(PretrainedConfig):
|
| 50 |
+
"""Pretrained Hugging Face configuration for RADIO models."""
|
| 51 |
+
|
| 52 |
+
def __init__(
|
| 53 |
+
self,
|
| 54 |
+
args: Optional[dict] = None,
|
| 55 |
+
version: Optional[str] = DEFAULT_VERSION,
|
| 56 |
+
patch_size: Optional[int] = None,
|
| 57 |
+
max_resolution: Optional[int] = None,
|
| 58 |
+
preferred_resolution: Optional[Resolution] = None,
|
| 59 |
+
adaptor_names: Union[str, List[str]] = None,
|
| 60 |
+
adaptor_configs: Dict[str, Dict[str, int]] = None,
|
| 61 |
+
vitdet_window_size: Optional[int] = None,
|
| 62 |
+
feature_normalizer_config: Optional[dict] = None,
|
| 63 |
+
inter_feature_normalizer_config: Optional[dict] = None,
|
| 64 |
+
**kwargs,
|
| 65 |
+
):
|
| 66 |
+
self.args = args
|
| 67 |
+
for field in ["dtype", "amp_dtype"]:
|
| 68 |
+
if self.args is not None and field in self.args:
|
| 69 |
+
# Convert to a string in order to make it serializable.
|
| 70 |
+
# For example for torch.float32 we will store "float32",
|
| 71 |
+
# for "bfloat16" we will store "bfloat16".
|
| 72 |
+
self.args[field] = str(args[field]).split(".")[-1]
|
| 73 |
+
self.version = version
|
| 74 |
+
resource = RESOURCE_MAP[version]
|
| 75 |
+
self.patch_size = patch_size or resource.patch_size
|
| 76 |
+
self.max_resolution = max_resolution or resource.max_resolution
|
| 77 |
+
self.preferred_resolution = (
|
| 78 |
+
preferred_resolution or resource.preferred_resolution
|
| 79 |
+
)
|
| 80 |
+
self.adaptor_names = adaptor_names
|
| 81 |
+
self.adaptor_configs = adaptor_configs
|
| 82 |
+
self.vitdet_window_size = vitdet_window_size
|
| 83 |
+
self.feature_normalizer_config = feature_normalizer_config
|
| 84 |
+
self.inter_feature_normalizer_config = inter_feature_normalizer_config
|
| 85 |
+
super().__init__(**kwargs)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
class RADIOModel(PreTrainedModel):
|
| 90 |
+
"""Pretrained Hugging Face model for RADIO.
|
| 91 |
+
|
| 92 |
+
This class inherits from PreTrainedModel, which provides
|
| 93 |
+
HuggingFace's functionality for loading and saving models.
|
| 94 |
+
"""
|
| 95 |
+
|
| 96 |
+
config_class = RADIOConfig
|
| 97 |
+
|
| 98 |
+
def __init__(self, config: RADIOConfig):
|
| 99 |
+
super().__init__(config)
|
| 100 |
+
if hasattr(super(), "post_init"):
|
| 101 |
+
super().post_init()
|
| 102 |
+
|
| 103 |
+
RADIOArgs = namedtuple("RADIOArgs", config.args.keys())
|
| 104 |
+
args = RADIOArgs(**config.args)
|
| 105 |
+
self.config = config
|
| 106 |
+
|
| 107 |
+
model = create_model_from_args(args)
|
| 108 |
+
input_conditioner: InputConditioner = get_default_conditioner()
|
| 109 |
+
|
| 110 |
+
dtype = getattr(args, "dtype", torch.float32)
|
| 111 |
+
if isinstance(dtype, str):
|
| 112 |
+
# Convert the dtype's string representation back to a dtype.
|
| 113 |
+
dtype = getattr(torch, dtype)
|
| 114 |
+
model.to(dtype=dtype)
|
| 115 |
+
input_conditioner.dtype = dtype
|
| 116 |
+
|
| 117 |
+
summary_idxs = torch.tensor(
|
| 118 |
+
[i for i, t in enumerate(args.teachers) if t.get("use_summary", True)],
|
| 119 |
+
dtype=torch.int64,
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
adaptor_configs = config.adaptor_configs
|
| 123 |
+
adaptor_names = config.adaptor_names or []
|
| 124 |
+
|
| 125 |
+
adaptors = dict()
|
| 126 |
+
for adaptor_name in adaptor_names:
|
| 127 |
+
mlp_config = adaptor_configs[adaptor_name]
|
| 128 |
+
adaptor = GenericAdaptor(args, None, None, mlp_config)
|
| 129 |
+
adaptor.head_idx = mlp_config["head_idx"]
|
| 130 |
+
adaptors[adaptor_name] = adaptor
|
| 131 |
+
|
| 132 |
+
feature_normalizer = None
|
| 133 |
+
if config.feature_normalizer_config is not None:
|
| 134 |
+
# Actual normalization values will be restored when loading checkpoint weights.
|
| 135 |
+
feature_normalizer = FeatureNormalizer(config.feature_normalizer_config["embed_dim"])
|
| 136 |
+
|
| 137 |
+
inter_feature_normalizer = None
|
| 138 |
+
if config.inter_feature_normalizer_config is not None:
|
| 139 |
+
inter_feature_normalizer = IntermediateFeatureNormalizer(
|
| 140 |
+
config.inter_feature_normalizer_config["num_intermediates"],
|
| 141 |
+
config.inter_feature_normalizer_config["embed_dim"],
|
| 142 |
+
rot_per_layer=config.inter_feature_normalizer_config["rot_per_layer"],
|
| 143 |
+
dtype=dtype)
|
| 144 |
+
|
| 145 |
+
self.radio_model = RADIOModelBase(
|
| 146 |
+
model,
|
| 147 |
+
input_conditioner,
|
| 148 |
+
summary_idxs=summary_idxs,
|
| 149 |
+
patch_size=config.patch_size,
|
| 150 |
+
max_resolution=config.max_resolution,
|
| 151 |
+
window_size=config.vitdet_window_size,
|
| 152 |
+
preferred_resolution=config.preferred_resolution,
|
| 153 |
+
adaptors=adaptors,
|
| 154 |
+
feature_normalizer=feature_normalizer,
|
| 155 |
+
inter_feature_normalizer=inter_feature_normalizer,
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
@property
|
| 159 |
+
def adaptors(self) -> nn.ModuleDict:
|
| 160 |
+
return self.radio_model.adaptors
|
| 161 |
+
|
| 162 |
+
@property
|
| 163 |
+
def model(self) -> VisionTransformer:
|
| 164 |
+
return self.radio_model.model
|
| 165 |
+
|
| 166 |
+
@property
|
| 167 |
+
def input_conditioner(self) -> InputConditioner:
|
| 168 |
+
return self.radio_model.input_conditioner
|
| 169 |
+
|
| 170 |
+
@property
|
| 171 |
+
def num_summary_tokens(self) -> int:
|
| 172 |
+
return self.radio_model.num_summary_tokens
|
| 173 |
+
|
| 174 |
+
@property
|
| 175 |
+
def patch_size(self) -> int:
|
| 176 |
+
return self.radio_model.patch_size
|
| 177 |
+
|
| 178 |
+
@property
|
| 179 |
+
def max_resolution(self) -> int:
|
| 180 |
+
return self.radio_model.max_resolution
|
| 181 |
+
|
| 182 |
+
@property
|
| 183 |
+
def preferred_resolution(self) -> Resolution:
|
| 184 |
+
return self.radio_model.preferred_resolution
|
| 185 |
+
|
| 186 |
+
@property
|
| 187 |
+
def window_size(self) -> int:
|
| 188 |
+
return self.radio_model.window_size
|
| 189 |
+
|
| 190 |
+
@property
|
| 191 |
+
def min_resolution_step(self) -> int:
|
| 192 |
+
return self.radio_model.min_resolution_step
|
| 193 |
+
|
| 194 |
+
def make_preprocessor_external(self) -> Callable[[torch.Tensor], torch.Tensor]:
|
| 195 |
+
return self.radio_model.make_preprocessor_external()
|
| 196 |
+
|
| 197 |
+
def get_nearest_supported_resolution(self, height: int, width: int) -> Resolution:
|
| 198 |
+
return self.radio_model.get_nearest_supported_resolution(height, width)
|
| 199 |
+
|
| 200 |
+
def switch_to_deploy(self):
|
| 201 |
+
return self.radio_model.switch_to_deploy()
|
| 202 |
+
|
| 203 |
+
def forward(self, x: torch.Tensor):
|
| 204 |
+
return self.radio_model.forward(x)
|
input_conditioner.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
|
| 9 |
+
from typing import Union, Tuple
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
from torch import nn
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
norm_t = Union[Tuple[float, float, float], torch.Tensor]
|
| 16 |
+
|
| 17 |
+
class InputConditioner(nn.Module):
|
| 18 |
+
def __init__(self,
|
| 19 |
+
input_scale: float,
|
| 20 |
+
norm_mean: norm_t,
|
| 21 |
+
norm_std: norm_t,
|
| 22 |
+
dtype: torch.dtype = None,
|
| 23 |
+
):
|
| 24 |
+
super().__init__()
|
| 25 |
+
|
| 26 |
+
self.dtype = dtype
|
| 27 |
+
|
| 28 |
+
self.register_buffer("norm_mean", _to_tensor(norm_mean) / input_scale)
|
| 29 |
+
self.register_buffer("norm_std", _to_tensor(norm_std) / input_scale)
|
| 30 |
+
|
| 31 |
+
def forward(self, x: torch.Tensor):
|
| 32 |
+
y = (x - self.norm_mean) / self.norm_std
|
| 33 |
+
if self.dtype is not None:
|
| 34 |
+
y = y.to(self.dtype)
|
| 35 |
+
return y
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def get_default_conditioner():
|
| 39 |
+
from timm.data.constants import OPENAI_CLIP_MEAN, OPENAI_CLIP_STD
|
| 40 |
+
|
| 41 |
+
return InputConditioner(
|
| 42 |
+
input_scale=1.0,
|
| 43 |
+
norm_mean=OPENAI_CLIP_MEAN,
|
| 44 |
+
norm_std=OPENAI_CLIP_STD,
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def _to_tensor(v: norm_t):
|
| 49 |
+
return torch.as_tensor(v, dtype=torch.float32).view(-1, 1, 1)
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6b97a28721e2f319103d29e4df2552d3a46c25c2c70beb59305cc671adeebfc4
|
| 3 |
+
size 2606616136
|
open_clip_adaptor.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
from argparse import Namespace
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
from torch import nn
|
| 12 |
+
import torch.nn.functional as F
|
| 13 |
+
|
| 14 |
+
from .adaptor_registry import adaptor_registry, dict_t, state_t
|
| 15 |
+
|
| 16 |
+
from .adaptor_generic import GenericAdaptor
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class OpenCLIP_RADIO(GenericAdaptor):
|
| 20 |
+
def __init__(self, main_config: Namespace, adaptor_config: dict_t, state: state_t):
|
| 21 |
+
super().__init__(main_config, adaptor_config, state)
|
| 22 |
+
|
| 23 |
+
import open_clip
|
| 24 |
+
|
| 25 |
+
self.oc_model = open_clip.create_model_from_pretrained(
|
| 26 |
+
model_name=adaptor_config['model'],
|
| 27 |
+
pretrained=adaptor_config['pretrained'],
|
| 28 |
+
return_transform=False,
|
| 29 |
+
)
|
| 30 |
+
# Unload these parameters
|
| 31 |
+
self.oc_model.visual = None
|
| 32 |
+
|
| 33 |
+
self.tokenizer = open_clip.get_tokenizer(model_name=adaptor_config['model'])
|
| 34 |
+
|
| 35 |
+
def encode_text(self, text, normalize: bool = False):
|
| 36 |
+
return self.oc_model.encode_text(text, normalize=normalize)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
@adaptor_registry.register_adaptor("open_clip")
|
| 40 |
+
def create_open_clip_adaptor(main_config: Namespace, adaptor_config: dict_t, state: state_t):
|
| 41 |
+
return OpenCLIP_RADIO(main_config, adaptor_config, state)
|
preprocessor_config.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"crop_size": {
|
| 3 |
+
"height": 432,
|
| 4 |
+
"width": 432
|
| 5 |
+
},
|
| 6 |
+
"do_center_crop": false,
|
| 7 |
+
"do_convert_rgb": true,
|
| 8 |
+
"do_normalize": false,
|
| 9 |
+
"do_rescale": true,
|
| 10 |
+
"do_resize": true,
|
| 11 |
+
"image_processor_type": "CLIPImageProcessor",
|
| 12 |
+
"processor_class": "CLIPProcessor",
|
| 13 |
+
"resample": 3,
|
| 14 |
+
"size": {
|
| 15 |
+
"shortest_edge": 432
|
| 16 |
+
}
|
| 17 |
+
}
|
radio_model.py
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
from typing import Callable, Dict, Iterable, List, NamedTuple, Optional, Tuple, Union
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
from torch import nn
|
| 12 |
+
|
| 13 |
+
from timm.models import create_model, VisionTransformer
|
| 14 |
+
|
| 15 |
+
from .enable_cpe_support import enable_cpe
|
| 16 |
+
from .input_conditioner import InputConditioner
|
| 17 |
+
from .adaptor_base import AdaptorBase, RadioOutput, AdaptorInput
|
| 18 |
+
from . import eradio_model
|
| 19 |
+
from .enable_spectral_reparam import configure_spectral_reparam_from_args
|
| 20 |
+
from .feature_normalizer import FeatureNormalizer, IntermediateFeatureNormalizer
|
| 21 |
+
from . import dual_hybrid_vit
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class Resolution(NamedTuple):
|
| 25 |
+
height: int
|
| 26 |
+
width: int
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class RADIOModel(nn.Module):
|
| 30 |
+
def __init__(
|
| 31 |
+
self,
|
| 32 |
+
model: nn.Module,
|
| 33 |
+
input_conditioner: InputConditioner,
|
| 34 |
+
patch_size: int,
|
| 35 |
+
max_resolution: int,
|
| 36 |
+
preferred_resolution: Resolution,
|
| 37 |
+
summary_idxs: Optional[torch.Tensor] = None,
|
| 38 |
+
window_size: int = None,
|
| 39 |
+
adaptors: Dict[str, AdaptorBase] = None,
|
| 40 |
+
feature_normalizer: Optional[FeatureNormalizer] = None,
|
| 41 |
+
inter_feature_normalizer: Optional[IntermediateFeatureNormalizer] = None,
|
| 42 |
+
):
|
| 43 |
+
super().__init__()
|
| 44 |
+
|
| 45 |
+
self.model = model
|
| 46 |
+
self.input_conditioner = input_conditioner
|
| 47 |
+
if summary_idxs is not None:
|
| 48 |
+
self.register_buffer('summary_idxs', summary_idxs)
|
| 49 |
+
else:
|
| 50 |
+
self.summary_idxs = None
|
| 51 |
+
|
| 52 |
+
self._preferred_resolution = preferred_resolution
|
| 53 |
+
self._patch_size = patch_size
|
| 54 |
+
self._max_resolution = max_resolution
|
| 55 |
+
self._window_size = window_size
|
| 56 |
+
|
| 57 |
+
adaptors = adaptors or dict()
|
| 58 |
+
self.adaptors = nn.ModuleDict(adaptors)
|
| 59 |
+
|
| 60 |
+
if feature_normalizer is None:
|
| 61 |
+
feature_normalizer = nn.Identity()
|
| 62 |
+
self.feature_normalizer = feature_normalizer
|
| 63 |
+
self.inter_feature_normalizer = inter_feature_normalizer
|
| 64 |
+
|
| 65 |
+
@property
|
| 66 |
+
def num_summary_tokens(self) -> int:
|
| 67 |
+
if hasattr(self.model, 'num_summary_tokens'):
|
| 68 |
+
return self.model.num_summary_tokens
|
| 69 |
+
|
| 70 |
+
patch_gen = getattr(self.model, "patch_generator", None)
|
| 71 |
+
if patch_gen is not None:
|
| 72 |
+
return patch_gen.num_skip
|
| 73 |
+
elif getattr(self.model, 'global_pool', None) == 'avg':
|
| 74 |
+
return 0
|
| 75 |
+
return 1
|
| 76 |
+
|
| 77 |
+
@property
|
| 78 |
+
def num_cls_tokens(self) -> int:
|
| 79 |
+
if hasattr(self.model, 'num_cls_tokens'):
|
| 80 |
+
return self.model.num_cls_tokens
|
| 81 |
+
|
| 82 |
+
patch_gen = getattr(self.model, 'patch_generator', None)
|
| 83 |
+
if patch_gen is not None:
|
| 84 |
+
return patch_gen.num_cls_tokens
|
| 85 |
+
elif getattr(self.model, 'global_pool', None) == 'avg':
|
| 86 |
+
return 0
|
| 87 |
+
return 1
|
| 88 |
+
|
| 89 |
+
@property
|
| 90 |
+
def patch_size(self) -> int:
|
| 91 |
+
if self._patch_size is not None:
|
| 92 |
+
return self._patch_size
|
| 93 |
+
if hasattr(self.model, "patch_size"):
|
| 94 |
+
return self.model.patch_size
|
| 95 |
+
patch_gen = getattr(self.model, "patch_generator", None)
|
| 96 |
+
if patch_gen is not None:
|
| 97 |
+
return patch_gen.patch_size
|
| 98 |
+
return None
|
| 99 |
+
|
| 100 |
+
@property
|
| 101 |
+
def max_resolution(self) -> int:
|
| 102 |
+
return self._max_resolution
|
| 103 |
+
|
| 104 |
+
@property
|
| 105 |
+
def preferred_resolution(self) -> Resolution:
|
| 106 |
+
return self._preferred_resolution
|
| 107 |
+
|
| 108 |
+
@property
|
| 109 |
+
def window_size(self) -> int:
|
| 110 |
+
return self._window_size
|
| 111 |
+
|
| 112 |
+
@property
|
| 113 |
+
def min_resolution_step(self) -> int:
|
| 114 |
+
res = self.patch_size
|
| 115 |
+
if self.window_size is not None:
|
| 116 |
+
res *= self.window_size
|
| 117 |
+
return res
|
| 118 |
+
|
| 119 |
+
@property
|
| 120 |
+
def blocks(self) -> Iterable[nn.Module]:
|
| 121 |
+
blocks = getattr(self.model, 'blocks', None)
|
| 122 |
+
if blocks is not None:
|
| 123 |
+
return blocks
|
| 124 |
+
return None
|
| 125 |
+
|
| 126 |
+
@property
|
| 127 |
+
def embed_dim(self) -> int:
|
| 128 |
+
return self.model.embed_dim
|
| 129 |
+
|
| 130 |
+
def make_preprocessor_external(self) -> Callable[[torch.Tensor], torch.Tensor]:
|
| 131 |
+
ret = self.input_conditioner
|
| 132 |
+
self.input_conditioner = nn.Identity()
|
| 133 |
+
return ret
|
| 134 |
+
|
| 135 |
+
def get_nearest_supported_resolution(self, height: int, width: int) -> Resolution:
|
| 136 |
+
height = int(round(height / self.min_resolution_step) * self.min_resolution_step)
|
| 137 |
+
width = int(round(width / self.min_resolution_step) * self.min_resolution_step)
|
| 138 |
+
|
| 139 |
+
height = max(height, self.min_resolution_step)
|
| 140 |
+
width = max(width, self.min_resolution_step)
|
| 141 |
+
|
| 142 |
+
return Resolution(height=height, width=width)
|
| 143 |
+
|
| 144 |
+
def switch_to_deploy(self):
|
| 145 |
+
fn = getattr(self.model, 'switch_to_deploy', None)
|
| 146 |
+
if fn is not None:
|
| 147 |
+
fn()
|
| 148 |
+
|
| 149 |
+
def forward(self, x: torch.Tensor, feature_fmt: str = 'NLC') -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 150 |
+
'''
|
| 151 |
+
Forward process for model.
|
| 152 |
+
Args:
|
| 153 |
+
x: Input tensor. Unless `make_preprocessor_external` has been called, then the dynamic range of `x` is expected to be `[0, 1]`,
|
| 154 |
+
otherwise `x` is expected to be mean centered with unit standard deviation.
|
| 155 |
+
feature_format: ['NLC', 'NCHW'] - The output format for the features.
|
| 156 |
+
'''
|
| 157 |
+
res_step = self.min_resolution_step
|
| 158 |
+
if res_step is not None and (x.shape[-2] % res_step != 0 or x.shape[-1] % res_step != 0):
|
| 159 |
+
raise ValueError('The input resolution must be a multiple of `self.min_resolution_step`. '
|
| 160 |
+
'`self.get_nearest_supported_resolution(<height>, <width>) is provided as a convenience API. '
|
| 161 |
+
f'Input: {x.shape[-2:]}, Nearest: {self.get_nearest_supported_resolution(*x.shape[-2:])}')
|
| 162 |
+
|
| 163 |
+
x = self.input_conditioner(x)
|
| 164 |
+
y = self.model.forward_features(x)
|
| 165 |
+
ret = self._extract_final(x, y, feature_fmt=feature_fmt)
|
| 166 |
+
return ret
|
| 167 |
+
|
| 168 |
+
def _extract_final(self, x: torch.Tensor, y: torch.Tensor, feature_fmt: str = 'NLC'):
|
| 169 |
+
if isinstance(self.model, VisionTransformer):
|
| 170 |
+
patch_gen = getattr(self.model, "patch_generator", None)
|
| 171 |
+
if patch_gen is not None:
|
| 172 |
+
all_summary = y[:, : patch_gen.num_cls_tokens]
|
| 173 |
+
if self.summary_idxs is not None:
|
| 174 |
+
bb_summary = all_summary[:, self.summary_idxs]
|
| 175 |
+
else:
|
| 176 |
+
bb_summary = all_summary
|
| 177 |
+
all_feat = y[:, patch_gen.num_skip :]
|
| 178 |
+
elif self.model.global_pool == "avg":
|
| 179 |
+
all_summary = y[:, self.model.num_prefix_tokens :].mean(dim=1)
|
| 180 |
+
bb_summary = all_summary
|
| 181 |
+
all_feat = y
|
| 182 |
+
else:
|
| 183 |
+
all_summary = y[:, 0]
|
| 184 |
+
bb_summary = all_summary
|
| 185 |
+
all_feat = y[:, 1:]
|
| 186 |
+
elif isinstance(self.model, eradio_model.ERADIO):
|
| 187 |
+
_, f = y
|
| 188 |
+
all_feat = f.flatten(2).transpose(1, 2)
|
| 189 |
+
all_summary = all_feat.mean(dim=1)
|
| 190 |
+
bb_summary = all_summary
|
| 191 |
+
elif isinstance(y, (list, tuple)):
|
| 192 |
+
all_summary, all_feat = y
|
| 193 |
+
bb_summary = all_summary
|
| 194 |
+
else:
|
| 195 |
+
all_summary = y[:, :self.num_cls_tokens]
|
| 196 |
+
if self.summary_idxs is not None and all_summary.shape[1] > 1:
|
| 197 |
+
if all_summary.shape[1] == 1:
|
| 198 |
+
# Create dummy duplicates
|
| 199 |
+
all_summary = all_summary.expand(-1, 128, -1)
|
| 200 |
+
bb_summary = all_summary[:, self.summary_idxs]
|
| 201 |
+
else:
|
| 202 |
+
bb_summary = all_summary
|
| 203 |
+
all_feat = y[:, self.num_summary_tokens:]
|
| 204 |
+
|
| 205 |
+
all_feat = self.feature_normalizer(all_feat)
|
| 206 |
+
|
| 207 |
+
if feature_fmt == 'NCHW':
|
| 208 |
+
fmt_feat = (all_feat.reshape(all_feat.shape[0], x.shape[-2] // self.patch_size, x.shape[-1] // self.patch_size, all_feat.shape[2])
|
| 209 |
+
.permute(0, 3, 1, 2)
|
| 210 |
+
)
|
| 211 |
+
elif feature_fmt == 'NLC':
|
| 212 |
+
fmt_feat = all_feat
|
| 213 |
+
else:
|
| 214 |
+
raise ValueError(f'Unsupported feature_fmt: {feature_fmt}. Must be one of ["NLC", "NCHW"]')
|
| 215 |
+
|
| 216 |
+
ret = RadioOutput(bb_summary.flatten(1), fmt_feat)
|
| 217 |
+
|
| 218 |
+
if self.adaptors:
|
| 219 |
+
ret = dict(backbone=ret)
|
| 220 |
+
for name, adaptor in self.adaptors.items():
|
| 221 |
+
if all_summary.ndim == 3:
|
| 222 |
+
if all_summary.shape[1] == 1:
|
| 223 |
+
summary = all_summary[:, 0]
|
| 224 |
+
else:
|
| 225 |
+
summary = all_summary[:, adaptor.head_idx]
|
| 226 |
+
else:
|
| 227 |
+
summary = all_summary
|
| 228 |
+
ada_input = AdaptorInput(images=x, summary=summary.float(), features=all_feat, feature_fmt=feature_fmt, patch_size=self.patch_size)
|
| 229 |
+
v = adaptor(ada_input).to(torch.float32)
|
| 230 |
+
ret[name] = v
|
| 231 |
+
|
| 232 |
+
return ret
|
| 233 |
+
|
| 234 |
+
def forward_intermediates(
|
| 235 |
+
self,
|
| 236 |
+
x: torch.Tensor,
|
| 237 |
+
indices: Optional[Union[int, List[int], Tuple[int]]] = None,
|
| 238 |
+
return_prefix_tokens: bool = False,
|
| 239 |
+
norm: bool = False,
|
| 240 |
+
stop_early: bool = False,
|
| 241 |
+
output_fmt: str = 'NCHW',
|
| 242 |
+
intermediates_only: bool = False,
|
| 243 |
+
aggregation: Optional[str] = "sparse",
|
| 244 |
+
norm_alpha_scheme: Optional[str] = "post-alpha",
|
| 245 |
+
) -> List[RadioOutput]:
|
| 246 |
+
""" Forward features that returns intermediates.
|
| 247 |
+
Args:
|
| 248 |
+
x: Input image tensor
|
| 249 |
+
indices: Take last n blocks if int, select matching indices if sequence
|
| 250 |
+
return_prefix_tokens: Return both prefix and spatial intermediate tokens
|
| 251 |
+
norm: Apply norm layer to all intermediates
|
| 252 |
+
stop_early: Stop iterating over blocks when last desired intermediate hit
|
| 253 |
+
output_fmt: Shape of intermediate feature outputs. Options: NCHW, NLC
|
| 254 |
+
intermediates_only: Only return intermediate features
|
| 255 |
+
aggregation: intermediate layer aggregation method (sparse or dense).
|
| 256 |
+
Dense accumulation is done by averaging the features in each group.
|
| 257 |
+
norm_alpha_scheme: apply alpha before ("pre-alpha") or after accumulation ("post-alpha"), or don't normalize ("none")
|
| 258 |
+
Only affects dense aggregation
|
| 259 |
+
Returns:
|
| 260 |
+
List of RadioOutput objects.
|
| 261 |
+
"""
|
| 262 |
+
x = self.input_conditioner(x)
|
| 263 |
+
intermediates = self.model.forward_intermediates(
|
| 264 |
+
x,
|
| 265 |
+
indices=indices,
|
| 266 |
+
return_prefix_tokens=return_prefix_tokens,
|
| 267 |
+
norm=norm,
|
| 268 |
+
stop_early=stop_early,
|
| 269 |
+
output_fmt=output_fmt,
|
| 270 |
+
intermediates_only=intermediates_only,
|
| 271 |
+
aggregation=aggregation,
|
| 272 |
+
inter_feature_normalizer=self.inter_feature_normalizer,
|
| 273 |
+
norm_alpha_scheme=norm_alpha_scheme,
|
| 274 |
+
)
|
| 275 |
+
|
| 276 |
+
if not intermediates_only:
|
| 277 |
+
final, intermediates = intermediates
|
| 278 |
+
|
| 279 |
+
def prepare_summary(summ: Optional[torch.Tensor]):
|
| 280 |
+
if summ is None:
|
| 281 |
+
return summ
|
| 282 |
+
if self.summary_idxs is not None and summ.shape[1] > 1:
|
| 283 |
+
summ = summ[:, self.summary_idxs]
|
| 284 |
+
return summ.flatten(1)
|
| 285 |
+
|
| 286 |
+
if return_prefix_tokens:
|
| 287 |
+
radio_outputs = [
|
| 288 |
+
RadioOutput(prepare_summary(summary), features)
|
| 289 |
+
for summary, features in intermediates
|
| 290 |
+
]
|
| 291 |
+
else:
|
| 292 |
+
radio_outputs = intermediates
|
| 293 |
+
|
| 294 |
+
if intermediates_only:
|
| 295 |
+
return radio_outputs
|
| 296 |
+
else:
|
| 297 |
+
final = self._extract_final(x, final, feature_fmt=output_fmt)
|
| 298 |
+
return final, radio_outputs
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
def create_model_from_args(args) -> nn.Module:
|
| 302 |
+
in_chans = 3
|
| 303 |
+
if args.in_chans is not None:
|
| 304 |
+
in_chans = args.in_chans
|
| 305 |
+
elif args.input_size is not None:
|
| 306 |
+
in_chans = args.input_size[0]
|
| 307 |
+
|
| 308 |
+
# Skip weight initialization unless it's explicitly requested.
|
| 309 |
+
weight_init = args.model_kwargs.pop("weight_init", "skip")
|
| 310 |
+
|
| 311 |
+
model = create_model(
|
| 312 |
+
args.model,
|
| 313 |
+
pretrained=args.pretrained,
|
| 314 |
+
in_chans=in_chans,
|
| 315 |
+
num_classes=args.num_classes,
|
| 316 |
+
drop_rate=args.drop,
|
| 317 |
+
drop_path_rate=args.drop_path,
|
| 318 |
+
drop_block_rate=args.drop_block,
|
| 319 |
+
global_pool=args.gp,
|
| 320 |
+
bn_momentum=args.bn_momentum,
|
| 321 |
+
bn_eps=args.bn_eps,
|
| 322 |
+
scriptable=args.torchscript,
|
| 323 |
+
checkpoint_path=args.initial_checkpoint,
|
| 324 |
+
weight_init=weight_init,
|
| 325 |
+
**args.model_kwargs,
|
| 326 |
+
)
|
| 327 |
+
|
| 328 |
+
if hasattr(model, 'norm') and not getattr(args, 'model_norm', False):
|
| 329 |
+
model.norm = nn.Identity()
|
| 330 |
+
|
| 331 |
+
model.head = nn.Identity()
|
| 332 |
+
|
| 333 |
+
if args.cpe_max_size is not None:
|
| 334 |
+
uq_teachers = set(t['name'] for t in args.teachers)
|
| 335 |
+
enable_cpe(
|
| 336 |
+
model,
|
| 337 |
+
args.cpe_max_size,
|
| 338 |
+
num_cls_tokens=len(uq_teachers) if args.cls_token_per_teacher else 1,
|
| 339 |
+
register_multiple=getattr(args, 'register_multiple', None),
|
| 340 |
+
num_registers=getattr(args, 'cpe_num_registers', None),
|
| 341 |
+
)
|
| 342 |
+
|
| 343 |
+
return model
|
vit_patch_generator.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
| 4 |
+
# and proprietary rights in and to this software, related documentation
|
| 5 |
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
| 6 |
+
# distribution of this software and related documentation without an express
|
| 7 |
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
| 8 |
+
|
| 9 |
+
import math
|
| 10 |
+
from typing import Union, Tuple, Optional
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
import torch.nn.functional as F
|
| 14 |
+
from torch import nn
|
| 15 |
+
from einops import rearrange
|
| 16 |
+
|
| 17 |
+
from .cls_token import ClsToken
|
| 18 |
+
|
| 19 |
+
input_dim_t = Union[int, Tuple[int, int]]
|
| 20 |
+
|
| 21 |
+
try:
|
| 22 |
+
# raise ImportError()
|
| 23 |
+
from indirect_grid_sample import indirect_grid_sample
|
| 24 |
+
except ImportError:
|
| 25 |
+
indirect_grid_sample = None
|
| 26 |
+
|
| 27 |
+
class ViTPatchGenerator(nn.Module):
|
| 28 |
+
def __init__(self,
|
| 29 |
+
patch_size: int,
|
| 30 |
+
embed_dim: int,
|
| 31 |
+
input_dims: input_dim_t,
|
| 32 |
+
abs_pos: bool = True,
|
| 33 |
+
normalize_patches: bool = False,
|
| 34 |
+
cls_token: bool = False,
|
| 35 |
+
max_input_dims: Optional[input_dim_t] = None,
|
| 36 |
+
pos_dropout: float = 0.0,
|
| 37 |
+
return_pos_enc: bool = False,
|
| 38 |
+
num_cls_tokens: int = 1,
|
| 39 |
+
register_multiple: Optional[int] = None,
|
| 40 |
+
num_registers: Optional[int] = None,
|
| 41 |
+
patch_bias: bool = False,
|
| 42 |
+
device=None, dtype=None,
|
| 43 |
+
):
|
| 44 |
+
super().__init__()
|
| 45 |
+
|
| 46 |
+
if isinstance(input_dims, int):
|
| 47 |
+
input_dims = (input_dims, input_dims)
|
| 48 |
+
|
| 49 |
+
if max_input_dims is None:
|
| 50 |
+
max_input_dims = input_dims
|
| 51 |
+
if isinstance(max_input_dims, int):
|
| 52 |
+
max_input_dims = (max_input_dims, max_input_dims)
|
| 53 |
+
|
| 54 |
+
max_input_dims = tuple(
|
| 55 |
+
int(math.ceil(d / patch_size) * patch_size)
|
| 56 |
+
for d in max_input_dims
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
self.cpe_mode = max_input_dims != input_dims
|
| 60 |
+
self.pos_dropout = pos_dropout
|
| 61 |
+
self.return_pos_enc = return_pos_enc
|
| 62 |
+
|
| 63 |
+
factory = dict(device=device, dtype=dtype)
|
| 64 |
+
|
| 65 |
+
self.patch_size = patch_size
|
| 66 |
+
self.abs_pos = abs_pos
|
| 67 |
+
self.embed_dim = embed_dim
|
| 68 |
+
|
| 69 |
+
self.num_rows = max_input_dims[0] // patch_size
|
| 70 |
+
self.num_cols = max_input_dims[1] // patch_size
|
| 71 |
+
self.input_dims = tuple(d // patch_size for d in input_dims)
|
| 72 |
+
self.num_patches = self.num_rows * self.num_cols
|
| 73 |
+
self.max_input_dims = max_input_dims
|
| 74 |
+
|
| 75 |
+
self.im_to_patches = Im2Patches(patch_size)
|
| 76 |
+
self.embedder = ViTPatchLinear(patch_size, embed_dim, bias=patch_bias, **factory)
|
| 77 |
+
|
| 78 |
+
if abs_pos:
|
| 79 |
+
scale = embed_dim ** -0.5
|
| 80 |
+
self.pos_embed = nn.Parameter(torch.randn(1, self.num_patches, embed_dim, **factory) * scale)
|
| 81 |
+
|
| 82 |
+
self.cls_token = ClsToken(
|
| 83 |
+
embed_dim,
|
| 84 |
+
num_tokens=num_cls_tokens,
|
| 85 |
+
enabled=cls_token,
|
| 86 |
+
register_multiple=register_multiple,
|
| 87 |
+
num_registers=num_registers,
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
self.patch_normalizer = nn.LayerNorm(embed_dim) if normalize_patches else nn.Identity()
|
| 91 |
+
|
| 92 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 93 |
+
patches = self.embed_patches(x)
|
| 94 |
+
patches, pos_enc = self.apply_pos_enc(patches, input_size=x.shape[2:])
|
| 95 |
+
patches = self.cls_token(patches)
|
| 96 |
+
patches = self.patch_normalizer(patches)
|
| 97 |
+
if self.return_pos_enc:
|
| 98 |
+
return patches, pos_enc
|
| 99 |
+
return patches
|
| 100 |
+
|
| 101 |
+
@property
|
| 102 |
+
def apply_cls_token(self):
|
| 103 |
+
return self.cls_token.enabled
|
| 104 |
+
|
| 105 |
+
@property
|
| 106 |
+
def num_cls_tokens(self):
|
| 107 |
+
return self.cls_token.num_tokens
|
| 108 |
+
|
| 109 |
+
@property
|
| 110 |
+
def num_cls_patches(self):
|
| 111 |
+
return self.cls_token.num_patches
|
| 112 |
+
|
| 113 |
+
@property
|
| 114 |
+
def num_registers(self):
|
| 115 |
+
return self.cls_token.num_registers
|
| 116 |
+
|
| 117 |
+
@property
|
| 118 |
+
def num_skip(self):
|
| 119 |
+
return self.num_cls_tokens + self.num_registers
|
| 120 |
+
|
| 121 |
+
def no_weight_decay(self):
|
| 122 |
+
return [
|
| 123 |
+
'pos_embed',
|
| 124 |
+
]
|
| 125 |
+
|
| 126 |
+
def _load_embed(self, src_embed: torch.Tensor, targ_embed: nn.Parameter):
|
| 127 |
+
if src_embed.shape != targ_embed.shape:
|
| 128 |
+
src_size = int(math.sqrt(src_embed.shape[1]))
|
| 129 |
+
|
| 130 |
+
assert src_size ** 2 == src_embed.shape[1], 'Unable to interpolate non-square embedding'
|
| 131 |
+
|
| 132 |
+
src_embed = rearrange(src_embed, 'b (h w) c -> b c h w', h=src_size, w=src_size)
|
| 133 |
+
src_embed = F.interpolate(src_embed, size=(self.num_rows, self.num_cols), mode='bicubic', align_corners=True, antialias=False)
|
| 134 |
+
src_embed = rearrange(src_embed, 'b c h w -> b (h w) c')
|
| 135 |
+
targ_embed.data.copy_(src_embed)
|
| 136 |
+
|
| 137 |
+
def _load_projection(self, src_proj_weight: torch.Tensor, targ_proj_weight: torch.Tensor):
|
| 138 |
+
if src_proj_weight.shape != targ_proj_weight.shape:
|
| 139 |
+
src_patch_size = int(math.sqrt(src_proj_weight.shape[1] // 3))
|
| 140 |
+
|
| 141 |
+
assert (src_patch_size ** 2) * 3 == src_proj_weight.shape[1], 'Unable to interpolate non-square patch size'
|
| 142 |
+
|
| 143 |
+
src_proj_weight = rearrange(src_proj_weight, 'b (c h w) -> b c h w', c=3, h=src_patch_size, w=src_patch_size)
|
| 144 |
+
src_proj_weight = F.interpolate(src_proj_weight, size=(self.patch_size, self.patch_size), mode='bicubic', align_corners=True, antialias=False)
|
| 145 |
+
src_proj_weight = rearrange(src_proj_weight, 'b c h w -> b (c h w)')
|
| 146 |
+
targ_proj_weight.data.copy_(src_proj_weight)
|
| 147 |
+
|
| 148 |
+
def embed_patches(self, x: torch.Tensor) -> torch.Tensor:
|
| 149 |
+
patches = self.im_to_patches(x)
|
| 150 |
+
patches = self.embedder(patches)
|
| 151 |
+
return patches
|
| 152 |
+
|
| 153 |
+
def apply_pos_enc(self,
|
| 154 |
+
patches: torch.Tensor,
|
| 155 |
+
patch_idxs: Optional[torch.Tensor] = None,
|
| 156 |
+
input_size: Optional[Tuple[int, int]] = None,
|
| 157 |
+
) -> torch.Tensor:
|
| 158 |
+
if not self.abs_pos:
|
| 159 |
+
return patches
|
| 160 |
+
|
| 161 |
+
pos_enc = self.get_pos_enc(patches.shape[0], patch_idxs, input_size)
|
| 162 |
+
|
| 163 |
+
if self.training and self.pos_dropout > 0:
|
| 164 |
+
keeps = torch.rand(patches.shape[0], 1, 1, dtype=pos_enc.dtype, device=pos_enc.device) > self.pos_dropout
|
| 165 |
+
pos_enc_drop = torch.where(keeps, pos_enc, 0)
|
| 166 |
+
else:
|
| 167 |
+
pos_enc_drop = pos_enc
|
| 168 |
+
|
| 169 |
+
return patches + pos_enc_drop, pos_enc
|
| 170 |
+
|
| 171 |
+
def get_pos_enc(self,
|
| 172 |
+
batch_size: int,
|
| 173 |
+
patch_idxs: Optional[torch.Tensor] = None,
|
| 174 |
+
input_size: Optional[Tuple[int, int]] = None,
|
| 175 |
+
) -> torch.Tensor:
|
| 176 |
+
if input_size is None:
|
| 177 |
+
input_dims = self.input_dims
|
| 178 |
+
else:
|
| 179 |
+
input_dims = tuple(d // self.patch_size for d in input_size)
|
| 180 |
+
|
| 181 |
+
pos_embed = self._get_pos_embeddings(batch_size, input_dims)
|
| 182 |
+
|
| 183 |
+
if patch_idxs is None:
|
| 184 |
+
return pos_embed
|
| 185 |
+
|
| 186 |
+
exp_patch_idxs = patch_idxs.unsqueeze(-1).expand(-1, -1, pos_embed.shape[-1])
|
| 187 |
+
|
| 188 |
+
pos_embed = torch.gather(pos_embed.expand(patch_idxs.shape[0], -1, -1), dim=1, index=exp_patch_idxs)
|
| 189 |
+
return pos_embed
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
def _get_pos_embeddings(self, batch_size: int, input_dims: Tuple[int, int]):
|
| 193 |
+
if (self.num_rows, self.num_cols) == input_dims:
|
| 194 |
+
return self.pos_embed
|
| 195 |
+
|
| 196 |
+
pos_embed = self.pos_embed.reshape(1, self.num_rows, self.num_cols, -1).permute(0, 3, 1, 2)
|
| 197 |
+
|
| 198 |
+
def window_select(pos_embed):
|
| 199 |
+
if input_dims[0] < pos_embed.shape[-2]:
|
| 200 |
+
pos_embed = pos_embed[..., :input_dims[0], :]
|
| 201 |
+
if input_dims[1] < pos_embed.shape[-1]:
|
| 202 |
+
pos_embed = pos_embed[..., :, :input_dims[1]]
|
| 203 |
+
return pos_embed
|
| 204 |
+
|
| 205 |
+
if self.cpe_mode:
|
| 206 |
+
if self.training:
|
| 207 |
+
min_scale = math.sqrt(0.1)
|
| 208 |
+
scale = torch.rand(batch_size, 1, 1, device=pos_embed.device) * (1 - min_scale) + min_scale
|
| 209 |
+
aspect_min = math.log(3 / 4)
|
| 210 |
+
aspect_max = -aspect_min
|
| 211 |
+
aspect = torch.exp(torch.rand(batch_size, 1, 1, device=pos_embed.device) * (aspect_max - aspect_min) + aspect_min)
|
| 212 |
+
|
| 213 |
+
scale_x = scale * aspect
|
| 214 |
+
scale_y = scale * (1 / aspect)
|
| 215 |
+
scale_xy = torch.stack([scale_x, scale_y], dim=-1).clamp_(0, 1)
|
| 216 |
+
|
| 217 |
+
pos_xy = torch.rand(batch_size, 1, 1, 2, device=pos_embed.device) * (1 - scale_xy)
|
| 218 |
+
|
| 219 |
+
lin_x = torch.linspace(0, 1, steps=input_dims[1], device=pos_embed.device)[None, None].expand(batch_size, input_dims[0], -1)
|
| 220 |
+
lin_y = torch.linspace(0, 1, steps=input_dims[0], device=pos_embed.device)[None, :, None].expand(batch_size, -1, input_dims[1])
|
| 221 |
+
|
| 222 |
+
lin_xy = torch.stack([lin_x, lin_y], dim=-1)
|
| 223 |
+
|
| 224 |
+
grid_xy = lin_xy * scale_xy + pos_xy
|
| 225 |
+
|
| 226 |
+
# Convert to [-1, 1] range
|
| 227 |
+
grid_xy.mul_(2).sub_(1)
|
| 228 |
+
|
| 229 |
+
pos_embed = F.grid_sample(
|
| 230 |
+
pos_embed.float().expand(batch_size, -1, -1, -1),
|
| 231 |
+
grid=grid_xy,
|
| 232 |
+
mode='bilinear',
|
| 233 |
+
padding_mode='zeros',
|
| 234 |
+
align_corners=True,
|
| 235 |
+
).to(pos_embed.dtype)
|
| 236 |
+
else:
|
| 237 |
+
# i_rows, i_cols = input_dims
|
| 238 |
+
# p_rows, p_cols = pos_embed.shape[2:]
|
| 239 |
+
# if i_rows <= p_rows and i_cols <= p_cols:
|
| 240 |
+
# left = (p_cols - i_cols) // 2
|
| 241 |
+
# top = (p_rows - i_rows) // 2
|
| 242 |
+
# pos_embed = pos_embed[..., top:top+i_rows, left:left+i_cols]
|
| 243 |
+
# else:
|
| 244 |
+
max_dim = max(input_dims)
|
| 245 |
+
pos_embed = F.interpolate(pos_embed.float(), size=(max_dim, max_dim), align_corners=True, mode='bilinear').to(pos_embed.dtype)
|
| 246 |
+
|
| 247 |
+
pos_embed = window_select(pos_embed)
|
| 248 |
+
else:
|
| 249 |
+
pos_embed = window_select(pos_embed)
|
| 250 |
+
|
| 251 |
+
if pos_embed.shape[-2:] != input_dims:
|
| 252 |
+
pos_embed = F.interpolate(pos_embed.float(), size=input_dims, align_corners=True, mode='bilinear').to(pos_embed.dtype)
|
| 253 |
+
|
| 254 |
+
pos_embed = pos_embed.flatten(2).permute(0, 2, 1)
|
| 255 |
+
|
| 256 |
+
return pos_embed
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
class Im2Patches(nn.Module):
|
| 260 |
+
def __init__(self, patch_size: int):
|
| 261 |
+
super().__init__()
|
| 262 |
+
self.patch_size = patch_size
|
| 263 |
+
|
| 264 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 265 |
+
if self.patch_size == 1:
|
| 266 |
+
patches = x.flatten(2)
|
| 267 |
+
patches = patches.permute(0, 2, 1)
|
| 268 |
+
return patches
|
| 269 |
+
|
| 270 |
+
py = x.shape[-2] // self.patch_size
|
| 271 |
+
px = x.shape[-1] // self.patch_size
|
| 272 |
+
patches = rearrange(x, 'b c (py yy) (px xx) -> b (py px) (c yy xx)',
|
| 273 |
+
py=py, yy=self.patch_size,
|
| 274 |
+
px=px, xx=self.patch_size,
|
| 275 |
+
)
|
| 276 |
+
return patches
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
class ViTPatchLinear(nn.Linear):
|
| 280 |
+
def __init__(self, patch_size: int, embed_dim: int, bias: bool = False, **factory):
|
| 281 |
+
super().__init__(
|
| 282 |
+
3 * (patch_size ** 2),
|
| 283 |
+
embed_dim,
|
| 284 |
+
bias=bias,
|
| 285 |
+
**factory
|
| 286 |
+
)
|
| 287 |
+
self.patch_size = patch_size
|
vitdet.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections import defaultdict
|
| 2 |
+
from contextlib import contextmanager
|
| 3 |
+
from logging import getLogger
|
| 4 |
+
import math
|
| 5 |
+
import sys
|
| 6 |
+
from typing import List, Union, Iterable
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
import torch
|
| 10 |
+
from torch import nn
|
| 11 |
+
|
| 12 |
+
from timm.models import VisionTransformer
|
| 13 |
+
from einops import rearrange
|
| 14 |
+
|
| 15 |
+
from .extra_models import DinoWrapper
|
| 16 |
+
|
| 17 |
+
DEFAULT_NUM_WINDOWED = 5
|
| 18 |
+
DEFAULT_NUM_GLOBAL = 4
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class VitDetArgs:
|
| 22 |
+
def __init__(self,
|
| 23 |
+
window_size: int,
|
| 24 |
+
num_summary_tokens: int,
|
| 25 |
+
num_windowed: int = None,
|
| 26 |
+
num_global: int = None,
|
| 27 |
+
):
|
| 28 |
+
self.window_size = window_size
|
| 29 |
+
self.num_summary_tokens = num_summary_tokens
|
| 30 |
+
self.num_windowed = num_windowed
|
| 31 |
+
self.num_global = num_global
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def apply_vitdet_arch(model: Union[VisionTransformer, DinoWrapper], args: VitDetArgs):
|
| 35 |
+
if isinstance(model, VisionTransformer):
|
| 36 |
+
patch_embed = getattr(model, 'patch_generator', model.patch_embed)
|
| 37 |
+
|
| 38 |
+
return ViTDetHook(patch_embed, model.blocks, args)
|
| 39 |
+
elif isinstance(model, DinoWrapper):
|
| 40 |
+
inner = model.inner
|
| 41 |
+
|
| 42 |
+
patch_embed = getattr(inner, 'patch_generator', inner.patch_embed)
|
| 43 |
+
return ViTDetHook(patch_embed, inner.blocks, args)
|
| 44 |
+
else:
|
| 45 |
+
print(f'Warning: Unable to apply VitDet aug!', file=sys.stderr)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
class ViTDetHook:
|
| 49 |
+
def __init__(self,
|
| 50 |
+
embedder: nn.Module,
|
| 51 |
+
blocks: nn.Sequential,
|
| 52 |
+
args: VitDetArgs,
|
| 53 |
+
):
|
| 54 |
+
self.blocks = blocks
|
| 55 |
+
self.num_summary_tokens = args.num_summary_tokens
|
| 56 |
+
self.window_size = args.window_size
|
| 57 |
+
|
| 58 |
+
self._input_resolution = None
|
| 59 |
+
self._num_windows = None
|
| 60 |
+
self._cls_patch = None
|
| 61 |
+
self._order_cache = dict()
|
| 62 |
+
|
| 63 |
+
embedder.register_forward_pre_hook(self._enter_model)
|
| 64 |
+
|
| 65 |
+
# This will decide if we window-fy the patches
|
| 66 |
+
# and enable vit-det for this iteration, and if so,
|
| 67 |
+
# rearrange the patches for efficient mode switching
|
| 68 |
+
blocks.register_forward_pre_hook(self._enter_blocks)
|
| 69 |
+
|
| 70 |
+
is_global = True
|
| 71 |
+
if args.num_windowed is not None:
|
| 72 |
+
period = args.num_windowed + 1
|
| 73 |
+
else:
|
| 74 |
+
num_global = args.num_global or DEFAULT_NUM_GLOBAL
|
| 75 |
+
period = max(len(blocks) // num_global, 1)
|
| 76 |
+
|
| 77 |
+
for i, layer in enumerate(blocks[:-1]):
|
| 78 |
+
ctr = i % period
|
| 79 |
+
if ctr == 0:
|
| 80 |
+
layer.register_forward_pre_hook(self._to_windows)
|
| 81 |
+
is_global = False
|
| 82 |
+
elif ctr == period - 1:
|
| 83 |
+
layer.register_forward_pre_hook(self._to_global)
|
| 84 |
+
is_global = True
|
| 85 |
+
|
| 86 |
+
# Always ensure the final layer is a global layer
|
| 87 |
+
if not is_global:
|
| 88 |
+
blocks[-1].register_forward_pre_hook(self._to_global)
|
| 89 |
+
|
| 90 |
+
blocks.register_forward_hook(self._exit_model)
|
| 91 |
+
|
| 92 |
+
def _enter_model(self, _, input: List[torch.Tensor]):
|
| 93 |
+
self._input_resolution = input[0].shape[-2:]
|
| 94 |
+
|
| 95 |
+
def _enter_blocks(self, _, input: List[torch.Tensor]):
|
| 96 |
+
# print(f'{get_rank()} - ViTDet Window Size: {self._window_size}', file=sys.stderr)
|
| 97 |
+
|
| 98 |
+
patches = input[0]
|
| 99 |
+
patches = self._rearrange_patches(patches)
|
| 100 |
+
|
| 101 |
+
return (patches,) + input[1:]
|
| 102 |
+
|
| 103 |
+
def _to_windows(self, _, input: List[torch.Tensor]):
|
| 104 |
+
patches = input[0]
|
| 105 |
+
|
| 106 |
+
if self.num_summary_tokens:
|
| 107 |
+
self._cls_patch = patches[:, :self.num_summary_tokens]
|
| 108 |
+
patches = patches[:, self.num_summary_tokens:]
|
| 109 |
+
|
| 110 |
+
patches = rearrange(
|
| 111 |
+
patches, 'b (p t) c -> (b p) t c',
|
| 112 |
+
p=self._num_windows, t=self.window_size ** 2,
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
+
return (patches,) + input[1:]
|
| 116 |
+
|
| 117 |
+
def _to_global(self, _, input: List[torch.Tensor]):
|
| 118 |
+
patches = input[0]
|
| 119 |
+
|
| 120 |
+
patches = rearrange(
|
| 121 |
+
patches, '(b p) t c -> b (p t) c',
|
| 122 |
+
p=self._num_windows, t=self.window_size ** 2,
|
| 123 |
+
b=patches.shape[0] // self._num_windows,
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
if self.num_summary_tokens:
|
| 127 |
+
patches = torch.cat([
|
| 128 |
+
self._cls_patch,
|
| 129 |
+
patches,
|
| 130 |
+
], dim=1)
|
| 131 |
+
|
| 132 |
+
return (patches,) + input[1:]
|
| 133 |
+
|
| 134 |
+
def _exit_model(self, _, inputs: List[torch.Tensor], patches: torch.Tensor):
|
| 135 |
+
# Return patches to their original order
|
| 136 |
+
patch_order = self._order_cache[self._input_resolution][0]
|
| 137 |
+
patch_order = patch_order.reshape(1, -1, 1).expand_as(patches)
|
| 138 |
+
|
| 139 |
+
ret_patches = torch.empty_like(patches)
|
| 140 |
+
ret_patches = torch.scatter(
|
| 141 |
+
ret_patches,
|
| 142 |
+
dim=1,
|
| 143 |
+
index=patch_order,
|
| 144 |
+
src=patches,
|
| 145 |
+
)
|
| 146 |
+
|
| 147 |
+
return ret_patches
|
| 148 |
+
|
| 149 |
+
def _rearrange_patches(self, patches: torch.Tensor):
|
| 150 |
+
# We rearrange the patches so that we can efficiently
|
| 151 |
+
# switch between windowed and global mode by just
|
| 152 |
+
# reshaping the tensor
|
| 153 |
+
|
| 154 |
+
patch_order, self._num_windows = self._order_cache.get(self._input_resolution, (None, None))
|
| 155 |
+
if patch_order is None:
|
| 156 |
+
num_feat_patches = patches.shape[1] - self.num_summary_tokens
|
| 157 |
+
num_pixels = self._input_resolution[0] * self._input_resolution[1]
|
| 158 |
+
|
| 159 |
+
patch_size = int(round(math.sqrt(num_pixels / num_feat_patches)))
|
| 160 |
+
rows = self._input_resolution[-2] // patch_size
|
| 161 |
+
cols = self._input_resolution[-1] // patch_size
|
| 162 |
+
|
| 163 |
+
w_rows = rows // self.window_size
|
| 164 |
+
w_cols = cols // self.window_size
|
| 165 |
+
|
| 166 |
+
patch_order = torch.arange(0, num_feat_patches, device=patches.device)
|
| 167 |
+
|
| 168 |
+
patch_order = rearrange(
|
| 169 |
+
patch_order, '(wy py wx px) -> (wy wx py px)',
|
| 170 |
+
wy=w_rows, wx=w_cols,
|
| 171 |
+
py=self.window_size, px=self.window_size,
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
if self.num_summary_tokens:
|
| 175 |
+
patch_order = torch.cat([
|
| 176 |
+
torch.arange(self.num_summary_tokens, dtype=patch_order.dtype, device=patch_order.device),
|
| 177 |
+
patch_order + self.num_summary_tokens,
|
| 178 |
+
])
|
| 179 |
+
|
| 180 |
+
self._num_windows = w_rows * w_cols
|
| 181 |
+
self._order_cache[self._input_resolution] = (
|
| 182 |
+
patch_order,
|
| 183 |
+
self._num_windows,
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
patch_order = patch_order.reshape(1, -1, 1).expand_as(patches)
|
| 187 |
+
patches = torch.gather(patches, dim=1, index=patch_order)
|
| 188 |
+
return patches
|