Safetensors
custom_code
README.md CHANGED
@@ -1,6 +1,5 @@
1
  ---
2
  license: other
3
  license_name: nvidia-open-model-license
4
- license_link: >-
5
- https://developer.download.nvidia.com/licenses/nvidia-open-model-license-agreement-june-2024.pdf
6
  ---
 
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
  ---
adaptor_attn.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2026, 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
+ from .adaptor_base import AdaptorModuleBase
19
+ from .adaptor_mlp import MLP2
20
+
21
+
22
+ class AttnFDHead(AdaptorModuleBase):
23
+ def __init__(
24
+ self,
25
+ input_size: int,
26
+ hidden_size: int,
27
+ output_size: int,
28
+ num_inner: int = 0,
29
+ pre_norm: bool = False,
30
+ device: torch.device = None,
31
+ upsample_factor: int = 1,
32
+ upsample_rank: int = 0,
33
+ **kwargs # Ignore kwargs that might be to other "mlp" verions, e.g. teacher_summary_idxs
34
+ ) -> None:
35
+ super().__init__(requires_summary_and_spatial=False)
36
+ from timm.models.vision_transformer import Block
37
+ self.blocks = nn.Sequential(*[
38
+ Block(input_size, num_heads=16, init_values=1e-5)
39
+ for _ in range(2)
40
+ ])
41
+ self.mlp = MLP2(input_size, hidden_size, output_size,
42
+ num_inner=0, pre_norm=pre_norm, device=device,
43
+ upsample_factor=upsample_factor, upsample_rank=upsample_rank, **kwargs)
44
+
45
+ def forward(self, x: torch.Tensor, **kwargs) -> torch.Tensor:
46
+ x = self.blocks(x)
47
+ x = self.mlp(x)
48
+ return x
adaptor_base.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 AdaptorModuleBase(nn.Module):
36
+ def __init__(
37
+ self,
38
+ requires_summary_and_spatial: bool,
39
+ handles_summary_and_spatial: bool = False
40
+ ) -> None:
41
+ super().__init__()
42
+ self.requires_summary_and_spatial = requires_summary_and_spatial
43
+ self.handles_summary_and_spatial = handles_summary_and_spatial
44
+
45
+ assert not handles_summary_and_spatial or requires_summary_and_spatial, "If handles summary and spatial, must require it too!"
46
+
47
+
48
+ class AdaptorBase(nn.Module):
49
+ def __init__(self):
50
+ super().__init__()
51
+ self.head_idx = 0
52
+
53
+ def forward(self, input: AdaptorInput) -> RadioOutput:
54
+ raise NotImplementedError("Subclasses must implement this!")
adaptor_generic.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_module_factory 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
+ summary_mlp_version = main_config.mlp_version
23
+ feature_mlp_version = getattr(main_config, 'spatial_mlp_version', None) or summary_mlp_version
24
+
25
+ extra_args = dict()
26
+ ups = None
27
+ ups_rank = None
28
+ if adaptor_config is not None:
29
+ ups = adaptor_config.get('fd_upsample_factor', None)
30
+ ups_rank = adaptor_config.get('fd_upsample_rank', None)
31
+ summary_mlp_version = adaptor_config.get('mlp_version', summary_mlp_version)
32
+ feature_mlp_version = adaptor_config.get('spatial_mlp_version', feature_mlp_version)
33
+ elif mlp_config is not None:
34
+ ups = mlp_config["feature"].get('upsample_factor', None)
35
+ ups_rank = mlp_config["feature"].get('upsample_rank', None)
36
+ if ups is not None:
37
+ extra_args['upsample_factor'] = ups
38
+ extra_args['upsample_rank'] = ups_rank
39
+
40
+ if state is not None:
41
+ spectral_heads = getattr(main_config, 'spectral_heads', False)
42
+ self.head_mlp = create_mlp_from_state(summary_mlp_version, state, 'summary.', spectral_weights=spectral_heads, is_summary=True)
43
+ self.feat_mlp = create_mlp_from_state(feature_mlp_version, state, 'feature.', spectral_weights=spectral_heads, is_summary=False, **extra_args)
44
+ else:
45
+ assert mlp_config is not None, "Config must not be None if state is None"
46
+
47
+ self.head_mlp = create_mlp_from_config(
48
+ summary_mlp_version,
49
+ mlp_config["summary"]["input_dim"],
50
+ mlp_config["summary"]["hidden_dim"],
51
+ mlp_config["summary"]["output_dim"],
52
+ mlp_config["summary"]["num_inner"],
53
+ is_summary=True,
54
+ )
55
+ self.feat_mlp = create_mlp_from_config(
56
+ feature_mlp_version,
57
+ mlp_config["feature"]["input_dim"],
58
+ mlp_config["feature"]["hidden_dim"],
59
+ mlp_config["feature"]["output_dim"],
60
+ mlp_config["feature"]["num_inner"],
61
+ is_summary=False,
62
+ **extra_args
63
+ )
64
+
65
+ def forward(self, input: AdaptorInput) -> RadioOutput:
66
+ # Convert input'd type to the type of the first parameter of the adaptor.
67
+ first_param = next(self.parameters())
68
+ summary = self.head_mlp(input.summary.to(dtype=first_param.dtype)).to(dtype=input.summary.dtype)
69
+ feat = self.feat_mlp(input.features.to(dtype=first_param.dtype), images=input.images, patch_size=input.patch_size).to(dtype=input.features.dtype)
70
+
71
+ if input.feature_fmt == 'NCHW':
72
+ 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])
73
+ .permute(0, 3, 1, 2)
74
+ )
75
+
76
+ return RadioOutput(summary, feat)
adaptor_mlp.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2026, 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
+ from .adaptor_base import AdaptorModuleBase
19
+
20
+
21
+ class MLP(AdaptorModuleBase):
22
+ def __init__(self, input_size: int, hidden_size: int, output_size: int,
23
+ num_inner: int = 0, device: torch.device = None, **kwargs):
24
+ super(MLP, self).__init__(requires_summary_and_spatial=False)
25
+ self.fc1 = nn.Linear(input_size, hidden_size, device=device)
26
+ self.norm = nn.LayerNorm(hidden_size, device=device)
27
+ self.relu = nn.ReLU()
28
+
29
+ inner = []
30
+ for _ in range(num_inner):
31
+ inner.extend([
32
+ nn.Linear(hidden_size, hidden_size, device=device),
33
+ nn.LayerNorm(hidden_size, device=device),
34
+ nn.ReLU(),
35
+ ])
36
+ if inner:
37
+ self.inner = nn.Sequential(*inner)
38
+ else:
39
+ self.inner = nn.Identity()
40
+
41
+ self.fc2 = nn.Linear(hidden_size, output_size, device=device)
42
+
43
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
44
+ x = self.fc1(x)
45
+ x = self.norm(x)
46
+ x = self.relu(x)
47
+ x = self.inner(x)
48
+ x = self.fc2(x)
49
+ return x
50
+
51
+
52
+ class MLP2(AdaptorModuleBase):
53
+ def __init__(self, input_size: int, hidden_size: int, output_size: int,
54
+ num_inner: int = 0,
55
+ pre_norm: bool = False, device: torch.device = None,
56
+ upsample_factor: int = 1,
57
+ upsample_rank: int = None,
58
+ from_config: bool = False,
59
+ **kwargs):
60
+ super().__init__(requires_summary_and_spatial=False)
61
+
62
+ self.pre_norm = nn.Sequential(
63
+ nn.LayerNorm(input_size),
64
+ nn.GELU(),
65
+ ) if pre_norm else nn.Identity()
66
+
67
+ self.upsample_factor = upsample_factor
68
+ sq_ups = upsample_factor ** 2
69
+
70
+ self._real_output_dim = output_size // sq_ups
71
+
72
+ # hidden_size *= upsample_factor
73
+ # output_size *= (upsample_factor ** 2)
74
+
75
+ self.fc1 = nn.Linear(input_size, hidden_size, device=device)
76
+
77
+ blocks = []
78
+ for _ in range(num_inner):
79
+ blocks.append(nn.Sequential(
80
+ nn.LayerNorm(hidden_size, device=device),
81
+ nn.GELU(),
82
+ nn.Linear(hidden_size, hidden_size, device=device),
83
+ ))
84
+ self.blocks = nn.ModuleList(blocks)
85
+
86
+ self.final = nn.Sequential(
87
+ nn.LayerNorm(hidden_size, device=device),
88
+ nn.GELU(),
89
+ nn.Linear(hidden_size, output_size, device=device),
90
+ )
91
+
92
+ def forward(self, x: torch.Tensor, images: Optional[torch.Tensor] = None, patch_size: Optional[int] = None) -> torch.Tensor:
93
+ x = self.pre_norm(x)
94
+ x = self.fc1(x)
95
+ for block in self.blocks:
96
+ x = x + block(x)
97
+ x = self.final(x)
98
+
99
+ if self.upsample_factor > 1:
100
+ if images is None:
101
+ raise ValueError(f'`images` cannot be `None` when the head\'s `upsample_factor > 1`!')
102
+ if patch_size is None:
103
+ raise ValueError(f'`patch_size` cannot be `None` when the head\'s `upsample_factor > 1`!')
104
+ h, w = tuple(d // patch_size for d in images.shape[-2:])
105
+ x = rearrange(x, 'b (h w) (u1 u2 c) -> b (h u1 w u2) c',
106
+ h=h, w=w, u1=self.upsample_factor, u2=self.upsample_factor,
107
+ c=self._real_output_dim)
108
+
109
+ return x
adaptor_module_factory.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2026, 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
+ from .adaptor_mlp import MLP, MLP2
19
+ from .adaptor_attn import AttnFDHead
20
+
21
+
22
+ MLP_SUMMARY_FACTORY = {
23
+ 'v1': MLP,
24
+ 'v2': MLP2,
25
+ }
26
+
27
+ MLP_FD_FACTORY = {
28
+ 'v1': MLP,
29
+ 'v2': MLP2,
30
+ 'attn': AttnFDHead,
31
+ }
32
+
33
+
34
+ def strip_prefix(state: Dict[str, torch.Tensor], prefix: str):
35
+ state = {
36
+ k[len(prefix):]: v
37
+ for k, v in state.items()
38
+ if k.startswith(prefix)
39
+ }
40
+ return state
41
+
42
+
43
+ def get_mlp_info_from_state(version: str, state: Dict[str, torch.Tensor], prefix: str = '', spectral_weights: bool = False):
44
+ state = strip_prefix(state, prefix)
45
+
46
+ weight_suffix = 'weight' if not spectral_weights else 'parametrizations.weight.original'
47
+
48
+ if version == 'v1':
49
+ hidden_dim, input_dim = state[f'fc1.{weight_suffix}'].shape
50
+ output_dim = state[f'fc2.{weight_suffix}'].shape[0]
51
+
52
+ for num_inner in range(1000):
53
+ k = f'inner.{num_inner}.0.weight'
54
+ if k not in state:
55
+ break
56
+ elif version == 'v2':
57
+ hidden_dim, input_dim = state[f'fc1.{weight_suffix}'].shape
58
+ output_dim = state[f'final.2.{weight_suffix}'].shape[0]
59
+
60
+ for num_inner in range(1000):
61
+ k = f'blocks.{num_inner}.0.weight'
62
+ if k not in state:
63
+ break
64
+ elif version == 'attn':
65
+ hidden_dim, input_dim = state[f'mlp.fc1.{weight_suffix}'].shape
66
+ output_dim = state[f'mlp.final.2.{weight_suffix}'].shape[0]
67
+ num_inner = 0
68
+ else:
69
+ raise ValueError(f'Unsupported MLP version: {version}')
70
+
71
+ return input_dim, hidden_dim, output_dim, num_inner
72
+
73
+
74
+ def create_mlp_from_config(version: str, input_dim: int, hidden_dim: int, output_dim: int, num_inner: int, is_summary: bool = True, **kwargs):
75
+ factory = MLP_SUMMARY_FACTORY if is_summary else MLP_FD_FACTORY
76
+
77
+ ret: nn.Module = factory[version](input_dim, hidden_dim, output_dim, num_inner, from_config=True, **kwargs)
78
+
79
+ return ret
80
+
81
+
82
+ def create_mlp_from_state(version: str, state: Dict[str, torch.Tensor], prefix: str = '', spectral_weights: bool = False, is_summary: bool = True, **kwargs):
83
+ state = strip_prefix(state, prefix)
84
+
85
+ input_dim, hidden_dim, output_dim, num_inner = get_mlp_info_from_state(version, state, spectral_weights=spectral_weights)
86
+
87
+ ret: nn.Module = create_mlp_from_config(version, input_dim, hidden_dim, output_dim, num_inner, is_summary=is_summary, **kwargs)
88
+ if spectral_weights:
89
+ enable_spectral_reparam(ret, init_norm_to_current=False, state_dict_guidance=state)
90
+
91
+ ret.load_state_dict(state)
92
+
93
+ if spectral_weights:
94
+ disable_spectral_reparam(ret)
95
+
96
+ 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,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ supports_vitdet: bool = True
22
+ vitdet_num_windowed: Optional[int] = None
23
+ vitdet_num_global: Optional[int] = None
24
+
25
+
26
+ RESOURCE_MAP = {
27
+ # RADIOv2.5
28
+ "radio_v2.5-b": RadioResource(
29
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio-v2.5-b_half.pth.tar?download=true",
30
+ patch_size=16,
31
+ max_resolution=2048,
32
+ preferred_resolution=(768, 768),
33
+ vitdet_num_global=4,
34
+ ),
35
+ "radio_v2.5-l": RadioResource(
36
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio-v2.5-l_half.pth.tar?download=true",
37
+ patch_size=16,
38
+ max_resolution=2048,
39
+ preferred_resolution=(768, 768),
40
+ vitdet_num_global=4,
41
+ ),
42
+ "radio_v2.5-h": RadioResource(
43
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.5-h.pth.tar?download=true",
44
+ patch_size=16,
45
+ max_resolution=2048,
46
+ preferred_resolution=(768, 768),
47
+ vitdet_num_global=4,
48
+ ),
49
+ "radio_v2.5-h-norm": RadioResource(
50
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.5-h-norm.pth.tar?download=true",
51
+ patch_size=16,
52
+ max_resolution=2048,
53
+ preferred_resolution=(768, 768),
54
+ vitdet_num_global=4,
55
+ ),
56
+ "radio_v2.5-g": RadioResource(
57
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.5-g.pth.tar?download=true",
58
+ patch_size=14,
59
+ max_resolution=1792,
60
+ preferred_resolution=(896, 896),
61
+ vitdet_num_global=8,
62
+ ),
63
+ # RADIO
64
+ "radio_v2.1": RadioResource(
65
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.1_bf16.pth.tar?download=true",
66
+ patch_size=16,
67
+ max_resolution=2048,
68
+ preferred_resolution=Resolution(432, 432),
69
+ vitdet_num_windowed=5,
70
+ ),
71
+ "radio_v2": RadioResource(
72
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.pth.tar?download=true",
73
+ patch_size=16,
74
+ max_resolution=2048,
75
+ preferred_resolution=Resolution(432, 432),
76
+ vitdet_num_windowed=5,
77
+ ),
78
+ "radio_v1": RadioResource(
79
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio_v1.pth.tar?download=true",
80
+ patch_size=14,
81
+ max_resolution=1050,
82
+ preferred_resolution=Resolution(378, 378),
83
+ ),
84
+ # E-RADIO
85
+ "e-radio_v2": RadioResource(
86
+ "https://huggingface.co/nvidia/RADIO/resolve/main/eradio_v2.pth.tar?download=true",
87
+ patch_size=16,
88
+ max_resolution=2048,
89
+ preferred_resolution=Resolution(512, 512),
90
+ ),
91
+ # C-RADIO
92
+ "c-radio_v2-g": RadioResource(
93
+ # NOTE: C-RADIO models are bound by different license terms than that present in the LICENSE file.
94
+ # Please refer to the readme, or to https://huggingface.co/nvidia/C-RADIOv2-g for more information.
95
+ "https://huggingface.co/nvidia/C-RADIOv2-g/resolve/main/c-radio_v2-g_half.pth.tar",
96
+ patch_size=16,
97
+ max_resolution=2048,
98
+ preferred_resolution=(768, 768),
99
+ vitdet_num_global=8,
100
+ ),
101
+ "c-radio_v2-vlm-h": RadioResource(
102
+ # NOTE: C-RADIO models are bound by different license terms than that present in the LICENSE file.
103
+ # Please refer to the readme, or to https://huggingface.co/nvidia/C-RADIOv2-VLM-H for more information.
104
+ "https://huggingface.co/nvidia/C-RADIOv2-VLM-H/resolve/main/c-radio_v2-vlm-h.pth.tar",
105
+ patch_size=16,
106
+ max_resolution=2048,
107
+ preferred_resolution=(768, 768),
108
+ vitdet_num_global=8,
109
+ ),
110
+ "c-radio_v3-b": RadioResource(
111
+ # NOTE: C-RADIO models are bound by different license terms than that present in the LICENSE file.
112
+ # Please refer to the readme, or to https://huggingface.co/nvidia/C-RADIOv3-B for more information.
113
+ "https://huggingface.co/nvidia/C-RADIOv3-B/resolve/main/c-radio_v3-b_half.pth.tar?download=true",
114
+ patch_size=16,
115
+ max_resolution=2048,
116
+ preferred_resolution=Resolution(512, 512),
117
+ supports_vitdet=False,
118
+ ),
119
+ "c-radio_v3-l": RadioResource(
120
+ # NOTE: C-RADIO models are bound by different license terms than that present in the LICENSE file.
121
+ # Please refer to the readme, or to https://huggingface.co/nvidia/C-RADIOv3-L for more information.
122
+ "https://huggingface.co/nvidia/C-RADIOv3-L/resolve/main/c-radio-v3_l_half.pth.tar?download=true",
123
+ patch_size=16,
124
+ max_resolution=2048,
125
+ preferred_resolution=Resolution(512, 512),
126
+ supports_vitdet=False,
127
+ ),
128
+ "c-radio_v3-h": RadioResource(
129
+ # NOTE: C-RADIO models are bound by different license terms than that present in the LICENSE file.
130
+ # Please refer to the readme, or to https://huggingface.co/nvidia/C-RADIOv3-H for more information.
131
+ "https://huggingface.co/nvidia/C-RADIOv3-H/resolve/main/c-radio_v3-h_half.pth.tar?download=true",
132
+ patch_size=16,
133
+ max_resolution=2048,
134
+ preferred_resolution=Resolution(512, 512),
135
+ supports_vitdet=False,
136
+ ),
137
+ "c-radio_v3-g": RadioResource(
138
+ # NOTE: C-RADIO models are bound by different license terms than that present in the LICENSE file.
139
+ # Please refer to the readme, or to https://huggingface.co/nvidia/C-RADIOv3-g for more information.
140
+ "https://huggingface.co/nvidia/C-RADIOv3-g/resolve/main/c-radio_v3-g_half.pth.tar?download=true",
141
+ patch_size=16,
142
+ max_resolution=2048,
143
+ preferred_resolution=Resolution(512, 512),
144
+ supports_vitdet=False,
145
+ ),
146
+ "c-radio_v4-so400m": RadioResource(
147
+ # NOTE: C-RADIO models are bound by different license terms than that present in the LICENSE file.
148
+ # Please refer to the readme, or to https://huggingface.co/nvidia/C-RADIOv4-SO400M for more information.
149
+ "https://huggingface.co/nvidia/C-RADIOv4-SO400M/resolve/main/c-radio_v4-so400m_half.pth.tar?download=true",
150
+ patch_size=16,
151
+ max_resolution=2048,
152
+ preferred_resolution=Resolution(512, 512),
153
+ supports_vitdet=True,
154
+ ),
155
+ "c-radio_v4-h": RadioResource(
156
+ # NOTE: C-RADIO models are bound by different license terms than that present in the LICENSE file.
157
+ # Please refer to the readme, or to https://huggingface.co/nvidia/C-RADIOv4-H for more information.
158
+ "https://huggingface.co/nvidia/C-RADIOv4-H/resolve/main/c-radio_v4-h_half.pth.tar?download=true",
159
+ patch_size=16,
160
+ max_resolution=2048,
161
+ preferred_resolution=Resolution(512, 512),
162
+ supports_vitdet=True,
163
+ ),
164
+ }
165
+
166
+ DEFAULT_VERSION = "c-radio_v4-h"
config.json ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ "auto_workload_inspector": false,
15
+ "bn_eps": null,
16
+ "bn_momentum": null,
17
+ "cache_dir": null,
18
+ "channels_last": false,
19
+ "checkpoint_folder": null,
20
+ "checkpoint_hist": 10,
21
+ "chk_keep_forever": 100,
22
+ "class_map": "",
23
+ "clip_grad": null,
24
+ "clip_mode": "norm",
25
+ "cls_token_per_teacher": true,
26
+ "coco_annotations_file": "/datasets/coco2017-adlsa/annotations/captions_val2017.json",
27
+ "coco_image_dir": "/datasets/coco2017-adlsa/val2017",
28
+ "color_jitter": 0.4,
29
+ "cooldown_epochs": 0,
30
+ "cpe_max_size": 2048,
31
+ "cpe_num_registers": 7,
32
+ "crd_loss": false,
33
+ "crd_loss_weight": 0.8,
34
+ "crop_pct": null,
35
+ "cutmix": 0.0,
36
+ "cutmix_minmax": null,
37
+ "dataset_download": false,
38
+ "debug_full_knn": false,
39
+ "decay_epochs": 90,
40
+ "decay_milestones": [
41
+ 90,
42
+ 180,
43
+ 270
44
+ ],
45
+ "decay_rate": 0.1,
46
+ "depchain": true,
47
+ "detect_anomaly": false,
48
+ "dist_bn": "reduce",
49
+ "dist_norm_weight": 0.0,
50
+ "distributed": true,
51
+ "drop": 0.0,
52
+ "drop_block": null,
53
+ "drop_connect": null,
54
+ "drop_path": null,
55
+ "dtype": "float32",
56
+ "epoch": 299,
57
+ "epoch_repeats": 0.0,
58
+ "eval": false,
59
+ "eval_metric": "knn_top1",
60
+ "eval_teacher": false,
61
+ "eval_teacher_only": false,
62
+ "eval_throughput": false,
63
+ "fast_norm": false,
64
+ "fd_loss_fn": "MSE",
65
+ "feature_normalization": "PHI_STANDARDIZE",
66
+ "feature_summarizer": "cls_token",
67
+ "feature_upscale_factor": null,
68
+ "force_disable_damp": false,
69
+ "force_disable_spectral_reparam": false,
70
+ "force_new_wandb_id": false,
71
+ "force_spectral_reparam": false,
72
+ "freeze_bn": false,
73
+ "fsdp": true,
74
+ "full_equivariance": false,
75
+ "fuser": "",
76
+ "gp": null,
77
+ "grad_accum_steps": 1,
78
+ "grad_checkpointing": false,
79
+ "head_init_bias": null,
80
+ "head_init_scale": null,
81
+ "head_lr": null,
82
+ "head_warmup": 0,
83
+ "head_weight_decay": 0.0005,
84
+ "hflip": 0.5,
85
+ "img_size": null,
86
+ "in_chans": null,
87
+ "initial_checkpoint": null,
88
+ "input_size": null,
89
+ "interpolation": "",
90
+ "layer_decay": null,
91
+ "local_rank": 0,
92
+ "log_interval": 50,
93
+ "log_mlflow": false,
94
+ "log_teacher_timings": true,
95
+ "log_train_metrics_per_epoch": true,
96
+ "log_train_metrics_per_log_interval": true,
97
+ "log_wandb": true,
98
+ "loss_auto_balance": false,
99
+ "lr_base": 0.1,
100
+ "lr_base_scale": "",
101
+ "lr_base_size": 256,
102
+ "lr_cycle_decay": 0.5,
103
+ "lr_cycle_limit": 1,
104
+ "lr_cycle_mul": 1.0,
105
+ "lr_k_decay": 1.0,
106
+ "lr_noise": null,
107
+ "lr_noise_pct": 0.67,
108
+ "lr_noise_std": 1.0,
109
+ "mean": null,
110
+ "mesa": false,
111
+ "min_lr": 1e-05,
112
+ "mixup": 0.0,
113
+ "mixup_mode": "batch",
114
+ "mixup_off_epoch": 0,
115
+ "mixup_prob": 1.0,
116
+ "mixup_switch_prob": 0.5,
117
+ "mlp_hidden_size": 1520,
118
+ "mlp_num_inner": 2,
119
+ "mlp_version": "v2",
120
+ "model": "radio1d_huge_patch16_224",
121
+ "model_kwargs": {
122
+ "cka_weight": 0.0,
123
+ "cpe_max_size": 2048,
124
+ "decoder_grad_checkpointing": 0,
125
+ "downscale_levels": [
126
+ 24
127
+ ],
128
+ "dynamic_rate": false,
129
+ "grad_checkpointing": 18,
130
+ "k_sample_config": {
131
+ "type": "triangle"
132
+ },
133
+ "num_cls_tokens": 4,
134
+ "num_registers": 6,
135
+ "progressive_reduction": false,
136
+ "register_multiple": 0,
137
+ "uniform_k": true
138
+ },
139
+ "model_norm": false,
140
+ "momentum": 0.9,
141
+ "no_custom_validation": false,
142
+ "no_ddp_bb": true,
143
+ "no_knn": false,
144
+ "no_prefetcher": false,
145
+ "no_resume_opt": false,
146
+ "no_save_checkpoint": false,
147
+ "no_val": false,
148
+ "num_classes": null,
149
+ "on_demand_workload_inspector": false,
150
+ "one_logger_app_tag": "",
151
+ "one_logger_is_baseline": false,
152
+ "one_logger_run_name": "",
153
+ "onelogger": null,
154
+ "opt_betas": null,
155
+ "opt_eps": null,
156
+ "overfit": false,
157
+ "patience_epochs": 10,
158
+ "perf_test_no_aug": false,
159
+ "perf_test_no_decode": false,
160
+ "perf_test_no_io": false,
161
+ "perf_test_only_dataloader": false,
162
+ "perf_test_simple_aug": false,
163
+ "pin_mem": false,
164
+ "prefetcher": true,
165
+ "pretrained": false,
166
+ "processed_neck_outputs": [
167
+ "decoder"
168
+ ],
169
+ "profile_train_exit_after_profiling": false,
170
+ "profile_train_export_chrome_trace": true,
171
+ "profile_train_export_csv": false,
172
+ "profile_train_iterations": 0,
173
+ "qradio": false,
174
+ "qradio_max_tokens": 512,
175
+ "qradio_min_tokens": 32,
176
+ "qradio_patch_token_mask_initial_ratio": 0.95,
177
+ "qradio_progressive_2d": false,
178
+ "qradio_quantizer": null,
179
+ "qradio_ramp_alpha": 1.5,
180
+ "rank": 0,
181
+ "ratio": [
182
+ 0.75,
183
+ 1.3333333333333333
184
+ ],
185
+ "recount": 1,
186
+ "recovery_interval": 0,
187
+ "register_multiple": 0,
188
+ "remode": "pixel",
189
+ "reprob": 0.0,
190
+ "reset_loss_state": false,
191
+ "resplit": false,
192
+ "sample_tracking": false,
193
+ "save_images": false,
194
+ "scale": [
195
+ 0.5,
196
+ 1.0
197
+ ],
198
+ "sched": "cosine",
199
+ "seed": 42,
200
+ "shift_equivariance": false,
201
+ "smoothing": 0.1,
202
+ "source_tracking": false,
203
+ "spectral_heads": false,
204
+ "spectral_reparam": false,
205
+ "spectral_weight_decay": null,
206
+ "split_bn": false,
207
+ "start_epoch": null,
208
+ "std": null,
209
+ "stream_teachers": false,
210
+ "student_intermediate_indices": null,
211
+ "student_load_skip_state_dict_keys_regex": null,
212
+ "student_reinit_model_layers_regex": null,
213
+ "student_strict_load_ignore_mismatched_shape_keys_regex": null,
214
+ "student_strict_load_ignore_missing_keys_regex": null,
215
+ "student_strict_load_ignore_unexpected_keys_regex": null,
216
+ "student_strict_load_state_dict": false,
217
+ "sync_bn": false,
218
+ "sync_resolutions_across_ranks": true,
219
+ "synchronize_step": false,
220
+ "teachers": [
221
+ {
222
+ "model": "siglip2-g-384",
223
+ "name": "siglip2-g",
224
+ "type": "siglip2",
225
+ "use_summary": true
226
+ },
227
+ {
228
+ "model": "dinov3_vit7b16",
229
+ "name": "dino_v3_7b",
230
+ "type": "dino_v3",
231
+ "use_summary": true
232
+ },
233
+ {
234
+ "model": "default",
235
+ "name": "sam3",
236
+ "type": "sam3",
237
+ "use_summary": false
238
+ }
239
+ ],
240
+ "timing_warmup_iters": 20,
241
+ "tokenizer_kwargs": {},
242
+ "tokenizer_type": null,
243
+ "tome": null,
244
+ "torchcompile": null,
245
+ "torchscript": false,
246
+ "train_interpolation": "random",
247
+ "train_split": "train",
248
+ "tta": 0,
249
+ "untie_neck_weights": false,
250
+ "use_coco": false,
251
+ "use_multi_epochs_loader": false,
252
+ "val_ema_only": false,
253
+ "val_split": "val",
254
+ "vflip": 0.0,
255
+ "vitdet_version": 1,
256
+ "wandb_entity": "",
257
+ "wandb_id": "",
258
+ "wandb_job_type": "",
259
+ "wandb_name": "",
260
+ "wandb_project": "",
261
+ "wandb_tags": null,
262
+ "warmup_lr": 1e-05,
263
+ "warmup_prefix": false,
264
+ "worker_seeding": "all",
265
+ "workers": 8,
266
+ "workload_inspector_analyze_nsys_traces": false,
267
+ "workload_inspector_baseline_start_iter": 1500,
268
+ "workload_inspector_major_slowdown_p95_factor": 10.0,
269
+ "workload_inspector_minor_slowdown_p95_factor": 3.0,
270
+ "workload_inspector_no_slowdown_check": false,
271
+ "workload_inspector_simulate_slowdown_num_times": 1,
272
+ "workload_inspector_simulate_slowdown_start_iter": null,
273
+ "world_size": 256
274
+ },
275
+ "auto_map": {
276
+ "AutoConfig": "hf_model.RADIOConfig",
277
+ "AutoModel": "hf_model.RADIOModel"
278
+ },
279
+ "feature_normalizer_config": null,
280
+ "inter_feature_normalizer_config": null,
281
+ "max_resolution": 2048,
282
+ "patch_size": 16,
283
+ "preferred_resolution": [
284
+ 512,
285
+ 512
286
+ ],
287
+ "torch_dtype": "float32",
288
+ "transformers_version": "4.51.3",
289
+ "version": "c-radio_v4-h",
290
+ "vitdet_window_size": null
291
+ }
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,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 contextlib import contextmanager
10
+ from typing import Callable, List, Mapping,Optional, Set, Tuple, Union
11
+ from types import MethodType
12
+
13
+ import torch
14
+ from torch import nn
15
+
16
+ from timm.models import VisionTransformer, checkpoint_seq
17
+
18
+ from .feature_normalizer import IntermediateFeatureNormalizerBase, NullIntermediateFeatureNormalizer
19
+
20
+ from .extra_models import DinoWrapper
21
+ from .vit_patch_generator import ViTPatchGenerator
22
+ from .forward_intermediates import forward_intermediates
23
+ from .dual_hybrid_vit import HybridModel
24
+ from .radio1d import RADIO1D
25
+
26
+
27
+ def _forward_cpe(self: VisionTransformer, x: torch.Tensor) -> torch.Tensor:
28
+ x = self.patch_generator(x)
29
+ if getattr(self, 'grad_checkpointing', False) and not torch.jit.is_scripting():
30
+ x = checkpoint_seq(self.blocks, x)
31
+ else:
32
+ x = self.blocks(x)
33
+ x = self.norm(x)
34
+ return x
35
+
36
+
37
+ @contextmanager
38
+ def _video_mode(self: VisionTransformer, t: int):
39
+ """
40
+ Context manager to temporarily set the model in video mode.
41
+ This is used to handle models that support both image and video inputs.
42
+ """
43
+ original_num_frames = self.patch_generator.num_video_frames
44
+ self.patch_generator.num_video_frames = t
45
+ try:
46
+ yield
47
+ finally:
48
+ self.patch_generator.num_video_frames = original_num_frames
49
+
50
+
51
+ def _take_indices(
52
+ num_blocks: int,
53
+ n: Optional[Union[int, List[int], Tuple[int]]],
54
+ ) -> Tuple[Set[int], int]:
55
+ if isinstance(n, int):
56
+ assert n >= 0
57
+ take_indices = {x for x in range(num_blocks - n, num_blocks)}
58
+ else:
59
+ take_indices = {num_blocks + idx if idx < 0 else idx for idx in n}
60
+ return take_indices, max(take_indices)
61
+
62
+
63
+ def _forward_intermediates_cpe(
64
+ self,
65
+ x: torch.Tensor,
66
+ norm: bool = False,
67
+ **kwargs,
68
+ ) -> Union[List[torch.Tensor], Tuple[torch.Tensor, List[torch.Tensor]]]:
69
+ return forward_intermediates(
70
+ self,
71
+ patch_extractor=self.patch_generator,
72
+ num_summary_tokens=self.patch_generator.num_skip,
73
+ num_cls_tokens=self.patch_generator.num_cls_tokens,
74
+ norm=self.norm if norm else lambda y: y,
75
+ x=x,
76
+ **kwargs,
77
+ )
78
+
79
+
80
+ def _forward_cpe_dinov2(self: DinoWrapper, x: torch.Tensor) -> torch.Tensor:
81
+ y = _forward_cpe(self.inner, x)
82
+
83
+ return y[:, 0], y[:, self.num_summary_tokens:]
84
+
85
+
86
+ def _forward_intermediates_cpe_dinov2(self: DinoWrapper, *args, **kwargs):
87
+ return _forward_intermediates_cpe(self.inner, *args, **kwargs)
88
+
89
+
90
+ def _enable_cpe_for_timm_vit(model: VisionTransformer,
91
+ forward_fn: Callable,
92
+ max_img_size: Union[int, Tuple[int, int]] = 1024,
93
+ num_cls_tokens: int = 1,
94
+ pos_dropout: float = 0.1,
95
+ register_multiple: int = Optional[None],
96
+ num_registers: int = Optional[None],
97
+ ):
98
+ if not isinstance(model, VisionTransformer):
99
+ raise ValueError("CPE only support for VisionTransformer models!")
100
+
101
+ patch_size = model.patch_embed.patch_size[0]
102
+ embed_dim = model.embed_dim
103
+ input_dims = model.patch_embed.img_size
104
+ normalize_patches = not isinstance(model.patch_embed.norm, nn.Identity)
105
+ cls_token = model.cls_token is not None
106
+
107
+ max_img_size = int(round(max_img_size / patch_size) * patch_size)
108
+
109
+ patch_generator = ViTPatchGenerator(
110
+ patch_size=patch_size,
111
+ embed_dim=embed_dim,
112
+ input_dims=input_dims,
113
+ normalize_patches=normalize_patches,
114
+ cls_token=cls_token,
115
+ max_input_dims=max_img_size,
116
+ pos_dropout=pos_dropout,
117
+ num_cls_tokens=num_cls_tokens,
118
+ register_multiple=register_multiple,
119
+ num_registers=num_registers,
120
+ )
121
+
122
+ model.patch_generator = patch_generator
123
+ model.patch_embed = None
124
+ model.cls_token = None
125
+ model.pos_embed = None
126
+ model.pos_drop = None
127
+ model.patch_size = patch_size
128
+ model.num_cls_tokens = num_cls_tokens
129
+ model.num_registers = patch_generator.num_registers
130
+
131
+ model.forward_features = MethodType(forward_fn, model)
132
+ model.forward_intermediates = MethodType(_forward_intermediates_cpe, model)
133
+
134
+
135
+ def _enable_cpe_for_dv2_reg_vit(model: DinoWrapper,
136
+ max_img_size: Union[int, Tuple[int, int]] = 1024,
137
+ num_cls_tokens: int = 1,
138
+ pos_dropout: float = 0.1,
139
+ register_multiple: int = Optional[None],
140
+ num_registers: int = Optional[None],
141
+ ):
142
+ patch_size = model.patch_size
143
+ embed_dim = model.embed_dim
144
+ input_dims = model.inner.patch_embed.patches_resolution
145
+ normalize_patches = not isinstance(model.inner.patch_embed.norm, nn.Identity)
146
+ cls_token = True
147
+
148
+ max_img_size = int(round(max_img_size / patch_size) * patch_size)
149
+
150
+ patch_generator = ViTPatchGenerator(
151
+ patch_size=patch_size,
152
+ embed_dim=embed_dim,
153
+ input_dims=input_dims,
154
+ normalize_patches=normalize_patches,
155
+ cls_token=cls_token,
156
+ max_input_dims=max_img_size,
157
+ pos_dropout=pos_dropout,
158
+ num_cls_tokens=num_cls_tokens,
159
+ register_multiple=register_multiple,
160
+ num_registers=num_registers,
161
+ patch_bias=True,
162
+ )
163
+
164
+ inner = model.inner
165
+ inner.patch_generator = patch_generator
166
+ inner.patch_embed = None
167
+ inner.cls_token = None
168
+ inner.pos_embed = None
169
+ inner.register_tokens = None
170
+ inner.patch_size = patch_size
171
+
172
+ model.forward_features = MethodType(_forward_cpe_dinov2, model)
173
+ model.forward_intermediates = MethodType(_forward_intermediates_cpe_dinov2, model)
174
+
175
+
176
+ def enable_cpe(model: nn.Module,
177
+ *args,
178
+ **kwargs,
179
+ ):
180
+ if isinstance(model, RADIO1D):
181
+ # RADIO1D already handles CPE enabling internally
182
+ pass
183
+ elif isinstance(model, VisionTransformer):
184
+ _enable_cpe_for_timm_vit(model, _forward_cpe, *args, **kwargs)
185
+ elif isinstance(model, DinoWrapper):
186
+ _enable_cpe_for_dv2_reg_vit(model, *args, **kwargs)
187
+ elif isinstance(model, HybridModel):
188
+ _enable_cpe_for_timm_vit(model.vit, *args, **kwargs)
189
+ else:
190
+ raise ValueError(f'CPE not supported for this model type: {type(model)}')
191
+
192
+ model.cpe_video_mode = MethodType(_video_mode, model)
enable_damp.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
20
+
21
+ # For now, don't do anything
22
+ class DAMP(nn.Identity):
23
+ def __init__(self, std: float):
24
+ super().__init__()
25
+ self.std = std
26
+
27
+
28
+ def enable_damp(model: nn.Module, std: float):
29
+ if isinstance(model, (list, tuple)):
30
+ for m in model:
31
+ enable_damp(m, std)
32
+ return
33
+
34
+ for name, module in model.named_modules():
35
+ if isinstance(module, nn.Linear):
36
+ parametrize.register_parametrization(module, 'weight', DAMP(std))
37
+
38
+
39
+ def configure_damp_from_args(model: nn.Module, args):
40
+ damp = getattr(args, 'damp', None)
41
+ if damp:
42
+ enable_damp(model, damp)
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,1398 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ try:
23
+ from timm.models import register_model
24
+ except ImportError:
25
+ from timm.models.registry import register_model
26
+
27
+ try:
28
+ from timm.layers import trunc_normal_, DropPath, LayerNorm2d
29
+ except ImportError:
30
+ from timm.models.layers import trunc_normal_, DropPath, LayerNorm2d
31
+ import numpy as np
32
+ import torch.nn.functional as F
33
+ import math
34
+ import warnings
35
+
36
+ #######################
37
+ ## Codebase from YOLOv8
38
+ ## BEGINNING
39
+ #######################
40
+
41
+ class C2f(nn.Module):
42
+ """Faster Implementation of CSP Bottleneck with 2 convolutions."""
43
+ """From YOLOv8 codebase"""
44
+ 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
45
+ super().__init__()
46
+ if drop_path is None:
47
+ drop_path = [0.0] * n
48
+
49
+ self.c = int(c2 * e) # hidden channels
50
+ self.cv1 = Conv(c1, 2 * self.c, 1, 1)
51
+ self.cv2 = Conv((2 + n) * self.c, c2, 1) # optional act=FReLU(c2)
52
+ 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))
53
+
54
+ def forward(self, x):
55
+ """Forward pass through C2f layer."""
56
+ y = list(self.cv1(x).chunk(2, 1))
57
+ y.extend(m(y[-1]) for m in self.m)
58
+ return self.cv2(torch.cat(y, 1))
59
+
60
+ def forward_split(self, x):
61
+ """Forward pass using split() instead of chunk()."""
62
+ y = list(self.cv1(x).split((self.c, self.c), 1))
63
+ y.extend(m(y[-1]) for m in self.m)
64
+ return self.cv2(torch.cat(y, 1))
65
+
66
+ class Bottleneck(nn.Module):
67
+ """Standard bottleneck."""
68
+
69
+ 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
70
+ super().__init__()
71
+ c_ = int(c2 * e) # hidden channels
72
+ self.cv1 = Conv(c1, c_, k[0], 1)
73
+ self.cv2 = Conv(c_, c2, k[1], 1, g=g)
74
+ self.add = shortcut and c1 == c2
75
+ self.drop_path1 = DropPath(drop_path) if drop_path > 0. else nn.Identity()
76
+
77
+ def forward(self, x):
78
+ """'forward()' applies the YOLOv5 FPN to input data."""
79
+ return x + self.drop_path1(self.cv2(self.cv1(x))) if self.add else self.cv2(self.cv1(x))
80
+
81
+
82
+ class Conv(nn.Module):
83
+ """Modified to support layer fusion"""
84
+ default_act = nn.SiLU() # default activation
85
+
86
+ def __init__(self, a, b, kernel_size=1, stride=1, padding=None, g=1, dilation=1, bn_weight_init=1, bias=False, act=True):
87
+ super().__init__()
88
+
89
+ self.conv = torch.nn.Conv2d(a, b, kernel_size, stride, autopad(kernel_size, padding, dilation), dilation, g, bias=False)
90
+ if 1:
91
+ self.bn = torch.nn.BatchNorm2d(b)
92
+ torch.nn.init.constant_(self.bn.weight, bn_weight_init)
93
+ torch.nn.init.constant_(self.bn.bias, 0)
94
+ self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()
95
+
96
+
97
+ def forward(self,x):
98
+ x = self.conv(x)
99
+ x = self.bn(x)
100
+ x = self.act(x)
101
+ return x
102
+
103
+ @torch.no_grad()
104
+ def switch_to_deploy(self):
105
+ # return 1
106
+ if not isinstance(self.bn, nn.Identity):
107
+ c, bn = self.conv, self.bn
108
+ w = bn.weight / (bn.running_var + bn.eps) ** 0.5
109
+ w = c.weight * w[:, None, None, None]
110
+ b = bn.bias - bn.running_mean * bn.weight / \
111
+ (bn.running_var + bn.eps)**0.5
112
+
113
+ self.conv.weight.data.copy_(w)
114
+ self.conv.bias = nn.Parameter(b)
115
+
116
+ self.bn = nn.Identity()
117
+
118
+ def autopad(k, p=None, d=1): # kernel, padding, dilation
119
+ """Pad to 'same' shape outputs."""
120
+ if d > 1:
121
+ k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k] # actual kernel-size
122
+ if p is None:
123
+ p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad
124
+ return p
125
+
126
+
127
+ #######################
128
+ ## Codebase from YOLOv8
129
+ ## END
130
+ #######################
131
+
132
+ def pixel_unshuffle(data, factor=2):
133
+ # performs nn.PixelShuffle(factor) in reverse, torch has some bug for ONNX and TRT, so doing it manually
134
+ B, C, H, W = data.shape
135
+ 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)
136
+
137
+ class SwiGLU(nn.Module):
138
+ # should be more advanced, but doesnt improve results so far
139
+ def forward(self, x):
140
+ x, gate = x.chunk(2, dim=-1)
141
+ return F.silu(gate) * x
142
+
143
+
144
+ def window_partition(x, window_size):
145
+ """
146
+ Function for partitioning image into windows and later do windowed attention
147
+ Args:
148
+ x: (B, C, H, W)
149
+ window_size: window size
150
+ Returns:
151
+ windows - local window features (num_windows*B, window_size*window_size, C)
152
+ (Hp, Wp) - the size of the padded image
153
+ """
154
+ B, C, H, W = x.shape
155
+
156
+ if window_size == 0 or (window_size==H and window_size==W):
157
+ windows = x.flatten(2).transpose(1, 2)
158
+ Hp, Wp = H, W
159
+ else:
160
+ pad_h = (window_size - H % window_size) % window_size
161
+ pad_w = (window_size - W % window_size) % window_size
162
+ if pad_h > 0 or pad_w > 0:
163
+ x = F.pad(x, (0, pad_w, 0, pad_h), mode="reflect")
164
+ Hp, Wp = H + pad_h, W + pad_w
165
+
166
+ x = x.view(B, C, Hp // window_size, window_size, Wp // window_size, window_size)
167
+ windows = x.permute(0, 2, 4, 3, 5, 1).reshape(-1, window_size*window_size, C)
168
+
169
+ return windows, (Hp, Wp)
170
+
171
+ class Conv2d_BN(nn.Module):
172
+ '''
173
+ Conv2d + BN layer with folding capability to speed up inference
174
+ Can be merged with Conv() function with additional arguments
175
+ '''
176
+ def __init__(self, a, b, kernel_size=1, stride=1, padding=0, dilation=1, groups=1, bn_weight_init=1, bias=False):
177
+ super().__init__()
178
+ self.conv = torch.nn.Conv2d(a, b, kernel_size, stride, padding, dilation, groups, bias=False)
179
+ if 1:
180
+ self.bn = torch.nn.BatchNorm2d(b)
181
+ torch.nn.init.constant_(self.bn.weight, bn_weight_init)
182
+ torch.nn.init.constant_(self.bn.bias, 0)
183
+
184
+ def forward(self,x):
185
+ x = self.conv(x)
186
+ x = self.bn(x)
187
+ return x
188
+
189
+ @torch.no_grad()
190
+ def switch_to_deploy(self):
191
+ if not isinstance(self.bn, nn.Identity):
192
+ c, bn = self.conv, self.bn
193
+ w = bn.weight / (bn.running_var + bn.eps) ** 0.5
194
+ w = c.weight * w[:, None, None, None]
195
+ b = bn.bias - bn.running_mean * bn.weight / \
196
+ (bn.running_var + bn.eps)**0.5
197
+ self.conv.weight.data.copy_(w)
198
+ self.conv.bias = nn.Parameter(b)
199
+ self.bn = nn.Identity()
200
+
201
+
202
+
203
+ def window_reverse(windows, window_size, H, W, pad_hw):
204
+ """
205
+ Windows to the full feature map
206
+ Args:
207
+ windows: local window features (num_windows*B, window_size, window_size, C)
208
+ window_size: Window size
209
+ H: Height of image
210
+ W: Width of image
211
+ pad_w - a tuple of image passing used in windowing step
212
+ Returns:
213
+ x: (B, C, H, W)
214
+
215
+ """
216
+ # print(f"window_reverse, windows.shape {windows.shape}")
217
+ Hp, Wp = pad_hw
218
+ if window_size == 0 or (window_size==H and window_size==W):
219
+ B = int(windows.shape[0] / (Hp * Wp / window_size / window_size))
220
+ x = windows.transpose(1, 2).view(B, -1, H, W)
221
+ else:
222
+ B = int(windows.shape[0] / (Hp * Wp / window_size / window_size))
223
+ x = windows.view(B, Hp // window_size, Wp // window_size, window_size, window_size, -1)
224
+ x = x.permute(0, 5, 1, 3, 2, 4).reshape(B,windows.shape[2], Hp, Wp)
225
+
226
+ if Hp > H or Wp > W:
227
+ x = x[:, :, :H, :W, ].contiguous()
228
+
229
+ return x
230
+
231
+
232
+
233
+ class PosEmbMLPSwinv2D(nn.Module):
234
+ """
235
+ 2D positional embedding from Swin Transformer v2
236
+ Added functionality to store the positional embedding in the model and not recompute it every time
237
+ """
238
+ def __init__(
239
+ self, window_size, pretrained_window_size, num_heads, seq_length, no_log=False, cpb_mlp_hidden=512,
240
+ ):
241
+ super().__init__()
242
+ self.window_size = window_size
243
+ self.num_heads = num_heads
244
+ # mlp to generate continuous relative position bias
245
+ self.cpb_mlp = nn.Sequential(
246
+ nn.Linear(2, cpb_mlp_hidden, bias=True),
247
+ nn.ReLU(inplace=True),
248
+ nn.Linear(cpb_mlp_hidden, num_heads, bias=False),
249
+ )
250
+
251
+ self.grid_exists = False
252
+ self.seq_length = seq_length
253
+ self.deploy = False
254
+ self.num_heads = num_heads
255
+ self.no_log = no_log
256
+ self.pretrained_window_size = pretrained_window_size
257
+ self.relative_bias_window_size = window_size
258
+
259
+ relative_coords_table, relative_position_index, relative_bias = self.relative_bias_initialization(window_size, num_heads,
260
+ pretrained_window_size, seq_length,
261
+ no_log)
262
+
263
+ self.register_buffer("relative_coords_table", relative_coords_table)
264
+ self.register_buffer("relative_position_index", relative_position_index)
265
+ self.register_buffer("relative_bias", relative_bias) # for EMA
266
+
267
+ def relative_bias_initialization(self, window_size, num_heads, pretrained_window_size, seq_length, no_log):
268
+ # as in separate function to support window size chage after model weights loading
269
+ relative_coords_h = torch.arange(
270
+ -(window_size[0] - 1), window_size[0], dtype=torch.float32
271
+ )
272
+ relative_coords_w = torch.arange(
273
+ -(window_size[1] - 1), window_size[1], dtype=torch.float32
274
+ )
275
+ relative_coords_table = (
276
+ torch.stack(torch.meshgrid([relative_coords_h, relative_coords_w]))
277
+ .permute(1, 2, 0)
278
+ .contiguous()
279
+ .unsqueeze(0)
280
+ ) # 1, 2*Wh-1, 2*Ww-1, 2
281
+ if pretrained_window_size[0] > 0:
282
+ relative_coords_table[:, :, :, 0] /= pretrained_window_size[0] - 1
283
+ relative_coords_table[:, :, :, 1] /= pretrained_window_size[1] - 1
284
+ else:
285
+ relative_coords_table[:, :, :, 0] /= self.window_size[0] - 1
286
+ relative_coords_table[:, :, :, 1] /= self.window_size[1] - 1
287
+
288
+ if not no_log:
289
+ relative_coords_table *= 8 # normalize to -8, 8
290
+ relative_coords_table = (
291
+ torch.sign(relative_coords_table)
292
+ * torch.log2(torch.abs(relative_coords_table) + 1.0)
293
+ / np.log2(8)
294
+ )
295
+
296
+ # get pair-wise relative position index for each token inside the window
297
+ coords_h = torch.arange(self.window_size[0])
298
+ coords_w = torch.arange(self.window_size[1])
299
+ coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww
300
+ coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww
301
+ relative_coords = (
302
+ coords_flatten[:, :, None] - coords_flatten[:, None, :]
303
+ ) # 2, Wh*Ww, Wh*Ww
304
+ relative_coords = relative_coords.permute(
305
+ 1, 2, 0
306
+ ).contiguous() # Wh*Ww, Wh*Ww, 2
307
+ relative_coords[:, :, 0] += self.window_size[0] - 1 # shift to start from 0
308
+ relative_coords[:, :, 1] += self.window_size[1] - 1
309
+ relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1
310
+ relative_position_index = relative_coords.sum(-1) # Wh*Ww, Wh*Ww
311
+
312
+ relative_bias = torch.zeros(1, num_heads, seq_length, seq_length)
313
+
314
+ self.relative_bias_window_size = window_size
315
+
316
+ return relative_coords_table, relative_position_index, relative_bias
317
+
318
+
319
+ def switch_to_deploy(self):
320
+ self.deploy = True
321
+ self.grid_exists = True
322
+
323
+ def forward(self, input_tensor):
324
+ # for efficiency, we want this forward to be folded into a single operation (sum)
325
+ # if resolution stays the same, then we dont need to recompute MLP layers
326
+
327
+ if not self.deploy or self.training:
328
+ self.grid_exists = False
329
+
330
+ #compare if all elements in self.window_size list match those in self.relative_bias_window_size
331
+ if not all([self.window_size[i] == self.relative_bias_window_size[i] for i in range(len(self.window_size))]):
332
+ relative_coords_table, relative_position_index, relative_bias = self.relative_bias_initialization(self.window_size, self.num_heads,
333
+ self.pretrained_window_size, self.seq_length,
334
+ self.no_log)
335
+
336
+ self.relative_coords_table = relative_coords_table.to(self.relative_coords_table.device)
337
+ self.relative_position_index = relative_position_index.to(self.relative_position_index.device)
338
+ self.relative_bias = relative_bias.to(self.relative_bias.device)
339
+
340
+ if self.deploy and self.grid_exists:
341
+ input_tensor = input_tensor + self.relative_bias
342
+ return input_tensor
343
+
344
+ if 1:
345
+ self.grid_exists = True
346
+
347
+ relative_position_bias_table = self.cpb_mlp(
348
+ self.relative_coords_table
349
+ ).view(-1, self.num_heads)
350
+ relative_position_bias = relative_position_bias_table[
351
+ self.relative_position_index.view(-1)
352
+ ].view(
353
+ self.window_size[0] * self.window_size[1],
354
+ self.window_size[0] * self.window_size[1],
355
+ -1,
356
+ ) # Wh*Ww,Wh*Ww,nH
357
+
358
+ relative_position_bias = relative_position_bias.permute(
359
+ 2, 0, 1
360
+ ).contiguous() # nH, Wh*Ww, Wh*Ww
361
+ relative_position_bias = 16 * torch.sigmoid(relative_position_bias)
362
+
363
+ self.relative_bias = relative_position_bias.unsqueeze(0)
364
+
365
+ input_tensor = input_tensor + self.relative_bias
366
+ return input_tensor
367
+
368
+
369
+ class GRAAttentionBlock(nn.Module):
370
+ def __init__(self, window_size, dim_in, dim_out,
371
+ num_heads, drop_path=0., qk_scale=None, qkv_bias=False,
372
+ norm_layer=nn.LayerNorm, layer_scale=None,
373
+ use_swiglu=True,
374
+ subsample_ratio=1, dim_ratio=1, conv_base=False,
375
+ do_windowing=True, multi_query=False, use_shift=0,
376
+ cpb_mlp_hidden=512, conv_groups_ratio=0):
377
+ '''
378
+ Global Resolution Attention Block , see README for details
379
+ Attention with subsampling to get a bigger receptive field for attention
380
+ conv_base - use conv2d instead of avgpool2d for downsample / upsample
381
+
382
+
383
+ '''
384
+ super().__init__()
385
+
386
+ self.shift_size=window_size//2 if use_shift else 0
387
+
388
+ self.do_windowing = do_windowing
389
+ self.subsample_ratio = subsample_ratio
390
+
391
+
392
+
393
+ if do_windowing:
394
+ if conv_base:
395
+ self.downsample_op = nn.Conv2d(dim_in, dim_out, kernel_size=subsample_ratio, stride=subsample_ratio) if subsample_ratio > 1 else nn.Identity()
396
+
397
+
398
+ self.downsample_mixer = nn.Identity()
399
+ self.upsample_mixer = nn.Identity()
400
+ self.upsample_op = nn.ConvTranspose2d(dim_in, dim_out, kernel_size=subsample_ratio, stride=subsample_ratio) if subsample_ratio > 1 else nn.Identity()
401
+ else:
402
+ self.downsample_op = nn.AvgPool2d(kernel_size=subsample_ratio, stride=subsample_ratio) if subsample_ratio > 1 else nn.Identity()
403
+ self.downsample_mixer = Conv2d_BN(dim_in, dim_out, kernel_size=1, stride=1) if subsample_ratio > 1 else nn.Identity()
404
+ self.upsample_mixer = nn.Upsample(scale_factor=subsample_ratio, mode='nearest') if subsample_ratio > 1 else nn.Identity()
405
+ 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()
406
+
407
+
408
+ # in case there is no downsampling conv we want to have it separately
409
+ # will help with information propagation between windows
410
+ if subsample_ratio == 1:
411
+ # conv_groups_ratio=0
412
+ 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)
413
+ # 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)
414
+ # self.pre_conv_act = nn.ReLU6()
415
+ #for simplicity:
416
+ self.pre_conv_act = nn.Identity()
417
+ if conv_groups_ratio == -1:
418
+ self.pre_conv = nn.Identity()
419
+ self.pre_conv_act = nn.Identity()
420
+
421
+ self.window_size = window_size
422
+
423
+ self.norm1 = norm_layer(dim_in)
424
+
425
+ self.attn = WindowAttention(
426
+ dim_in,
427
+ num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale,
428
+ resolution=window_size,
429
+ seq_length=window_size**2, dim_out=dim_in, multi_query=multi_query,
430
+ shift_size=self.shift_size, cpb_mlp_hidden=cpb_mlp_hidden)
431
+
432
+ self.drop_path1 = DropPath(drop_path) if drop_path > 0. else nn.Identity()
433
+
434
+ use_layer_scale = layer_scale is not None and type(layer_scale) in [int, float]
435
+ self.gamma1 = nn.Parameter(layer_scale * torch.ones(dim_in)) if use_layer_scale else 1
436
+
437
+ ### mlp layer
438
+ mlp_ratio = 4
439
+ self.norm2 = norm_layer(dim_in)
440
+ mlp_hidden_dim = int(dim_in * mlp_ratio)
441
+
442
+ activation = nn.GELU if not use_swiglu else SwiGLU
443
+ mlp_hidden_dim = int((4 * dim_in * 1 / 2) / 64) * 64 if use_swiglu else mlp_hidden_dim
444
+
445
+ self.mlp = Mlp(in_features=dim_in, hidden_features=mlp_hidden_dim, act_layer=activation, use_swiglu=use_swiglu)
446
+
447
+ self.gamma2 = nn.Parameter(layer_scale * torch.ones(dim_in)) if layer_scale else 1
448
+ self.drop_path2=DropPath(drop_path) if drop_path > 0. else nn.Identity()
449
+
450
+
451
+ def forward(self, x):
452
+ skip_connection = x
453
+ attn_mask = None
454
+
455
+ # in case there is no downsampling conv we want to have it separately
456
+ # will help with information propagation
457
+ if self.subsample_ratio == 1:
458
+ x = self.pre_conv_act(self.pre_conv(x)) + skip_connection
459
+
460
+ if self.do_windowing:
461
+ # performing windowing if required
462
+ x = self.downsample_op(x)
463
+ x = self.downsample_mixer(x)
464
+
465
+ if self.window_size>0:
466
+ H, W = x.shape[2], x.shape[3]
467
+
468
+ if self.shift_size > 0 and H>self.window_size and W>self.window_size:
469
+ # @swin like cyclic shift, doesnt show better performance
470
+ x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(2, 3))
471
+
472
+ x, pad_hw = window_partition(x, self.window_size)
473
+
474
+ if self.shift_size > 0 and H>self.window_size and W>self.window_size:
475
+ # set atten matrix to have -100 and the top right square
476
+ # attn[:, :, :-self.shift_size, -self.shift_size:] = -100.0
477
+ # calculate attention mask for SW-MSA
478
+ # not used in final version, can be useful for some cases especially for high res
479
+ H, W = pad_hw
480
+ img_mask = torch.zeros((1, H, W, 1), device=x.device) # 1 H W 1
481
+ h_slices = (slice(0, -self.window_size),
482
+ slice(-self.window_size, -self.shift_size),
483
+ slice(-self.shift_size, None))
484
+ w_slices = (slice(0, -self.window_size),
485
+ slice(-self.window_size, -self.shift_size),
486
+ slice(-self.shift_size, None))
487
+ cnt = 0
488
+ for h in h_slices:
489
+ for w in w_slices:
490
+ img_mask[:, h, w, :] = cnt
491
+ cnt += 1
492
+ img_mask = img_mask.transpose(1,2).transpose(1,3)
493
+ mask_windows = window_partition(img_mask, self.window_size) # nW, window_size, window_size, 1
494
+
495
+ mask_windows = mask_windows[0].view(-1, self.window_size * self.window_size)
496
+ attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2)
497
+ attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0))
498
+
499
+ # window attention
500
+ x = x + self.drop_path1(self.gamma1*self.attn(self.norm1(x), attn_mask=attn_mask)) # or pass H,W
501
+ # mlp layer
502
+ x = x + self.drop_path2(self.gamma2*self.mlp(self.norm2(x)))
503
+
504
+ if self.do_windowing:
505
+ if self.window_size > 0:
506
+ x = window_reverse(x, self.window_size, H, W, pad_hw)
507
+
508
+ # reverse cyclic shift
509
+ if self.shift_size > 0 and H>self.window_size and W>self.window_size:
510
+ # @swin like cyclic shift, not tested
511
+ x = torch.roll(x, shifts=(self.shift_size, self.shift_size), dims=(2, 3))
512
+
513
+ x = self.upsample_mixer(x)
514
+ x = self.upsample_op(x)
515
+
516
+
517
+ if x.shape[2] != skip_connection.shape[2] or x.shape[3] != skip_connection.shape[3]:
518
+ x = torch.nn.functional.pad(x, ( 0, -x.shape[3] + skip_connection.shape[3], 0, -x.shape[2] + skip_connection.shape[2]), mode="reflect")
519
+ # need to add skip connection because downsampling and upsampling will break residual connection
520
+ # 0.5 is needed to make sure that the skip connection is not too strong
521
+ # in case of no downsample / upsample we can show that 0.5 compensates for the residual connection
522
+ x = 0.5 * x + 0.5 * skip_connection
523
+ return x
524
+
525
+
526
+
527
+
528
+ class MultiResolutionAttention(nn.Module):
529
+ """
530
+ MultiResolutionAttention (MRA) module
531
+ The idea is to use multiple attention blocks with different resolution
532
+ Feature maps are downsampled / upsampled for each attention block on different blocks
533
+ Every attention block supports windowing
534
+ """
535
+
536
+ def __init__(self, window_size, sr_ratio,
537
+ dim, dim_ratio, num_heads,
538
+ do_windowing=True,
539
+ layer_scale=1e-5, norm_layer=nn.LayerNorm,
540
+ drop_path = 0, qkv_bias=False, qk_scale=1.0,
541
+ use_swiglu=True, multi_query=False, conv_base=False,
542
+ use_shift=0, cpb_mlp_hidden=512, conv_groups_ratio=0) -> None:
543
+ """
544
+ Args:
545
+ input_resolution: input image resolution
546
+ window_size: window size
547
+ compression_ratio: compression ratio
548
+ max_depth: maximum depth of the GRA module
549
+ use_shift: do window shifting
550
+ """
551
+ super().__init__()
552
+
553
+ depth = len(sr_ratio)
554
+
555
+ self.attention_blocks = nn.ModuleList()
556
+
557
+
558
+ for i in range(depth):
559
+ subsample_ratio = sr_ratio[i]
560
+ if len(window_size) > i:
561
+ window_size_local = window_size[i]
562
+ else:
563
+ window_size_local = window_size[0]
564
+
565
+ self.attention_blocks.append(GRAAttentionBlock(window_size=window_size_local,
566
+ dim_in=dim, dim_out=dim, num_heads=num_heads,
567
+ qkv_bias=qkv_bias, qk_scale=qk_scale, norm_layer=norm_layer,
568
+ layer_scale=layer_scale, drop_path=drop_path,
569
+ use_swiglu=use_swiglu, subsample_ratio=subsample_ratio, dim_ratio=dim_ratio,
570
+ do_windowing=do_windowing, multi_query=multi_query, conv_base=conv_base,
571
+ use_shift=use_shift, cpb_mlp_hidden=cpb_mlp_hidden, conv_groups_ratio=conv_groups_ratio),
572
+ )
573
+
574
+ def forward(self, x):
575
+
576
+ for attention_block in self.attention_blocks:
577
+ x = attention_block(x)
578
+
579
+ return x
580
+
581
+
582
+
583
+ class Mlp(nn.Module):
584
+ """
585
+ Multi-Layer Perceptron (MLP) block
586
+ """
587
+
588
+ def __init__(self,
589
+ in_features,
590
+ hidden_features=None,
591
+ out_features=None,
592
+ act_layer=nn.GELU,
593
+ use_swiglu=True,
594
+ drop=0.):
595
+ """
596
+ Args:
597
+ in_features: input features dimension.
598
+ hidden_features: hidden features dimension.
599
+ out_features: output features dimension.
600
+ act_layer: activation function.
601
+ drop: dropout rate.
602
+ """
603
+
604
+ super().__init__()
605
+ out_features = out_features or in_features
606
+ hidden_features = hidden_features or in_features
607
+ self.fc1 = nn.Linear(in_features, hidden_features * (2 if use_swiglu else 1), bias=False)
608
+ self.act = act_layer()
609
+ self.fc2 = nn.Linear(hidden_features, out_features, bias=False)
610
+
611
+ def forward(self, x):
612
+ x_size = x.size()
613
+ x = x.view(-1, x_size[-1])
614
+ x = self.fc1(x)
615
+ x = self.act(x)
616
+ x = self.fc2(x)
617
+ x = x.view(x_size)
618
+ return x
619
+
620
+ class Downsample(nn.Module):
621
+ """
622
+ Down-sampling block
623
+ Pixel Unshuffle is used for down-sampling, works great accuracy - wise but takes 10% more TRT time
624
+ """
625
+
626
+ def __init__(self,
627
+ dim,
628
+ shuffle = False,
629
+ ):
630
+ """
631
+ Args:
632
+ dim: feature size dimension.
633
+ shuffle: idea with
634
+ keep_dim: bool argument for maintaining the resolution.
635
+ """
636
+
637
+ super().__init__()
638
+ dim_out = 2 * dim
639
+
640
+ if shuffle:
641
+ self.norm = lambda x: pixel_unshuffle(x, factor=2)
642
+ self.reduction = Conv2d_BN(dim*4, dim_out, 1, 1, 0, bias=False)
643
+ # pixel unshuffleging works well but doesnt provide any speedup
644
+ else:
645
+ # removed layer norm for better, in this formulation we are getting 10% better speed
646
+ # LayerNorm for high resolution inputs will be a pain as it pools over the entire spatial dimension
647
+ # therefore we remove it compared to the original implementation in FasterViT
648
+ self.norm = nn.Identity()
649
+ self.reduction = Conv2d_BN(dim, dim_out, 3, 2, 1, bias=False)
650
+
651
+
652
+ def forward(self, x):
653
+ x = self.norm(x)
654
+ x = self.reduction(x)
655
+ return x
656
+
657
+
658
+ class PatchEmbed(nn.Module):
659
+ """
660
+ Patch embedding block
661
+ Used to convert image into an initial set of feature maps with lower resolution
662
+ """
663
+
664
+ def __init__(self, in_chans=3, in_dim=64, dim=96, shuffle_down=False):
665
+ """
666
+ Args:
667
+ in_chans: number of input channels.
668
+ in_dim: intermediate feature size dimension to speed up stem.
669
+ dim: final stem channel number
670
+ shuffle_down: use PixelUnshuffle for down-sampling, effectively increases the receptive field
671
+ """
672
+
673
+ super().__init__()
674
+ # shuffle_down = False
675
+ if not shuffle_down:
676
+ self.proj = nn.Identity()
677
+ self.conv_down = nn.Sequential(
678
+ Conv2d_BN(in_chans, in_dim, 3, 2, 1, bias=False),
679
+ nn.ReLU(),
680
+ Conv2d_BN(in_dim, dim, 3, 2, 1, bias=False),
681
+ nn.ReLU()
682
+ )
683
+ else:
684
+ self.proj = lambda x: pixel_unshuffle(x, factor=4)
685
+ self.conv_down = nn.Sequential(Conv2d_BN(in_chans*16, dim, 3, 1, 1),
686
+ nn.ReLU(),
687
+ )
688
+
689
+ def forward(self, x):
690
+ x = self.proj(x)
691
+ x = self.conv_down(x)
692
+ return x
693
+
694
+
695
+
696
+ class ConvBlock(nn.Module):
697
+ """
698
+ Convolutional block, used in first couple of stages
699
+ Experimented with plan resnet-18 like modules, they are the best in terms of throughput
700
+ Finally, YOLOv8 idea seem to work fine (resnet-18 like block with squeezed feature dimension, and feature concatendation at the end)
701
+ """
702
+ def __init__(self, dim,
703
+ drop_path=0.,
704
+ layer_scale=None,
705
+ kernel_size=3,
706
+ ):
707
+ super().__init__()
708
+
709
+ self.conv1 = Conv2d_BN(dim, dim, kernel_size=kernel_size, stride=1, padding=1)
710
+ self.act1 = nn.GELU()
711
+
712
+ self.conv2 = Conv2d_BN(dim, dim, kernel_size=kernel_size, stride=1, padding=1)
713
+
714
+ self.layer_scale = layer_scale
715
+ if layer_scale is not None and type(layer_scale) in [int, float]:
716
+ self.gamma = nn.Parameter(layer_scale * torch.ones(dim))
717
+ self.layer_scale = True
718
+ else:
719
+ self.layer_scale = False
720
+ self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
721
+
722
+ def forward(self, x):
723
+ input = x
724
+
725
+ x = self.conv1(x)
726
+ x = self.act1(x)
727
+ x = self.conv2(x)
728
+
729
+ if self.layer_scale:
730
+ x = x * self.gamma.view(1, -1, 1, 1)
731
+ x = input + self.drop_path(x)
732
+ return x
733
+
734
+
735
+ class WindowAttention(nn.Module):
736
+ # Windowed Attention from SwinV2
737
+ # use a MLP trick to deal with various input image resolutions, then fold it to improve speed
738
+
739
+ def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, resolution=0,
740
+ seq_length=0, dim_out=None, multi_query=False, shift_size=0, cpb_mlp_hidden=512):
741
+ # taken from EdgeViT and tweaked with attention bias.
742
+ super().__init__()
743
+ if not dim_out: dim_out = dim
744
+ self.shift_size = shift_size
745
+ self.multi_query = multi_query
746
+ self.num_heads = num_heads
747
+ head_dim = dim // num_heads
748
+ self.head_dim = dim // num_heads
749
+
750
+ self.dim_internal = dim
751
+
752
+ self.scale = qk_scale or head_dim ** -0.5
753
+ if not multi_query:
754
+ self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
755
+ else:
756
+ self.qkv = nn.Linear(dim, dim + 2*self.head_dim, bias=qkv_bias)
757
+
758
+ self.proj = nn.Linear(dim, dim_out, bias=False)
759
+ # attention positional bias
760
+ self.pos_emb_funct = PosEmbMLPSwinv2D(window_size=[resolution, resolution],
761
+ pretrained_window_size=[resolution, resolution],
762
+ num_heads=num_heads,
763
+ seq_length=seq_length,
764
+ cpb_mlp_hidden=cpb_mlp_hidden)
765
+
766
+ self.resolution = resolution
767
+
768
+ def forward(self, x, attn_mask = None):
769
+ B, N, C = x.shape
770
+
771
+ if not self.multi_query:
772
+ qkv = self.qkv(x).reshape(B, -1, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
773
+ q, k, v = qkv[0], qkv[1], qkv[2]
774
+ else:
775
+ qkv = self.qkv(x)
776
+ (q, k, v) = qkv.split([self.dim_internal, self.head_dim, self.head_dim], dim=2)
777
+
778
+ q = q.reshape(B, -1, self.num_heads, C // self.num_heads).permute(0, 2, 1, 3)
779
+ k = k.reshape(B, -1, 1, C // self.num_heads).permute(0, 2, 1, 3)
780
+ v = v.reshape(B, -1, 1, C // self.num_heads).permute(0, 2, 1, 3)
781
+
782
+ attn = (q @ k.transpose(-2, -1)) * self.scale
783
+
784
+ attn = self.pos_emb_funct(attn)
785
+
786
+ #add window shift
787
+ if attn_mask is not None:
788
+ nW = attn_mask.shape[0]
789
+ attn = attn.view(B // nW, nW, self.num_heads, N, N) + attn_mask.unsqueeze(1).unsqueeze(0)
790
+ attn = attn.view(-1, self.num_heads, N, N)
791
+
792
+ attn = attn.softmax(dim=-1)
793
+ x = (attn @ v).transpose(1, 2).reshape(B, -1, C)
794
+ x = self.proj(x)
795
+ return x
796
+
797
+
798
+
799
+ class ERADIOLayer(nn.Module):
800
+ """
801
+ E-RADIO Layer
802
+ """
803
+
804
+ def __init__(self,
805
+ dim,
806
+ depth,
807
+ num_heads,
808
+ window_size,
809
+ conv=False,
810
+ downsample=True,
811
+ mlp_ratio=4.,
812
+ qkv_bias=False,
813
+ qk_scale=None,
814
+ norm_layer=nn.LayerNorm,
815
+ drop_path=0.,
816
+ layer_scale=None,
817
+ layer_scale_conv=None,
818
+ sr_dim_ratio=1,
819
+ sr_ratio=1,
820
+ multi_query=False,
821
+ use_swiglu=True,
822
+ yolo_arch=False,
823
+ downsample_shuffle=False,
824
+ conv_base=False,
825
+ use_shift=False,
826
+ cpb_mlp_hidden=512,
827
+ conv_groups_ratio=0,
828
+ verbose: bool = True,
829
+
830
+ ):
831
+ """
832
+ Args:
833
+ dim: feature size dimension.
834
+ depth: number of layers in each stage.
835
+ input_resolution: input image resolution.
836
+ window_size: window size in each stage.
837
+ downsample: bool argument for down-sampling.
838
+ mlp_ratio: MLP ratio.
839
+ num_heads: number of heads in each stage.
840
+ qkv_bias: bool argument for query, key, value learnable bias.
841
+ qk_scale: bool argument to scaling query, key.
842
+ drop: dropout rate.
843
+ attn_drop: attention dropout rate.
844
+ drop_path: drop path rate.
845
+ norm_layer: normalization layer.
846
+ layer_scale: layer scaling coefficient.
847
+ use_shift: SWIN like window shifting for half the window size for every alternating layer (considering multi-resolution)
848
+ conv_groups_ratio: group ratio for conv when no subsampling in multi-res attention
849
+ """
850
+
851
+ super().__init__()
852
+ self.conv = conv
853
+ self.yolo_arch=False
854
+ self.verbose = verbose
855
+ if conv:
856
+ if not yolo_arch:
857
+ self.blocks = nn.ModuleList([
858
+ ConvBlock(dim=dim,
859
+ drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path,
860
+ layer_scale=layer_scale_conv)
861
+ for i in range(depth)])
862
+ self.blocks = nn.Sequential(*self.blocks)
863
+ else:
864
+ self.blocks = C2f(dim,dim,n=depth,shortcut=True,e=0.5)
865
+ self.yolo_arch=True
866
+ else:
867
+ if not isinstance(window_size, list): window_size = [window_size]
868
+ self.window_size = window_size[0]
869
+ self.do_single_windowing = True
870
+ if not isinstance(sr_ratio, list): sr_ratio = [sr_ratio]
871
+ self.sr_ratio = sr_ratio
872
+ if any([sr!=1 for sr in sr_ratio]) or len(set(window_size))>1:
873
+ self.do_single_windowing = False
874
+ do_windowing = True
875
+ else:
876
+ self.do_single_windowing = True
877
+ do_windowing = False
878
+
879
+ #for v2_2
880
+ if conv_groups_ratio != -1:
881
+ self.do_single_windowing = False
882
+ do_windowing = True
883
+
884
+ self.blocks = nn.ModuleList()
885
+ for i in range(depth):
886
+ self.blocks.append(
887
+ MultiResolutionAttention(window_size=window_size,
888
+ sr_ratio=sr_ratio,
889
+ dim=dim,
890
+ dim_ratio = sr_dim_ratio,
891
+ num_heads=num_heads,
892
+ norm_layer=norm_layer,
893
+ drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path,
894
+ layer_scale=layer_scale,
895
+ qkv_bias=qkv_bias,
896
+ qk_scale=qk_scale,
897
+ use_swiglu=use_swiglu,
898
+ do_windowing=do_windowing,
899
+ multi_query=multi_query,
900
+ conv_base=conv_base,
901
+ cpb_mlp_hidden=cpb_mlp_hidden,
902
+ use_shift =0 if ((not use_shift) or ((i) % 2 == 0)) else True ,
903
+ conv_groups_ratio=conv_groups_ratio,
904
+ ))
905
+ self.blocks = nn.Sequential(*self.blocks)
906
+
907
+ self.transformer = not conv
908
+ self.downsample = None if not downsample else Downsample(dim=dim, shuffle=downsample_shuffle)
909
+
910
+
911
+ def forward(self, x):
912
+ B, C, H, W = x.shape
913
+
914
+ # do padding for transforemr
915
+ interpolate = True
916
+ if self.transformer and interpolate:
917
+ # Windowed Attention will split feature map into windows with the size of window_size x window_size
918
+ # if the resolution is not divisible by window_size, we need to interpolate the feature map
919
+ # can be done via padding, but doing so after training hurts the model performance.
920
+ # interpolation affects the performance as well, but not as much as padding
921
+ if isinstance(self.window_size, list) or isinstance(self.window_size, tuple):
922
+ current_max_window_size = max(self.window_size)
923
+ else:
924
+ current_max_window_size = self.window_size
925
+
926
+ max_window_size = max([res_upsample*current_max_window_size for res_upsample in self.sr_ratio])
927
+ if H % max_window_size != 0 or W % max_window_size != 0:
928
+ new_h = int(np.ceil(H/max_window_size)*max_window_size)
929
+ new_w = int(np.ceil(W/max_window_size)*max_window_size)
930
+ x = F.interpolate(x, size=(new_h, new_w), mode='nearest')
931
+ if self.verbose:
932
+ 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}.")
933
+
934
+
935
+ if self.transformer and self.do_single_windowing:
936
+ H, W = x.shape[2], x.shape[3]
937
+ x, pad_hw = window_partition(x, self.window_size)
938
+
939
+ #run main blocks
940
+ x = self.blocks(x)
941
+
942
+ if self.transformer and self.do_single_windowing:
943
+ x = window_reverse(x, self.window_size, H, W, pad_hw)
944
+
945
+ if self.transformer and interpolate:
946
+ #lets keep original resolution, might be not ideal, but for the upsampling tower we need to keep the expected resolution.
947
+ x = F.interpolate(x, size=(H, W), mode='nearest')
948
+
949
+ if self.downsample is None:
950
+ return x, x
951
+
952
+ return self.downsample(x), x # changing to output pre downsampled features
953
+
954
+
955
+ class InterpolateLayer(nn.Module):
956
+ def __init__(self, size=None, scale_factor=None, mode='nearest'):
957
+ super(InterpolateLayer, self).__init__()
958
+ self.size = size
959
+ self.scale_factor = scale_factor
960
+ self.mode = mode
961
+
962
+ def forward(self, x):
963
+ return F.interpolate(x, size=self.size, scale_factor=self.scale_factor, mode=self.mode)
964
+
965
+
966
+ class HiResNeck(nn.Module):
967
+ """
968
+ The block is used to output dense features from all stages
969
+ Otherwise, by default, only the last stage features are returned with E-RADIO
970
+ """
971
+ def __init__(self, dim, depths, neck_start_stage, full_features_head_dim, downsample_enabled):
972
+
973
+ '''
974
+ Hi Resolution neck to support output of high res features that are useful for dense tasks.
975
+ depths - total number of layers in the base model
976
+ neck_start_stage - when to start the neck, 0 - start from the first stage, 1 - start from the second stage etc.
977
+ earlier layers result in higher resolution features at the cost of compute
978
+ full_features_head_dim - number of channels in the dense features head
979
+ '''
980
+ super().__init__()
981
+ # create feature projection layers for segmentation output
982
+ self.neck_features_proj = nn.ModuleList()
983
+ self.neck_start_stage = neck_start_stage
984
+ upsample_ratio = 1
985
+ for i in range(len(depths)):
986
+ level_n_features_output = int(dim * 2 ** i)
987
+
988
+ if self.neck_start_stage > i: continue
989
+
990
+ if (upsample_ratio > 1) or full_features_head_dim!=level_n_features_output:
991
+ feature_projection = nn.Sequential()
992
+ if False:
993
+ feature_projection.add_module("norm",nn.BatchNorm2d(level_n_features_output)) #fast, but worse
994
+ feature_projection.add_module("dconv", nn.ConvTranspose2d(level_n_features_output,
995
+ full_features_head_dim, kernel_size=upsample_ratio, stride=upsample_ratio))
996
+ else:
997
+ # B, in_channels, H, W -> B, in_channels, H*upsample_ratio, W*upsample_ratio
998
+ # print("upsample ratio", upsample_ratio, level_n_features_output, level_n_features_output)
999
+ feature_projection.add_module("upsample", InterpolateLayer(scale_factor=upsample_ratio, mode='nearest'))
1000
+ 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))
1001
+ feature_projection.add_module("norm",nn.BatchNorm2d(level_n_features_output))
1002
+ # B, in_channels, H*upsample_ratio, W*upsample_ratio -> B, full_features_head_dim, H*upsample_ratio, W*upsample_ratio
1003
+ feature_projection.add_module("conv2", nn.Conv2d(level_n_features_output, full_features_head_dim, kernel_size=1, stride=1, padding=0))
1004
+ else:
1005
+ feature_projection = nn.Sequential()
1006
+
1007
+ self.neck_features_proj.append(feature_projection)
1008
+
1009
+ if i>0 and downsample_enabled[i]:
1010
+ upsample_ratio *= 2
1011
+
1012
+ def forward(self, x, il_level=-1, full_features=None):
1013
+ if self.neck_start_stage > il_level:
1014
+ return full_features
1015
+
1016
+ if full_features is None:
1017
+ full_features = self.neck_features_proj[il_level - self.neck_start_stage](x)
1018
+ else:
1019
+ #upsample torch tensor x to match full_features size, and add to full_features
1020
+ feature_projection = self.neck_features_proj[il_level - self.neck_start_stage](x)
1021
+ if feature_projection.shape[2] != full_features.shape[2] or feature_projection.shape[3] != full_features.shape[3]:
1022
+ 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]))
1023
+ full_features = full_features + feature_projection
1024
+ return full_features
1025
+
1026
+ class ERADIO(nn.Module):
1027
+ """
1028
+ Efficient RADIO
1029
+ """
1030
+
1031
+ def __init__(self,
1032
+ dim,
1033
+ in_dim,
1034
+ depths,
1035
+ window_size,
1036
+ mlp_ratio,
1037
+ num_heads,
1038
+ drop_path_rate=0.2,
1039
+ in_chans=3,
1040
+ num_classes=1000,
1041
+ qkv_bias=False,
1042
+ qk_scale=None,
1043
+ layer_scale=None,
1044
+ layer_scale_conv=None,
1045
+ layer_norm_last=False,
1046
+ sr_ratio = [1, 1, 1, 1],
1047
+ max_depth = -1,
1048
+ conv_base=False,
1049
+ use_swiglu=False,
1050
+ multi_query=False,
1051
+ norm_layer=nn.LayerNorm,
1052
+ drop_uniform=False,
1053
+ yolo_arch=False,
1054
+ shuffle_down=False,
1055
+ downsample_shuffle=False,
1056
+ return_full_features=False,
1057
+ full_features_head_dim=128,
1058
+ neck_start_stage=1,
1059
+ use_neck=False,
1060
+ use_shift=False,
1061
+ cpb_mlp_hidden=512,
1062
+ conv_groups_ratio=0,
1063
+ verbose: bool = False,
1064
+ **kwargs):
1065
+ """
1066
+ Args:
1067
+ dim: feature size dimension.
1068
+ depths: number of layers in each stage.
1069
+ window_size: window size in each stage.
1070
+ mlp_ratio: MLP ratio.
1071
+ num_heads: number of heads in each stage.
1072
+ drop_path_rate: drop path rate.
1073
+ in_chans: number of input channels.
1074
+ num_classes: number of classes.
1075
+ qkv_bias: bool argument for query, key, value learnable bias.
1076
+ qk_scale: bool argument to scaling query, key.
1077
+ drop_rate: dropout rate.
1078
+ attn_drop_rate: attention dropout rate.
1079
+ norm_layer: normalization layer.
1080
+ layer_scale: layer scaling coefficient.
1081
+ return_full_features: output dense features as well as logits
1082
+ full_features_head_dim: number of channels in the dense features head
1083
+ neck_start_stage: a stage id to start full feature neck. Model has 4 stages, indix starts with 0
1084
+ for 224 resolution, the output of the stage before downsample:
1085
+ stage 0: 56x56, stage 1: 28x28, stage 2: 14x14, stage 3: 7x7
1086
+ use_neck: even for summarization embedding use neck
1087
+ use_shift: SWIN like window shifting but without masking attention
1088
+ conv_groups_ratio: will be used for conv blocks where there is no multires attention,
1089
+ if 0 then normal conv,
1090
+ if 1 then channels are independent,
1091
+ if -1 then no conv at all
1092
+
1093
+ """
1094
+ super().__init__()
1095
+
1096
+ num_features = int(dim * 2 ** (len(depths) - 1))
1097
+ self.num_classes = num_classes
1098
+ self.patch_embed = PatchEmbed(in_chans=in_chans, in_dim=in_dim, dim=dim, shuffle_down=shuffle_down)
1099
+ # set return_full_features true if we want to return full features from all stages
1100
+ self.return_full_features = return_full_features
1101
+ self.use_neck = use_neck
1102
+
1103
+ dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))]
1104
+ if drop_uniform:
1105
+ dpr = [drop_path_rate for x in range(sum(depths))]
1106
+
1107
+ if not isinstance(max_depth, list): max_depth = [max_depth] * len(depths)
1108
+
1109
+ self.levels = nn.ModuleList()
1110
+ for i in range(len(depths)):
1111
+ conv = True if (i == 0 or i == 1) else False
1112
+
1113
+ level = ERADIOLayer(dim=int(dim * 2 ** i),
1114
+ depth=depths[i],
1115
+ num_heads=num_heads[i],
1116
+ window_size=window_size[i],
1117
+ mlp_ratio=mlp_ratio,
1118
+ qkv_bias=qkv_bias,
1119
+ qk_scale=qk_scale,
1120
+ conv=conv,
1121
+ drop_path=dpr[sum(depths[:i]):sum(depths[:i + 1])],
1122
+ downsample=(i < len(depths) - 1),
1123
+ layer_scale=layer_scale,
1124
+ layer_scale_conv=layer_scale_conv,
1125
+ sr_ratio=sr_ratio[i],
1126
+ use_swiglu=use_swiglu,
1127
+ multi_query=multi_query,
1128
+ norm_layer=norm_layer,
1129
+ yolo_arch=yolo_arch,
1130
+ downsample_shuffle=downsample_shuffle,
1131
+ conv_base=conv_base,
1132
+ cpb_mlp_hidden=cpb_mlp_hidden,
1133
+ use_shift=use_shift,
1134
+ conv_groups_ratio=conv_groups_ratio,
1135
+ verbose=verbose)
1136
+
1137
+ self.levels.append(level)
1138
+
1139
+ if self.return_full_features or self.use_neck:
1140
+ #num_heads
1141
+ downsample_enabled = [self.levels[i-1].downsample is not None for i in range(len(self.levels))]
1142
+ self.high_res_neck = HiResNeck(dim, depths, neck_start_stage, full_features_head_dim, downsample_enabled)
1143
+
1144
+ self.switched_to_deploy = False
1145
+
1146
+ self.norm = LayerNorm2d(num_features) if layer_norm_last else nn.BatchNorm2d(num_features)
1147
+ self.avgpool = nn.AdaptiveAvgPool2d(1)
1148
+ self.head = nn.Linear(num_features, num_classes) if num_classes > 0 else nn.Identity()
1149
+ self.apply(self._init_weights)
1150
+
1151
+ def _init_weights(self, m):
1152
+ if isinstance(m, nn.Linear):
1153
+ trunc_normal_(m.weight, std=.02)
1154
+ if isinstance(m, nn.Linear) and m.bias is not None:
1155
+ nn.init.constant_(m.bias, 0)
1156
+ elif isinstance(m, nn.LayerNorm):
1157
+ nn.init.constant_(m.bias, 0)
1158
+ nn.init.constant_(m.weight, 1.0)
1159
+ elif isinstance(m, LayerNorm2d):
1160
+ nn.init.constant_(m.bias, 0)
1161
+ nn.init.constant_(m.weight, 1.0)
1162
+ elif isinstance(m, nn.BatchNorm2d):
1163
+ nn.init.ones_(m.weight)
1164
+ nn.init.zeros_(m.bias)
1165
+
1166
+ @torch.jit.ignore
1167
+ def no_weight_decay_keywords(self):
1168
+ return {'rpb'}
1169
+
1170
+ def forward_features(self, x):
1171
+ _, _, H, W = x.shape
1172
+ if H % 32 != 0 or W % 32 != 0:
1173
+ raise ValueError(f"E-RADIO requires input dimensions to be divisible by 32 but got H x W: {H} x {W}")
1174
+ x = self.patch_embed(x)
1175
+ full_features = None
1176
+ for il, level in enumerate(self.levels):
1177
+ x, pre_downsample_x = level(x)
1178
+
1179
+ if self.return_full_features or self.use_neck:
1180
+ full_features = self.high_res_neck(pre_downsample_x, il, full_features)
1181
+
1182
+ # x = self.norm(full_features if (self.return_full_features or self.use_neck) else x)
1183
+ x = self.norm(x) # new version for
1184
+
1185
+ if not self.return_full_features:
1186
+ return x, None
1187
+
1188
+ return x, full_features
1189
+
1190
+ def forward(self, x):
1191
+ x, full_features = self.forward_features(x)
1192
+
1193
+ x = self.avgpool(x)
1194
+ x = torch.flatten(x, 1)
1195
+
1196
+ x = self.head(x)
1197
+ if full_features is not None:
1198
+ return x, full_features
1199
+ return x
1200
+
1201
+ def switch_to_deploy(self):
1202
+ '''
1203
+ A method to perform model self-compression
1204
+ merges BN into conv layers
1205
+ converts MLP relative positional bias into precomputed buffers
1206
+ '''
1207
+ if not self.switched_to_deploy:
1208
+ for level in [self.patch_embed, self.levels, self.head]:
1209
+ for module in level.modules():
1210
+ if hasattr(module, 'switch_to_deploy'):
1211
+ module.switch_to_deploy()
1212
+ self.switched_to_deploy = True
1213
+
1214
+
1215
+ def change_window_size(self, new_window_size):
1216
+ """
1217
+ E-RADIO employs windowed attention, which may be sensitive to the choice of this parameter,
1218
+ especially in cases of uneven partitioning of the feature maps.
1219
+ E-RADIO allows for the adjustment of the window size after training,
1220
+ making it adaptable to different input image resolutions.
1221
+ The recommended values for window size based on input resolution are as follows:
1222
+
1223
+ Input Resolution | Window Size
1224
+ 224 | 7
1225
+ 256 | 8
1226
+ 386 | 12
1227
+ 512 | 16
1228
+ 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
1229
+ img_res/16/2
1230
+ 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.
1231
+ Manual way to change resolution -> model.change_window_size(resolution)
1232
+ """
1233
+ window_size = new_window_size
1234
+ print(f"Setting window size to {window_size}")
1235
+ for module in self.modules():
1236
+ if hasattr(module, "window_size"):
1237
+ # check if tuple or a number
1238
+ if isinstance(module.window_size, tuple):
1239
+ if module.window_size[0] != window_size:
1240
+ module.window_size = (window_size, window_size)
1241
+ elif isinstance(module.window_size, list):
1242
+ if module.window_size[0] != window_size:
1243
+ module.window_size = [window_size, window_size]
1244
+ else:
1245
+ module.window_size = window_size
1246
+
1247
+
1248
+ def set_optimal_window_size(self, image_dim, max_window_size = 16):
1249
+ """
1250
+ Using hand picked window size for various resolutions.
1251
+
1252
+ E-RADIO employs windowed attention, which may be sensitive to the choice of this parameter,
1253
+ especially in cases of uneven partitioning of the feature maps.
1254
+ E-RADIO allows for the adjustment of the window size after training,
1255
+ making it adaptable to different input image resolutions.
1256
+ The recommended values for window size based on input resolution are as follows:
1257
+
1258
+ Input Resolution | Window Size
1259
+ 224 | 7
1260
+ 256 | 8
1261
+ 386 | 12
1262
+ 512 | 16
1263
+ 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
1264
+ img_res/16/2
1265
+ 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.
1266
+ Manual way to change resolution -> model.change_window_size(resolution)
1267
+
1268
+ """
1269
+ # import math
1270
+
1271
+ def divisorGenerator(n):
1272
+ large_divisors = []
1273
+ for i in range(1, int(math.sqrt(n) + 1)):
1274
+ if n % i == 0:
1275
+ yield i
1276
+ if i*i != n:
1277
+ large_divisors.append(n / i)
1278
+ for divisor in reversed(large_divisors):
1279
+ yield divisor
1280
+
1281
+ if isinstance(image_dim, list) or isinstance(image_dim, tuple):
1282
+ image_dim = min(image_dim)
1283
+
1284
+ # we do windowed attention in the 3rd stage for the first time, therefore //16,
1285
+ # we do subsampled attention with downsample by 2 so need to get //32 actually
1286
+ # ideally we should rewrite this to be dependent on the structure of the model like what if subsampled is removed etc
1287
+ all_divisors = np.array(list(divisorGenerator(image_dim//32)))
1288
+ new_window_size = int(min(all_divisors[all_divisors <= max_window_size][-1], max_window_size))
1289
+
1290
+ # for image_dim in [128, 224, 256, 384, 512, 768, 1024]:
1291
+ # all_divisors = np.array(list(divisorGenerator(image_dim//32)))
1292
+ # new_window_size = int(min(all_divisors[all_divisors <= max_window_size][-1], max_window_size))
1293
+ # print(f"Setting window size to {new_window_size} for image resolution {image_dim}")
1294
+
1295
+ self.change_window_size(new_window_size = new_window_size)
1296
+
1297
+
1298
+ @register_model
1299
+ def eradio_large_fullres_ws16(pretrained=False, **kwargs):
1300
+ model = ERADIO(
1301
+ depths=[3, 3, 5, 5],
1302
+ num_heads=[2, 4, 8, 16],
1303
+ window_size=[None, None, [16, 16], 16],
1304
+ dim=192,
1305
+ in_dim=64,
1306
+ mlp_ratio=4,
1307
+ drop_path_rate=0.0,
1308
+ sr_ratio=[1, 1, [2, 1], 1],
1309
+ use_swiglu=False,
1310
+ yolo_arch=True,
1311
+ shuffle_down=False,
1312
+ conv_base=True,
1313
+ use_neck=True,
1314
+ full_features_head_dim=1536,
1315
+ neck_start_stage=2,
1316
+ **kwargs,
1317
+ )
1318
+ if pretrained:
1319
+ model.load_state_dict(torch.load(pretrained)["state_dict"])
1320
+ return model
1321
+
1322
+
1323
+ @register_model
1324
+ def eradio_xxxtiny(pretrained=False, **kwargs): # ,
1325
+ model = ERADIO(
1326
+ depths=[1, 3, 4, 5],
1327
+ num_heads=[2, 4, 8, 16],
1328
+ window_size=[None, None, [16, 16], 16],
1329
+ dim=32,
1330
+ in_dim=32,
1331
+ mlp_ratio=4,
1332
+ drop_path_rate=0.0,
1333
+ sr_ratio=[1, 1, [2, 1], 1],
1334
+ use_swiglu=False,
1335
+ yolo_arch=True,
1336
+ shuffle_down=False,
1337
+ conv_base=True,
1338
+ use_neck=True,
1339
+ full_features_head_dim=256,
1340
+ neck_start_stage=2,
1341
+ **kwargs,
1342
+ )
1343
+ if pretrained:
1344
+ model.load_state_dict(torch.load(pretrained))
1345
+ return model
1346
+
1347
+ @register_model
1348
+ def eradio_xxxtiny_8x_ws12(pretrained=False, **kwargs):
1349
+ model = ERADIO(depths=[1, 3, 4, 5],
1350
+ num_heads=[2, 4, 8, 16],
1351
+ window_size=[None, None, [12, 12], 12],
1352
+ dim=32,
1353
+ in_dim=32,
1354
+ mlp_ratio=4,
1355
+ drop_path_rate=0.0,
1356
+ sr_ratio=[1, 1, [2, 1], 1],
1357
+ use_swiglu=False,
1358
+ downsample_shuffle=False,
1359
+ yolo_arch=True,
1360
+ shuffle_down=False,
1361
+ cpb_mlp_hidden=64,
1362
+ use_neck=True,
1363
+ full_features_head_dim=256,
1364
+ neck_start_stage=2,
1365
+ conv_groups_ratio = 1,
1366
+ **kwargs)
1367
+ if pretrained:
1368
+ model.load_state_dict(torch.load(pretrained)["state_dict"])
1369
+ return model
1370
+
1371
+
1372
+ @register_model
1373
+ def eradio_xxxtiny_8x_ws16(pretrained=False, **kwargs):
1374
+ model = ERADIO(depths=[1, 3, 4, 5],
1375
+ num_heads=[2, 4, 8, 16],
1376
+ window_size=[None, None, [16, 16], 16],
1377
+ dim=32,
1378
+ in_dim=32,
1379
+ mlp_ratio=4,
1380
+ drop_path_rate=0.0,
1381
+ sr_ratio=[1, 1, [2, 1], 1],
1382
+ use_swiglu=False,
1383
+ downsample_shuffle=False,
1384
+ yolo_arch=True,
1385
+ shuffle_down=False,
1386
+ cpb_mlp_hidden=64,
1387
+ use_neck=True,
1388
+ full_features_head_dim=256,
1389
+ neck_start_stage=1,
1390
+ conv_groups_ratio = 1,
1391
+ **kwargs)
1392
+ if pretrained:
1393
+ model.load_state_dict(torch.load(pretrained)["state_dict"])
1394
+ return model
1395
+
1396
+ @register_model
1397
+ def eradio(pretrained=False, **kwargs):
1398
+ return eradio_large_fullres_ws16(pretrained=pretrained, **kwargs)
extra_models.py ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ try:
11
+ from timm.models import register_model
12
+ except ImportError:
13
+ from timm.models.registry import register_model
14
+ from timm.data.constants import IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD
15
+
16
+ from .forward_intermediates import forward_intermediates
17
+ from .input_conditioner import InputConditioner
18
+
19
+ _has_torch_sdpa = hasattr(F, 'scaled_dot_product_attention')
20
+
21
+
22
+ class PaliGemmaWrapper(nn.Module):
23
+ def __init__(self, vis_model: nn.Module, embed_dim: int):
24
+ super().__init__()
25
+
26
+ self.vis_model = vis_model
27
+ self.embed_dim = embed_dim
28
+
29
+ @property
30
+ def patch_size(self):
31
+ return self.vis_model.embeddings.patch_size
32
+
33
+ @property
34
+ def blocks(self):
35
+ return self.vis_model.encoder.layers
36
+
37
+ @property
38
+ def embed_dim(self):
39
+ return self.vis_model.embeddings.embed_dim
40
+
41
+ def forward(self, x: torch.Tensor):
42
+ outputs = self.vis_model(
43
+ x,
44
+ return_dict=False,
45
+ interpolate_pos_encoding=True,
46
+ )
47
+
48
+ features = outputs[0].to(torch.float32)
49
+
50
+ summary = features.mean(dim=1)
51
+
52
+ return summary, features
53
+
54
+ def forward_features(self, x: torch.Tensor):
55
+ return self(x)
56
+
57
+
58
+ def _get_paligemma_model(repo: str, embed_dim: int = None, dtype: torch.dtype = torch.bfloat16):
59
+ from transformers import PaliGemmaForConditionalGeneration, __version__ as tx_version
60
+
61
+ if LooseVersion(tx_version) > LooseVersion('4.44.2'):
62
+ warnings.warn(f'Your transformers version "{tx_version}" is higher than 4.44.2, and for whatever reason, PaliGemma might be broken.')
63
+
64
+ extra_args = dict()
65
+
66
+ if dtype is not None:
67
+ extra_args['torch_dtype'] = dtype
68
+ rev = str(dtype).split('.')[-1]
69
+ extra_args['revision'] = rev
70
+
71
+ model = PaliGemmaForConditionalGeneration.from_pretrained(repo, **extra_args)
72
+
73
+ vis_model = model.vision_tower.vision_model
74
+
75
+ vis_model = PaliGemmaWrapper(vis_model, embed_dim)
76
+
77
+ return vis_model
78
+
79
+ @register_model
80
+ def paligemma_896_student(**kwargs):
81
+ model = _get_paligemma_model('google/paligemma-3b-pt-896', embed_dim=1152, dtype=None)
82
+
83
+ return model
84
+
85
+
86
+ def dv2_sdpa(self, x: torch.Tensor) -> torch.Tensor:
87
+ B, N, C = x.shape
88
+ qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
89
+
90
+ q, k, v = qkv[0], qkv[1], qkv[2]
91
+ x = F.scaled_dot_product_attention(
92
+ q, k, v,
93
+ is_causal=False,
94
+ dropout_p=self.attn_drop.p if self.training else 0.,
95
+ scale=self.scale,
96
+ )
97
+ x = x.transpose(1, 2).reshape(B, N, C)
98
+ x = self.proj(x)
99
+ x = self.proj_drop(x)
100
+ return x
101
+
102
+ def _load_dino_v2(dino_v2_model, cache_dir: Optional[str] = None, pretrained=True, **kwargs):
103
+ if cache_dir:
104
+ torch.hub.set_dir(cache_dir)
105
+ model: nn.Module = torch.hub.load(
106
+ 'facebookresearch/dinov2',
107
+ dino_v2_model,
108
+ pretrained=pretrained,
109
+ # **kwargs,
110
+ )
111
+
112
+ if _has_torch_sdpa:
113
+ for n, m in model.named_modules():
114
+ if n.endswith('.attn'):
115
+ m.forward = MethodType(dv2_sdpa, m)
116
+
117
+ return model
118
+
119
+ class DinoWrapper(nn.Module):
120
+ def __init__(self, dino_model: nn.Module):
121
+ super().__init__()
122
+
123
+ self.inner = dino_model
124
+ dino_model.blocks = nn.Sequential(*dino_model.blocks)
125
+
126
+ @property
127
+ def embed_dim(self):
128
+ return self.inner.embed_dim
129
+
130
+ @property
131
+ def patch_size(self):
132
+ return self.inner.patch_size
133
+
134
+ @property
135
+ def num_cls_tokens(self):
136
+ return getattr(self.inner, 'num_tokens', 1)
137
+
138
+ @property
139
+ def num_registers(self):
140
+ return getattr(self.inner, 'num_register_tokens', 0)
141
+
142
+ @property
143
+ def num_summary_tokens(self):
144
+ return self.num_cls_tokens + self.num_registers
145
+
146
+ @property
147
+ def blocks(self):
148
+ return self.inner.blocks
149
+
150
+ def forward(self, *args, **kwargs) -> Tuple[torch.Tensor, torch.Tensor]:
151
+ parts = self.inner.forward_features(*args, **kwargs)
152
+
153
+ cls_token = parts['x_norm_clstoken']
154
+ features = parts['x_norm_patchtokens']
155
+
156
+ return cls_token, features
157
+
158
+ def forward_features(self, x: torch.Tensor):
159
+ x = self.inner.prepare_tokens_with_masks(x)
160
+ x = self.inner.blocks(x)
161
+ x_norm = self.inner.norm(x)
162
+
163
+ return x_norm[:, 0], x_norm[:, self.num_summary_tokens:]
164
+
165
+ def patchify(self, x: torch.Tensor) -> torch.Tensor:
166
+ return self.inner.prepare_tokens_with_masks(x)
167
+
168
+ def forward_intermediates(self,
169
+ x: torch.Tensor,
170
+ norm: bool = False,
171
+ **kwargs,
172
+ ) -> Union[List[torch.Tensor], Tuple[torch.Tensor, List[torch.Tensor]]]:
173
+ return forward_intermediates(
174
+ self,
175
+ patch_extractor=self.inner.prepare_tokens_with_masks,
176
+ num_summary_tokens=self.num_summary_tokens,
177
+ num_cls_tokens=self.num_cls_tokens,
178
+ norm=self.inner.norm if norm else lambda y: y,
179
+ x=x,
180
+ **kwargs,
181
+ )
182
+
183
+
184
+ def _dino_student(arch: str, **kwargs):
185
+ from . import dinov2_arch
186
+
187
+ factory = getattr(dinov2_arch, arch)
188
+ model = factory()
189
+
190
+ model = DinoWrapper(model)
191
+
192
+ conditioner = InputConditioner(
193
+ input_scale=1.0,
194
+ norm_mean=IMAGENET_DEFAULT_MEAN,
195
+ norm_std=IMAGENET_DEFAULT_STD,
196
+ )
197
+
198
+ model.input_conditioner = conditioner
199
+
200
+ return model
201
+
202
+
203
+ @register_model
204
+ def dino_v2_l_student(**kwargs):
205
+ return _dino_student('dinov2_vitl14_reg', **kwargs)
206
+
207
+ @register_model
208
+ def dino_v2_g_student(**kwargs):
209
+ return _dino_student('dinov2_vitg14_reg', **kwargs)
extra_timm_models.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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, PretrainedCfg
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=False, **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=False, **dict(model_args, **kwargs))
82
+
83
+ return model
84
+
85
+
86
+ @register_model
87
+ def vit_so400m_patch16_224(pretrained=False, **kwargs) -> VisionTransformer:
88
+ """ ViT model matching the architecture of the So400M model from
89
+ "Scaling Vision Transformers to 400 Million Parameters" (https://arxiv.org/abs/2302.05442).
90
+ """
91
+ if pretrained:
92
+ raise ValueError('There is no pretrained weights for vit_so400m_patch16_224')
93
+ mlp_ratio = 4304 / 1152
94
+
95
+ model_args = dict(patch_size=16, embed_dim=1152, depth=27, num_heads=16, mlp_ratio=mlp_ratio)
96
+ model = _create_vision_transformer('vit_so400m_patch16_224', pretrained=pretrained, **dict(model_args, **kwargs))
97
+ return model
98
+
99
+
100
+ @register_model
101
+ def vit_so400m_v2_patch16_224(pretrained=False, **kwargs) -> VisionTransformer:
102
+ """ ViT model matching the architecture of the So400M model from
103
+ "Scaling Vision Transformers to 400 Million Parameters" (https://arxiv.org/abs/2302.05442).
104
+ """
105
+ if pretrained:
106
+ raise ValueError('There is no pretrained weights for vit_so400m_patch16_224')
107
+
108
+ normal_target = 4304
109
+ # TP4 requires channels to be a multiple of 4, and then within that, FP8 requires a multiple of 8,
110
+ # thus, a multiple of 32 is required.
111
+ tp4_fp8_safe_target = ((normal_target + 31) // 32) * 32
112
+
113
+ mlp_ratio = tp4_fp8_safe_target / 1152
114
+
115
+ model_args = dict(patch_size=16, embed_dim=1152, depth=27, num_heads=16, mlp_ratio=mlp_ratio)
116
+ model = _create_vision_transformer('vit_so400m_v2_patch16_224', pretrained=pretrained, **dict(model_args, **kwargs))
117
+ return model
118
+
119
+
120
+ @register_model
121
+ def vit_huge_patch16_224(pretrained=False, **kwargs) -> VisionTransformer:
122
+ """ ViT-Huge model (ViT-H/16) from original paper (https://arxiv.org/abs/2010.11929).
123
+ """
124
+ model_args = dict(patch_size=16, embed_dim=1280, depth=32, num_heads=16)
125
+ if pretrained:
126
+ # There is no pretrained version of ViT-H/16, but we can adapt a ViT-H/14 for this purpose
127
+ model = _create_vision_transformer('vit_huge_patch14_224', pretrained=True, **dict(model_args, **kwargs))
128
+ else:
129
+ model = _create_vision_transformer('vit_huge_patch16_224', pretrained=False, **dict(model_args, **kwargs))
130
+ return model
131
+
132
+
133
+ @register_model
134
+ def vit_huge_patch16_224_mlpnorm(pretrained=False, **kwargs) -> VisionTransformer:
135
+ """ ViT-Huge model (ViT-H/16) from original paper (https://arxiv.org/abs/2010.11929).
136
+ """
137
+ model = vit_huge_patch16_224(pretrained=pretrained, **kwargs)
138
+
139
+ for m in model.modules():
140
+ if isinstance(m, Mlp) and not isinstance(m.norm, nn.LayerNorm):
141
+ m.norm = nn.LayerNorm(m.fc1.out_features)
142
+
143
+ return model
144
+
145
+
146
+ @register_model
147
+ def vit_giant_patch16_224(pretrained=False, scaled_ln: bool = False, **kwargs) -> VisionTransformer:
148
+ """ ViT-giant model (ViT-g/16) from original paper (https://arxiv.org/abs/2010.11929).
149
+ """
150
+ model_args = dict(patch_size=16, embed_dim=1536, depth=40, num_heads=24)
151
+ model = _create_vision_transformer('vit_giant_patch16_224', pretrained=False, **dict(model_args, **kwargs))
152
+ if scaled_ln:
153
+ _apply_scaled_ln(model)
154
+ return model
155
+
156
+
157
+ @register_model
158
+ def vit_bigG_patch14_224(pretrained=False, **kwargs) -> VisionTransformer:
159
+ model_args = dict(patch_size=14, embed_dim=1664, depth=48, num_heads=16, init_values=1e-6)
160
+ model = _create_vision_transformer('vit_bigG_patch14', pretrained=False, **dict(model_args, **kwargs))
161
+ return model
162
+
163
+
164
+ def _create_vision_transformer(*args, **kwargs):
165
+ if kwargs.get('pretrained_cfg', None) is None:
166
+ # This prevents the warning from being emitted
167
+ kwargs['pretrained_cfg'] = PretrainedCfg()
168
+
169
+ model = _timm_create_vision_transformer(*args, **kwargs)
170
+ _patch_layer_scale(model)
171
+ return model
172
+
173
+
174
+ def _patch_layer_scale(model: VisionTransformer):
175
+ def replace_ls(old_ls: TIMMLayerScale):
176
+ new_ls = dinov2_arch.LayerScale(old_ls.gamma.shape[0], inplace=old_ls.inplace)
177
+ new_ls.load_state_dict(old_ls.state_dict())
178
+ return new_ls
179
+
180
+ # Monkey patch: Replace TIMM's LayerScale with our modified DINOv2 one, that uses a param name
181
+ # other than gamma, so that HFHub doesn't mess with it!
182
+ for mod in model.modules():
183
+ if isinstance(mod, Block):
184
+ if isinstance(mod.ls1, TIMMLayerScale):
185
+ mod.ls1 = replace_ls(mod.ls1)
186
+ if isinstance(mod.ls2, TIMMLayerScale):
187
+ mod.ls2 = replace_ls(mod.ls2)
188
+ pass
189
+
190
+
191
+ class ScaledLayerNorm(nn.LayerNorm):
192
+ '''
193
+ https://arxiv.org/pdf/2502.05795v1
194
+ '''
195
+ def __init__(self, ln_base: nn.LayerNorm, depth: int = 0):
196
+ super().__init__(ln_base.normalized_shape, eps=ln_base.eps, elementwise_affine=ln_base.elementwise_affine)
197
+ self.load_state_dict(ln_base.state_dict())
198
+ self.register_buffer('ln_scale', torch.tensor(1.0 / math.sqrt(depth)), persistent=False)
199
+
200
+ def forward(self, x):
201
+ y = super().forward(x)
202
+ y = y * self.ln_scale
203
+ return y
204
+
205
+
206
+ class DyT(nn.Module):
207
+ def __init__(self, C: int, init_alpha: float):
208
+ super().__init__()
209
+ self.alpha = nn.Parameter(torch.full((1,), init_alpha))
210
+ self.gamma = nn.Parameter(torch.ones(C))
211
+ self.beta = nn.Parameter(torch.zeros(C))
212
+
213
+ def forward(self, x: torch.Tensor):
214
+ x = F.tanh(self.alpha * x)
215
+ return self.gamma * x + self.beta
216
+
217
+ @register_model
218
+ def vit_large_dyt_patch16_224(pretrained: bool = False, **kwargs) -> VisionTransformer:
219
+ """ ViT-Large model (ViT-L/16) from original paper (https://arxiv.org/abs/2010.11929).
220
+ ImageNet-1k weights fine-tuned from in21k @ 224x224, source https://github.com/google-research/vision_transformer.
221
+ """
222
+ model_args = dict(patch_size=16, embed_dim=1024, depth=24, num_heads=16)
223
+ model = _create_vision_transformer('vit_large_dyt_patch16_224', pretrained=pretrained, **dict(model_args, **kwargs))
224
+
225
+ def _replace_ln_with_dyt(ln: nn.LayerNorm, depth: int):
226
+ return DyT(ln.normalized_shape[0], init_alpha=0.9)
227
+ _replace_ln(model, _replace_ln_with_dyt)
228
+
229
+ return model
230
+
231
+
232
+ def _apply_scaled_ln(model: VisionTransformer):
233
+ warnings.warn('Post-LayerNorm scaling activated!')
234
+
235
+ _replace_ln(model, lambda ln, depth: ScaledLayerNorm(ln, depth=depth))
236
+
237
+ def _replace_ln(model: VisionTransformer, fn):
238
+ def _inner_replace_ln(block: Block, depth: int, key: str):
239
+ prev = getattr(block, key)
240
+ if isinstance(prev, nn.LayerNorm):
241
+ setattr(block, key, fn(prev, depth=depth))
242
+
243
+ for i, block in enumerate(model.blocks):
244
+ _inner_replace_ln(block, i + 1, 'norm1')
245
+ _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,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_module_factory import create_mlp_from_config
29
+ from .adaptor_mlp import MLP, MLP2
30
+ from .adaptor_attn import AttnFDHead
31
+ from .adaptor_registry import adaptor_registry
32
+ from .cls_token import ClsToken
33
+ from .dinov2_arch import dinov2_vitg14_reg
34
+ from .enable_cpe_support import enable_cpe
35
+ from .enable_damp import configure_damp_from_args
36
+ from .enable_spectral_reparam import configure_spectral_reparam_from_args
37
+ from .eradio_model import eradio
38
+ from .feature_normalizer import FeatureNormalizer, IntermediateFeatureNormalizer
39
+ from .forward_intermediates import forward_intermediates
40
+ from .radio_model import create_model_from_args
41
+ from .radio_model import RADIOModel as RADIOModelBase, Resolution
42
+ from .input_conditioner import get_default_conditioner, InputConditioner
43
+ from .open_clip_adaptor import OpenCLIP_RADIO
44
+ from .siglip2_adaptor import SigLIP2Adaptor
45
+ from .vit_patch_generator import ViTPatchGenerator
46
+ from .vitdet import apply_vitdet_arch, VitDetArgs
47
+ from .radio1d import RADIO1D
48
+
49
+ # Register extra models
50
+ from .extra_timm_models import *
51
+ from .extra_models import *
52
+
53
+
54
+ class RADIOConfig(PretrainedConfig):
55
+ """Pretrained Hugging Face configuration for RADIO models."""
56
+
57
+ def __init__(
58
+ self,
59
+ args: Optional[dict] = None,
60
+ version: Optional[str] = DEFAULT_VERSION,
61
+ patch_size: Optional[int] = None,
62
+ max_resolution: Optional[int] = None,
63
+ preferred_resolution: Optional[Resolution] = None,
64
+ adaptor_names: Union[str, List[str]] = None,
65
+ adaptor_configs: Dict[str, Dict[str, int]] = None,
66
+ vitdet_window_size: Optional[int] = None,
67
+ feature_normalizer_config: Optional[dict] = None,
68
+ inter_feature_normalizer_config: Optional[dict] = None,
69
+ **kwargs,
70
+ ):
71
+ self.args = args
72
+ for field in ["dtype", "amp_dtype"]:
73
+ if self.args is not None and field in self.args:
74
+ # Convert to a string in order to make it serializable.
75
+ # For example for torch.float32 we will store "float32",
76
+ # for "bfloat16" we will store "bfloat16".
77
+ self.args[field] = str(args[field]).split(".")[-1]
78
+ self.version = version
79
+ resource = RESOURCE_MAP[version]
80
+ self.patch_size = patch_size or resource.patch_size
81
+ self.max_resolution = max_resolution or resource.max_resolution
82
+ self.preferred_resolution = (
83
+ preferred_resolution or resource.preferred_resolution
84
+ )
85
+ self.adaptor_names = adaptor_names
86
+ self.adaptor_configs = adaptor_configs
87
+ self.vitdet_window_size = vitdet_window_size
88
+ self.feature_normalizer_config = feature_normalizer_config
89
+ self.inter_feature_normalizer_config = inter_feature_normalizer_config
90
+ super().__init__(**kwargs)
91
+
92
+
93
+
94
+ class RADIOModel(PreTrainedModel):
95
+ """Pretrained Hugging Face model for RADIO.
96
+
97
+ This class inherits from PreTrainedModel, which provides
98
+ HuggingFace's functionality for loading and saving models.
99
+ """
100
+
101
+ config_class = RADIOConfig
102
+
103
+ def __init__(self, config: RADIOConfig):
104
+ super().__init__(config)
105
+ if hasattr(super(), "post_init"):
106
+ super().post_init()
107
+
108
+ RADIOArgs = namedtuple("RADIOArgs", config.args.keys())
109
+ args = RADIOArgs(**config.args)
110
+ self.config = config
111
+
112
+ model = create_model_from_args(args)
113
+ input_conditioner: InputConditioner = get_default_conditioner()
114
+
115
+ dtype = getattr(args, "dtype", torch.float32)
116
+ if isinstance(dtype, str):
117
+ # Convert the dtype's string representation back to a dtype.
118
+ dtype = getattr(torch, dtype)
119
+ model.to(dtype=dtype)
120
+ input_conditioner.dtype = dtype
121
+
122
+ summary_idxs = torch.tensor(
123
+ [i for i, t in enumerate(args.teachers) if t.get("use_summary", True)],
124
+ dtype=torch.int64,
125
+ )
126
+
127
+ adaptor_configs = config.adaptor_configs
128
+ adaptor_names = config.adaptor_names or []
129
+
130
+ adaptors = dict()
131
+ for adaptor_name in adaptor_names:
132
+ mlp_config = adaptor_configs[adaptor_name]
133
+ adaptor = GenericAdaptor(args, None, None, mlp_config)
134
+ adaptor.head_idx = mlp_config["head_idx"]
135
+ adaptors[adaptor_name] = adaptor
136
+
137
+ feature_normalizer = None
138
+ if config.feature_normalizer_config is not None:
139
+ # Actual normalization values will be restored when loading checkpoint weights.
140
+ feature_normalizer = FeatureNormalizer(config.feature_normalizer_config["embed_dim"])
141
+
142
+ inter_feature_normalizer = None
143
+ if config.inter_feature_normalizer_config is not None:
144
+ inter_feature_normalizer = IntermediateFeatureNormalizer(
145
+ config.inter_feature_normalizer_config["num_intermediates"],
146
+ config.inter_feature_normalizer_config["embed_dim"],
147
+ rot_per_layer=config.inter_feature_normalizer_config["rot_per_layer"],
148
+ dtype=dtype)
149
+
150
+ self.radio_model = RADIOModelBase(
151
+ model,
152
+ input_conditioner,
153
+ summary_idxs=summary_idxs,
154
+ patch_size=config.patch_size,
155
+ max_resolution=config.max_resolution,
156
+ window_size=config.vitdet_window_size,
157
+ preferred_resolution=config.preferred_resolution,
158
+ adaptors=adaptors,
159
+ feature_normalizer=feature_normalizer,
160
+ inter_feature_normalizer=inter_feature_normalizer,
161
+ )
162
+
163
+ @property
164
+ def adaptors(self) -> nn.ModuleDict:
165
+ return self.radio_model.adaptors
166
+
167
+ @property
168
+ def model(self) -> VisionTransformer:
169
+ return self.radio_model.model
170
+
171
+ @property
172
+ def input_conditioner(self) -> InputConditioner:
173
+ return self.radio_model.input_conditioner
174
+
175
+ @property
176
+ def num_summary_tokens(self) -> int:
177
+ return self.radio_model.num_summary_tokens
178
+
179
+ @property
180
+ def patch_size(self) -> int:
181
+ return self.radio_model.patch_size
182
+
183
+ @property
184
+ def max_resolution(self) -> int:
185
+ return self.radio_model.max_resolution
186
+
187
+ @property
188
+ def preferred_resolution(self) -> Resolution:
189
+ return self.radio_model.preferred_resolution
190
+
191
+ @property
192
+ def window_size(self) -> int:
193
+ return self.radio_model.window_size
194
+
195
+ @property
196
+ def min_resolution_step(self) -> int:
197
+ return self.radio_model.min_resolution_step
198
+
199
+ def make_preprocessor_external(self) -> Callable[[torch.Tensor], torch.Tensor]:
200
+ return self.radio_model.make_preprocessor_external()
201
+
202
+ def get_nearest_supported_resolution(self, height: int, width: int) -> Resolution:
203
+ return self.radio_model.get_nearest_supported_resolution(height, width)
204
+
205
+ def switch_to_deploy(self):
206
+ return self.radio_model.switch_to_deploy()
207
+
208
+ def forward(self, x: torch.Tensor, feature_fmt: str = 'NLC', num_tokens: Optional[int] = None,
209
+ neck_name: Optional[str] = None):
210
+ '''
211
+ Forward pass through the underlying RADIOModel. Mirrors `RADIOModel.forward`'s
212
+ signature so HF users can select `num_tokens` / `neck_name` per call without
213
+ having to reach into `self.radio_model`.
214
+ '''
215
+ return self.radio_model.forward(
216
+ x,
217
+ feature_fmt=feature_fmt,
218
+ num_tokens=num_tokens,
219
+ neck_name=neck_name,
220
+ )
input_conditioner.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ def backward(self, x: torch.Tensor):
38
+ y = x * self.norm_std + self.norm_mean
39
+ return y.to(self.dtype)
40
+
41
+
42
+ def get_default_conditioner():
43
+ from timm.data.constants import OPENAI_CLIP_MEAN, OPENAI_CLIP_STD
44
+
45
+ return InputConditioner(
46
+ input_scale=1.0,
47
+ norm_mean=OPENAI_CLIP_MEAN,
48
+ norm_std=OPENAI_CLIP_STD,
49
+ )
50
+
51
+
52
+ def _to_tensor(v: norm_t):
53
+ return torch.as_tensor(v, dtype=torch.float32).view(-1, 1, 1)
model-00001-of-00002.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3f10d2b4579199d240050a84705cb02162d1e5516f97e348d7ff11089a8b523e
3
+ size 4980363592
model-00002-of-00002.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c4516eae8e0cd7f390ca7a73ce6c2d51fb9c066cd756f076c6095d3a92d27ffe
3
+ size 826088048
model.safetensors.index.json ADDED
@@ -0,0 +1,476 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "metadata": {
3
+ "total_size": 5806397488
4
+ },
5
+ "weight_map": {
6
+ "radio_model.input_conditioner.norm_mean": "model-00002-of-00002.safetensors",
7
+ "radio_model.input_conditioner.norm_std": "model-00002-of-00002.safetensors",
8
+ "radio_model.model._iter_count": "model-00001-of-00002.safetensors",
9
+ "radio_model.model.blocks.0.attn.proj.bias": "model-00001-of-00002.safetensors",
10
+ "radio_model.model.blocks.0.attn.proj.weight": "model-00001-of-00002.safetensors",
11
+ "radio_model.model.blocks.0.attn.qkv.bias": "model-00001-of-00002.safetensors",
12
+ "radio_model.model.blocks.0.attn.qkv.weight": "model-00001-of-00002.safetensors",
13
+ "radio_model.model.blocks.0.mlp.fc1.bias": "model-00001-of-00002.safetensors",
14
+ "radio_model.model.blocks.0.mlp.fc1.weight": "model-00001-of-00002.safetensors",
15
+ "radio_model.model.blocks.0.mlp.fc2.bias": "model-00001-of-00002.safetensors",
16
+ "radio_model.model.blocks.0.mlp.fc2.weight": "model-00001-of-00002.safetensors",
17
+ "radio_model.model.blocks.0.norm1.bias": "model-00001-of-00002.safetensors",
18
+ "radio_model.model.blocks.0.norm1.weight": "model-00001-of-00002.safetensors",
19
+ "radio_model.model.blocks.0.norm2.bias": "model-00001-of-00002.safetensors",
20
+ "radio_model.model.blocks.0.norm2.weight": "model-00001-of-00002.safetensors",
21
+ "radio_model.model.blocks.1.attn.proj.bias": "model-00001-of-00002.safetensors",
22
+ "radio_model.model.blocks.1.attn.proj.weight": "model-00001-of-00002.safetensors",
23
+ "radio_model.model.blocks.1.attn.qkv.bias": "model-00001-of-00002.safetensors",
24
+ "radio_model.model.blocks.1.attn.qkv.weight": "model-00001-of-00002.safetensors",
25
+ "radio_model.model.blocks.1.mlp.fc1.bias": "model-00001-of-00002.safetensors",
26
+ "radio_model.model.blocks.1.mlp.fc1.weight": "model-00001-of-00002.safetensors",
27
+ "radio_model.model.blocks.1.mlp.fc2.bias": "model-00001-of-00002.safetensors",
28
+ "radio_model.model.blocks.1.mlp.fc2.weight": "model-00001-of-00002.safetensors",
29
+ "radio_model.model.blocks.1.norm1.bias": "model-00001-of-00002.safetensors",
30
+ "radio_model.model.blocks.1.norm1.weight": "model-00001-of-00002.safetensors",
31
+ "radio_model.model.blocks.1.norm2.bias": "model-00001-of-00002.safetensors",
32
+ "radio_model.model.blocks.1.norm2.weight": "model-00001-of-00002.safetensors",
33
+ "radio_model.model.blocks.10.attn.proj.bias": "model-00001-of-00002.safetensors",
34
+ "radio_model.model.blocks.10.attn.proj.weight": "model-00001-of-00002.safetensors",
35
+ "radio_model.model.blocks.10.attn.qkv.bias": "model-00001-of-00002.safetensors",
36
+ "radio_model.model.blocks.10.attn.qkv.weight": "model-00001-of-00002.safetensors",
37
+ "radio_model.model.blocks.10.mlp.fc1.bias": "model-00001-of-00002.safetensors",
38
+ "radio_model.model.blocks.10.mlp.fc1.weight": "model-00001-of-00002.safetensors",
39
+ "radio_model.model.blocks.10.mlp.fc2.bias": "model-00001-of-00002.safetensors",
40
+ "radio_model.model.blocks.10.mlp.fc2.weight": "model-00001-of-00002.safetensors",
41
+ "radio_model.model.blocks.10.norm1.bias": "model-00001-of-00002.safetensors",
42
+ "radio_model.model.blocks.10.norm1.weight": "model-00001-of-00002.safetensors",
43
+ "radio_model.model.blocks.10.norm2.bias": "model-00001-of-00002.safetensors",
44
+ "radio_model.model.blocks.10.norm2.weight": "model-00001-of-00002.safetensors",
45
+ "radio_model.model.blocks.11.attn.proj.bias": "model-00001-of-00002.safetensors",
46
+ "radio_model.model.blocks.11.attn.proj.weight": "model-00001-of-00002.safetensors",
47
+ "radio_model.model.blocks.11.attn.qkv.bias": "model-00001-of-00002.safetensors",
48
+ "radio_model.model.blocks.11.attn.qkv.weight": "model-00001-of-00002.safetensors",
49
+ "radio_model.model.blocks.11.mlp.fc1.bias": "model-00001-of-00002.safetensors",
50
+ "radio_model.model.blocks.11.mlp.fc1.weight": "model-00001-of-00002.safetensors",
51
+ "radio_model.model.blocks.11.mlp.fc2.bias": "model-00001-of-00002.safetensors",
52
+ "radio_model.model.blocks.11.mlp.fc2.weight": "model-00001-of-00002.safetensors",
53
+ "radio_model.model.blocks.11.norm1.bias": "model-00001-of-00002.safetensors",
54
+ "radio_model.model.blocks.11.norm1.weight": "model-00001-of-00002.safetensors",
55
+ "radio_model.model.blocks.11.norm2.bias": "model-00001-of-00002.safetensors",
56
+ "radio_model.model.blocks.11.norm2.weight": "model-00001-of-00002.safetensors",
57
+ "radio_model.model.blocks.12.attn.proj.bias": "model-00001-of-00002.safetensors",
58
+ "radio_model.model.blocks.12.attn.proj.weight": "model-00001-of-00002.safetensors",
59
+ "radio_model.model.blocks.12.attn.qkv.bias": "model-00001-of-00002.safetensors",
60
+ "radio_model.model.blocks.12.attn.qkv.weight": "model-00001-of-00002.safetensors",
61
+ "radio_model.model.blocks.12.mlp.fc1.bias": "model-00001-of-00002.safetensors",
62
+ "radio_model.model.blocks.12.mlp.fc1.weight": "model-00001-of-00002.safetensors",
63
+ "radio_model.model.blocks.12.mlp.fc2.bias": "model-00001-of-00002.safetensors",
64
+ "radio_model.model.blocks.12.mlp.fc2.weight": "model-00001-of-00002.safetensors",
65
+ "radio_model.model.blocks.12.norm1.bias": "model-00001-of-00002.safetensors",
66
+ "radio_model.model.blocks.12.norm1.weight": "model-00001-of-00002.safetensors",
67
+ "radio_model.model.blocks.12.norm2.bias": "model-00001-of-00002.safetensors",
68
+ "radio_model.model.blocks.12.norm2.weight": "model-00001-of-00002.safetensors",
69
+ "radio_model.model.blocks.13.attn.proj.bias": "model-00001-of-00002.safetensors",
70
+ "radio_model.model.blocks.13.attn.proj.weight": "model-00001-of-00002.safetensors",
71
+ "radio_model.model.blocks.13.attn.qkv.bias": "model-00001-of-00002.safetensors",
72
+ "radio_model.model.blocks.13.attn.qkv.weight": "model-00001-of-00002.safetensors",
73
+ "radio_model.model.blocks.13.mlp.fc1.bias": "model-00001-of-00002.safetensors",
74
+ "radio_model.model.blocks.13.mlp.fc1.weight": "model-00001-of-00002.safetensors",
75
+ "radio_model.model.blocks.13.mlp.fc2.bias": "model-00001-of-00002.safetensors",
76
+ "radio_model.model.blocks.13.mlp.fc2.weight": "model-00001-of-00002.safetensors",
77
+ "radio_model.model.blocks.13.norm1.bias": "model-00001-of-00002.safetensors",
78
+ "radio_model.model.blocks.13.norm1.weight": "model-00001-of-00002.safetensors",
79
+ "radio_model.model.blocks.13.norm2.bias": "model-00001-of-00002.safetensors",
80
+ "radio_model.model.blocks.13.norm2.weight": "model-00001-of-00002.safetensors",
81
+ "radio_model.model.blocks.14.attn.proj.bias": "model-00001-of-00002.safetensors",
82
+ "radio_model.model.blocks.14.attn.proj.weight": "model-00001-of-00002.safetensors",
83
+ "radio_model.model.blocks.14.attn.qkv.bias": "model-00001-of-00002.safetensors",
84
+ "radio_model.model.blocks.14.attn.qkv.weight": "model-00001-of-00002.safetensors",
85
+ "radio_model.model.blocks.14.mlp.fc1.bias": "model-00001-of-00002.safetensors",
86
+ "radio_model.model.blocks.14.mlp.fc1.weight": "model-00001-of-00002.safetensors",
87
+ "radio_model.model.blocks.14.mlp.fc2.bias": "model-00001-of-00002.safetensors",
88
+ "radio_model.model.blocks.14.mlp.fc2.weight": "model-00001-of-00002.safetensors",
89
+ "radio_model.model.blocks.14.norm1.bias": "model-00001-of-00002.safetensors",
90
+ "radio_model.model.blocks.14.norm1.weight": "model-00001-of-00002.safetensors",
91
+ "radio_model.model.blocks.14.norm2.bias": "model-00001-of-00002.safetensors",
92
+ "radio_model.model.blocks.14.norm2.weight": "model-00001-of-00002.safetensors",
93
+ "radio_model.model.blocks.15.attn.proj.bias": "model-00001-of-00002.safetensors",
94
+ "radio_model.model.blocks.15.attn.proj.weight": "model-00001-of-00002.safetensors",
95
+ "radio_model.model.blocks.15.attn.qkv.bias": "model-00001-of-00002.safetensors",
96
+ "radio_model.model.blocks.15.attn.qkv.weight": "model-00001-of-00002.safetensors",
97
+ "radio_model.model.blocks.15.mlp.fc1.bias": "model-00001-of-00002.safetensors",
98
+ "radio_model.model.blocks.15.mlp.fc1.weight": "model-00001-of-00002.safetensors",
99
+ "radio_model.model.blocks.15.mlp.fc2.bias": "model-00001-of-00002.safetensors",
100
+ "radio_model.model.blocks.15.mlp.fc2.weight": "model-00001-of-00002.safetensors",
101
+ "radio_model.model.blocks.15.norm1.bias": "model-00001-of-00002.safetensors",
102
+ "radio_model.model.blocks.15.norm1.weight": "model-00001-of-00002.safetensors",
103
+ "radio_model.model.blocks.15.norm2.bias": "model-00001-of-00002.safetensors",
104
+ "radio_model.model.blocks.15.norm2.weight": "model-00001-of-00002.safetensors",
105
+ "radio_model.model.blocks.16.attn.proj.bias": "model-00001-of-00002.safetensors",
106
+ "radio_model.model.blocks.16.attn.proj.weight": "model-00001-of-00002.safetensors",
107
+ "radio_model.model.blocks.16.attn.qkv.bias": "model-00001-of-00002.safetensors",
108
+ "radio_model.model.blocks.16.attn.qkv.weight": "model-00001-of-00002.safetensors",
109
+ "radio_model.model.blocks.16.mlp.fc1.bias": "model-00001-of-00002.safetensors",
110
+ "radio_model.model.blocks.16.mlp.fc1.weight": "model-00001-of-00002.safetensors",
111
+ "radio_model.model.blocks.16.mlp.fc2.bias": "model-00001-of-00002.safetensors",
112
+ "radio_model.model.blocks.16.mlp.fc2.weight": "model-00001-of-00002.safetensors",
113
+ "radio_model.model.blocks.16.norm1.bias": "model-00001-of-00002.safetensors",
114
+ "radio_model.model.blocks.16.norm1.weight": "model-00001-of-00002.safetensors",
115
+ "radio_model.model.blocks.16.norm2.bias": "model-00001-of-00002.safetensors",
116
+ "radio_model.model.blocks.16.norm2.weight": "model-00001-of-00002.safetensors",
117
+ "radio_model.model.blocks.17.attn.proj.bias": "model-00001-of-00002.safetensors",
118
+ "radio_model.model.blocks.17.attn.proj.weight": "model-00001-of-00002.safetensors",
119
+ "radio_model.model.blocks.17.attn.qkv.bias": "model-00001-of-00002.safetensors",
120
+ "radio_model.model.blocks.17.attn.qkv.weight": "model-00001-of-00002.safetensors",
121
+ "radio_model.model.blocks.17.mlp.fc1.bias": "model-00001-of-00002.safetensors",
122
+ "radio_model.model.blocks.17.mlp.fc1.weight": "model-00001-of-00002.safetensors",
123
+ "radio_model.model.blocks.17.mlp.fc2.bias": "model-00001-of-00002.safetensors",
124
+ "radio_model.model.blocks.17.mlp.fc2.weight": "model-00001-of-00002.safetensors",
125
+ "radio_model.model.blocks.17.norm1.bias": "model-00001-of-00002.safetensors",
126
+ "radio_model.model.blocks.17.norm1.weight": "model-00001-of-00002.safetensors",
127
+ "radio_model.model.blocks.17.norm2.bias": "model-00001-of-00002.safetensors",
128
+ "radio_model.model.blocks.17.norm2.weight": "model-00001-of-00002.safetensors",
129
+ "radio_model.model.blocks.18.attn.proj.bias": "model-00001-of-00002.safetensors",
130
+ "radio_model.model.blocks.18.attn.proj.weight": "model-00001-of-00002.safetensors",
131
+ "radio_model.model.blocks.18.attn.qkv.bias": "model-00001-of-00002.safetensors",
132
+ "radio_model.model.blocks.18.attn.qkv.weight": "model-00001-of-00002.safetensors",
133
+ "radio_model.model.blocks.18.mlp.fc1.bias": "model-00001-of-00002.safetensors",
134
+ "radio_model.model.blocks.18.mlp.fc1.weight": "model-00001-of-00002.safetensors",
135
+ "radio_model.model.blocks.18.mlp.fc2.bias": "model-00001-of-00002.safetensors",
136
+ "radio_model.model.blocks.18.mlp.fc2.weight": "model-00001-of-00002.safetensors",
137
+ "radio_model.model.blocks.18.norm1.bias": "model-00001-of-00002.safetensors",
138
+ "radio_model.model.blocks.18.norm1.weight": "model-00001-of-00002.safetensors",
139
+ "radio_model.model.blocks.18.norm2.bias": "model-00001-of-00002.safetensors",
140
+ "radio_model.model.blocks.18.norm2.weight": "model-00001-of-00002.safetensors",
141
+ "radio_model.model.blocks.19.attn.proj.bias": "model-00001-of-00002.safetensors",
142
+ "radio_model.model.blocks.19.attn.proj.weight": "model-00001-of-00002.safetensors",
143
+ "radio_model.model.blocks.19.attn.qkv.bias": "model-00001-of-00002.safetensors",
144
+ "radio_model.model.blocks.19.attn.qkv.weight": "model-00001-of-00002.safetensors",
145
+ "radio_model.model.blocks.19.mlp.fc1.bias": "model-00001-of-00002.safetensors",
146
+ "radio_model.model.blocks.19.mlp.fc1.weight": "model-00001-of-00002.safetensors",
147
+ "radio_model.model.blocks.19.mlp.fc2.bias": "model-00001-of-00002.safetensors",
148
+ "radio_model.model.blocks.19.mlp.fc2.weight": "model-00001-of-00002.safetensors",
149
+ "radio_model.model.blocks.19.norm1.bias": "model-00001-of-00002.safetensors",
150
+ "radio_model.model.blocks.19.norm1.weight": "model-00001-of-00002.safetensors",
151
+ "radio_model.model.blocks.19.norm2.bias": "model-00001-of-00002.safetensors",
152
+ "radio_model.model.blocks.19.norm2.weight": "model-00001-of-00002.safetensors",
153
+ "radio_model.model.blocks.2.attn.proj.bias": "model-00001-of-00002.safetensors",
154
+ "radio_model.model.blocks.2.attn.proj.weight": "model-00001-of-00002.safetensors",
155
+ "radio_model.model.blocks.2.attn.qkv.bias": "model-00001-of-00002.safetensors",
156
+ "radio_model.model.blocks.2.attn.qkv.weight": "model-00001-of-00002.safetensors",
157
+ "radio_model.model.blocks.2.mlp.fc1.bias": "model-00001-of-00002.safetensors",
158
+ "radio_model.model.blocks.2.mlp.fc1.weight": "model-00001-of-00002.safetensors",
159
+ "radio_model.model.blocks.2.mlp.fc2.bias": "model-00001-of-00002.safetensors",
160
+ "radio_model.model.blocks.2.mlp.fc2.weight": "model-00001-of-00002.safetensors",
161
+ "radio_model.model.blocks.2.norm1.bias": "model-00001-of-00002.safetensors",
162
+ "radio_model.model.blocks.2.norm1.weight": "model-00001-of-00002.safetensors",
163
+ "radio_model.model.blocks.2.norm2.bias": "model-00001-of-00002.safetensors",
164
+ "radio_model.model.blocks.2.norm2.weight": "model-00001-of-00002.safetensors",
165
+ "radio_model.model.blocks.20.attn.proj.bias": "model-00001-of-00002.safetensors",
166
+ "radio_model.model.blocks.20.attn.proj.weight": "model-00001-of-00002.safetensors",
167
+ "radio_model.model.blocks.20.attn.qkv.bias": "model-00001-of-00002.safetensors",
168
+ "radio_model.model.blocks.20.attn.qkv.weight": "model-00001-of-00002.safetensors",
169
+ "radio_model.model.blocks.20.mlp.fc1.bias": "model-00001-of-00002.safetensors",
170
+ "radio_model.model.blocks.20.mlp.fc1.weight": "model-00001-of-00002.safetensors",
171
+ "radio_model.model.blocks.20.mlp.fc2.bias": "model-00001-of-00002.safetensors",
172
+ "radio_model.model.blocks.20.mlp.fc2.weight": "model-00001-of-00002.safetensors",
173
+ "radio_model.model.blocks.20.norm1.bias": "model-00001-of-00002.safetensors",
174
+ "radio_model.model.blocks.20.norm1.weight": "model-00001-of-00002.safetensors",
175
+ "radio_model.model.blocks.20.norm2.bias": "model-00001-of-00002.safetensors",
176
+ "radio_model.model.blocks.20.norm2.weight": "model-00001-of-00002.safetensors",
177
+ "radio_model.model.blocks.21.attn.proj.bias": "model-00001-of-00002.safetensors",
178
+ "radio_model.model.blocks.21.attn.proj.weight": "model-00001-of-00002.safetensors",
179
+ "radio_model.model.blocks.21.attn.qkv.bias": "model-00001-of-00002.safetensors",
180
+ "radio_model.model.blocks.21.attn.qkv.weight": "model-00001-of-00002.safetensors",
181
+ "radio_model.model.blocks.21.mlp.fc1.bias": "model-00001-of-00002.safetensors",
182
+ "radio_model.model.blocks.21.mlp.fc1.weight": "model-00001-of-00002.safetensors",
183
+ "radio_model.model.blocks.21.mlp.fc2.bias": "model-00001-of-00002.safetensors",
184
+ "radio_model.model.blocks.21.mlp.fc2.weight": "model-00001-of-00002.safetensors",
185
+ "radio_model.model.blocks.21.norm1.bias": "model-00001-of-00002.safetensors",
186
+ "radio_model.model.blocks.21.norm1.weight": "model-00001-of-00002.safetensors",
187
+ "radio_model.model.blocks.21.norm2.bias": "model-00001-of-00002.safetensors",
188
+ "radio_model.model.blocks.21.norm2.weight": "model-00001-of-00002.safetensors",
189
+ "radio_model.model.blocks.22.attn.proj.bias": "model-00001-of-00002.safetensors",
190
+ "radio_model.model.blocks.22.attn.proj.weight": "model-00001-of-00002.safetensors",
191
+ "radio_model.model.blocks.22.attn.qkv.bias": "model-00001-of-00002.safetensors",
192
+ "radio_model.model.blocks.22.attn.qkv.weight": "model-00001-of-00002.safetensors",
193
+ "radio_model.model.blocks.22.mlp.fc1.bias": "model-00001-of-00002.safetensors",
194
+ "radio_model.model.blocks.22.mlp.fc1.weight": "model-00001-of-00002.safetensors",
195
+ "radio_model.model.blocks.22.mlp.fc2.bias": "model-00001-of-00002.safetensors",
196
+ "radio_model.model.blocks.22.mlp.fc2.weight": "model-00001-of-00002.safetensors",
197
+ "radio_model.model.blocks.22.norm1.bias": "model-00001-of-00002.safetensors",
198
+ "radio_model.model.blocks.22.norm1.weight": "model-00001-of-00002.safetensors",
199
+ "radio_model.model.blocks.22.norm2.bias": "model-00001-of-00002.safetensors",
200
+ "radio_model.model.blocks.22.norm2.weight": "model-00001-of-00002.safetensors",
201
+ "radio_model.model.blocks.23.attn.proj.bias": "model-00001-of-00002.safetensors",
202
+ "radio_model.model.blocks.23.attn.proj.weight": "model-00001-of-00002.safetensors",
203
+ "radio_model.model.blocks.23.attn.qkv.bias": "model-00001-of-00002.safetensors",
204
+ "radio_model.model.blocks.23.attn.qkv.weight": "model-00001-of-00002.safetensors",
205
+ "radio_model.model.blocks.23.mlp.fc1.bias": "model-00001-of-00002.safetensors",
206
+ "radio_model.model.blocks.23.mlp.fc1.weight": "model-00001-of-00002.safetensors",
207
+ "radio_model.model.blocks.23.mlp.fc2.bias": "model-00001-of-00002.safetensors",
208
+ "radio_model.model.blocks.23.mlp.fc2.weight": "model-00001-of-00002.safetensors",
209
+ "radio_model.model.blocks.23.norm1.bias": "model-00001-of-00002.safetensors",
210
+ "radio_model.model.blocks.23.norm1.weight": "model-00001-of-00002.safetensors",
211
+ "radio_model.model.blocks.23.norm2.bias": "model-00001-of-00002.safetensors",
212
+ "radio_model.model.blocks.23.norm2.weight": "model-00001-of-00002.safetensors",
213
+ "radio_model.model.blocks.24.attn.proj.bias": "model-00001-of-00002.safetensors",
214
+ "radio_model.model.blocks.24.attn.proj.weight": "model-00001-of-00002.safetensors",
215
+ "radio_model.model.blocks.24.attn.qkv.bias": "model-00001-of-00002.safetensors",
216
+ "radio_model.model.blocks.24.attn.qkv.weight": "model-00001-of-00002.safetensors",
217
+ "radio_model.model.blocks.24.mlp.fc1.bias": "model-00001-of-00002.safetensors",
218
+ "radio_model.model.blocks.24.mlp.fc1.weight": "model-00001-of-00002.safetensors",
219
+ "radio_model.model.blocks.24.mlp.fc2.bias": "model-00001-of-00002.safetensors",
220
+ "radio_model.model.blocks.24.mlp.fc2.weight": "model-00001-of-00002.safetensors",
221
+ "radio_model.model.blocks.24.norm1.bias": "model-00001-of-00002.safetensors",
222
+ "radio_model.model.blocks.24.norm1.weight": "model-00001-of-00002.safetensors",
223
+ "radio_model.model.blocks.24.norm2.bias": "model-00001-of-00002.safetensors",
224
+ "radio_model.model.blocks.24.norm2.weight": "model-00001-of-00002.safetensors",
225
+ "radio_model.model.blocks.25.attn.proj.bias": "model-00001-of-00002.safetensors",
226
+ "radio_model.model.blocks.25.attn.proj.weight": "model-00001-of-00002.safetensors",
227
+ "radio_model.model.blocks.25.attn.qkv.bias": "model-00001-of-00002.safetensors",
228
+ "radio_model.model.blocks.25.attn.qkv.weight": "model-00001-of-00002.safetensors",
229
+ "radio_model.model.blocks.25.mlp.fc1.bias": "model-00001-of-00002.safetensors",
230
+ "radio_model.model.blocks.25.mlp.fc1.weight": "model-00001-of-00002.safetensors",
231
+ "radio_model.model.blocks.25.mlp.fc2.bias": "model-00001-of-00002.safetensors",
232
+ "radio_model.model.blocks.25.mlp.fc2.weight": "model-00001-of-00002.safetensors",
233
+ "radio_model.model.blocks.25.norm1.bias": "model-00001-of-00002.safetensors",
234
+ "radio_model.model.blocks.25.norm1.weight": "model-00001-of-00002.safetensors",
235
+ "radio_model.model.blocks.25.norm2.bias": "model-00001-of-00002.safetensors",
236
+ "radio_model.model.blocks.25.norm2.weight": "model-00001-of-00002.safetensors",
237
+ "radio_model.model.blocks.26.attn.proj.bias": "model-00001-of-00002.safetensors",
238
+ "radio_model.model.blocks.26.attn.proj.weight": "model-00001-of-00002.safetensors",
239
+ "radio_model.model.blocks.26.attn.qkv.bias": "model-00001-of-00002.safetensors",
240
+ "radio_model.model.blocks.26.attn.qkv.weight": "model-00001-of-00002.safetensors",
241
+ "radio_model.model.blocks.26.mlp.fc1.bias": "model-00001-of-00002.safetensors",
242
+ "radio_model.model.blocks.26.mlp.fc1.weight": "model-00001-of-00002.safetensors",
243
+ "radio_model.model.blocks.26.mlp.fc2.bias": "model-00001-of-00002.safetensors",
244
+ "radio_model.model.blocks.26.mlp.fc2.weight": "model-00001-of-00002.safetensors",
245
+ "radio_model.model.blocks.26.norm1.bias": "model-00001-of-00002.safetensors",
246
+ "radio_model.model.blocks.26.norm1.weight": "model-00001-of-00002.safetensors",
247
+ "radio_model.model.blocks.26.norm2.bias": "model-00001-of-00002.safetensors",
248
+ "radio_model.model.blocks.26.norm2.weight": "model-00001-of-00002.safetensors",
249
+ "radio_model.model.blocks.27.attn.proj.bias": "model-00001-of-00002.safetensors",
250
+ "radio_model.model.blocks.27.attn.proj.weight": "model-00001-of-00002.safetensors",
251
+ "radio_model.model.blocks.27.attn.qkv.bias": "model-00001-of-00002.safetensors",
252
+ "radio_model.model.blocks.27.attn.qkv.weight": "model-00001-of-00002.safetensors",
253
+ "radio_model.model.blocks.27.mlp.fc1.bias": "model-00001-of-00002.safetensors",
254
+ "radio_model.model.blocks.27.mlp.fc1.weight": "model-00001-of-00002.safetensors",
255
+ "radio_model.model.blocks.27.mlp.fc2.bias": "model-00001-of-00002.safetensors",
256
+ "radio_model.model.blocks.27.mlp.fc2.weight": "model-00001-of-00002.safetensors",
257
+ "radio_model.model.blocks.27.norm1.bias": "model-00001-of-00002.safetensors",
258
+ "radio_model.model.blocks.27.norm1.weight": "model-00001-of-00002.safetensors",
259
+ "radio_model.model.blocks.27.norm2.bias": "model-00001-of-00002.safetensors",
260
+ "radio_model.model.blocks.27.norm2.weight": "model-00001-of-00002.safetensors",
261
+ "radio_model.model.blocks.28.attn.proj.bias": "model-00001-of-00002.safetensors",
262
+ "radio_model.model.blocks.28.attn.proj.weight": "model-00001-of-00002.safetensors",
263
+ "radio_model.model.blocks.28.attn.qkv.bias": "model-00001-of-00002.safetensors",
264
+ "radio_model.model.blocks.28.attn.qkv.weight": "model-00001-of-00002.safetensors",
265
+ "radio_model.model.blocks.28.mlp.fc1.bias": "model-00001-of-00002.safetensors",
266
+ "radio_model.model.blocks.28.mlp.fc1.weight": "model-00001-of-00002.safetensors",
267
+ "radio_model.model.blocks.28.mlp.fc2.bias": "model-00001-of-00002.safetensors",
268
+ "radio_model.model.blocks.28.mlp.fc2.weight": "model-00001-of-00002.safetensors",
269
+ "radio_model.model.blocks.28.norm1.bias": "model-00001-of-00002.safetensors",
270
+ "radio_model.model.blocks.28.norm1.weight": "model-00001-of-00002.safetensors",
271
+ "radio_model.model.blocks.28.norm2.bias": "model-00001-of-00002.safetensors",
272
+ "radio_model.model.blocks.28.norm2.weight": "model-00001-of-00002.safetensors",
273
+ "radio_model.model.blocks.29.attn.proj.bias": "model-00001-of-00002.safetensors",
274
+ "radio_model.model.blocks.29.attn.proj.weight": "model-00001-of-00002.safetensors",
275
+ "radio_model.model.blocks.29.attn.qkv.bias": "model-00001-of-00002.safetensors",
276
+ "radio_model.model.blocks.29.attn.qkv.weight": "model-00001-of-00002.safetensors",
277
+ "radio_model.model.blocks.29.mlp.fc1.bias": "model-00001-of-00002.safetensors",
278
+ "radio_model.model.blocks.29.mlp.fc1.weight": "model-00001-of-00002.safetensors",
279
+ "radio_model.model.blocks.29.mlp.fc2.bias": "model-00001-of-00002.safetensors",
280
+ "radio_model.model.blocks.29.mlp.fc2.weight": "model-00001-of-00002.safetensors",
281
+ "radio_model.model.blocks.29.norm1.bias": "model-00001-of-00002.safetensors",
282
+ "radio_model.model.blocks.29.norm1.weight": "model-00001-of-00002.safetensors",
283
+ "radio_model.model.blocks.29.norm2.bias": "model-00001-of-00002.safetensors",
284
+ "radio_model.model.blocks.29.norm2.weight": "model-00001-of-00002.safetensors",
285
+ "radio_model.model.blocks.3.attn.proj.bias": "model-00001-of-00002.safetensors",
286
+ "radio_model.model.blocks.3.attn.proj.weight": "model-00001-of-00002.safetensors",
287
+ "radio_model.model.blocks.3.attn.qkv.bias": "model-00001-of-00002.safetensors",
288
+ "radio_model.model.blocks.3.attn.qkv.weight": "model-00001-of-00002.safetensors",
289
+ "radio_model.model.blocks.3.mlp.fc1.bias": "model-00001-of-00002.safetensors",
290
+ "radio_model.model.blocks.3.mlp.fc1.weight": "model-00001-of-00002.safetensors",
291
+ "radio_model.model.blocks.3.mlp.fc2.bias": "model-00001-of-00002.safetensors",
292
+ "radio_model.model.blocks.3.mlp.fc2.weight": "model-00001-of-00002.safetensors",
293
+ "radio_model.model.blocks.3.norm1.bias": "model-00001-of-00002.safetensors",
294
+ "radio_model.model.blocks.3.norm1.weight": "model-00001-of-00002.safetensors",
295
+ "radio_model.model.blocks.3.norm2.bias": "model-00001-of-00002.safetensors",
296
+ "radio_model.model.blocks.3.norm2.weight": "model-00001-of-00002.safetensors",
297
+ "radio_model.model.blocks.30.attn.proj.bias": "model-00001-of-00002.safetensors",
298
+ "radio_model.model.blocks.30.attn.proj.weight": "model-00001-of-00002.safetensors",
299
+ "radio_model.model.blocks.30.attn.qkv.bias": "model-00001-of-00002.safetensors",
300
+ "radio_model.model.blocks.30.attn.qkv.weight": "model-00001-of-00002.safetensors",
301
+ "radio_model.model.blocks.30.mlp.fc1.bias": "model-00001-of-00002.safetensors",
302
+ "radio_model.model.blocks.30.mlp.fc1.weight": "model-00001-of-00002.safetensors",
303
+ "radio_model.model.blocks.30.mlp.fc2.bias": "model-00001-of-00002.safetensors",
304
+ "radio_model.model.blocks.30.mlp.fc2.weight": "model-00001-of-00002.safetensors",
305
+ "radio_model.model.blocks.30.norm1.bias": "model-00001-of-00002.safetensors",
306
+ "radio_model.model.blocks.30.norm1.weight": "model-00001-of-00002.safetensors",
307
+ "radio_model.model.blocks.30.norm2.bias": "model-00001-of-00002.safetensors",
308
+ "radio_model.model.blocks.30.norm2.weight": "model-00001-of-00002.safetensors",
309
+ "radio_model.model.blocks.31.attn.proj.bias": "model-00001-of-00002.safetensors",
310
+ "radio_model.model.blocks.31.attn.proj.weight": "model-00001-of-00002.safetensors",
311
+ "radio_model.model.blocks.31.attn.qkv.bias": "model-00001-of-00002.safetensors",
312
+ "radio_model.model.blocks.31.attn.qkv.weight": "model-00001-of-00002.safetensors",
313
+ "radio_model.model.blocks.31.mlp.fc1.bias": "model-00001-of-00002.safetensors",
314
+ "radio_model.model.blocks.31.mlp.fc1.weight": "model-00001-of-00002.safetensors",
315
+ "radio_model.model.blocks.31.mlp.fc2.bias": "model-00001-of-00002.safetensors",
316
+ "radio_model.model.blocks.31.mlp.fc2.weight": "model-00001-of-00002.safetensors",
317
+ "radio_model.model.blocks.31.norm1.bias": "model-00001-of-00002.safetensors",
318
+ "radio_model.model.blocks.31.norm1.weight": "model-00001-of-00002.safetensors",
319
+ "radio_model.model.blocks.31.norm2.bias": "model-00001-of-00002.safetensors",
320
+ "radio_model.model.blocks.31.norm2.weight": "model-00001-of-00002.safetensors",
321
+ "radio_model.model.blocks.4.attn.proj.bias": "model-00001-of-00002.safetensors",
322
+ "radio_model.model.blocks.4.attn.proj.weight": "model-00001-of-00002.safetensors",
323
+ "radio_model.model.blocks.4.attn.qkv.bias": "model-00001-of-00002.safetensors",
324
+ "radio_model.model.blocks.4.attn.qkv.weight": "model-00001-of-00002.safetensors",
325
+ "radio_model.model.blocks.4.mlp.fc1.bias": "model-00001-of-00002.safetensors",
326
+ "radio_model.model.blocks.4.mlp.fc1.weight": "model-00001-of-00002.safetensors",
327
+ "radio_model.model.blocks.4.mlp.fc2.bias": "model-00001-of-00002.safetensors",
328
+ "radio_model.model.blocks.4.mlp.fc2.weight": "model-00001-of-00002.safetensors",
329
+ "radio_model.model.blocks.4.norm1.bias": "model-00001-of-00002.safetensors",
330
+ "radio_model.model.blocks.4.norm1.weight": "model-00001-of-00002.safetensors",
331
+ "radio_model.model.blocks.4.norm2.bias": "model-00001-of-00002.safetensors",
332
+ "radio_model.model.blocks.4.norm2.weight": "model-00001-of-00002.safetensors",
333
+ "radio_model.model.blocks.5.attn.proj.bias": "model-00001-of-00002.safetensors",
334
+ "radio_model.model.blocks.5.attn.proj.weight": "model-00001-of-00002.safetensors",
335
+ "radio_model.model.blocks.5.attn.qkv.bias": "model-00001-of-00002.safetensors",
336
+ "radio_model.model.blocks.5.attn.qkv.weight": "model-00001-of-00002.safetensors",
337
+ "radio_model.model.blocks.5.mlp.fc1.bias": "model-00001-of-00002.safetensors",
338
+ "radio_model.model.blocks.5.mlp.fc1.weight": "model-00001-of-00002.safetensors",
339
+ "radio_model.model.blocks.5.mlp.fc2.bias": "model-00001-of-00002.safetensors",
340
+ "radio_model.model.blocks.5.mlp.fc2.weight": "model-00001-of-00002.safetensors",
341
+ "radio_model.model.blocks.5.norm1.bias": "model-00001-of-00002.safetensors",
342
+ "radio_model.model.blocks.5.norm1.weight": "model-00001-of-00002.safetensors",
343
+ "radio_model.model.blocks.5.norm2.bias": "model-00001-of-00002.safetensors",
344
+ "radio_model.model.blocks.5.norm2.weight": "model-00001-of-00002.safetensors",
345
+ "radio_model.model.blocks.6.attn.proj.bias": "model-00001-of-00002.safetensors",
346
+ "radio_model.model.blocks.6.attn.proj.weight": "model-00001-of-00002.safetensors",
347
+ "radio_model.model.blocks.6.attn.qkv.bias": "model-00001-of-00002.safetensors",
348
+ "radio_model.model.blocks.6.attn.qkv.weight": "model-00001-of-00002.safetensors",
349
+ "radio_model.model.blocks.6.mlp.fc1.bias": "model-00001-of-00002.safetensors",
350
+ "radio_model.model.blocks.6.mlp.fc1.weight": "model-00001-of-00002.safetensors",
351
+ "radio_model.model.blocks.6.mlp.fc2.bias": "model-00001-of-00002.safetensors",
352
+ "radio_model.model.blocks.6.mlp.fc2.weight": "model-00001-of-00002.safetensors",
353
+ "radio_model.model.blocks.6.norm1.bias": "model-00001-of-00002.safetensors",
354
+ "radio_model.model.blocks.6.norm1.weight": "model-00001-of-00002.safetensors",
355
+ "radio_model.model.blocks.6.norm2.bias": "model-00001-of-00002.safetensors",
356
+ "radio_model.model.blocks.6.norm2.weight": "model-00001-of-00002.safetensors",
357
+ "radio_model.model.blocks.7.attn.proj.bias": "model-00001-of-00002.safetensors",
358
+ "radio_model.model.blocks.7.attn.proj.weight": "model-00001-of-00002.safetensors",
359
+ "radio_model.model.blocks.7.attn.qkv.bias": "model-00001-of-00002.safetensors",
360
+ "radio_model.model.blocks.7.attn.qkv.weight": "model-00001-of-00002.safetensors",
361
+ "radio_model.model.blocks.7.mlp.fc1.bias": "model-00001-of-00002.safetensors",
362
+ "radio_model.model.blocks.7.mlp.fc1.weight": "model-00001-of-00002.safetensors",
363
+ "radio_model.model.blocks.7.mlp.fc2.bias": "model-00001-of-00002.safetensors",
364
+ "radio_model.model.blocks.7.mlp.fc2.weight": "model-00001-of-00002.safetensors",
365
+ "radio_model.model.blocks.7.norm1.bias": "model-00001-of-00002.safetensors",
366
+ "radio_model.model.blocks.7.norm1.weight": "model-00001-of-00002.safetensors",
367
+ "radio_model.model.blocks.7.norm2.bias": "model-00001-of-00002.safetensors",
368
+ "radio_model.model.blocks.7.norm2.weight": "model-00001-of-00002.safetensors",
369
+ "radio_model.model.blocks.8.attn.proj.bias": "model-00001-of-00002.safetensors",
370
+ "radio_model.model.blocks.8.attn.proj.weight": "model-00001-of-00002.safetensors",
371
+ "radio_model.model.blocks.8.attn.qkv.bias": "model-00001-of-00002.safetensors",
372
+ "radio_model.model.blocks.8.attn.qkv.weight": "model-00001-of-00002.safetensors",
373
+ "radio_model.model.blocks.8.mlp.fc1.bias": "model-00001-of-00002.safetensors",
374
+ "radio_model.model.blocks.8.mlp.fc1.weight": "model-00001-of-00002.safetensors",
375
+ "radio_model.model.blocks.8.mlp.fc2.bias": "model-00001-of-00002.safetensors",
376
+ "radio_model.model.blocks.8.mlp.fc2.weight": "model-00001-of-00002.safetensors",
377
+ "radio_model.model.blocks.8.norm1.bias": "model-00001-of-00002.safetensors",
378
+ "radio_model.model.blocks.8.norm1.weight": "model-00001-of-00002.safetensors",
379
+ "radio_model.model.blocks.8.norm2.bias": "model-00001-of-00002.safetensors",
380
+ "radio_model.model.blocks.8.norm2.weight": "model-00001-of-00002.safetensors",
381
+ "radio_model.model.blocks.9.attn.proj.bias": "model-00001-of-00002.safetensors",
382
+ "radio_model.model.blocks.9.attn.proj.weight": "model-00001-of-00002.safetensors",
383
+ "radio_model.model.blocks.9.attn.qkv.bias": "model-00001-of-00002.safetensors",
384
+ "radio_model.model.blocks.9.attn.qkv.weight": "model-00001-of-00002.safetensors",
385
+ "radio_model.model.blocks.9.mlp.fc1.bias": "model-00001-of-00002.safetensors",
386
+ "radio_model.model.blocks.9.mlp.fc1.weight": "model-00001-of-00002.safetensors",
387
+ "radio_model.model.blocks.9.mlp.fc2.bias": "model-00001-of-00002.safetensors",
388
+ "radio_model.model.blocks.9.mlp.fc2.weight": "model-00001-of-00002.safetensors",
389
+ "radio_model.model.blocks.9.norm1.bias": "model-00001-of-00002.safetensors",
390
+ "radio_model.model.blocks.9.norm1.weight": "model-00001-of-00002.safetensors",
391
+ "radio_model.model.blocks.9.norm2.bias": "model-00001-of-00002.safetensors",
392
+ "radio_model.model.blocks.9.norm2.weight": "model-00001-of-00002.safetensors",
393
+ "radio_model.model.decoder.blocks.0.attn.proj.bias": "model-00001-of-00002.safetensors",
394
+ "radio_model.model.decoder.blocks.0.attn.proj.weight": "model-00001-of-00002.safetensors",
395
+ "radio_model.model.decoder.blocks.0.attn.qkv.weight": "model-00001-of-00002.safetensors",
396
+ "radio_model.model.decoder.blocks.0.mlp.fc1.bias": "model-00001-of-00002.safetensors",
397
+ "radio_model.model.decoder.blocks.0.mlp.fc1.weight": "model-00001-of-00002.safetensors",
398
+ "radio_model.model.decoder.blocks.0.mlp.fc2.bias": "model-00001-of-00002.safetensors",
399
+ "radio_model.model.decoder.blocks.0.mlp.fc2.weight": "model-00001-of-00002.safetensors",
400
+ "radio_model.model.decoder.blocks.0.norm1.bias": "model-00001-of-00002.safetensors",
401
+ "radio_model.model.decoder.blocks.0.norm1.weight": "model-00001-of-00002.safetensors",
402
+ "radio_model.model.decoder.blocks.0.norm2.bias": "model-00001-of-00002.safetensors",
403
+ "radio_model.model.decoder.blocks.0.norm2.weight": "model-00001-of-00002.safetensors",
404
+ "radio_model.model.decoder.blocks.1.attn.proj.bias": "model-00001-of-00002.safetensors",
405
+ "radio_model.model.decoder.blocks.1.attn.proj.weight": "model-00001-of-00002.safetensors",
406
+ "radio_model.model.decoder.blocks.1.attn.qkv.weight": "model-00001-of-00002.safetensors",
407
+ "radio_model.model.decoder.blocks.1.mlp.fc1.bias": "model-00002-of-00002.safetensors",
408
+ "radio_model.model.decoder.blocks.1.mlp.fc1.weight": "model-00002-of-00002.safetensors",
409
+ "radio_model.model.decoder.blocks.1.mlp.fc2.bias": "model-00002-of-00002.safetensors",
410
+ "radio_model.model.decoder.blocks.1.mlp.fc2.weight": "model-00002-of-00002.safetensors",
411
+ "radio_model.model.decoder.blocks.1.norm1.bias": "model-00001-of-00002.safetensors",
412
+ "radio_model.model.decoder.blocks.1.norm1.weight": "model-00001-of-00002.safetensors",
413
+ "radio_model.model.decoder.blocks.1.norm2.bias": "model-00001-of-00002.safetensors",
414
+ "radio_model.model.decoder.blocks.1.norm2.weight": "model-00001-of-00002.safetensors",
415
+ "radio_model.model.decoder.blocks.2.attn.proj.bias": "model-00002-of-00002.safetensors",
416
+ "radio_model.model.decoder.blocks.2.attn.proj.weight": "model-00002-of-00002.safetensors",
417
+ "radio_model.model.decoder.blocks.2.attn.qkv.weight": "model-00002-of-00002.safetensors",
418
+ "radio_model.model.decoder.blocks.2.mlp.fc1.bias": "model-00002-of-00002.safetensors",
419
+ "radio_model.model.decoder.blocks.2.mlp.fc1.weight": "model-00002-of-00002.safetensors",
420
+ "radio_model.model.decoder.blocks.2.mlp.fc2.bias": "model-00002-of-00002.safetensors",
421
+ "radio_model.model.decoder.blocks.2.mlp.fc2.weight": "model-00002-of-00002.safetensors",
422
+ "radio_model.model.decoder.blocks.2.norm1.bias": "model-00002-of-00002.safetensors",
423
+ "radio_model.model.decoder.blocks.2.norm1.weight": "model-00002-of-00002.safetensors",
424
+ "radio_model.model.decoder.blocks.2.norm2.bias": "model-00002-of-00002.safetensors",
425
+ "radio_model.model.decoder.blocks.2.norm2.weight": "model-00002-of-00002.safetensors",
426
+ "radio_model.model.decoder.blocks.3.attn.proj.bias": "model-00002-of-00002.safetensors",
427
+ "radio_model.model.decoder.blocks.3.attn.proj.weight": "model-00002-of-00002.safetensors",
428
+ "radio_model.model.decoder.blocks.3.attn.qkv.weight": "model-00002-of-00002.safetensors",
429
+ "radio_model.model.decoder.blocks.3.mlp.fc1.bias": "model-00002-of-00002.safetensors",
430
+ "radio_model.model.decoder.blocks.3.mlp.fc1.weight": "model-00002-of-00002.safetensors",
431
+ "radio_model.model.decoder.blocks.3.mlp.fc2.bias": "model-00002-of-00002.safetensors",
432
+ "radio_model.model.decoder.blocks.3.mlp.fc2.weight": "model-00002-of-00002.safetensors",
433
+ "radio_model.model.decoder.blocks.3.norm1.bias": "model-00002-of-00002.safetensors",
434
+ "radio_model.model.decoder.blocks.3.norm1.weight": "model-00002-of-00002.safetensors",
435
+ "radio_model.model.decoder.blocks.3.norm2.bias": "model-00002-of-00002.safetensors",
436
+ "radio_model.model.decoder.blocks.3.norm2.weight": "model-00002-of-00002.safetensors",
437
+ "radio_model.model.decoder.blocks.4.attn.proj.bias": "model-00002-of-00002.safetensors",
438
+ "radio_model.model.decoder.blocks.4.attn.proj.weight": "model-00002-of-00002.safetensors",
439
+ "radio_model.model.decoder.blocks.4.attn.qkv.weight": "model-00002-of-00002.safetensors",
440
+ "radio_model.model.decoder.blocks.4.mlp.fc1.bias": "model-00002-of-00002.safetensors",
441
+ "radio_model.model.decoder.blocks.4.mlp.fc1.weight": "model-00002-of-00002.safetensors",
442
+ "radio_model.model.decoder.blocks.4.mlp.fc2.bias": "model-00002-of-00002.safetensors",
443
+ "radio_model.model.decoder.blocks.4.mlp.fc2.weight": "model-00002-of-00002.safetensors",
444
+ "radio_model.model.decoder.blocks.4.norm1.bias": "model-00002-of-00002.safetensors",
445
+ "radio_model.model.decoder.blocks.4.norm1.weight": "model-00002-of-00002.safetensors",
446
+ "radio_model.model.decoder.blocks.4.norm2.bias": "model-00002-of-00002.safetensors",
447
+ "radio_model.model.decoder.blocks.4.norm2.weight": "model-00002-of-00002.safetensors",
448
+ "radio_model.model.decoder.blocks.5.attn.proj.bias": "model-00002-of-00002.safetensors",
449
+ "radio_model.model.decoder.blocks.5.attn.proj.weight": "model-00002-of-00002.safetensors",
450
+ "radio_model.model.decoder.blocks.5.attn.qkv.weight": "model-00002-of-00002.safetensors",
451
+ "radio_model.model.decoder.blocks.5.mlp.fc1.bias": "model-00002-of-00002.safetensors",
452
+ "radio_model.model.decoder.blocks.5.mlp.fc1.weight": "model-00002-of-00002.safetensors",
453
+ "radio_model.model.decoder.blocks.5.mlp.fc2.bias": "model-00002-of-00002.safetensors",
454
+ "radio_model.model.decoder.blocks.5.mlp.fc2.weight": "model-00002-of-00002.safetensors",
455
+ "radio_model.model.decoder.blocks.5.norm1.bias": "model-00002-of-00002.safetensors",
456
+ "radio_model.model.decoder.blocks.5.norm1.weight": "model-00002-of-00002.safetensors",
457
+ "radio_model.model.decoder.blocks.5.norm2.bias": "model-00002-of-00002.safetensors",
458
+ "radio_model.model.decoder.blocks.5.norm2.weight": "model-00002-of-00002.safetensors",
459
+ "radio_model.model.decoder.filler_tokens": "model-00001-of-00002.safetensors",
460
+ "radio_model.model.decoder.norm.bias": "model-00002-of-00002.safetensors",
461
+ "radio_model.model.decoder.norm.weight": "model-00002-of-00002.safetensors",
462
+ "radio_model.model.decoder.prefix_proj_blocks.0.weight": "model-00002-of-00002.safetensors",
463
+ "radio_model.model.decoder.prefix_tokens": "model-00001-of-00002.safetensors",
464
+ "radio_model.model.decoder.upscale_blocks.0.expansion.weight": "model-00002-of-00002.safetensors",
465
+ "radio_model.model.decoder.upscale_blocks.0.norm.bias": "model-00002-of-00002.safetensors",
466
+ "radio_model.model.decoder.upscale_blocks.0.norm.weight": "model-00002-of-00002.safetensors",
467
+ "radio_model.model.downscale_blocks.0.norm.bias": "model-00001-of-00002.safetensors",
468
+ "radio_model.model.downscale_blocks.0.norm.weight": "model-00001-of-00002.safetensors",
469
+ "radio_model.model.downscale_blocks.0.reduction.weight": "model-00001-of-00002.safetensors",
470
+ "radio_model.model.patch_generator.cls_token.token": "model-00001-of-00002.safetensors",
471
+ "radio_model.model.patch_generator.embedder.weight": "model-00001-of-00002.safetensors",
472
+ "radio_model.model.patch_generator.pos_embed": "model-00001-of-00002.safetensors",
473
+ "radio_model.model.prefix_proj_blocks.0.weight": "model-00001-of-00002.safetensors",
474
+ "radio_model.summary_idxs": "model-00001-of-00002.safetensors"
475
+ }
476
+ }
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
+ from .utils import rank_gate
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
+ with rank_gate():
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)
radio1d.py ADDED
@@ -0,0 +1,1785 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025, 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
+
15
+ """
16
+ RADIO1D: Vision Transformer with Variable-Length 1D Token Compression
17
+
18
+ This module implements RADIO1D, a Vision Transformer variant that compresses spatial tokens
19
+ into a variable-length 1D sequence of "global tokens" during encoding, then reconstructs
20
+ the full spatial resolution via a decoder.
21
+
22
+ Architecture Overview:
23
+ ======================
24
+
25
+ Input Image (B, 3, H_img, W_img) # Any size divisible by patch_size
26
+
27
+
28
+ ┌───────────────────────────────────────────────────────────────┐
29
+ │ ENCODER │
30
+ ├───────────────────────────────────────────────────────────────┤
31
+ │ Patch Embedding → [cls, registers, patches] │
32
+ │ (B, num_prefix + H*W, embed_dim) │
33
+ │ │ │
34
+ │ ▼ │
35
+ │ Transformer Blocks (before downscale) │
36
+ │ (B, num_prefix + H*W, embed_dim) │
37
+ │ │ │
38
+ │ ▼ │
39
+ │ ┌─────────────────────────────────────┐ │
40
+ │ │ PatchMerging (at downscale_levels) │ │
41
+ │ │ - Halves H, W: (H, W) → (H/2, W/2) │ │
42
+ │ │ - Doubles embed_dim: C → 2C │ │
43
+ │ └─────────────────────────────────────┘ │
44
+ │ │ │
45
+ │ ▼ │
46
+ │ Transformer Blocks (after downscale) │
47
+ │ (B, num_prefix + H/2*W/2, 2*embed_dim) │
48
+ │ │ │
49
+ │ ▼ │
50
+ │ Final Norm │
51
+ └───────────────────────────────────────────────────────────────┘
52
+
53
+
54
+ ┌───────────────────────────────────────────────────────────────┐
55
+ │ TOKEN SLICING │
56
+ ├───────────────────────────────────────────────────────────────┤
57
+ │ Sample num_tokens per sample from mode distribution │
58
+ │ (clamped to available spatial tokens H*W) │
59
+ │ │
60
+ │ slice_1d_tokens(): │
61
+ │ - prefix_tokens: (B, num_prefix, C) │
62
+ │ - global_tokens: (B, max_tokens, C) - sliced patches │
63
+ │ - global_token_mask: (B, max_tokens) - validity mask │
64
+ └───────────────────────────────────────────────────────────────┘
65
+
66
+
67
+ ┌───────────────────────────────────────────────────────────────┐
68
+ │ DECODER │
69
+ ├───────────────────────��───────────────────────────────────────┤
70
+ │ 1. Pad global_tokens with filler_tokens to H*W │
71
+ │ (filler_tokens interpolated if size differs from ref) │
72
+ │ │
73
+ │ 2. Use decoder's own learnable prefix_tokens (NOT encoder's) │
74
+ │ This ensures decoder only reconstructs from 1D tokens │
75
+ │ │
76
+ │ 3. Concatenate: [prefix_tokens, padded_patches] │
77
+ │ │
78
+ │ 4. Decoder Blocks (before upscale) │
79
+ │ │
80
+ │ 5. ┌─────────────────────────────────────┐ │
81
+ │ │ PatchSplitting (at upscale_levels) │ │
82
+ │ │ - Doubles H, W: (H, W) → (2H, 2W) │ │
83
+ │ │ - Halves embed_dim: C → C/2 │ │
84
+ │ └─────────────────────────────────────┘ │
85
+ │ │
86
+ │ 6. Decoder Blocks (after upscale) │
87
+ │ │
88
+ │ 7. Final Norm │
89
+ │ (B, num_prefix + H_out*W_out, target_embed_dim) │
90
+ └───────────────────────────────────────────────────────────────┘
91
+
92
+
93
+ ┌───────────────────────────────────────────────────────────────┐
94
+ │ OUTPUT │
95
+ ├───────────────────────────────────────────────────────────────┤
96
+ │ { │
97
+ │ "encoder": (B, num_prefix + max_tokens, encoder_C) │
98
+ │ "decoder": (B, num_prefix + H_out*W_out, target_C) │
99
+ │ } │
100
+ │ │
101
+ │ Sizes depend on input image size, not fixed! │
102
+ └───────────────────────────────────────────────────────────────┘
103
+
104
+ Key Components:
105
+ ===============
106
+
107
+ 1. PatchMerging (Encoder Downscaling)
108
+ - Merges 2x2 patches into 1: (H, W) -> (H/2, W/2)
109
+ - Doubles embedding dimension: C -> 2C
110
+ - Applied at specified downscale_levels (e.g., before block 19)
111
+
112
+ 2. PatchSplitting (Decoder Upscaling)
113
+ - Splits 1 patch into 2x2: (H, W) -> (2H, 2W)
114
+ - Halves embedding dimension: C -> C/2
115
+ - Inverse of PatchMerging
116
+
117
+ 3. Token Slicing (slice_1d_tokens)
118
+ - Samples variable num_tokens per sample from Gaussian mixture distribution
119
+ - Clamps num_tokens to available spatial tokens (important for smaller images)
120
+ - Returns separate prefix tokens (cls + registers) and global tokens (spatial)
121
+ - Pads to max tokens with validity mask
122
+
123
+ 4. RADIO1D_Decoder
124
+ - Uses its own learnable prefix_tokens (NOT the encoder's, to avoid information leak)
125
+ - Uses learnable filler_tokens to pad sliced tokens back to full sequence
126
+ - Applies transformer blocks with PatchSplitting for upscaling
127
+ - Reconstructs original spatial resolution and embedding dimension
128
+
129
+ Key Parameters:
130
+ ===============
131
+
132
+ - downscale_levels: Block indices for encoder downscaling, e.g., [19]
133
+ - modes: Token count modes for sampling, e.g., [64, 128, 196]
134
+ - mode_weights: Probability weights for modes, e.g., [0.33, 0.34, 0.33]
135
+ - decoder_depth: Number of decoder blocks, e.g., 6
136
+ - decoder_upscale_levels: Block indices for decoder upscaling, e.g., [3]
137
+
138
+ Training vs Inference:
139
+ ======================
140
+
141
+ - Training: Samples different num_tokens per sample from mode distribution.
142
+ The encoder output is padded to the max sampled value in the micro-batch.
143
+ - Inference: Uses provided num_tokens or defaults to max(modes).
144
+
145
+ Return Format:
146
+ ==============
147
+
148
+ Both outputs are full sequences [cls, registers, patches/global_tokens]:
149
+ - output["encoder"]: (B, num_prefix + max_tokens, encoder_C) - compressed representation
150
+ - output["decoder"]: (B, num_prefix + output_patches, target_C) - reconstructed full resolution
151
+
152
+ This format is compatible with the RADIO1D framework's expected return structure.
153
+
154
+ Variable Image Size Support:
155
+ ============================
156
+
157
+ The model supports any image size that is a multiple of the patch size in each dimension.
158
+ The decoder uses bilinear interpolation of filler_tokens to adapt to different input sizes.
159
+
160
+ Example sizes (with patch_size=16, downscale_levels=[19]):
161
+ - 224x224 -> 14x14 patches -> 7x7 after downscale -> 14x14 after decode = 196 output patches
162
+ - 448x448 -> 28x28 patches -> 14x14 after downscale -> 28x28 after decode = 784 output patches
163
+ - 320x384 -> 20x24 patches -> 10x12 after downscale -> 20x24 after decode = 480 output patches
164
+ """
165
+
166
+ from abc import ABC
167
+ from copy import deepcopy
168
+ from functools import partial
169
+ from logging import getLogger
170
+ import math
171
+ from typing import (
172
+ Callable,
173
+ Dict,
174
+ Final,
175
+ List,
176
+ Literal,
177
+ Optional,
178
+ Tuple,
179
+ Type,
180
+ Union,
181
+ )
182
+
183
+ import torch
184
+ import torch.distributed as dist
185
+ from torch import nn
186
+ from torch.nn.init import xavier_normal_
187
+ from torch.nn import ModuleList
188
+ from torch.nn import functional as F
189
+ from torch.distributed.nn.functional import all_reduce as all_reduce_with_gradients
190
+ from torch.utils.checkpoint import checkpoint
191
+ from timm.models import register_model, build_model_with_cfg
192
+ from timm.models.vision_transformer import (
193
+ VisionTransformer,
194
+ Mlp,
195
+ Attention,
196
+ Block,
197
+ )
198
+ from timm.layers import (
199
+ AttentionPoolLatent,
200
+ PatchEmbed,
201
+ PatchDropout,
202
+ LayerNorm,
203
+ trunc_normal_,
204
+ get_norm_layer,
205
+ get_act_layer,
206
+ to_2tuple,
207
+ )
208
+
209
+ from .vit_patch_generator import ViTPatchGenerator
210
+ from .utils import get_rank
211
+
212
+ logger = getLogger(__name__)
213
+
214
+
215
+ def round_ste(x: torch.Tensor) -> torch.Tensor:
216
+ """Straight-through estimator for the rounding operation."""
217
+ x_hat = x.detach().round()
218
+ return x + (x_hat - x).detach()
219
+
220
+
221
+ def const_ste(x: torch.Tensor, c: float) -> torch.Tensor:
222
+ """Straight-through estimator that returns a constant `c` with the same shape as `x`,
223
+ while routing gradients through `x` (identity)."""
224
+ return c - x.detach() + x
225
+
226
+
227
+ # Type definitions
228
+ LayerType = Union[str, Callable, Type[nn.Module]]
229
+
230
+
231
+ class PatchMerging(nn.Module):
232
+ """Patch Merging Layer.
233
+
234
+ Downsample features by merging 2x2 neighboring patches.
235
+ """
236
+
237
+ def __init__(
238
+ self,
239
+ dim: int,
240
+ out_dim: Optional[int] = None,
241
+ norm_layer: Type[nn.Module] = nn.LayerNorm,
242
+ size: Union[int, Tuple[int, int]] = 2,
243
+ device=None,
244
+ dtype=None,
245
+ ):
246
+ """
247
+ Args:
248
+ dim: Number of input channels.
249
+ out_dim: Number of output channels (or 2 * dim if None)
250
+ norm_layer: Normalization layer.
251
+ """
252
+ dd = {'device': device, 'dtype': dtype}
253
+ super().__init__()
254
+ self.dim = dim
255
+ self.out_dim = out_dim or 2 * dim
256
+ self.size: Tuple[int, int] = to_2tuple(size)
257
+ self.downscale = math.prod(self.size)
258
+ self.norm = norm_layer(self.downscale * dim, **dd)
259
+ self.reduction = nn.Linear(self.downscale * dim, self.out_dim, bias=False, **dd)
260
+
261
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
262
+ """Forward pass.
263
+
264
+ Args:
265
+ x: Input features with shape (B, H, W, C).
266
+
267
+ Returns:
268
+ Output features with shape (B, H//2, W//2, out_dim).
269
+ """
270
+ B, H, W, C = x.shape
271
+
272
+ pad_values = (0, 0, 0, W % self.size[1], 0, H % self.size[0])
273
+ x = nn.functional.pad(x, pad_values)
274
+ _, H, W, _ = x.shape
275
+
276
+ x = x.reshape(B, H // self.size[0], self.size[0], W // self.size[1], self.size[1], C).permute(0, 1, 3, 4, 2, 5).flatten(3)
277
+ x = self.norm(x)
278
+ x = self.reduction(x)
279
+ return x
280
+
281
+
282
+ def sample_multinomial_batch(
283
+ modes: List[int],
284
+ weights: List[float],
285
+ batch_size: int,
286
+ sigma: float = 30.0,
287
+ generator: Optional[torch.Generator] = None,
288
+ ) -> torch.Tensor:
289
+ """Sample token counts for each sample in a batch.
290
+
291
+ Uses torch.multinomial for sampling, ensuring reproducibility with torch.manual_seed().
292
+
293
+ Args:
294
+ modes: List of mode values (e.g., [128, 256, 512])
295
+ weights: List of weights for each mode
296
+ batch_size: Number of samples to generate
297
+ sigma: Standard deviation for Gaussian mixture
298
+
299
+ Returns:
300
+ Tensor of shape (batch_size,) with sampled token counts
301
+ """
302
+ min_val = min(modes)
303
+ max_val = max(modes)
304
+
305
+ # Create values tensor
306
+ values = torch.arange(min_val, max_val + 1, dtype=torch.long)
307
+
308
+ # Compute the probability density using a mixture of Gaussians
309
+ modes_t = torch.tensor(modes, dtype=torch.float32)
310
+ weights_t = torch.tensor(weights, dtype=torch.float32)
311
+
312
+ # values: (num_values,), modes_t: (num_modes,) -> broadcast to (num_values, num_modes)
313
+ diff = values.unsqueeze(1).float() - modes_t.unsqueeze(0) # (num_values, num_modes)
314
+ gaussian = torch.exp(-diff.pow(2) / (2 * sigma ** 2)) # (num_values, num_modes)
315
+ probs = (gaussian * weights_t.unsqueeze(0)).sum(dim=1) # (num_values,)
316
+ probs = probs / probs.sum()
317
+
318
+ # Sample indices using torch.multinomial
319
+ sampled_indices = torch.multinomial(probs, batch_size, replacement=True, generator=generator)
320
+
321
+ # Map indices to actual token counts
322
+ sampled_values = values[sampled_indices]
323
+ return sampled_values
324
+
325
+
326
+ class GradScale(torch.autograd.Function):
327
+ @staticmethod
328
+ def forward(ctx: torch.autograd.function.FunctionCtx, x: torch.Tensor, lambda_: Union[float, torch.Tensor] = 1.0):
329
+ lambda_ = torch.as_tensor(lambda_, dtype=x.dtype, device=x.device)
330
+ ctx.save_for_backward(lambda_)
331
+ return x.view_as(x)
332
+
333
+ @staticmethod
334
+ def backward(ctx: torch.autograd.function.FunctionCtx, grad_output: torch.Tensor):
335
+ lambda_, = ctx.saved_tensors
336
+ return lambda_ * grad_output, None
337
+
338
+
339
+ def slice_1d_tokens(
340
+ x: torch.Tensor,
341
+ num_tokens: torch.Tensor,
342
+ num_prefix_tokens: int,
343
+ max_tokens: Optional[int] = None,
344
+ use_last_tokens: bool = False,
345
+ dynamic: bool = False,
346
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
347
+ """Slice variable numbers of 1D tokens per sample.
348
+
349
+ Args:
350
+ x: Input tensor of shape (B, N, C) where N = num_prefix + num_spatial
351
+ num_tokens: Tensor of shape (B,) with number of tokens to keep per sample
352
+ num_prefix_tokens: Number of prefix tokens (cls, registers) to always keep
353
+ max_tokens: Maximum number of global tokens (for padding). If None, uses max(num_tokens)
354
+ use_last_tokens: If True, take the last num_tokens instead of the first
355
+
356
+ Returns:
357
+ Tuple of (prefix_tokens, global_tokens, global_token_mask):
358
+ - prefix_tokens: (B, num_prefix, C) prefix tokens (cls, registers)
359
+ - global_tokens: (B, max_tokens, C) padded 1D global tokens
360
+ - global_token_mask: (B, max_tokens) boolean mask (True = valid token)
361
+ """
362
+ B, N, C = x.shape
363
+ device = x.device
364
+ num_spatial = N - num_prefix_tokens
365
+
366
+ # Ensure num_tokens is on the right device and clamp to available spatial tokens
367
+ num_tokens = num_tokens.to(device)
368
+ num_tokens = num_tokens.clamp(max=num_spatial)
369
+
370
+ if max_tokens is None:
371
+ max_tokens = int(num_tokens.max().item())
372
+
373
+ # Separate prefix and global tokens
374
+ prefix = x[:, :num_prefix_tokens] # (B, num_prefix, C)
375
+ global_feats = x[:, num_prefix_tokens:] # (B, num_spatial, C)
376
+
377
+ # Create output tensor with padding for global tokens
378
+ global_tokens = torch.zeros(B, max_tokens, C, device=device, dtype=x.dtype)
379
+
380
+ # Create global token mask
381
+ token_indices = torch.arange(global_feats.shape[1], device=device).unsqueeze(0) # (1, max_tokens)
382
+ global_token_mask = token_indices < num_tokens.unsqueeze(1) # (B, max_tokens)
383
+
384
+ if dynamic:
385
+ zero_ste = const_ste(num_tokens, 0.0)
386
+ one_ste = const_ste(num_tokens, 1.0)
387
+ where_ste = torch.where(global_token_mask, one_ste.unsqueeze(1), zero_ste.unsqueeze(1))
388
+ # Reducing the gradient magnitude through this gate application stabilizes training.
389
+ # Since it's multiplicatively applied to every surviving token, then `1 / num_tokens` means
390
+ # that the signal is consistent across different token counts.
391
+ where_ste = GradScale.apply(where_ste, 1 / num_tokens.clamp_min(1).unsqueeze(-1))
392
+ global_feats = global_feats * where_ste.unsqueeze(-1)
393
+
394
+ cpu_num_tokens = num_tokens.tolist()
395
+
396
+ # Copy tokens for each sample (clamped to available)
397
+ for i in range(B):
398
+ n = int(cpu_num_tokens[i])
399
+ if use_last_tokens:
400
+ # Take the last n tokens from the spatial sequence
401
+ global_tokens[i, :n] = global_feats[i, -n:]
402
+ else:
403
+ # Take the first n tokens from the spatial sequence
404
+ global_tokens[i, :n] = global_feats[i, :n]
405
+
406
+ return prefix, global_tokens, global_token_mask[:, :max_tokens]
407
+
408
+
409
+ class PatchSplitting(nn.Module):
410
+ """Patch Splitting Layer - Inverse of PatchMerging.
411
+
412
+ Upsample features by splitting each patch into 2x2 neighboring patches.
413
+ """
414
+
415
+ def __init__(
416
+ self,
417
+ dim: int,
418
+ out_dim: Optional[int] = None,
419
+ norm_layer: Type[nn.Module] = nn.LayerNorm,
420
+ ):
421
+ """
422
+ Args:
423
+ dim: Number of input channels.
424
+ out_dim: Number of output channels (or dim // 2 if None)
425
+ norm_layer: Normalization layer.
426
+ """
427
+ super().__init__()
428
+ self.dim = dim
429
+ self.out_dim = out_dim or dim // 2
430
+ # Expand channels to 4x output dim, then reshape to 2x2 spatial
431
+ self.expansion = nn.Linear(dim, 4 * self.out_dim, bias=False)
432
+ self.norm = norm_layer(self.out_dim)
433
+
434
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
435
+ """Forward pass.
436
+
437
+ Args:
438
+ x: Input features with shape (B, H, W, C).
439
+
440
+ Returns:
441
+ Output features with shape (B, 2*H, 2*W, out_dim).
442
+ """
443
+ B, H, W, C = x.shape
444
+
445
+ # Expand channels: (B, H, W, C) -> (B, H, W, 4 * out_dim)
446
+ x = self.expansion(x)
447
+
448
+ # Reshape to split each token into 2x2 neighbors
449
+ # (B, H, W, 4 * out_dim) -> (B, H, W, 2, 2, out_dim) -> (B, 2*H, 2*W, out_dim)
450
+ x = x.reshape(B, H, W, 2, 2, self.out_dim)
451
+ x = x.permute(0, 1, 3, 2, 4, 5).reshape(B, 2 * H, 2 * W, self.out_dim)
452
+
453
+ x = self.norm(x)
454
+ return x
455
+
456
+
457
+ class RADIO1D_Decoder(nn.Module):
458
+ """Decoder for RADIO1D that reconstructs the original sequence length and embedding dimension.
459
+
460
+ Takes compressed global tokens from the encoder and reconstructs the full spatial resolution
461
+ by applying inverse patch merging (splitting) operations.
462
+ """
463
+
464
+ def __init__(
465
+ self,
466
+ input_embed_dim: int,
467
+ target_embed_dim: int,
468
+ ref_spatial_size: Tuple[int, int],
469
+ num_prefix_tokens: int,
470
+ depth: int,
471
+ upscale_levels: List[int],
472
+ num_heads: int = 16,
473
+ mlp_ratio: float = 4.0,
474
+ norm_layer: Type[nn.Module] = nn.LayerNorm,
475
+ ):
476
+ """
477
+ Args:
478
+ input_embed_dim: Embedding dimension of input (after encoder downscaling).
479
+ target_embed_dim: Target embedding dimension (original encoder dimension).
480
+ ref_spatial_size: Reference spatial size (H, W) for filler token initialization.
481
+ This is the expected spatial dimensions at the model's nominal image size
482
+ (e.g., (7, 7) for 224x224 with patch_size=16 and one downscale).
483
+ At runtime, filler tokens are interpolated to match actual input size.
484
+ num_prefix_tokens: Number of prefix tokens (cls + registers).
485
+ depth: Number of decoder blocks.
486
+ upscale_levels: List of block indices where upscaling should happen.
487
+ num_heads: Number of attention heads.
488
+ mlp_ratio: MLP ratio for transformer blocks.
489
+ norm_layer: Normalization layer.
490
+ """
491
+ super().__init__()
492
+
493
+ self.input_embed_dim = input_embed_dim
494
+ self.target_embed_dim = target_embed_dim
495
+ self.ref_H, self.ref_W = ref_spatial_size
496
+ self.num_prefix_tokens = num_prefix_tokens
497
+ self.upscale_levels = set(upscale_levels) if upscale_levels else set()
498
+
499
+ # Learnable filler tokens - initialized at reference size, interpolated at runtime if needed
500
+ ref_num_patches = self.ref_H * self.ref_W
501
+ scale = input_embed_dim ** -0.5
502
+ self.filler_tokens = nn.Parameter(torch.randn(ref_num_patches, input_embed_dim) * scale)
503
+
504
+ # Learnable prefix tokens for the decoder (independent from encoder's prefix tokens)
505
+ # This ensures the decoder only reconstructs from 1D global tokens, not encoder prefix info
506
+ self.prefix_tokens = nn.Parameter(torch.randn(num_prefix_tokens, input_embed_dim) * scale)
507
+
508
+ # Build blocks and upscale layers
509
+ embed_dim = input_embed_dim
510
+ blocks = []
511
+ upscale_blocks = []
512
+ prefix_proj_blocks = []
513
+
514
+ for i in range(depth):
515
+ if upscale_levels is not None and i in upscale_levels:
516
+ upscale_block = PatchSplitting(embed_dim)
517
+ # Projection for prefix tokens to match new (reduced) embed_dim
518
+ prefix_proj = nn.Linear(embed_dim, upscale_block.out_dim, bias=False)
519
+ num_heads = max(1, num_heads * upscale_block.out_dim // embed_dim)
520
+ embed_dim = upscale_block.out_dim
521
+ upscale_blocks.append(upscale_block)
522
+ prefix_proj_blocks.append(prefix_proj)
523
+
524
+ blocks.append(Block(
525
+ dim=embed_dim,
526
+ num_heads=num_heads,
527
+ mlp_ratio=mlp_ratio,
528
+ norm_layer=norm_layer,
529
+ ))
530
+
531
+ self.blocks = nn.ModuleList(blocks)
532
+ self.upscale_blocks = nn.ModuleList(upscale_blocks)
533
+ self.prefix_proj_blocks = nn.ModuleList(prefix_proj_blocks)
534
+
535
+ # Final norm
536
+ self.norm = norm_layer(embed_dim)
537
+
538
+ # Verify output dimension matches target
539
+ assert embed_dim == target_embed_dim, \
540
+ f"Decoder output dim {embed_dim} doesn't match target {target_embed_dim}"
541
+
542
+ def _apply_upscale(
543
+ self,
544
+ x: torch.Tensor,
545
+ upscale_idx: int,
546
+ H: int,
547
+ W: int,
548
+ ) -> Tuple[torch.Tensor, int, int]:
549
+ """Apply patch splitting upscale operation.
550
+
551
+ Args:
552
+ x: Input tensor of shape (B, N, C) where N = num_prefix_tokens + H*W
553
+ upscale_idx: Index into self.upscale_blocks and self.prefix_proj_blocks
554
+ H: Current spatial height (in patches)
555
+ W: Current spatial width (in patches)
556
+
557
+ Returns:
558
+ Tuple of (upscaled tensor, new H, new W)
559
+ """
560
+ B, N, C = x.shape
561
+
562
+ # Separate prefix tokens from patch tokens
563
+ prefix_tokens = x[:, :self.num_prefix_tokens] # (B, num_prefix, C)
564
+ patch_tokens = x[:, self.num_prefix_tokens:] # (B, H*W, C)
565
+
566
+ # Reshape patch tokens to spatial format for PatchSplitting
567
+ patch_tokens = patch_tokens.reshape(B, H, W, C)
568
+
569
+ # Apply patch splitting (spatial upsampling)
570
+ patch_tokens = self.upscale_blocks[upscale_idx](patch_tokens) # (B, 2H, 2W, C')
571
+
572
+ # Get new dimensions
573
+ _, H_new, W_new, C_new = patch_tokens.shape
574
+
575
+ # Reshape back to sequence format
576
+ patch_tokens = patch_tokens.reshape(B, H_new * W_new, C_new)
577
+
578
+ # Project prefix tokens to match new channel dimension
579
+ prefix_tokens = self.prefix_proj_blocks[upscale_idx](prefix_tokens) # (B, num_prefix, C')
580
+
581
+ # Concatenate prefix and patch tokens
582
+ x = torch.cat([prefix_tokens, patch_tokens], dim=1)
583
+
584
+ return x, H_new, W_new
585
+
586
+ def _get_filler_tokens(self, H: int, W: int, B: int, device: torch.device) -> torch.Tensor:
587
+ """Get filler tokens interpolated to the required spatial size.
588
+
589
+ Args:
590
+ H: Target height in patches
591
+ W: Target width in patches
592
+ B: Batch size
593
+ device: Target device
594
+
595
+ Returns:
596
+ Filler tokens of shape (B, H*W, C)
597
+ """
598
+ if H == self.ref_H and W == self.ref_W:
599
+ # No interpolation needed - matches reference size
600
+ filler = self.filler_tokens.unsqueeze(0).expand(B, -1, -1)
601
+ else:
602
+ # Interpolate filler tokens to match the required size
603
+ # Reshape to 2D grid, interpolate, then flatten
604
+ filler_2d = self.filler_tokens.reshape(self.ref_H, self.ref_W, -1).permute(2, 0, 1).unsqueeze(0)
605
+ # Interpolate: (1, C, ref_H, ref_W) -> (1, C, H, W)
606
+ filler_2d = nn.functional.interpolate(filler_2d, size=(H, W), mode='bilinear', align_corners=False)
607
+ # Reshape back: (1, C, H, W) -> (1, H*W, C)
608
+ filler = filler_2d.squeeze(0).permute(1, 2, 0).reshape(1, H * W, -1)
609
+ filler = filler.expand(B, -1, -1)
610
+
611
+ return filler.to(device)
612
+
613
+ def forward(
614
+ self,
615
+ global_tokens: torch.Tensor,
616
+ global_token_mask: torch.Tensor,
617
+ input_size: Tuple[int, int],
618
+ ) -> Tuple[torch.Tensor, int, int]:
619
+ """Forward pass through decoder.
620
+
621
+ Args:
622
+ global_tokens: Global tokens from encoder (B, num_tokens, C_in), possibly padded
623
+ global_token_mask: Boolean mask for valid global tokens (B, num_tokens)
624
+ input_size: Tuple of (H, W) spatial dimensions of the downscaled patches
625
+
626
+ Returns:
627
+ Tuple of (features, H, W):
628
+ - features: Reconstructed features (B, num_prefix + H*W, target_embed_dim)
629
+ - H: Output spatial height
630
+ - W: Output spatial width
631
+ """
632
+ B = global_tokens.shape[0]
633
+ H, W = input_size
634
+ device = global_tokens.device
635
+
636
+ # Get filler tokens (interpolated if needed for variable image sizes)
637
+ filler = self._get_filler_tokens(H, W, B, device)
638
+
639
+ # Combine global tokens with filler tokens
640
+ # Valid global tokens replace the corresponding filler tokens
641
+ patch_tokens = filler.clone()
642
+ # Use the mask to place valid global tokens
643
+ for i in range(B):
644
+ n_valid = global_token_mask[i].sum().int().item()
645
+ patch_tokens[i, :n_valid] = global_tokens[i, :n_valid]
646
+
647
+ # Use decoder's own learnable prefix tokens (not encoder's, to avoid information leak)
648
+ prefix_tokens = self.prefix_tokens.unsqueeze(0).expand(B, -1, -1)
649
+
650
+ # Concatenate prefix tokens and patch tokens
651
+ x = torch.cat([prefix_tokens, patch_tokens], dim=1) # (B, num_prefix + H*W, C)
652
+
653
+ # Apply decoder blocks with optional upscaling
654
+ upscale_idx = 0
655
+ for i, blk in enumerate(self.blocks):
656
+ # Apply upscale before this block if specified
657
+ if i in self.upscale_levels:
658
+ x, H, W = self._apply_upscale(x, upscale_idx, H, W)
659
+ upscale_idx += 1
660
+
661
+ # Apply transformer block
662
+ x = blk(x)
663
+
664
+ x = self.norm(x)
665
+
666
+ return x, H, W
667
+
668
+
669
+ class KSampleDistribution(ABC):
670
+ def __init__(self, synchronized: bool = False):
671
+ self.synchronized = synchronized
672
+ g = None
673
+ if synchronized:
674
+ g = torch.Generator(device='cuda')
675
+ g.manual_seed(42)
676
+ self.generator = g
677
+
678
+ def set_curr_step(self, step: int):
679
+ if self.generator is not None:
680
+ self.generator.manual_seed(step)
681
+
682
+ def get_max_tokens(self, outside_max: int) -> int:
683
+ return outside_max
684
+
685
+ def get_expected_tokens(self, outside_max: int) -> int:
686
+ return self.get_max_tokens(outside_max)
687
+
688
+ def _sample(self, batch_size: int, max_tokens: int) -> torch.Tensor:
689
+ ...
690
+
691
+ def sample(self, batch_size: int, max_tokens: int) -> torch.Tensor:
692
+ inner_bs = 1 if self.synchronized else batch_size
693
+ inner_sample = self._sample(inner_bs, max_tokens)
694
+ if self.synchronized:
695
+ inner_sample = inner_sample.expand(batch_size)
696
+ return inner_sample
697
+
698
+
699
+ class MultiModeGaussSampleDistribution(KSampleDistribution):
700
+ def __init__(self, modes: List[int], mode_weights: List[float], max_tokens: Optional[int] = None, synchronized: bool = False):
701
+ super().__init__(synchronized=synchronized)
702
+ if len(modes) != len(mode_weights):
703
+ raise ValueError("modes and mode_weights must have the same length")
704
+ assert all(mode > 0 for mode in modes)
705
+ assert all(weight >= 0 for weight in mode_weights)
706
+ assert sum(mode_weights) == 1.0
707
+ self.modes = modes
708
+ self.mode_weights = mode_weights
709
+ self._max_tokens = max_tokens
710
+
711
+ def get_max_tokens(self, outside_max: int) -> int:
712
+ my_max = self._max_tokens or max(self.modes)
713
+ return min(my_max, outside_max)
714
+
715
+ def _sample(self, batch_size: int, max_tokens: int) -> torch.Tensor:
716
+ num_tokens_per_sample = sample_multinomial_batch(self.modes, self.mode_weights, batch_size, generator=self.generator)
717
+ torch.clamp_max_(num_tokens_per_sample, max_tokens)
718
+ return num_tokens_per_sample
719
+
720
+
721
+ class UniformKSampleDistribution(KSampleDistribution):
722
+ def _sample(self, batch_size: int, max_tokens: int) -> torch.Tensor:
723
+ f = torch.rand(batch_size, dtype=torch.float32, device='cuda', generator=self.generator)
724
+ f = torch.round(f * max_tokens)
725
+ f = torch.clamp(f, min=1.0, max=max_tokens)
726
+ return f.long()
727
+
728
+ def get_expected_tokens(self, outside_max: int) -> int:
729
+ return outside_max // 2
730
+
731
+ class BetaKSampleDistribution(KSampleDistribution):
732
+ def __init__(self, target_pct: float = 0.25, synchronized: bool = False):
733
+ super().__init__(synchronized=synchronized)
734
+ self.target_pct = target_pct
735
+
736
+ # This is one particular solution where the mode of the beta distribution is equal to target_pct
737
+ # `mode = (alpha - 1) / (alpha + beta - 2)`
738
+ # and we add the additional constraint that
739
+ # `alpha + beta = 2 / rate`
740
+ rate = torch.as_tensor(target_pct, dtype=torch.float32, device='cuda')
741
+ alpha = 3 - (2 * rate)
742
+ beta = (2 / rate) - 3 + (2 * rate)
743
+
744
+ self.alpha = alpha
745
+ self.beta = beta
746
+ self.beta_dist = torch.distributions.Beta(alpha, beta)
747
+
748
+ def _sample(self, batch_size: int, max_tokens: int) -> torch.Tensor:
749
+ f = torch._sample_dirichlet(
750
+ self.beta_dist._dirichlet.concentration,
751
+ generator=self.generator,
752
+ ).select(-1, 0)
753
+ f = torch.round(f * max_tokens)
754
+ f = torch.clamp(f, min=1.0, max=max_tokens)
755
+ return f.long()
756
+
757
+ def get_expected_tokens(self, outside_max: int) -> int:
758
+ beta_mean = self.alpha / (self.alpha + self.beta)
759
+ return int(beta_mean * outside_max)
760
+
761
+
762
+ class TriangleKSampleDistribution(KSampleDistribution):
763
+ '''
764
+ Triangle distribution, defined as p(x) = 2 - 2x for x in [0, 1]
765
+ with expected value 1/3.
766
+ '''
767
+
768
+ def _sample(self, batch_size: int, max_tokens: int) -> torch.Tensor:
769
+ u = torch.rand(batch_size, dtype=torch.float32, device='cuda', generator=self.generator)
770
+ # Use inverse transform sampling
771
+ f = 1 - torch.sqrt(u.clamp_min_(1e-8))
772
+ f = torch.round(f * (max_tokens - 1)) + 1
773
+ f = torch.clamp(f, min=1.0, max=max_tokens)
774
+ return f.long()
775
+
776
+
777
+ def get_expected_tokens(self, outside_max: int) -> int:
778
+ return outside_max // 3
779
+
780
+
781
+ class InterpolateKSampleDistributions(KSampleDistribution):
782
+ def __init__(self, dist_a: Union[KSampleDistribution, dict, str], dist_b: Union[KSampleDistribution, dict, str], num_steps: int, synchronized: bool = False):
783
+ super().__init__(synchronized=synchronized)
784
+ self.dist_a = self._instantiate(dist_a)
785
+ self.dist_b = self._instantiate(dist_b)
786
+ self.num_steps = num_steps
787
+ self.curr_step = 0
788
+
789
+ def set_curr_step(self, step: int):
790
+ super().set_curr_step(step)
791
+ self.curr_step = step
792
+ self.dist_a.set_curr_step(step)
793
+ self.dist_b.set_curr_step(step)
794
+
795
+ def get_max_tokens(self, outside_max: int) -> int:
796
+ return max(self.dist_a.get_max_tokens(outside_max), self.dist_b.get_max_tokens(outside_max))
797
+
798
+ def get_expected_tokens(self, outside_max: int) -> int:
799
+ ea = self.dist_a.get_expected_tokens(outside_max)
800
+ eb = self.dist_b.get_expected_tokens(outside_max)
801
+ alpha = max(0, min(1, self.curr_step / self.num_steps))
802
+ return int((1 - alpha) * ea + alpha * eb)
803
+
804
+ def _sample(self, batch_size: int, max_tokens: int) -> torch.Tensor:
805
+ sample_a = self.dist_a.sample(batch_size, max_tokens).float()
806
+ sample_b = self.dist_b.sample(batch_size, max_tokens).float()
807
+ alpha = max(0, min(1, self.curr_step / self.num_steps))
808
+ f = (1 - alpha) * sample_a + alpha * sample_b
809
+ f = torch.round(f).clamp(min=1.0, max=max_tokens)
810
+ return f.long()
811
+
812
+ def _instantiate(self, dist: Union[KSampleDistribution, dict, str]) -> KSampleDistribution:
813
+ if isinstance(dist, KSampleDistribution):
814
+ return dist
815
+ elif isinstance(dist, dict):
816
+ dist_type = dist.pop('type')
817
+ if dist_type not in _K_SAMPLER_FACTORY:
818
+ raise ValueError(f"Unknown KSampleDistribution type: {dist_type}")
819
+ return _K_SAMPLER_FACTORY[dist_type](**dist)
820
+ elif isinstance(dist, str):
821
+ if dist not in _K_SAMPLER_FACTORY:
822
+ raise ValueError(f"Unknown KSampleDistribution type: {dist}")
823
+ return _K_SAMPLER_FACTORY[dist]()
824
+ else:
825
+ raise ValueError("dist must be a KSampleDistribution instance, a dict, or a str")
826
+
827
+
828
+ _K_SAMPLER_FACTORY = {
829
+ 'multimode_gaussian': MultiModeGaussSampleDistribution,
830
+ 'uniform': UniformKSampleDistribution,
831
+ 'beta': BetaKSampleDistribution,
832
+ 'triangle': TriangleKSampleDistribution,
833
+ 'interpolate': InterpolateKSampleDistributions,
834
+ }
835
+
836
+
837
+ class RADIO1D(VisionTransformer):
838
+ """ Vision Transformer
839
+
840
+ A PyTorch impl of : `An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale`
841
+ - https://arxiv.org/abs/2010.11929
842
+ """
843
+ dynamic_img_size: Final[bool]
844
+ _iter_count: torch.Tensor
845
+ dynamic_rate_vec: Optional[torch.Tensor]
846
+
847
+ def __init__(
848
+ self,
849
+ img_size: Union[int, Tuple[int, int]] = 224,
850
+ patch_size: Union[int, Tuple[int, int]] = 16,
851
+ in_chans: int = 3,
852
+ num_classes: int = 1000,
853
+ global_pool: Literal['', 'avg', 'avgmax', 'max', 'token', 'map'] = 'token',
854
+ embed_dim: int = 768,
855
+ depth: int = 12,
856
+ num_heads: int = 12,
857
+ mlp_ratio: float = 4.,
858
+ qkv_bias: bool = True,
859
+ qk_norm: bool = False,
860
+ # scale_attn_norm: bool = False,
861
+ # scale_mlp_norm: bool = False,
862
+ proj_bias: bool = True,
863
+ init_values: Optional[float] = None,
864
+ class_token: bool = True,
865
+ pos_embed: str = 'learn',
866
+ no_embed_class: bool = False,
867
+ reg_tokens: int = 0,
868
+ pre_norm: bool = False,
869
+ final_norm: bool = True,
870
+ fc_norm: Optional[bool] = None,
871
+ pool_include_prefix: bool = False,
872
+ dynamic_img_size: bool = False,
873
+ dynamic_img_pad: bool = False,
874
+ drop_rate: float = 0.,
875
+ pos_drop_rate: float = 0.,
876
+ patch_drop_rate: float = 0.,
877
+ proj_drop_rate: float = 0.,
878
+ attn_drop_rate: float = 0.,
879
+ drop_path_rate: float = 0.,
880
+ weight_init: Literal['skip', 'jax', 'jax_nlhb', 'moco', ''] = '',
881
+ fix_init: bool = False,
882
+ embed_layer: Callable = PatchEmbed,
883
+ embed_norm_layer: Optional[LayerType] = None,
884
+ norm_layer: Optional[LayerType] = None,
885
+ act_layer: Optional[LayerType] = None,
886
+ block_fn: Type[nn.Module] = Block,
887
+ mlp_layer: Type[nn.Module] = Mlp,
888
+ num_cls_tokens: Optional[int] = None,
889
+ cpe_max_size: Optional[int] = None,
890
+ num_registers: Optional[int] = None,
891
+ register_multiple: Optional[int] = None,
892
+ downscale_levels: Optional[List[int]] = None,
893
+ k_sample_config: Optional[dict] = None,
894
+ decoder_depth: int = 6,
895
+ decoder_upscale_levels: Optional[List[int]] = None,
896
+ dynamic_rate: bool = False,
897
+ dynamic_temperature: float = 1.0,
898
+ progressive_reduction: bool = False,
899
+ cka_weight: float = 0.0,
900
+ cka_weight_final: Optional[float] = None,
901
+ uniform_k: bool = False,
902
+ grad_checkpointing: Union[bool, int] = False,
903
+ decoder_grad_checkpointing: Union[bool, int] = False,
904
+ downscale_expansion_factor: float = 2.0,
905
+ ) -> None:
906
+ """
907
+ Args:
908
+ img_size: Input image size.
909
+ patch_size: Patch size.
910
+ in_chans: Number of image input channels.
911
+ num_classes: Number of classes for classification head.
912
+ global_pool: Type of global pooling for final sequence (default: 'token').
913
+ embed_dim: Transformer embedding dimension.
914
+ depth: Depth of transformer.
915
+ num_heads: Number of attention heads.
916
+ mlp_ratio: Ratio of mlp hidden dim to embedding dim.
917
+ qkv_bias: Enable bias for qkv projections if True.
918
+ init_values: Layer-scale init values (layer-scale enabled if not None).
919
+ class_token: Use class token.
920
+ no_embed_class: Don't include position embeddings for class (or reg) tokens.
921
+ reg_tokens: Number of register tokens.
922
+ pre_norm: Enable norm after embeddings, before transformer blocks (standard in CLIP ViT).
923
+ final_norm: Enable norm after transformer blocks, before head (standard in most ViT).
924
+ fc_norm: Move final norm after pool (instead of before), if None, enabled when global_pool == 'avg'.
925
+ drop_rate: Head dropout rate.
926
+ pos_drop_rate: Position embedding dropout rate.
927
+ attn_drop_rate: Attention dropout rate.
928
+ drop_path_rate: Stochastic depth rate.
929
+ weight_init: Weight initialization scheme.
930
+ fix_init: Apply weight initialization fix (scaling w/ layer index).
931
+ embed_layer: Patch embedding layer.
932
+ embed_norm_layer: Normalization layer to use / override in patch embed module.
933
+ norm_layer: Normalization layer.
934
+ act_layer: MLP activation layer.
935
+ block_fn: Transformer block layer.
936
+ num_cls_tokens: Number of class tokens.
937
+ cpe_max_size: Maximum size of the input image.
938
+ num_registers: Number of registers.
939
+ register_multiple: Register multiple.
940
+ downscale_levels: Downscale levels.
941
+ modes: Modes for the input image size.
942
+ mode_weights: Weights for the modes.
943
+ decoder_depth: Number of decoder blocks.
944
+ decoder_upscale_levels: Block indices in decoder where upscaling should happen.
945
+ """
946
+ super().__init__()
947
+ assert global_pool in ('', 'avg', 'avgmax', 'max', 'token', 'map')
948
+ assert class_token or global_pool != 'token'
949
+ assert pos_embed in ('', 'none', 'learn')
950
+ use_fc_norm = global_pool in ('avg', 'avgmax', 'max') if fc_norm is None else fc_norm
951
+ norm_layer = get_norm_layer(norm_layer) or LayerNorm
952
+ embed_norm_layer = get_norm_layer(embed_norm_layer)
953
+ act_layer = get_act_layer(act_layer) or nn.GELU
954
+
955
+ self.num_classes = num_classes
956
+ self.global_pool = global_pool
957
+ self.num_features = self.head_hidden_size = self.embed_dim = embed_dim # for consistency with other models
958
+ self.num_prefix_tokens = 1 if class_token else 0
959
+ self.num_prefix_tokens += reg_tokens
960
+ self.num_reg_tokens = reg_tokens
961
+ self.has_class_token = class_token
962
+ self.no_embed_class = no_embed_class
963
+ self.pool_include_prefix = pool_include_prefix
964
+ self.dynamic_img_size = dynamic_img_size
965
+ self.grad_checkpointing = False
966
+ self.dynamic_rate = dynamic_rate
967
+ self.dynamic_temperature = dynamic_temperature
968
+ self.progressive_reduction = progressive_reduction
969
+ self.cka_weight = cka_weight
970
+ self.cka_weight_final = cka_weight_final or cka_weight
971
+
972
+ if dynamic_rate:
973
+ if num_registers is None or num_registers == 0:
974
+ raise ValueError("dynamic_rate requires at least one register token")
975
+ self.register_buffer('dynamic_rate_vec', torch.randn(embed_dim))
976
+ self.dynamic_rate_projector = nn.Linear(embed_dim, 1)
977
+
978
+ if k_sample_config is None:
979
+ self.k_sampler = UniformKSampleDistribution(synchronized=dynamic_rate or uniform_k)
980
+ else:
981
+ k_sample_config = deepcopy(k_sample_config)
982
+ sampler_type = k_sample_config.pop('type')
983
+ self.k_sampler = _K_SAMPLER_FACTORY[sampler_type](**k_sample_config, synchronized=dynamic_rate or uniform_k)
984
+
985
+ embed_args = {}
986
+ if dynamic_img_size:
987
+ # flatten deferred until after pos embed
988
+ embed_args.update(dict(strict_img_size=False, output_fmt='NHWC'))
989
+ if embed_norm_layer is not None:
990
+ embed_args['norm_layer'] = embed_norm_layer
991
+ self.patch_embed = embed_layer(
992
+ img_size=img_size,
993
+ patch_size=patch_size,
994
+ in_chans=in_chans,
995
+ embed_dim=embed_dim,
996
+ bias=not pre_norm, # disable bias if pre-norm is used (e.g. CLIP)
997
+ dynamic_img_pad=dynamic_img_pad,
998
+ **embed_args,
999
+ )
1000
+ num_patches = self.patch_embed.num_patches
1001
+ reduction = self.patch_embed.feat_ratio() if hasattr(self.patch_embed, 'feat_ratio') else patch_size
1002
+
1003
+ self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) if class_token else None
1004
+ self.reg_token = nn.Parameter(torch.zeros(1, reg_tokens, embed_dim)) if reg_tokens else None
1005
+ embed_len = num_patches if no_embed_class else num_patches + self.num_prefix_tokens
1006
+ if not pos_embed or pos_embed == 'none':
1007
+ self.pos_embed = None
1008
+ else:
1009
+ self.pos_embed = nn.Parameter(torch.randn(1, embed_len, embed_dim) * .02)
1010
+ self.pos_drop = nn.Dropout(p=pos_drop_rate)
1011
+ if patch_drop_rate > 0:
1012
+ self.patch_drop = PatchDropout(
1013
+ patch_drop_rate,
1014
+ num_prefix_tokens=self.num_prefix_tokens,
1015
+ )
1016
+ else:
1017
+ self.patch_drop = nn.Identity()
1018
+ self.norm_pre = norm_layer(embed_dim) if pre_norm else nn.Identity()
1019
+
1020
+ if cpe_max_size is not None:
1021
+ # Replace patch embed with CPE patch generator
1022
+ input_dims = img_size
1023
+ max_img_size = int(round(cpe_max_size / patch_size) * patch_size)
1024
+ self.patch_generator = ViTPatchGenerator(
1025
+ patch_size=patch_size,
1026
+ embed_dim=embed_dim,
1027
+ input_dims=input_dims,
1028
+ normalize_patches=pre_norm,
1029
+ cls_token=self.has_class_token,
1030
+ max_input_dims=max_img_size,
1031
+ pos_dropout=pos_drop_rate,
1032
+ num_cls_tokens=num_cls_tokens,
1033
+ register_multiple=register_multiple,
1034
+ num_registers=num_registers,
1035
+ #init_from=self,
1036
+ #adaptive_patch_tokenizer_config=None,
1037
+ )
1038
+ self.patch_embed = None
1039
+ self.cls_token = None
1040
+ self.pos_embed = None
1041
+ self.pos_drop = None
1042
+ self.num_cls_tokens = num_cls_tokens
1043
+ self.num_registers = num_registers
1044
+ self.num_prefix_tokens = self.patch_generator.num_cls_patches
1045
+ else:
1046
+ self.patch_generator = None
1047
+
1048
+ dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule
1049
+
1050
+ # Save original dimensions for decoder creation
1051
+ original_embed_dim = embed_dim
1052
+ original_num_patches = num_patches
1053
+ original_num_heads = num_heads
1054
+
1055
+ downscale_blocks = []
1056
+ prefix_proj_blocks = [] # Projection layers for prefix tokens during downscaling
1057
+ blocks = []
1058
+ feature_info = []
1059
+ for i in range(depth):
1060
+ if downscale_levels is not None and i in downscale_levels:
1061
+ downscale_block = PatchMerging(embed_dim)
1062
+ # Projection for prefix tokens to match new embed_dim
1063
+ prefix_proj = nn.Linear(embed_dim, downscale_block.out_dim, bias=False)
1064
+ num_heads = int(num_heads * downscale_block.out_dim // embed_dim)
1065
+ embed_dim = downscale_block.out_dim
1066
+ downscale_blocks.append(downscale_block)
1067
+ prefix_proj_blocks.append(prefix_proj)
1068
+
1069
+ blocks.append(block_fn(
1070
+ dim=embed_dim,
1071
+ num_heads=num_heads,
1072
+ mlp_ratio=mlp_ratio,
1073
+ qkv_bias=qkv_bias,
1074
+ qk_norm=qk_norm,
1075
+ # scale_attn_norm=scale_attn_norm,
1076
+ # scale_mlp_norm=scale_mlp_norm,
1077
+ proj_bias=proj_bias,
1078
+ init_values=init_values,
1079
+ proj_drop=proj_drop_rate,
1080
+ attn_drop=attn_drop_rate,
1081
+ drop_path=dpr[i],
1082
+ norm_layer=norm_layer,
1083
+ act_layer=act_layer,
1084
+ mlp_layer=mlp_layer,
1085
+ ))
1086
+ feature_info.append(dict(module=f'blocks.{i}', num_chs=embed_dim, reduction=reduction))
1087
+
1088
+ self.blocks = ModuleList(blocks)
1089
+ self.downscale_blocks = ModuleList(downscale_blocks)
1090
+ self.prefix_proj_blocks = ModuleList(prefix_proj_blocks)
1091
+ self.downscale_levels = set(downscale_levels) if downscale_levels else set()
1092
+ self.feature_info = feature_info
1093
+ self.norm = norm_layer(embed_dim) if final_norm and not use_fc_norm else nn.Identity()
1094
+
1095
+ if isinstance(grad_checkpointing, bool):
1096
+ self.grad_checkpointing = len(blocks) if grad_checkpointing else 0
1097
+ else:
1098
+ self.grad_checkpointing = min(grad_checkpointing, len(blocks))
1099
+
1100
+ # Classifier Head
1101
+ if global_pool == 'map':
1102
+ self.attn_pool = AttentionPoolLatent(
1103
+ self.embed_dim,
1104
+ num_heads=num_heads,
1105
+ mlp_ratio=mlp_ratio,
1106
+ norm_layer=norm_layer,
1107
+ act_layer=act_layer,
1108
+ )
1109
+ else:
1110
+ self.attn_pool = None
1111
+ self.fc_norm = norm_layer(embed_dim) if final_norm and use_fc_norm else nn.Identity()
1112
+ self.head_drop = nn.Dropout(drop_rate)
1113
+ self.head = nn.Linear(self.embed_dim, num_classes) if num_classes > 0 else nn.Identity()
1114
+
1115
+ # Create decoder (always needed to reconstruct from sliced global tokens)
1116
+ # Compute dimensions after encoder (with or without downscaling)
1117
+ if downscale_levels:
1118
+ num_downscales = len(downscale_levels)
1119
+ encoder_num_patches = original_num_patches // (4 ** num_downscales)
1120
+ encoder_embed_dim = embed_dim # embed_dim after all downscales
1121
+ # Default upscale_levels: upscale at the midpoint of decoder
1122
+ if decoder_upscale_levels is None:
1123
+ decoder_upscale_levels = [decoder_depth // 2] * num_downscales
1124
+ else:
1125
+ encoder_num_patches = original_num_patches
1126
+ encoder_embed_dim = original_embed_dim
1127
+ decoder_upscale_levels = [] # No upscaling needed
1128
+
1129
+ # Compute reference spatial size for decoder filler tokens
1130
+ ref_H = ref_W = int(encoder_num_patches ** 0.5)
1131
+ self.decoder = RADIO1D_Decoder(
1132
+ input_embed_dim=encoder_embed_dim,
1133
+ target_embed_dim=original_embed_dim,
1134
+ ref_spatial_size=(ref_H, ref_W), # Reference size for filler token init
1135
+ num_prefix_tokens=self.num_prefix_tokens,
1136
+ depth=decoder_depth,
1137
+ upscale_levels=decoder_upscale_levels,
1138
+ num_heads=original_num_heads,
1139
+ mlp_ratio=mlp_ratio,
1140
+ norm_layer=norm_layer,
1141
+ )
1142
+
1143
+ # Iteration counter for logging (not a parameter, won't be saved in state_dict)
1144
+ self.register_buffer('_iter_count', torch.tensor(0, dtype=torch.long))
1145
+
1146
+ self.register_buffer('_total_num_tokens', torch.tensor(0, dtype=torch.long), persistent=False)
1147
+ self.register_buffer('_total_num_samples', torch.tensor(0, dtype=torch.long), persistent=False)
1148
+
1149
+ if weight_init != 'skip':
1150
+ self.init_weights(weight_init)
1151
+ if fix_init:
1152
+ self.fix_init_weight()
1153
+
1154
+ def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs) -> None:
1155
+ ret = super()._load_from_state_dict(state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs)
1156
+ self.k_sampler.set_curr_step(int(self._iter_count.item()))
1157
+ return ret
1158
+
1159
+ def _apply_downscale(self, x: torch.Tensor, downscale_idx: int, H: int, W: int) -> Tuple[torch.Tensor, int, int]:
1160
+ """Apply patch merging downscale operation.
1161
+
1162
+ Args:
1163
+ x: Input tensor of shape (B, N, C) where N = num_prefix_tokens + H*W
1164
+ downscale_idx: Index into self.downscale_blocks and self.prefix_proj_blocks
1165
+ H: Current spatial height (in patches)
1166
+ W: Current spatial width (in patches)
1167
+
1168
+ Returns:
1169
+ Tuple of (downscaled tensor, new H, new W)
1170
+ """
1171
+ B, N, C = x.shape
1172
+ num_prefix = self.num_prefix_tokens
1173
+
1174
+ # Separate prefix tokens (cls, registers) from patch tokens
1175
+ prefix_tokens = x[:, :num_prefix] # (B, num_prefix, C)
1176
+ patch_tokens = x[:, num_prefix:] # (B, H*W, C)
1177
+
1178
+ # Reshape patch tokens to spatial format for PatchMerging
1179
+ patch_tokens = patch_tokens.reshape(B, H, W, C)
1180
+
1181
+ # Apply patch merging (spatial downsampling)
1182
+ patch_tokens = self.downscale_blocks[downscale_idx](patch_tokens) # (B, H', W', C')
1183
+
1184
+ # Get new dimensions
1185
+ _, H_new, W_new, C_new = patch_tokens.shape
1186
+
1187
+ # Reshape back to sequence format
1188
+ patch_tokens = patch_tokens.reshape(B, H_new * W_new, C_new)
1189
+
1190
+ # Project prefix tokens to match new channel dimension
1191
+ prefix_tokens = self.prefix_proj_blocks[downscale_idx](prefix_tokens) # (B, num_prefix, C')
1192
+
1193
+ # Concatenate prefix and patch tokens
1194
+ x = torch.cat([prefix_tokens, patch_tokens], dim=1)
1195
+
1196
+ return x, H_new, W_new
1197
+
1198
+ def forward_encoder(
1199
+ self,
1200
+ x: torch.Tensor,
1201
+ attn_mask: Optional[torch.Tensor] = None,
1202
+ num_tokens: Optional[int] = None,
1203
+ use_last_tokens: bool = False,
1204
+ ) -> dict:
1205
+ """Forward pass through encoder only (embeddings, transformer blocks, token slicing).
1206
+
1207
+ Args:
1208
+ x: Input image tensor of shape (B, C, H, W)
1209
+ attn_mask: Optional attention mask
1210
+ num_tokens: Number of 1D tokens to output per sample.
1211
+ If None during training: samples per-sample from mode distribution
1212
+ If None during inference: uses expected number of tokens
1213
+ If negative, uses dynamic rate prediction
1214
+ use_last_tokens: If True, take the last num_tokens instead of the first
1215
+
1216
+ Returns:
1217
+ Dict with keys:
1218
+ - "encoder": (B, num_prefix + max_tokens, C) - prefix tokens + 1D global tokens
1219
+ - "global_tokens": (B, max_tokens, C) - sliced global tokens (for decoder input)
1220
+ - "global_token_mask": (B, max_tokens) - validity mask for global tokens
1221
+ - "encoder_spatial_size": (H, W) - spatial dimensions after encoding
1222
+ - "original_spatial_size": (H, W) - original spatial dimensions before padding
1223
+ """
1224
+ B = x.shape[0]
1225
+
1226
+ # Infer spatial dimensions from input image before patch embedding
1227
+ _, _, H_img, W_img = x.shape
1228
+ if self.patch_embed is not None:
1229
+ patch_size = self.patch_embed.patch_size[0]
1230
+ x = self.patch_embed(x)
1231
+ x = self._pos_embed(x)
1232
+ x = self.patch_drop(x)
1233
+ x = self.norm_pre(x)
1234
+ else:
1235
+ images = x # Save for visualization
1236
+ patch_size = self.patch_generator.patch_size
1237
+ x = self.patch_generator(x)
1238
+
1239
+ # Compute spatial dimensions (in patches) for downscaling
1240
+ H = H_img // patch_size
1241
+ W = W_img // patch_size
1242
+ # Save original dimensions before any padding during downscaling
1243
+ original_H, original_W = H, W
1244
+
1245
+ # Sample num_tokens per sample, clamped to available spatial tokens
1246
+ num_spatial_tokens = H * W
1247
+ total_downscale = math.prod(ds.downscale for ds in self.downscale_blocks)
1248
+ num_spatial_tokens //= total_downscale
1249
+
1250
+ if num_tokens is not None:
1251
+ num_tokens = min(num_tokens, num_spatial_tokens)
1252
+ num_tokens_per_sample = torch.full((B,), num_tokens, dtype=torch.long, device=x.device)
1253
+ else:
1254
+ if self.training:
1255
+ num_tokens_per_sample = self.k_sampler.sample(B, max_tokens=num_spatial_tokens)
1256
+ else:
1257
+ # In eval mode, return all available tokens if num_tokens not specified
1258
+ num_tokens = num_spatial_tokens
1259
+ num_tokens_per_sample = torch.full((B,), num_tokens, dtype=torch.long, device=x.device)
1260
+
1261
+ is_dynamic = False
1262
+ if self.dynamic_rate and (num_tokens is None or num_tokens < 0):
1263
+ if num_tokens is not None:
1264
+ num_tokens = -num_tokens
1265
+ target_rate_pct = num_tokens_per_sample.float() / num_spatial_tokens
1266
+ rate_vec = (target_rate_pct * 2 - 1).unsqueeze(1) * self.dynamic_rate_vec.unsqueeze(0)
1267
+ x0 = x[:, :self.num_cls_tokens]
1268
+ x1 = x[:, self.num_cls_tokens + 1:]
1269
+ # Replace the first register instead with this dynamic rate vector, allowing
1270
+ # the model to know what the target rate will be
1271
+ x = torch.cat([x0, rate_vec.unsqueeze(1), x1], dim=1)
1272
+ is_dynamic = True
1273
+
1274
+ # Apply transformer blocks with optional downscaling
1275
+ downscale_idx = 0
1276
+ use_checkpoint = self.grad_checkpointing and not torch.jit.is_scripting()
1277
+
1278
+ first_downscale_level = min(self.downscale_levels)
1279
+ last_downscale_level = max(self.downscale_levels)
1280
+
1281
+ curr_num_tokens = None
1282
+ total_num_to_drop = None
1283
+
1284
+ if attn_mask is not None:
1285
+ raise NotImplementedError("attn_mask not currently supported at input.")
1286
+
1287
+ for i, blk in enumerate(self.blocks):
1288
+ # Apply downscale before this block if specified
1289
+ if i in self.downscale_levels:
1290
+ if i == first_downscale_level and is_dynamic:
1291
+ dyn_token = x[:, self.num_cls_tokens]
1292
+ dyn_pred_logits = self.dynamic_rate_projector(dyn_token).squeeze(1)
1293
+ if self.training:
1294
+ gumbels = torch.rand(*dyn_pred_logits.shape, 2, dtype=dyn_pred_logits.dtype, device=dyn_pred_logits.device).clamp_min_(1e-8)
1295
+ gumbels = -(-gumbels.log()).log() # Sample from Gumbel(0,1)
1296
+ gumbels[..., 1].neg_()
1297
+ gumbels = gumbels.sum(dim=-1)
1298
+ dyn_pred = dyn_pred_logits + gumbels / self.dynamic_temperature
1299
+ else:
1300
+ dyn_pred = dyn_pred_logits
1301
+ dyn_pred = F.sigmoid(dyn_pred)
1302
+ dyn_keep = 1 + dyn_pred * (num_spatial_tokens - 1)
1303
+ dyn_keep = round_ste(dyn_keep)
1304
+ target_num_tokens_per_sample = num_tokens_per_sample
1305
+ num_tokens_per_sample = dyn_keep
1306
+
1307
+ x, H, W = self._apply_downscale(x, downscale_idx, H, W)
1308
+ downscale_idx += 1
1309
+
1310
+ if self.progressive_reduction:
1311
+ if i == last_downscale_level:
1312
+ assert attn_mask is None
1313
+ attn_mask = torch.ones(B, x.shape[1], dtype=torch.bool, device=x.device)
1314
+ curr_num_tokens = torch.full((B,), x.shape[1] - self.num_prefix_tokens, dtype=torch.int64, device=x.device)
1315
+ ct_seq = torch.arange(0, x.shape[1], dtype=torch.int64, device=x.device).unsqueeze(0).expand(B, -1)
1316
+ elif i > last_downscale_level:
1317
+ assert curr_num_tokens is not None
1318
+ num_remaining_blocks = len(self.blocks) - i
1319
+ curr_num_to_drop = (curr_num_tokens - num_tokens_per_sample.long()) // num_remaining_blocks
1320
+ max_keep = curr_num_tokens + self.num_prefix_tokens - curr_num_to_drop
1321
+ curr_attn_mask = ct_seq[:, :x.shape[1]] < max_keep.unsqueeze(1)
1322
+ attn_mask = attn_mask & curr_attn_mask
1323
+ curr_num_tokens -= curr_num_to_drop
1324
+
1325
+ # We only need to keep up to the longest valid sequence, so this allows us to progressively
1326
+ # truncate x, saving on compute
1327
+ is_valid_for_any = torch.any(attn_mask, dim=0)
1328
+ curr_valid_length = torch.count_nonzero(is_valid_for_any, dim=0).item()
1329
+ x = x[:, :curr_valid_length]
1330
+ attn_mask = attn_mask[:, :curr_valid_length]
1331
+
1332
+ my_use_checkpoint = use_checkpoint and i < self.grad_checkpointing
1333
+
1334
+ fwd_fn = partial(checkpoint, blk, use_reentrant=False) if my_use_checkpoint else blk
1335
+
1336
+ # Apply transformer block
1337
+ if attn_mask is not None:
1338
+ if attn_mask.ndim == 4:
1339
+ my_mask = attn_mask
1340
+ elif attn_mask.ndim == 2:
1341
+ my_mask = attn_mask.unsqueeze(1).unsqueeze(1)
1342
+ else:
1343
+ my_mask = None
1344
+ # x = fwd_fn(x, attn_mask=my_mask)
1345
+ x = fwd_fn(x)
1346
+
1347
+ x = self.norm(x)
1348
+
1349
+ #if self.patch_generator is not None:
1350
+ # x = self.patch_generator.broadcast_masks(x, apt_masks, pos_enc=pos_enc)
1351
+ # self.patch_generator.maybe_visualize(images, x, apt_masks, self)
1352
+
1353
+ self._total_num_tokens += num_tokens_per_sample.sum().long()
1354
+ self._total_num_samples += B
1355
+
1356
+ # Log token counts every 100 iterations
1357
+ self._iter_count += 1
1358
+ if self._iter_count % 100 == 0 and get_rank() == 0:
1359
+ # min_tokens = num_tokens_per_sample.min().item()
1360
+ # max_tokens = num_tokens_per_sample.max().item()
1361
+ # mean_tokens = num_tokens_per_sample.float().mean().item()
1362
+ # print(f"[RADIO1D iter {self._iter_count.item()}] 1D tokens: min={min_tokens}, max={max_tokens}, mean={mean_tokens:.1f}, use_last_tokens={use_last_tokens}")
1363
+ mean_tokens = self._total_num_tokens.float() / self._total_num_samples.float()
1364
+ print(f"[RADIO1D iter {self._iter_count.item()}] avg 1D tokens: {mean_tokens.item():.2f}")
1365
+
1366
+ # Slice tokens with per-sample counts, padded to max in this micro-batch
1367
+ prefix_tokens, global_tokens, global_token_mask = slice_1d_tokens(
1368
+ x,
1369
+ num_tokens_per_sample,
1370
+ num_prefix_tokens=self.num_prefix_tokens,
1371
+ use_last_tokens=use_last_tokens,
1372
+ dynamic=self.dynamic_rate,
1373
+ )
1374
+
1375
+ # Return encoder output with metadata needed for decoder
1376
+ encoder_output = torch.cat([prefix_tokens, global_tokens], dim=1)
1377
+ ret = {
1378
+ "encoder": encoder_output,
1379
+ "global_tokens": global_tokens,
1380
+ "global_token_mask": global_token_mask,
1381
+ "encoder_spatial_size": (H, W),
1382
+ "original_spatial_size": (original_H, original_W),
1383
+ }
1384
+ if is_dynamic:
1385
+ ret["dynamic_rate"] = dict(
1386
+ pred_rate=num_tokens_per_sample,
1387
+ pred_pct=dyn_pred,
1388
+ target_rate=target_num_tokens_per_sample,
1389
+ target_pct=target_num_tokens_per_sample / num_spatial_tokens,
1390
+ pred_logits=dyn_pred_logits,
1391
+ num_spatial_tokens=num_spatial_tokens,
1392
+ )
1393
+
1394
+ self.apply_aux_losses(ret)
1395
+
1396
+ return ret
1397
+
1398
+ def forward_decoder(
1399
+ self,
1400
+ global_tokens: torch.Tensor,
1401
+ global_token_mask: torch.Tensor,
1402
+ encoder_spatial_size: Tuple[int, int],
1403
+ original_spatial_size: Tuple[int, int],
1404
+ ) -> torch.Tensor:
1405
+ """Forward pass through decoder only.
1406
+
1407
+ Args:
1408
+ global_tokens: Global tokens from encoder (B, max_tokens, C)
1409
+ global_token_mask: Boolean mask for valid global tokens (B, max_tokens)
1410
+ encoder_spatial_size: Tuple of (H, W) spatial dimensions after encoding
1411
+ original_spatial_size: Tuple of (H, W) original spatial dimensions before padding
1412
+
1413
+ Returns:
1414
+ Decoded features (B, num_prefix + H*W, target_embed_dim)
1415
+ """
1416
+ B = global_tokens.shape[0]
1417
+ H, W = encoder_spatial_size
1418
+ original_H, original_W = original_spatial_size
1419
+
1420
+ # Decode back to original resolution (decoder uses its own prefix tokens)
1421
+ decoded, decoded_H, decoded_W = self.decoder(
1422
+ global_tokens=global_tokens,
1423
+ global_token_mask=global_token_mask,
1424
+ input_size=(H, W),
1425
+ )
1426
+
1427
+ # Crop to original dimensions if needed (handles odd H/W that were padded during downscaling)
1428
+ if decoded_H != original_H or decoded_W != original_W:
1429
+ prefix = decoded[:, :self.num_prefix_tokens]
1430
+ patches = decoded[:, self.num_prefix_tokens:] # (B, decoded_H*decoded_W, C)
1431
+ patches = patches.reshape(B, decoded_H, decoded_W, -1)
1432
+ patches = patches[:, :original_H, :original_W, :].reshape(B, original_H * original_W, -1)
1433
+ decoded = torch.cat([prefix, patches], dim=1)
1434
+
1435
+ return decoded
1436
+
1437
+ def apply_aux_losses(self, encoder_result: dict):
1438
+ if not self.training or not self.dynamic_rate:
1439
+ return
1440
+
1441
+ dyn_dict = encoder_result['dynamic_rate']
1442
+ pred_rate = dyn_dict['pred_rate']
1443
+ pred_pct = dyn_dict['pred_pct']
1444
+ target_rate = dyn_dict['target_rate']
1445
+ target_pct = dyn_dict['target_pct']
1446
+ pred_logits = dyn_dict['pred_logits']
1447
+ num_spatial_tokens = dyn_dict['num_spatial_tokens']
1448
+
1449
+ pred_local_num_tokens = pred_rate.sum()
1450
+ pred_global_num_tokens = pred_local_num_tokens.clone()
1451
+ local_num_tokens = torch.tensor(num_spatial_tokens * pred_rate.shape[0], dtype=torch.float32, device=pred_rate.device)
1452
+ global_num_tokens = local_num_tokens.clone()
1453
+ if dist.is_initialized():
1454
+ dist.all_reduce(global_num_tokens, op=dist.ReduceOp.SUM)
1455
+ pred_global_num_tokens = all_reduce_with_gradients(pred_global_num_tokens, op=dist.ReduceOp.SUM)
1456
+
1457
+ global_pred_pct = pred_global_num_tokens / global_num_tokens
1458
+
1459
+ loss_rate = F.mse_loss(global_pred_pct, target_pct[0])
1460
+
1461
+ aux_losses: Dict[str, torch.Tensor] = getattr(self, 'auxiliary_losses', dict())
1462
+ self.auxiliary_losses = aux_losses
1463
+ aux_losses['dynamic_rate_mse'] = 1.0 * loss_rate.mean()
1464
+
1465
+ quantile = 0.98
1466
+ quantile_sym = (1.0 - quantile) / 2 + quantile
1467
+ log_q = math.log(quantile_sym / (1 - quantile_sym))
1468
+ logit_threshold = log_q / self.dynamic_temperature
1469
+ logit_excess = F.relu(torch.abs(pred_logits) - logit_threshold).pow(2)
1470
+ aux_losses['dynamic_rate_logit_penalty'] = 0.1 * logit_excess.mean()
1471
+
1472
+ aux_losses['dynamic_rate_abs_diff'] = (global_pred_pct - target_pct[0]).abs().detach()
1473
+
1474
+ # caps = ', '.join(f'{v * 100:.1f}%' for v in pred_pct[:4].tolist())
1475
+ # viz_caption = f"Dynamic Rate Pred: Target: {target_pct[0].item() * 100:.1f}%, Achieved: {global_pred_pct.item() * 100:.1f}%, Pred: [{caps}]"
1476
+ # FeatureDistillationLoss.VIZ_CAPTION = viz_caption
1477
+ pass
1478
+
1479
+ def forward_features(
1480
+ self,
1481
+ x: torch.Tensor,
1482
+ attn_mask: Optional[torch.Tensor] = None,
1483
+ num_tokens: Optional[int] = None,
1484
+ use_last_tokens: bool = False,
1485
+ neck_name: Optional[str] = None,
1486
+ ) -> dict:
1487
+ """Forward pass through encoder and (optionally) decoder.
1488
+
1489
+ Args:
1490
+ x: Input image tensor of shape (B, C, H, W)
1491
+ attn_mask: Optional attention mask
1492
+ num_tokens: Number of 1D tokens to output per sample.
1493
+ If None during training: samples per-sample from mode distribution
1494
+ If None during inference: uses max(modes)
1495
+ use_last_tokens: If True, take the last num_tokens instead of the first
1496
+ neck_name: If "encoder", skip the decoder pass and return only the
1497
+ encoder output. If "decoder" or None, run both (the decoder
1498
+ always depends on the encoder output).
1499
+
1500
+ Returns:
1501
+ Dict with keys:
1502
+ - "encoder": (B, num_prefix + max_tokens, C) - prefix tokens + 1D global tokens
1503
+ - "decoder": (B, num_prefix + H*W, target_embed_dim) - reconstructed full sequence
1504
+ (omitted when neck_name == "encoder")
1505
+ """
1506
+ encoder_result = self.forward_encoder(x, attn_mask=attn_mask, num_tokens=num_tokens, use_last_tokens=use_last_tokens)
1507
+
1508
+ if neck_name == "encoder":
1509
+ return {"encoder": encoder_result["encoder"]}
1510
+
1511
+ decoded = self.forward_decoder(
1512
+ global_tokens=encoder_result["global_tokens"],
1513
+ global_token_mask=encoder_result["global_token_mask"],
1514
+ encoder_spatial_size=encoder_result["encoder_spatial_size"],
1515
+ original_spatial_size=encoder_result["original_spatial_size"],
1516
+ )
1517
+
1518
+ # encoder: [prefix_tokens (cls + registers), global_tokens]
1519
+ # decoder: [prefix_tokens, decoded_patches] (already concatenated by decoder)
1520
+ return {"encoder": encoder_result["encoder"], "decoder": decoded}
1521
+
1522
+ def forward_intermediates(
1523
+ self,
1524
+ x: torch.Tensor,
1525
+ indices: Optional[Union[int, List[int]]] = None,
1526
+ return_prefix_tokens: bool = False,
1527
+ norm: bool = False,
1528
+ stop_early: bool = False,
1529
+ output_fmt: str = 'NCHW',
1530
+ intermediates_only: bool = False,
1531
+ output_dict: bool = False,
1532
+ attn_mask: Optional[torch.Tensor] = None,
1533
+ ) -> Union[List[torch.Tensor], Tuple[torch.Tensor, List[torch.Tensor]], dict]:
1534
+ """Forward features that returns intermediates.
1535
+
1536
+ Args:
1537
+ x: Input image tensor
1538
+ indices: Take last n blocks if int, all if None, select matching indices if sequence
1539
+ return_prefix_tokens: Return both prefix and spatial intermediate tokens
1540
+ norm: Apply norm layer to all intermediates
1541
+ stop_early: Stop iterating over blocks when last desired intermediate hit
1542
+ output_fmt: Shape of intermediate feature outputs ('NCHW' or 'NLC')
1543
+ intermediates_only: Only return intermediate features
1544
+ output_dict: Return outputs as a dictionary
1545
+ attn_mask: Optional attention mask
1546
+
1547
+ Returns:
1548
+ Depends on flags:
1549
+ - intermediates_only=True: List of intermediate features
1550
+ - output_dict=True: Dict with 'image_features' and 'image_intermediates'
1551
+ - Otherwise: Tuple of (final_features, intermediates)
1552
+ """
1553
+ assert output_fmt in ('NCHW', 'NLC'), f"Invalid output_fmt: {output_fmt}"
1554
+
1555
+ # Determine which block indices to collect. Match timm semantics:
1556
+ # - int -> "take the last N blocks"
1557
+ # - list -> verbatim, with Python-style negative indexing relative to num_blocks
1558
+ num_blocks = len(self.blocks)
1559
+ if indices is None:
1560
+ take_indices = list(range(num_blocks))
1561
+ elif isinstance(indices, int):
1562
+ take_indices = list(range(max(0, num_blocks - indices), num_blocks))
1563
+ else:
1564
+ take_indices = [i if i >= 0 else num_blocks + i for i in indices]
1565
+
1566
+ max_index = max(take_indices) if take_indices else num_blocks - 1
1567
+
1568
+ # Infer spatial dimensions from input image before patch embedding
1569
+ B, _, H_img, W_img = x.shape
1570
+ if self.patch_embed is not None:
1571
+ patch_size = self.patch_embed.patch_size[0]
1572
+ x = self.patch_embed(x)
1573
+ x = self._pos_embed(x)
1574
+ x = self.patch_drop(x)
1575
+ x = self.norm_pre(x)
1576
+ apt_masks = None
1577
+ pos_enc = None
1578
+ else:
1579
+ images = x
1580
+ patch_size = self.patch_generator.patch_size
1581
+ x = self.patch_generator(x)
1582
+
1583
+ #if apt_attn_mask is not None:
1584
+ # attn_mask = apt_attn_mask
1585
+
1586
+ # Compute spatial dimensions (in patches) for downscaling
1587
+ H = H_img // patch_size
1588
+ W = W_img // patch_size
1589
+
1590
+ # Collect intermediate activations
1591
+ intermediates = []
1592
+ intermediates_prefix = [] if return_prefix_tokens else None
1593
+ downscale_idx = 0
1594
+
1595
+ for i, blk in enumerate(self.blocks):
1596
+ # Apply downscale before this block if specified
1597
+ if i in self.downscale_levels:
1598
+ x, H, W = self._apply_downscale(x, downscale_idx, H, W)
1599
+ downscale_idx += 1
1600
+
1601
+ # Apply transformer block
1602
+ if attn_mask is not None:
1603
+ x = blk(x, attn_mask=attn_mask)
1604
+ else:
1605
+ x = blk(x)
1606
+
1607
+ # Collect intermediate if this index is requested
1608
+ if i in take_indices:
1609
+ # Get spatial tokens (excluding prefix tokens)
1610
+ num_prefix = self.num_prefix_tokens
1611
+ feat = x[:, num_prefix:] # (B, H*W, C)
1612
+
1613
+ if norm:
1614
+ feat = self.norm(feat)
1615
+
1616
+ # Reshape to output format
1617
+ if output_fmt == 'NCHW':
1618
+ C = feat.shape[-1]
1619
+ feat = feat.reshape(B, H, W, C).permute(0, 3, 1, 2).contiguous()
1620
+ # else 'NLC' - keep as is
1621
+
1622
+ intermediates.append(feat)
1623
+
1624
+ if return_prefix_tokens:
1625
+ prefix = x[:, :num_prefix]
1626
+ if norm:
1627
+ prefix = self.norm(prefix)
1628
+ intermediates_prefix.append(prefix)
1629
+
1630
+ # Stop early if we've collected all needed intermediates
1631
+ if stop_early and i >= max_index:
1632
+ break
1633
+
1634
+ # Compute final features if needed
1635
+ if not intermediates_only:
1636
+ # Continue from where we left off if we stopped early
1637
+ if stop_early and max_index < num_blocks - 1:
1638
+ for i in range(max_index + 1, num_blocks):
1639
+ if i in self.downscale_levels:
1640
+ x, H, W = self._apply_downscale(x, downscale_idx, H, W)
1641
+ downscale_idx += 1
1642
+ if attn_mask is not None:
1643
+ x = blk(x, attn_mask=attn_mask)
1644
+ else:
1645
+ x = self.blocks[i](x)
1646
+
1647
+ x = self.norm(x)
1648
+
1649
+ if self.patch_generator is not None:
1650
+ x = self.patch_generator.broadcast_masks(x, apt_masks, pos_enc=pos_enc)
1651
+
1652
+ # Match the canonical (timm-ViT) `forward_intermediates` output shape:
1653
+ # when prefix tokens are requested, return a list of (prefix, features)
1654
+ # tuples so callers can iterate `for summary, features in intermediates`.
1655
+ if return_prefix_tokens and not output_dict:
1656
+ intermediates = list(zip(intermediates_prefix, intermediates))
1657
+
1658
+ if output_dict:
1659
+ result = {
1660
+ 'image_intermediates': intermediates,
1661
+ }
1662
+ if not intermediates_only:
1663
+ result['image_features'] = x
1664
+ if return_prefix_tokens:
1665
+ result['image_intermediates_prefix'] = intermediates_prefix
1666
+ return result
1667
+ elif intermediates_only:
1668
+ return intermediates
1669
+ else:
1670
+ return x, intermediates
1671
+
1672
+ def get_first_downscale_block_idx(self) -> Optional[int]:
1673
+ """Return the index of the first downscaling block, or None if no downscaling."""
1674
+ if not self.downscale_levels:
1675
+ return None
1676
+ return min(self.downscale_levels)
1677
+
1678
+ def forward(
1679
+ self,
1680
+ x: torch.Tensor,
1681
+ attn_mask: Optional[torch.Tensor] = None,
1682
+ num_tokens: Optional[int] = None,
1683
+ use_last_tokens: bool = False,
1684
+ ) -> dict:
1685
+ """Forward pass through encoder only.
1686
+
1687
+ Args:
1688
+ x: Input image tensor of shape (B, C, H, W)
1689
+ attn_mask: Optional attention mask
1690
+ num_tokens: Number of 1D tokens to use per sample (for slicing)
1691
+ use_last_tokens: If True, take the last num_tokens instead of the first
1692
+
1693
+ Returns:
1694
+ Dict with keys:
1695
+ - "encoder": (B, num_prefix + max_tokens, C) - prefix tokens + 1D global tokens
1696
+ - "global_tokens": (B, max_tokens, C) - sliced global tokens (for decoder input)
1697
+ - "global_token_mask": (B, max_tokens) - validity mask for global tokens
1698
+ - "encoder_spatial_size": (H, W) - spatial dimensions after encoding
1699
+ - "original_spatial_size": (H, W) - original spatial dimensions before padding
1700
+ """
1701
+ return self.forward_encoder(x, attn_mask=attn_mask, num_tokens=num_tokens, use_last_tokens=use_last_tokens)
1702
+
1703
+
1704
+ @register_model
1705
+ def radio1d_large_patch16_224(pretrained=False, **kwargs) -> VisionTransformer:
1706
+ """ ViT-Huge model (ViT-H/16) from original paper (https://arxiv.org/abs/2010.11929).
1707
+ """
1708
+ if pretrained:
1709
+ raise ValueError('There is no pretrained weights for radio1d_large_patch16_224')
1710
+
1711
+ model_args = dict(patch_size=16, embed_dim=1024, depth=24, num_heads=16)
1712
+ model = _create_vision_transformer('radio1d_large_patch16_224', pretrained=pretrained, **dict(model_args, **kwargs))
1713
+ return model
1714
+
1715
+
1716
+ @register_model
1717
+ def radio1d_so400m_patch16_224(pretrained=False, **kwargs) -> VisionTransformer:
1718
+ """ ViT model matching the architecture of the So400M model from
1719
+ "Scaling Vision Transformers to 400 Million Parameters" (https://arxiv.org/abs/2302.05442).
1720
+ """
1721
+ if pretrained:
1722
+ raise ValueError('There is no pretrained weights for vit_so400m_patch16_224')
1723
+ mlp_ratio = 4304 / 1152
1724
+
1725
+ model_args = dict(patch_size=16, embed_dim=1152, depth=27, num_heads=16, mlp_ratio=mlp_ratio)
1726
+ model = _create_vision_transformer('radio1d_so400m_patch16_224', pretrained=pretrained, **dict(model_args, **kwargs))
1727
+
1728
+ return model
1729
+
1730
+
1731
+ @register_model
1732
+ def radio1d_huge_patch16_224(pretrained=False, **kwargs) -> VisionTransformer:
1733
+ """ ViT-Huge model (ViT-H/16) from original paper (https://arxiv.org/abs/2010.11929).
1734
+ """
1735
+ if pretrained:
1736
+ raise ValueError('There is no pretrained weights for radio1d_huge_patch16_224')
1737
+
1738
+ model_args = dict(patch_size=16, embed_dim=1280, depth=32, num_heads=16)
1739
+ model = _create_vision_transformer('radio1d_huge_patch16_224', pretrained=pretrained, **dict(model_args, **kwargs))
1740
+ return model
1741
+
1742
+
1743
+ def magneto_init(model: VisionTransformer, num_blocks: int = None):
1744
+ '''
1745
+ Initialization following [Magneto](http://arxiv.org/abs/2210.06423)
1746
+ '''
1747
+ attention_modules = [m for m in model.modules() if isinstance(m, Attention)]
1748
+ mlp_modules = [m for m in model.modules() if isinstance(m, Mlp)]
1749
+
1750
+ if num_blocks is None:
1751
+ num_blocks = len(model.blocks)
1752
+ gamma = math.sqrt(math.log(2 * num_blocks))
1753
+
1754
+ for m in attention_modules:
1755
+ qkv = m.qkv
1756
+ q, k, v = qkv.weight.data.chunk(3, dim=0)
1757
+ xavier_normal_(q, gain=1)
1758
+ xavier_normal_(k, gain=1)
1759
+ xavier_normal_(v, gain=gamma)
1760
+ xavier_normal_(m.proj.weight.data, gain=gamma)
1761
+
1762
+ for m in mlp_modules:
1763
+ xavier_normal_(m.fc1.weight.data, gain=gamma)
1764
+ xavier_normal_(m.fc2.weight.data, gain=gamma)
1765
+
1766
+
1767
+ def _init_layerscale(model: VisionTransformer):
1768
+ # https://proceedings.neurips.cc/paper_files/paper/2022/file/ae0cba715b60c4052359b3d52a2cff7f-Paper-Conference.pdf
1769
+ for i, block in enumerate(model.blocks):
1770
+ if isinstance(block, Block):
1771
+ ls = 1 / math.sqrt(i + 1)
1772
+ block.ls1.gamma.data.fill_(ls)
1773
+ block.ls2.gamma.data.fill_(ls)
1774
+ elif isinstance(block, PatchMerging):
1775
+ ls = 1 / math.sqrt(i + 1)
1776
+ block.reduction.weight.data.fill_(ls)
1777
+
1778
+
1779
+ def _create_vision_transformer(name, pretrained=False, **kwargs):
1780
+ model = build_model_with_cfg(RADIO1D, name, pretrained=pretrained, **kwargs)
1781
+ if not pretrained:
1782
+ magneto_init(model)
1783
+ if kwargs.get('init_values', None) == -1234:
1784
+ _init_layerscale(model)
1785
+ return model
radio_model.py ADDED
@@ -0,0 +1,403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ from .radio1d import RADIO1D
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
+ neck_name: Optional[str] = None,
41
+ feature_normalizer: Optional[FeatureNormalizer] = None,
42
+ inter_feature_normalizer: Optional[IntermediateFeatureNormalizer] = None,
43
+ ):
44
+ super().__init__()
45
+
46
+ self.model = model
47
+ self.input_conditioner = input_conditioner
48
+ if summary_idxs is not None:
49
+ self.register_buffer('summary_idxs', summary_idxs)
50
+ else:
51
+ self.summary_idxs = None
52
+
53
+ self._preferred_resolution = preferred_resolution
54
+ self._patch_size = patch_size
55
+ self._max_resolution = max_resolution
56
+ self._window_size = window_size
57
+ self._neck_name = neck_name
58
+ adaptors = adaptors or dict()
59
+ self.adaptors = nn.ModuleDict(adaptors)
60
+
61
+ if feature_normalizer is None:
62
+ feature_normalizer = nn.Identity()
63
+ self.feature_normalizer = feature_normalizer
64
+ self.inter_feature_normalizer = inter_feature_normalizer
65
+
66
+ if adaptors:
67
+ self.max_token_slot = max(ada.head_idx for ada in adaptors.values())
68
+ else:
69
+ self.max_token_slot = -1
70
+
71
+ @property
72
+ def num_summary_tokens(self) -> int:
73
+ if hasattr(self.model, 'num_summary_tokens'):
74
+ return self.model.num_summary_tokens
75
+
76
+ patch_gen = getattr(self.model, "patch_generator", None)
77
+ if patch_gen is not None:
78
+ return patch_gen.num_skip
79
+ elif getattr(self.model, 'global_pool', None) == 'avg':
80
+ return 0
81
+ return 1
82
+
83
+ @property
84
+ def _num_cls_tokens(self) -> int:
85
+ if hasattr(self.model, 'num_cls_tokens'):
86
+ return self.model.num_cls_tokens
87
+
88
+ patch_gen = getattr(self.model, 'patch_generator', None)
89
+ if patch_gen is not None:
90
+ return patch_gen.num_cls_tokens
91
+ elif getattr(self.model, 'global_pool', None) == 'avg':
92
+ return 0
93
+ return 1
94
+
95
+ @property
96
+ def num_cls_tokens(self) -> int:
97
+ return max(self._num_cls_tokens, self.max_token_slot + 1)
98
+
99
+ @property
100
+ def patch_size(self) -> int:
101
+ if self._patch_size is not None:
102
+ return self._patch_size
103
+ if hasattr(self.model, "patch_size"):
104
+ return self.model.patch_size
105
+ patch_gen = getattr(self.model, "patch_generator", None)
106
+ if patch_gen is not None:
107
+ return patch_gen.patch_size
108
+ return None
109
+
110
+ @property
111
+ def max_resolution(self) -> int:
112
+ return self._max_resolution
113
+
114
+ @property
115
+ def preferred_resolution(self) -> Resolution:
116
+ return self._preferred_resolution
117
+
118
+ @property
119
+ def window_size(self) -> int:
120
+ return self._window_size
121
+
122
+ @property
123
+ def min_resolution_step(self) -> int:
124
+ res = self.patch_size
125
+ if self.window_size is not None:
126
+ res *= self.window_size
127
+ return res
128
+
129
+ @property
130
+ def blocks(self) -> Iterable[nn.Module]:
131
+ blocks = getattr(self.model, 'blocks', None)
132
+ if blocks is not None:
133
+ return blocks
134
+ return None
135
+
136
+ @property
137
+ def embed_dim(self) -> int:
138
+ return self.model.embed_dim
139
+
140
+ @property
141
+ def summary_dim(self) -> int:
142
+ embed_dim = self.embed_dim
143
+ if self.summary_idxs is not None:
144
+ embed_dim *= self.summary_idxs.shape[0]
145
+ return embed_dim
146
+
147
+ def make_preprocessor_external(self) -> Callable[[torch.Tensor], torch.Tensor]:
148
+ ret = self.input_conditioner
149
+ self.input_conditioner = nn.Identity()
150
+ return ret
151
+
152
+ def get_nearest_supported_resolution(self, height: int, width: int) -> Resolution:
153
+ height = int(round(height / self.min_resolution_step) * self.min_resolution_step)
154
+ width = int(round(width / self.min_resolution_step) * self.min_resolution_step)
155
+
156
+ height = max(height, self.min_resolution_step)
157
+ width = max(width, self.min_resolution_step)
158
+
159
+ return Resolution(height=height, width=width)
160
+
161
+ def switch_to_deploy(self):
162
+ fn = getattr(self.model, 'switch_to_deploy', None)
163
+ if fn is not None:
164
+ fn()
165
+
166
+ def cpe_video_mode(self, t: int):
167
+ '''
168
+ Context Manager.
169
+
170
+ Puts the patch generator into video mode, with the specified number of temporal frames.
171
+ In video mode, the expectation is that the input buffer is of shape `(B*T, C, H, W)`.
172
+ Video mode means that the same position viewport will be used for every frame in the temporal sequence, while keeping
173
+ distinct viewports for each video in the batch.
174
+
175
+ Usage:
176
+ with radio_model.cpe_video_mode(t=t):
177
+ y = radio_model(x)
178
+ '''
179
+ return self.model.cpe_video_mode(t)
180
+
181
+ def forward(self, x: torch.Tensor, feature_fmt: str = 'NLC', num_tokens: Optional[int] = None,
182
+ neck_name: Optional[str] = None) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
183
+ '''
184
+ Forward process for model.
185
+ Args:
186
+ x: Input tensor. Unless `make_preprocessor_external` has been called, then the dynamic range of `x` is expected to be `[0, 1]`,
187
+ otherwise `x` is expected to be mean centered with unit standard deviation.
188
+ feature_format: ['NLC', 'NCHW'] - The output format for the features.
189
+ num_tokens: Number of tokens to use for the model.
190
+ neck_name: Optional per-call override for the neck to return. When None,
191
+ falls back to the value passed to the constructor (`self._neck_name`).
192
+ '''
193
+ res_step = self.min_resolution_step
194
+ if res_step is not None and (x.shape[-2] % res_step != 0 or x.shape[-1] % res_step != 0):
195
+ raise ValueError(f'The input resolution must be a multiple of `self.min_resolution_step={res_step}`. '
196
+ '`self.get_nearest_supported_resolution(<height>, <width>) is provided as a convenience API. '
197
+ f'Input: {x.shape[-2:]}, Nearest: {self.get_nearest_supported_resolution(*x.shape[-2:])}')
198
+
199
+ effective_neck_name = neck_name if neck_name is not None else self._neck_name
200
+
201
+ x = self.input_conditioner(x)
202
+ ff_kwargs = {}
203
+ if num_tokens is not None:
204
+ ff_kwargs["num_tokens"] = num_tokens
205
+ # neck_name is currently only honored by RADIO1D's forward_features (it
206
+ # uses it to skip the decoder pass when only the encoder is requested).
207
+ # Pass it through only when the inner model supports it; for any other
208
+ # multi-neck model we still fall back to post-hoc dict lookup below.
209
+ if effective_neck_name is not None and isinstance(self.model, RADIO1D):
210
+ ff_kwargs["neck_name"] = effective_neck_name
211
+ y = self.model.forward_features(x, **ff_kwargs)
212
+ if effective_neck_name is not None:
213
+ if not effective_neck_name in y:
214
+ raise ValueError(f"Neck {effective_neck_name} not found in model. Available necks: {y.keys()}")
215
+ y = y[effective_neck_name]
216
+ if isinstance(y, dict):
217
+ ret = {k: self._extract_final(x, v, feature_fmt=feature_fmt) for k, v in y.items()}
218
+ else:
219
+ ret = self._extract_final(x, y, feature_fmt=feature_fmt)
220
+ return ret
221
+
222
+ def _extract_final(self, x: torch.Tensor, y: torch.Tensor, feature_fmt: str = 'NLC'):
223
+ if isinstance(self.model, VisionTransformer):
224
+ patch_gen = getattr(self.model, "patch_generator", None)
225
+ if patch_gen is not None:
226
+ all_summary = y[:, : self.num_cls_tokens]
227
+ if self.summary_idxs is not None:
228
+ bb_summary = all_summary[:, self.summary_idxs]
229
+ else:
230
+ bb_summary = all_summary
231
+ all_feat = y[:, patch_gen.num_skip :]
232
+ elif self.model.global_pool == "avg":
233
+ all_summary = y[:, self.model.num_prefix_tokens :].mean(dim=1)
234
+ bb_summary = all_summary
235
+ all_feat = y
236
+ else:
237
+ all_summary = y[:, 0]
238
+ bb_summary = all_summary
239
+ all_feat = y[:, 1:]
240
+ elif isinstance(self.model, eradio_model.ERADIO):
241
+ _, f = y
242
+ all_feat = f.flatten(2).transpose(1, 2)
243
+ all_summary = all_feat.mean(dim=1)
244
+ bb_summary = all_summary
245
+ elif isinstance(y, (list, tuple)):
246
+ all_summary, all_feat = y
247
+ bb_summary = all_summary
248
+ else:
249
+ all_summary = y[:, :self.num_cls_tokens]
250
+ if self.summary_idxs is not None and all_summary.shape[1] > 1:
251
+ if all_summary.shape[1] == 1:
252
+ # Create dummy duplicates
253
+ all_summary = all_summary.expand(-1, 128, -1)
254
+ bb_summary = all_summary[:, self.summary_idxs]
255
+ else:
256
+ bb_summary = all_summary
257
+ all_feat = y[:, self.num_summary_tokens:]
258
+
259
+ all_feat = self.feature_normalizer(all_feat)
260
+
261
+ if feature_fmt == 'NCHW':
262
+ 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])
263
+ .permute(0, 3, 1, 2)
264
+ )
265
+ elif feature_fmt == 'NLC':
266
+ fmt_feat = all_feat
267
+ else:
268
+ raise ValueError(f'Unsupported feature_fmt: {feature_fmt}. Must be one of ["NLC", "NCHW"]')
269
+
270
+ ret = RadioOutput(bb_summary.flatten(1), fmt_feat)
271
+
272
+ if self.adaptors:
273
+ ret = dict(backbone=ret)
274
+ for name, adaptor in self.adaptors.items():
275
+ if all_summary.ndim == 3:
276
+ if all_summary.shape[1] == 1:
277
+ summary = all_summary[:, 0]
278
+ else:
279
+ summary = all_summary[:, adaptor.head_idx]
280
+ else:
281
+ summary = all_summary
282
+ ada_input = AdaptorInput(images=x, summary=summary.float(), features=all_feat, feature_fmt=feature_fmt, patch_size=self.patch_size)
283
+ v = adaptor(ada_input).to(torch.float32)
284
+ ret[name] = v
285
+
286
+ return ret
287
+
288
+ def forward_intermediates(
289
+ self,
290
+ x: torch.Tensor,
291
+ indices: Optional[Union[int, List[int], Tuple[int]]] = None,
292
+ return_prefix_tokens: bool = False,
293
+ norm: bool = False,
294
+ stop_early: bool = False,
295
+ output_fmt: str = 'NCHW',
296
+ intermediates_only: bool = False,
297
+ aggregation: Optional[str] = "sparse",
298
+ norm_alpha_scheme: Optional[str] = "post-alpha",
299
+ ) -> List[RadioOutput]:
300
+ """ Forward features that returns intermediates.
301
+ Args:
302
+ x: Input image tensor
303
+ indices: Take last n blocks if int, select matching indices if sequence
304
+ return_prefix_tokens: Return both prefix and spatial intermediate tokens
305
+ norm: Apply norm layer to all intermediates
306
+ stop_early: Stop iterating over blocks when last desired intermediate hit
307
+ output_fmt: Shape of intermediate feature outputs. Options: NCHW, NLC
308
+ intermediates_only: Only return intermediate features
309
+ aggregation: intermediate layer aggregation method (sparse or dense).
310
+ Dense accumulation is done by averaging the features in each group.
311
+ norm_alpha_scheme: apply alpha before ("pre-alpha") or after accumulation ("post-alpha"), or don't normalize ("none")
312
+ Only affects dense aggregation
313
+ Returns:
314
+ List of RadioOutput objects.
315
+ """
316
+ x = self.input_conditioner(x)
317
+ fi_kwargs = dict(
318
+ indices=indices,
319
+ return_prefix_tokens=return_prefix_tokens,
320
+ norm=norm,
321
+ stop_early=stop_early,
322
+ output_fmt=output_fmt,
323
+ intermediates_only=intermediates_only,
324
+ )
325
+ # `aggregation`, `inter_feature_normalizer`, and `norm_alpha_scheme`
326
+ # are concepts of the timm-ViT `forward_intermediates` path provided
327
+ # by `enable_cpe_support`. RADIO1D has its own `forward_intermediates`
328
+ # that doesn't accept (or need) them — only forward those kwargs to
329
+ # models that actually support them.
330
+ if not isinstance(self.model, RADIO1D):
331
+ fi_kwargs["aggregation"] = aggregation
332
+ fi_kwargs["inter_feature_normalizer"] = self.inter_feature_normalizer
333
+ fi_kwargs["norm_alpha_scheme"] = norm_alpha_scheme
334
+ intermediates = self.model.forward_intermediates(x, **fi_kwargs)
335
+
336
+ if not intermediates_only:
337
+ final, intermediates = intermediates
338
+
339
+ def prepare_summary(summ: Optional[torch.Tensor]):
340
+ if summ is None:
341
+ return summ
342
+ if self.summary_idxs is not None and summ.shape[1] > 1:
343
+ summ = summ[:, self.summary_idxs]
344
+ return summ.flatten(1)
345
+
346
+ if return_prefix_tokens:
347
+ radio_outputs = [
348
+ RadioOutput(prepare_summary(summary), features)
349
+ for summary, features in intermediates
350
+ ]
351
+ else:
352
+ radio_outputs = intermediates
353
+
354
+ if intermediates_only:
355
+ return radio_outputs
356
+ else:
357
+ final = self._extract_final(x, final, feature_fmt=output_fmt)
358
+ return final, radio_outputs
359
+
360
+
361
+ def create_model_from_args(args) -> nn.Module:
362
+ in_chans = 3
363
+ if args.in_chans is not None:
364
+ in_chans = args.in_chans
365
+ elif args.input_size is not None:
366
+ in_chans = args.input_size[0]
367
+
368
+ # Skip weight initialization unless it's explicitly requested.
369
+ weight_init = args.model_kwargs.pop("weight_init", "skip")
370
+
371
+ model = create_model(
372
+ args.model,
373
+ pretrained=args.pretrained,
374
+ in_chans=in_chans,
375
+ num_classes=args.num_classes,
376
+ drop_rate=args.drop,
377
+ drop_path_rate=args.drop_path,
378
+ drop_block_rate=args.drop_block,
379
+ global_pool=args.gp,
380
+ bn_momentum=args.bn_momentum,
381
+ bn_eps=args.bn_eps,
382
+ scriptable=args.torchscript,
383
+ checkpoint_path=args.initial_checkpoint,
384
+ weight_init=weight_init,
385
+ **args.model_kwargs,
386
+ )
387
+
388
+ if hasattr(model, 'norm') and not getattr(args, 'model_norm', False):
389
+ model.norm = nn.Identity()
390
+
391
+ model.head = nn.Identity()
392
+
393
+ if args.cpe_max_size is not None:
394
+ uq_teachers = set(t['name'] for t in args.teachers)
395
+ enable_cpe(
396
+ model,
397
+ args.cpe_max_size,
398
+ num_cls_tokens=len(uq_teachers) if args.cls_token_per_teacher else 1,
399
+ register_multiple=getattr(args, 'register_multiple', None),
400
+ num_registers=getattr(args, 'cpe_num_registers', None),
401
+ )
402
+
403
+ return model
siglip2_adaptor.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ import string
10
+ from typing import List
11
+
12
+ import torch
13
+ from torch import nn
14
+ import torch.nn.functional as F
15
+
16
+ from .adaptor_registry import adaptor_registry, dict_t, state_t
17
+
18
+ from .adaptor_generic import GenericAdaptor
19
+ from .utils import rank_gate
20
+
21
+
22
+ _VERSION_MAP = {
23
+ 'siglip2-g-384': 'google/siglip2-giant-opt-patch16-384',
24
+ 'siglip2-so400m': 'google/siglip2-so400m-patch16-naflex',
25
+ }
26
+
27
+
28
+ class SigLIP2Adaptor(GenericAdaptor):
29
+ def __init__(self, main_config: Namespace, adaptor_config: dict_t, state: state_t):
30
+ super().__init__(main_config, adaptor_config, state)
31
+
32
+ version = adaptor_config['model']
33
+ version = _VERSION_MAP[version]
34
+
35
+ from transformers import AutoModel, AutoProcessor
36
+ with rank_gate():
37
+ model = AutoModel.from_pretrained(version, trust_remote_code=True)
38
+ proc = AutoProcessor.from_pretrained(version, trust_remote_code=True)
39
+
40
+ self.tokenizer = SigLIP2WrappedTokenizer(proc)
41
+ self.text_model = model.text_model
42
+
43
+ del model
44
+
45
+ def encode_text(self, text, normalize: bool = False):
46
+ output = self.text_model(**text, return_dict=True)
47
+ token = output.pooler_output
48
+
49
+ if normalize:
50
+ token = F.normalize(token, dim=-1)
51
+
52
+ return token
53
+
54
+
55
+ class SigLIP2WrappedTokenizer:
56
+ def __init__(self, proc):
57
+ self._proc = proc
58
+
59
+ def __call__(self, text: List[str]):
60
+ text = [canonicalize_text(t) for t in text]
61
+ ret = self._proc(text=text, return_tensors='pt', max_length=64, padding='max_length', truncation=True)
62
+ return ret
63
+
64
+
65
+ def canonicalize_text(
66
+ text: str,
67
+ *,
68
+ keep_punctuation_exact_string=None,
69
+ trans_punctuation: dict = str.maketrans("", "", string.punctuation),
70
+ ):
71
+ """Returns canonicalized `text` (lowercase and punctuation removed).
72
+
73
+ From: https://github.com/google-research/big_vision/blob/53f18caf27a9419231bbf08d3388b07671616d3d/big_vision/evaluators/proj/image_text/prompt_engineering.py#L94
74
+
75
+ Args:
76
+ text: string to be canonicalized.
77
+ keep_punctuation_exact_string: If provided, then this exact string kept.
78
+ For example providing '{}' will keep any occurrences of '{}' (but will
79
+ still remove '{' and '}' that appear separately).
80
+ """
81
+ text = text.replace("_", " ")
82
+ if keep_punctuation_exact_string:
83
+ text = keep_punctuation_exact_string.join(
84
+ part.translate(trans_punctuation)
85
+ for part in text.split(keep_punctuation_exact_string)
86
+ )
87
+ else:
88
+ text = text.translate(trans_punctuation)
89
+ text = text.lower()
90
+ text = " ".join(text.split())
91
+ return text.strip()
92
+
93
+
94
+ @adaptor_registry.register_adaptor("siglip2")
95
+ def create_siglip2_adaptor(main_config: Namespace, adaptor_config: dict_t, state: state_t):
96
+ return SigLIP2Adaptor(main_config, adaptor_config, state)
utils.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ import torch.distributed as dist
4
+
5
+
6
+ def get_rank(group: Optional[dist.ProcessGroup] = None):
7
+ return dist.get_rank(group) if dist.is_initialized() else 0
8
+
9
+
10
+ def get_world_size(group: Optional[dist.ProcessGroup] = None):
11
+ return dist.get_world_size(group) if dist.is_initialized() else 1
12
+
13
+
14
+ def barrier(group: Optional[dist.ProcessGroup] = None):
15
+ if dist.is_initialized():
16
+ dist.barrier(group)
17
+
18
+
19
+ class rank_gate:
20
+ '''
21
+ Execute the function on rank 0 first, followed by all other ranks. Useful when caches may need to be populated in a distributed environment.
22
+ '''
23
+ def __init__(self, func = None):
24
+ self.func = func
25
+
26
+ def __call__(self, *args, **kwargs):
27
+ rank = get_rank()
28
+ if rank == 0:
29
+ result = self.func(*args, **kwargs)
30
+ barrier()
31
+ if rank > 0:
32
+ result = self.func(*args, **kwargs)
33
+ return result
34
+
35
+ def __enter__(self, *args, **kwargs):
36
+ if get_rank() > 0:
37
+ barrier()
38
+
39
+ def __exit__(self, *args, **kwargs):
40
+ if get_rank() == 0:
41
+ barrier()
vit_patch_generator.py ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ self.num_video_frames = None
93
+
94
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
95
+ patches = self.embed_patches(x)
96
+ patches, pos_enc = self.apply_pos_enc(patches, input_size=x.shape[2:])
97
+ patches = self.cls_token(patches)
98
+ patches = self.patch_normalizer(patches)
99
+ if self.return_pos_enc:
100
+ return patches, pos_enc
101
+ return patches
102
+
103
+ @property
104
+ def apply_cls_token(self):
105
+ return self.cls_token.enabled
106
+
107
+ @property
108
+ def num_cls_tokens(self):
109
+ return self.cls_token.num_tokens
110
+
111
+ @property
112
+ def num_cls_patches(self):
113
+ return self.cls_token.num_patches
114
+
115
+ @property
116
+ def num_registers(self):
117
+ return self.cls_token.num_registers
118
+
119
+ @property
120
+ def num_skip(self):
121
+ return self.num_cls_tokens + self.num_registers
122
+
123
+ def no_weight_decay(self):
124
+ return [
125
+ 'pos_embed',
126
+ ]
127
+
128
+ def _load_embed(self, src_embed: torch.Tensor, targ_embed: nn.Parameter):
129
+ if src_embed.shape != targ_embed.shape:
130
+ src_size = int(math.sqrt(src_embed.shape[1]))
131
+
132
+ assert src_size ** 2 == src_embed.shape[1], 'Unable to interpolate non-square embedding'
133
+
134
+ src_embed = rearrange(src_embed, 'b (h w) c -> b c h w', h=src_size, w=src_size)
135
+ src_embed = F.interpolate(src_embed, size=(self.num_rows, self.num_cols), mode='bicubic', align_corners=True, antialias=False)
136
+ src_embed = rearrange(src_embed, 'b c h w -> b (h w) c')
137
+ targ_embed.data.copy_(src_embed)
138
+
139
+ def _load_projection(self, src_proj_weight: torch.Tensor, targ_proj_weight: torch.Tensor):
140
+ if src_proj_weight.shape != targ_proj_weight.shape:
141
+ src_patch_size = int(math.sqrt(src_proj_weight.shape[1] // 3))
142
+
143
+ assert (src_patch_size ** 2) * 3 == src_proj_weight.shape[1], 'Unable to interpolate non-square patch size'
144
+
145
+ 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)
146
+ src_proj_weight = F.interpolate(src_proj_weight, size=(self.patch_size, self.patch_size), mode='bicubic', align_corners=True, antialias=False)
147
+ src_proj_weight = rearrange(src_proj_weight, 'b c h w -> b (c h w)')
148
+ targ_proj_weight.data.copy_(src_proj_weight)
149
+
150
+ def embed_patches(self, x: torch.Tensor) -> torch.Tensor:
151
+ patches = self.im_to_patches(x)
152
+ patches = self.embedder(patches)
153
+ return patches
154
+
155
+ def apply_pos_enc(self,
156
+ patches: torch.Tensor,
157
+ patch_idxs: Optional[torch.Tensor] = None,
158
+ input_size: Optional[Tuple[int, int]] = None,
159
+ ) -> torch.Tensor:
160
+ if not self.abs_pos:
161
+ return patches
162
+
163
+ pos_enc = self.get_pos_enc(patches.shape[0], patch_idxs, input_size)
164
+
165
+ if self.training and self.pos_dropout > 0:
166
+ keeps = torch.rand(patches.shape[0], 1, 1, dtype=pos_enc.dtype, device=pos_enc.device) > self.pos_dropout
167
+ pos_enc_drop = torch.where(keeps, pos_enc, 0)
168
+ else:
169
+ pos_enc_drop = pos_enc
170
+
171
+ return patches + pos_enc_drop, pos_enc
172
+
173
+ def get_pos_enc(self,
174
+ batch_size: int,
175
+ patch_idxs: Optional[torch.Tensor] = None,
176
+ input_size: Optional[Tuple[int, int]] = None,
177
+ flatten: bool = True,
178
+ ) -> torch.Tensor:
179
+ if input_size is None:
180
+ input_dims = self.input_dims
181
+ else:
182
+ input_dims = tuple(d // self.patch_size for d in input_size)
183
+
184
+ pos_embed = self._get_pos_embeddings(batch_size, input_dims, flatten=flatten)
185
+
186
+ if not flatten and pos_embed.ndim == 3:
187
+ pos_embed = rearrange(pos_embed, 'b (h w) c -> b c h w', h=input_dims[0], w=input_dims[1])
188
+
189
+
190
+ if patch_idxs is None:
191
+ return pos_embed
192
+
193
+ exp_patch_idxs = patch_idxs.unsqueeze(-1).expand(-1, -1, pos_embed.shape[-1])
194
+
195
+ pos_embed = torch.gather(pos_embed.expand(patch_idxs.shape[0], -1, -1), dim=1, index=exp_patch_idxs)
196
+ return pos_embed
197
+
198
+
199
+ def _get_pos_embeddings(self, batch_size: int, input_dims: Tuple[int, int], flatten: bool = True):
200
+ if (self.num_rows, self.num_cols) == input_dims:
201
+ return self.pos_embed
202
+
203
+ pos_embed = self.pos_embed.reshape(1, self.num_rows, self.num_cols, -1).permute(0, 3, 1, 2)
204
+
205
+ def window_select(pos_embed):
206
+ if input_dims[0] < pos_embed.shape[-2]:
207
+ pos_embed = pos_embed[..., :input_dims[0], :]
208
+ if input_dims[1] < pos_embed.shape[-1]:
209
+ pos_embed = pos_embed[..., :, :input_dims[1]]
210
+ return pos_embed
211
+
212
+ if self.cpe_mode:
213
+ if self.training:
214
+ if self.num_video_frames is not None:
215
+ if batch_size % self.num_video_frames != 0:
216
+ raise ValueError(f'Batch size {batch_size} must be divisible by num_video_frames {self.num_video_frames} for CPE mode.')
217
+
218
+ batch_size //= self.num_video_frames
219
+
220
+ min_scale = math.sqrt(0.1)
221
+ scale = torch.rand(batch_size, 1, 1, device=pos_embed.device) * (1 - min_scale) + min_scale
222
+ aspect_min = math.log(3 / 4)
223
+ aspect_max = -aspect_min
224
+ aspect = torch.exp(torch.rand(batch_size, 1, 1, device=pos_embed.device) * (aspect_max - aspect_min) + aspect_min)
225
+
226
+ scale_x = scale * aspect
227
+ scale_y = scale * (1 / aspect)
228
+ scale_xy = torch.stack([scale_x, scale_y], dim=-1).clamp_(0, 1)
229
+
230
+ pos_xy = torch.rand(batch_size, 1, 1, 2, device=pos_embed.device) * (1 - scale_xy)
231
+
232
+ lin_x = torch.linspace(0, 1, steps=input_dims[1], device=pos_embed.device)[None, None].expand(batch_size, input_dims[0], -1)
233
+ lin_y = torch.linspace(0, 1, steps=input_dims[0], device=pos_embed.device)[None, :, None].expand(batch_size, -1, input_dims[1])
234
+
235
+ lin_xy = torch.stack([lin_x, lin_y], dim=-1)
236
+
237
+ grid_xy = lin_xy * scale_xy + pos_xy
238
+
239
+ # Convert to [-1, 1] range
240
+ grid_xy.mul_(2).sub_(1)
241
+
242
+ pos_embed = F.grid_sample(
243
+ pos_embed.float().expand(batch_size, -1, -1, -1),
244
+ grid=grid_xy,
245
+ mode='bilinear',
246
+ padding_mode='zeros',
247
+ align_corners=True,
248
+ ).to(pos_embed.dtype)
249
+
250
+ if self.num_video_frames is not None:
251
+ pos_embed = torch.repeat_interleave(pos_embed, self.num_video_frames, dim=0)
252
+ else:
253
+ # i_rows, i_cols = input_dims
254
+ # p_rows, p_cols = pos_embed.shape[2:]
255
+ # if i_rows <= p_rows and i_cols <= p_cols:
256
+ # left = (p_cols - i_cols) // 2
257
+ # top = (p_rows - i_rows) // 2
258
+ # pos_embed = pos_embed[..., top:top+i_rows, left:left+i_cols]
259
+ # else:
260
+ max_dim = max(input_dims)
261
+ pos_embed = F.interpolate(pos_embed.float(), size=(max_dim, max_dim), align_corners=False, mode='bilinear').to(pos_embed.dtype)
262
+
263
+ pos_embed = window_select(pos_embed)
264
+ else:
265
+ pos_embed = window_select(pos_embed)
266
+
267
+ if pos_embed.shape[-2:] != input_dims:
268
+ pos_embed = F.interpolate(pos_embed.float(), size=input_dims, align_corners=False, mode='bilinear').to(pos_embed.dtype)
269
+
270
+ if flatten:
271
+ pos_embed = pos_embed.flatten(2).permute(0, 2, 1)
272
+
273
+ return pos_embed
274
+
275
+
276
+ class Im2Patches(nn.Module):
277
+ def __init__(self, patch_size: int):
278
+ super().__init__()
279
+ self.patch_size = patch_size
280
+
281
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
282
+ if self.patch_size == 1:
283
+ patches = x.flatten(2)
284
+ patches = patches.permute(0, 2, 1)
285
+ return patches
286
+
287
+ py = x.shape[-2] // self.patch_size
288
+ px = x.shape[-1] // self.patch_size
289
+ patches = rearrange(x, 'b c (py yy) (px xx) -> b (py px) (c yy xx)',
290
+ py=py, yy=self.patch_size,
291
+ px=px, xx=self.patch_size,
292
+ )
293
+ return patches
294
+
295
+
296
+ class ViTPatchLinear(nn.Linear):
297
+ def __init__(self, patch_size: int, embed_dim: int, bias: bool = False, **factory):
298
+ super().__init__(
299
+ 3 * (patch_size ** 2),
300
+ embed_dim,
301
+ bias=bias,
302
+ **factory
303
+ )
304
+ 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