defm 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- defm/__init__.py +18 -0
- defm/configs/defm_efficientnet_b0.yaml +3 -0
- defm/configs/defm_efficientnet_b2.yaml +3 -0
- defm/configs/defm_efficientnet_b4.yaml +3 -0
- defm/configs/defm_efficientnet_b6.yaml +3 -0
- defm/configs/defm_regnet_y_1_6gf.yaml +3 -0
- defm/configs/defm_regnet_y_400mf.yaml +3 -0
- defm/configs/defm_regnet_y_800mf.yaml +3 -0
- defm/configs/defm_resnet18.yaml +3 -0
- defm/configs/defm_resnet34.yaml +3 -0
- defm/configs/defm_resnet50.yaml +3 -0
- defm/configs/defm_vit_l14.yaml +13 -0
- defm/configs/defm_vit_s14.yaml +13 -0
- defm/layers/__init__.py +16 -0
- defm/layers/attention.py +98 -0
- defm/layers/block.py +295 -0
- defm/layers/dino_head.py +72 -0
- defm/layers/drop_path.py +41 -0
- defm/layers/layer_scale.py +32 -0
- defm/layers/mlp.py +45 -0
- defm/layers/patch_embed.py +105 -0
- defm/layers/swiglu_ffn.py +77 -0
- defm/model_factory.py +85 -0
- defm/models/__init__.py +5 -0
- defm/models/bifpn.py +88 -0
- defm/models/efficientnet_bifpn.py +191 -0
- defm/models/regnet_bifpn.py +225 -0
- defm/models/resnet_bifpn.py +155 -0
- defm/models/vision_transformer.py +447 -0
- defm/utils/__init__.py +6 -0
- defm/utils/utils.py +263 -0
- defm-1.0.0.dist-info/LICENSE +202 -0
- defm-1.0.0.dist-info/METADATA +385 -0
- defm-1.0.0.dist-info/RECORD +36 -0
- defm-1.0.0.dist-info/WHEEL +5 -0
- defm-1.0.0.dist-info/top_level.txt +1 -0
defm/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Copyright (c) 2026, ETH Zurich, Manthan Patel
|
|
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
|
+
# # Expose package version: prefer installed metadata, fall back to a default.
|
|
7
|
+
# try:
|
|
8
|
+
# from importlib.metadata import version, PackageNotFoundError
|
|
9
|
+
# except Exception:
|
|
10
|
+
# # Python <3.8 fallback (unlikely here)
|
|
11
|
+
# from importlib_metadata import version, PackageNotFoundError # type: ignore
|
|
12
|
+
|
|
13
|
+
# try:
|
|
14
|
+
# __version__ = version("defm")
|
|
15
|
+
# except PackageNotFoundError:
|
|
16
|
+
# # fallback for source checkout / editable installs — keep in sync with pyproject.toml
|
|
17
|
+
# __version__ = "1.0.0"
|
|
18
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
arch: vit_large
|
|
2
|
+
patch_size: 14
|
|
3
|
+
in_chans: 3
|
|
4
|
+
layerscale: 1.0e-05
|
|
5
|
+
ffn_layer: swiglufused
|
|
6
|
+
block_chunks: 4
|
|
7
|
+
qkv_bias: true
|
|
8
|
+
proj_bias: true
|
|
9
|
+
ffn_bias: true
|
|
10
|
+
num_register_tokens: 0
|
|
11
|
+
interpolate_antialias: false
|
|
12
|
+
interpolate_offset: 0.1
|
|
13
|
+
global_crops_size: 224
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
arch: vit_small
|
|
2
|
+
patch_size: 14
|
|
3
|
+
in_chans: 3
|
|
4
|
+
layerscale: 1.0e-05
|
|
5
|
+
ffn_layer: swiglufused
|
|
6
|
+
block_chunks: 4
|
|
7
|
+
qkv_bias: true
|
|
8
|
+
proj_bias: true
|
|
9
|
+
ffn_bias: true
|
|
10
|
+
num_register_tokens: 0
|
|
11
|
+
interpolate_antialias: false
|
|
12
|
+
interpolate_offset: 0.1
|
|
13
|
+
global_crops_size: 224
|
defm/layers/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Copyright (c) 2026, ETH Zurich, Manthan Patel
|
|
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
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
7
|
+
#
|
|
8
|
+
# This source code is licensed under the Apache License, Version 2.0
|
|
9
|
+
# found in the LICENSE file in the root directory of this source tree.
|
|
10
|
+
|
|
11
|
+
from .dino_head import DINOHead
|
|
12
|
+
from .mlp import Mlp
|
|
13
|
+
from .patch_embed import PatchEmbed
|
|
14
|
+
from .swiglu_ffn import SwiGLUFFN, SwiGLUFFNFused
|
|
15
|
+
from .block import NestedTensorBlock
|
|
16
|
+
from .attention import MemEffAttention
|
defm/layers/attention.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Copyright (c) 2026, ETH Zurich, Manthan Patel
|
|
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
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
7
|
+
#
|
|
8
|
+
# This source code is licensed under the Apache License, Version 2.0
|
|
9
|
+
# found in the LICENSE file in the root directory of this source tree.
|
|
10
|
+
|
|
11
|
+
# References:
|
|
12
|
+
# https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
|
|
13
|
+
# https://github.com/rwightman/pytorch-image-models/tree/master/timm/models/vision_transformer.py
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
import warnings
|
|
18
|
+
|
|
19
|
+
from torch import Tensor
|
|
20
|
+
from torch import nn
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger("dinov2")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
XFORMERS_ENABLED = os.environ.get("XFORMERS_DISABLED") is None
|
|
27
|
+
try:
|
|
28
|
+
if XFORMERS_ENABLED:
|
|
29
|
+
from xformers.ops import memory_efficient_attention, unbind
|
|
30
|
+
|
|
31
|
+
XFORMERS_AVAILABLE = True
|
|
32
|
+
warnings.warn("xFormers is available (Attention)")
|
|
33
|
+
else:
|
|
34
|
+
warnings.warn("xFormers is disabled (Attention)")
|
|
35
|
+
raise ImportError
|
|
36
|
+
except ImportError:
|
|
37
|
+
XFORMERS_AVAILABLE = False
|
|
38
|
+
warnings.warn("xFormers is not available (Attention)")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Attention(nn.Module):
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
dim: int,
|
|
45
|
+
num_heads: int = 8,
|
|
46
|
+
qkv_bias: bool = False,
|
|
47
|
+
proj_bias: bool = True,
|
|
48
|
+
attn_drop: float = 0.0,
|
|
49
|
+
proj_drop: float = 0.0,
|
|
50
|
+
) -> None:
|
|
51
|
+
super().__init__()
|
|
52
|
+
self.num_heads = num_heads
|
|
53
|
+
head_dim = dim // num_heads
|
|
54
|
+
self.scale = head_dim**-0.5
|
|
55
|
+
|
|
56
|
+
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
|
57
|
+
self.attn_drop = nn.Dropout(attn_drop)
|
|
58
|
+
self.proj = nn.Linear(dim, dim, bias=proj_bias)
|
|
59
|
+
self.proj_drop = nn.Dropout(proj_drop)
|
|
60
|
+
|
|
61
|
+
def forward(self, x: Tensor) -> Tensor:
|
|
62
|
+
B, N, C = x.shape
|
|
63
|
+
qkv = (
|
|
64
|
+
self.qkv(x)
|
|
65
|
+
.reshape(B, N, 3, self.num_heads, C // self.num_heads)
|
|
66
|
+
.permute(2, 0, 3, 1, 4)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
q, k, v = qkv[0] * self.scale, qkv[1], qkv[2]
|
|
70
|
+
attn = q @ k.transpose(-2, -1)
|
|
71
|
+
|
|
72
|
+
attn = attn.softmax(dim=-1)
|
|
73
|
+
attn = self.attn_drop(attn)
|
|
74
|
+
|
|
75
|
+
x = (attn @ v).transpose(1, 2).reshape(B, N, C)
|
|
76
|
+
x = self.proj(x)
|
|
77
|
+
x = self.proj_drop(x)
|
|
78
|
+
return x
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class MemEffAttention(Attention):
|
|
82
|
+
def forward(self, x: Tensor, attn_bias=None) -> Tensor:
|
|
83
|
+
if not XFORMERS_AVAILABLE:
|
|
84
|
+
if attn_bias is not None:
|
|
85
|
+
raise AssertionError("xFormers is required for using nested tensors")
|
|
86
|
+
return super().forward(x)
|
|
87
|
+
|
|
88
|
+
B, N, C = x.shape
|
|
89
|
+
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads)
|
|
90
|
+
|
|
91
|
+
q, k, v = unbind(qkv, 2)
|
|
92
|
+
|
|
93
|
+
x = memory_efficient_attention(q, k, v, attn_bias=attn_bias)
|
|
94
|
+
x = x.reshape([B, N, C])
|
|
95
|
+
|
|
96
|
+
x = self.proj(x)
|
|
97
|
+
x = self.proj_drop(x)
|
|
98
|
+
return x
|
defm/layers/block.py
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# Copyright (c) 2026, ETH Zurich, Manthan Patel
|
|
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
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
7
|
+
#
|
|
8
|
+
# This source code is licensed under the Apache License, Version 2.0
|
|
9
|
+
# found in the LICENSE file in the root directory of this source tree.
|
|
10
|
+
|
|
11
|
+
# References:
|
|
12
|
+
# https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
|
|
13
|
+
# https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/patch_embed.py
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
from typing import Callable, List, Any, Tuple, Dict
|
|
18
|
+
import warnings
|
|
19
|
+
|
|
20
|
+
import torch
|
|
21
|
+
from torch import nn, Tensor
|
|
22
|
+
|
|
23
|
+
from .attention import Attention, MemEffAttention
|
|
24
|
+
from .drop_path import DropPath
|
|
25
|
+
from .layer_scale import LayerScale
|
|
26
|
+
from .mlp import Mlp
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger("dinov2")
|
|
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
|
|
36
|
+
|
|
37
|
+
XFORMERS_AVAILABLE = True
|
|
38
|
+
warnings.warn("xFormers is available (Block)")
|
|
39
|
+
else:
|
|
40
|
+
warnings.warn("xFormers is disabled (Block)")
|
|
41
|
+
raise ImportError
|
|
42
|
+
except ImportError:
|
|
43
|
+
XFORMERS_AVAILABLE = False
|
|
44
|
+
|
|
45
|
+
warnings.warn("xFormers is not available (Block)")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Block(nn.Module):
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
dim: int,
|
|
52
|
+
num_heads: int,
|
|
53
|
+
mlp_ratio: float = 4.0,
|
|
54
|
+
qkv_bias: bool = False,
|
|
55
|
+
proj_bias: bool = True,
|
|
56
|
+
ffn_bias: bool = True,
|
|
57
|
+
drop: float = 0.0,
|
|
58
|
+
attn_drop: float = 0.0,
|
|
59
|
+
init_values=None,
|
|
60
|
+
drop_path: float = 0.0,
|
|
61
|
+
act_layer: Callable[..., nn.Module] = nn.GELU,
|
|
62
|
+
norm_layer: Callable[..., nn.Module] = nn.LayerNorm,
|
|
63
|
+
attn_class: Callable[..., nn.Module] = Attention,
|
|
64
|
+
ffn_layer: Callable[..., nn.Module] = Mlp,
|
|
65
|
+
) -> None:
|
|
66
|
+
super().__init__()
|
|
67
|
+
# print(f"biases: qkv: {qkv_bias}, proj: {proj_bias}, ffn: {ffn_bias}")
|
|
68
|
+
self.norm1 = norm_layer(dim)
|
|
69
|
+
self.attn = attn_class(
|
|
70
|
+
dim,
|
|
71
|
+
num_heads=num_heads,
|
|
72
|
+
qkv_bias=qkv_bias,
|
|
73
|
+
proj_bias=proj_bias,
|
|
74
|
+
attn_drop=attn_drop,
|
|
75
|
+
proj_drop=drop,
|
|
76
|
+
)
|
|
77
|
+
self.ls1 = (
|
|
78
|
+
LayerScale(dim, init_values=init_values) if init_values else nn.Identity()
|
|
79
|
+
)
|
|
80
|
+
self.drop_path1 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
|
|
81
|
+
|
|
82
|
+
self.norm2 = norm_layer(dim)
|
|
83
|
+
mlp_hidden_dim = int(dim * mlp_ratio)
|
|
84
|
+
self.mlp = ffn_layer(
|
|
85
|
+
in_features=dim,
|
|
86
|
+
hidden_features=mlp_hidden_dim,
|
|
87
|
+
act_layer=act_layer,
|
|
88
|
+
drop=drop,
|
|
89
|
+
bias=ffn_bias,
|
|
90
|
+
)
|
|
91
|
+
self.ls2 = (
|
|
92
|
+
LayerScale(dim, init_values=init_values) if init_values else nn.Identity()
|
|
93
|
+
)
|
|
94
|
+
self.drop_path2 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
|
|
95
|
+
|
|
96
|
+
self.sample_drop_ratio = drop_path
|
|
97
|
+
|
|
98
|
+
def forward(self, x: Tensor) -> Tensor:
|
|
99
|
+
def attn_residual_func(x: Tensor) -> Tensor:
|
|
100
|
+
return self.ls1(self.attn(self.norm1(x)))
|
|
101
|
+
|
|
102
|
+
def ffn_residual_func(x: Tensor) -> Tensor:
|
|
103
|
+
return self.ls2(self.mlp(self.norm2(x)))
|
|
104
|
+
|
|
105
|
+
if self.training and self.sample_drop_ratio > 0.1:
|
|
106
|
+
# the overhead is compensated only for a drop path rate larger than 0.1
|
|
107
|
+
x = drop_add_residual_stochastic_depth(
|
|
108
|
+
x,
|
|
109
|
+
residual_func=attn_residual_func,
|
|
110
|
+
sample_drop_ratio=self.sample_drop_ratio,
|
|
111
|
+
)
|
|
112
|
+
x = drop_add_residual_stochastic_depth(
|
|
113
|
+
x,
|
|
114
|
+
residual_func=ffn_residual_func,
|
|
115
|
+
sample_drop_ratio=self.sample_drop_ratio,
|
|
116
|
+
)
|
|
117
|
+
elif self.training and self.sample_drop_ratio > 0.0:
|
|
118
|
+
x = x + self.drop_path1(attn_residual_func(x))
|
|
119
|
+
x = x + self.drop_path1(ffn_residual_func(x)) # FIXME: drop_path2
|
|
120
|
+
else:
|
|
121
|
+
x = x + attn_residual_func(x)
|
|
122
|
+
x = x + ffn_residual_func(x)
|
|
123
|
+
return x
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def drop_add_residual_stochastic_depth(
|
|
127
|
+
x: Tensor,
|
|
128
|
+
residual_func: Callable[[Tensor], Tensor],
|
|
129
|
+
sample_drop_ratio: float = 0.0,
|
|
130
|
+
) -> Tensor:
|
|
131
|
+
# 1) extract subset using permutation
|
|
132
|
+
b, n, d = x.shape
|
|
133
|
+
sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1)
|
|
134
|
+
brange = (torch.randperm(b, device=x.device))[:sample_subset_size]
|
|
135
|
+
x_subset = x[brange]
|
|
136
|
+
|
|
137
|
+
# 2) apply residual_func to get residual
|
|
138
|
+
residual = residual_func(x_subset)
|
|
139
|
+
|
|
140
|
+
x_flat = x.flatten(1)
|
|
141
|
+
residual = residual.flatten(1)
|
|
142
|
+
|
|
143
|
+
residual_scale_factor = b / sample_subset_size
|
|
144
|
+
|
|
145
|
+
# 3) add the residual
|
|
146
|
+
x_plus_residual = torch.index_add(
|
|
147
|
+
x_flat, 0, brange, residual.to(dtype=x.dtype), alpha=residual_scale_factor
|
|
148
|
+
)
|
|
149
|
+
return x_plus_residual.view_as(x)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def get_branges_scales(x, sample_drop_ratio=0.0):
|
|
153
|
+
b, n, d = x.shape
|
|
154
|
+
sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1)
|
|
155
|
+
brange = (torch.randperm(b, device=x.device))[:sample_subset_size]
|
|
156
|
+
residual_scale_factor = b / sample_subset_size
|
|
157
|
+
return brange, residual_scale_factor
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def add_residual(x, brange, residual, residual_scale_factor, scaling_vector=None):
|
|
161
|
+
if scaling_vector is None:
|
|
162
|
+
x_flat = x.flatten(1)
|
|
163
|
+
residual = residual.flatten(1)
|
|
164
|
+
x_plus_residual = torch.index_add(
|
|
165
|
+
x_flat, 0, brange, residual.to(dtype=x.dtype), alpha=residual_scale_factor
|
|
166
|
+
)
|
|
167
|
+
else:
|
|
168
|
+
x_plus_residual = scaled_index_add(
|
|
169
|
+
x,
|
|
170
|
+
brange,
|
|
171
|
+
residual.to(dtype=x.dtype),
|
|
172
|
+
scaling=scaling_vector,
|
|
173
|
+
alpha=residual_scale_factor,
|
|
174
|
+
)
|
|
175
|
+
return x_plus_residual
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
attn_bias_cache: Dict[Tuple, Any] = {}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def get_attn_bias_and_cat(x_list, branges=None):
|
|
182
|
+
"""
|
|
183
|
+
this will perform the index select, cat the tensors, and provide the attn_bias from cache
|
|
184
|
+
"""
|
|
185
|
+
batch_sizes = (
|
|
186
|
+
[b.shape[0] for b in branges]
|
|
187
|
+
if branges is not None
|
|
188
|
+
else [x.shape[0] for x in x_list]
|
|
189
|
+
)
|
|
190
|
+
all_shapes = tuple((b, x.shape[1]) for b, x in zip(batch_sizes, x_list))
|
|
191
|
+
if all_shapes not in attn_bias_cache.keys():
|
|
192
|
+
seqlens = []
|
|
193
|
+
for b, x in zip(batch_sizes, x_list):
|
|
194
|
+
for _ in range(b):
|
|
195
|
+
seqlens.append(x.shape[1])
|
|
196
|
+
attn_bias = fmha.BlockDiagonalMask.from_seqlens(seqlens)
|
|
197
|
+
attn_bias._batch_sizes = batch_sizes
|
|
198
|
+
attn_bias_cache[all_shapes] = attn_bias
|
|
199
|
+
|
|
200
|
+
if branges is not None:
|
|
201
|
+
cat_tensors = index_select_cat([x.flatten(1) for x in x_list], branges).view(
|
|
202
|
+
1, -1, x_list[0].shape[-1]
|
|
203
|
+
)
|
|
204
|
+
else:
|
|
205
|
+
tensors_bs1 = tuple(x.reshape([1, -1, *x.shape[2:]]) for x in x_list)
|
|
206
|
+
cat_tensors = torch.cat(tensors_bs1, dim=1)
|
|
207
|
+
|
|
208
|
+
return attn_bias_cache[all_shapes], cat_tensors
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def drop_add_residual_stochastic_depth_list(
|
|
212
|
+
x_list: List[Tensor],
|
|
213
|
+
residual_func: Callable[[Tensor, Any], Tensor],
|
|
214
|
+
sample_drop_ratio: float = 0.0,
|
|
215
|
+
scaling_vector=None,
|
|
216
|
+
) -> Tensor:
|
|
217
|
+
# 1) generate random set of indices for dropping samples in the batch
|
|
218
|
+
branges_scales = [
|
|
219
|
+
get_branges_scales(x, sample_drop_ratio=sample_drop_ratio) for x in x_list
|
|
220
|
+
]
|
|
221
|
+
branges = [s[0] for s in branges_scales]
|
|
222
|
+
residual_scale_factors = [s[1] for s in branges_scales]
|
|
223
|
+
|
|
224
|
+
# 2) get attention bias and index+concat the tensors
|
|
225
|
+
attn_bias, x_cat = get_attn_bias_and_cat(x_list, branges)
|
|
226
|
+
|
|
227
|
+
# 3) apply residual_func to get residual, and split the result
|
|
228
|
+
residual_list = attn_bias.split(residual_func(x_cat, attn_bias=attn_bias)) # type: ignore
|
|
229
|
+
|
|
230
|
+
outputs = []
|
|
231
|
+
for x, brange, residual, residual_scale_factor in zip(
|
|
232
|
+
x_list, branges, residual_list, residual_scale_factors
|
|
233
|
+
):
|
|
234
|
+
outputs.append(
|
|
235
|
+
add_residual(
|
|
236
|
+
x, brange, residual, residual_scale_factor, scaling_vector
|
|
237
|
+
).view_as(x)
|
|
238
|
+
)
|
|
239
|
+
return outputs
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class NestedTensorBlock(Block):
|
|
243
|
+
def forward_nested(self, x_list: List[Tensor]) -> List[Tensor]:
|
|
244
|
+
"""
|
|
245
|
+
x_list contains a list of tensors to nest together and run
|
|
246
|
+
"""
|
|
247
|
+
assert isinstance(self.attn, MemEffAttention)
|
|
248
|
+
|
|
249
|
+
if self.training and self.sample_drop_ratio > 0.0:
|
|
250
|
+
|
|
251
|
+
def attn_residual_func(x: Tensor, attn_bias=None) -> Tensor:
|
|
252
|
+
return self.attn(self.norm1(x), attn_bias=attn_bias)
|
|
253
|
+
|
|
254
|
+
def ffn_residual_func(x: Tensor, attn_bias=None) -> Tensor:
|
|
255
|
+
return self.mlp(self.norm2(x))
|
|
256
|
+
|
|
257
|
+
x_list = drop_add_residual_stochastic_depth_list(
|
|
258
|
+
x_list,
|
|
259
|
+
residual_func=attn_residual_func,
|
|
260
|
+
sample_drop_ratio=self.sample_drop_ratio,
|
|
261
|
+
scaling_vector=self.ls1.gamma
|
|
262
|
+
if isinstance(self.ls1, LayerScale)
|
|
263
|
+
else None,
|
|
264
|
+
)
|
|
265
|
+
x_list = drop_add_residual_stochastic_depth_list(
|
|
266
|
+
x_list,
|
|
267
|
+
residual_func=ffn_residual_func,
|
|
268
|
+
sample_drop_ratio=self.sample_drop_ratio,
|
|
269
|
+
scaling_vector=self.ls2.gamma
|
|
270
|
+
if isinstance(self.ls1, LayerScale)
|
|
271
|
+
else None,
|
|
272
|
+
)
|
|
273
|
+
return x_list
|
|
274
|
+
else:
|
|
275
|
+
|
|
276
|
+
def attn_residual_func(x: Tensor, attn_bias=None) -> Tensor:
|
|
277
|
+
return self.ls1(self.attn(self.norm1(x), attn_bias=attn_bias))
|
|
278
|
+
|
|
279
|
+
def ffn_residual_func(x: Tensor, attn_bias=None) -> Tensor:
|
|
280
|
+
return self.ls2(self.mlp(self.norm2(x)))
|
|
281
|
+
|
|
282
|
+
attn_bias, x = get_attn_bias_and_cat(x_list)
|
|
283
|
+
x = x + attn_residual_func(x, attn_bias=attn_bias)
|
|
284
|
+
x = x + ffn_residual_func(x)
|
|
285
|
+
return attn_bias.split(x)
|
|
286
|
+
|
|
287
|
+
def forward(self, x_or_x_list):
|
|
288
|
+
if isinstance(x_or_x_list, Tensor):
|
|
289
|
+
return super().forward(x_or_x_list)
|
|
290
|
+
elif isinstance(x_or_x_list, list):
|
|
291
|
+
if not XFORMERS_AVAILABLE:
|
|
292
|
+
raise AssertionError("xFormers is required for using nested tensors")
|
|
293
|
+
return self.forward_nested(x_or_x_list)
|
|
294
|
+
else:
|
|
295
|
+
raise AssertionError
|
defm/layers/dino_head.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Copyright (c) 2026, ETH Zurich, Manthan Patel
|
|
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
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
7
|
+
#
|
|
8
|
+
# This source code is licensed under the Apache License, Version 2.0
|
|
9
|
+
# found in the LICENSE file in the root directory of this source tree.
|
|
10
|
+
|
|
11
|
+
import torch
|
|
12
|
+
import torch.nn as nn
|
|
13
|
+
from torch.nn.init import trunc_normal_
|
|
14
|
+
from torch.nn.utils import weight_norm
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DINOHead(nn.Module):
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
in_dim,
|
|
21
|
+
out_dim,
|
|
22
|
+
use_bn=False,
|
|
23
|
+
nlayers=3,
|
|
24
|
+
hidden_dim=2048,
|
|
25
|
+
bottleneck_dim=256,
|
|
26
|
+
mlp_bias=True,
|
|
27
|
+
):
|
|
28
|
+
super().__init__()
|
|
29
|
+
nlayers = max(nlayers, 1)
|
|
30
|
+
self.mlp = _build_mlp(
|
|
31
|
+
nlayers,
|
|
32
|
+
in_dim,
|
|
33
|
+
bottleneck_dim,
|
|
34
|
+
hidden_dim=hidden_dim,
|
|
35
|
+
use_bn=use_bn,
|
|
36
|
+
bias=mlp_bias,
|
|
37
|
+
)
|
|
38
|
+
self.apply(self._init_weights)
|
|
39
|
+
self.last_layer = weight_norm(nn.Linear(bottleneck_dim, out_dim, bias=False))
|
|
40
|
+
self.last_layer.weight_g.data.fill_(1)
|
|
41
|
+
|
|
42
|
+
def _init_weights(self, m):
|
|
43
|
+
if isinstance(m, nn.Linear):
|
|
44
|
+
trunc_normal_(m.weight, std=0.02)
|
|
45
|
+
if isinstance(m, nn.Linear) and m.bias is not None:
|
|
46
|
+
nn.init.constant_(m.bias, 0)
|
|
47
|
+
|
|
48
|
+
def forward(self, x):
|
|
49
|
+
x = self.mlp(x)
|
|
50
|
+
eps = 1e-6 if x.dtype == torch.float16 else 1e-12
|
|
51
|
+
x = nn.functional.normalize(x, dim=-1, p=2, eps=eps)
|
|
52
|
+
x = self.last_layer(x)
|
|
53
|
+
return x
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _build_mlp(
|
|
57
|
+
nlayers, in_dim, bottleneck_dim, hidden_dim=None, use_bn=False, bias=True
|
|
58
|
+
):
|
|
59
|
+
if nlayers == 1:
|
|
60
|
+
return nn.Linear(in_dim, bottleneck_dim, bias=bias)
|
|
61
|
+
else:
|
|
62
|
+
layers = [nn.Linear(in_dim, hidden_dim, bias=bias)]
|
|
63
|
+
if use_bn:
|
|
64
|
+
layers.append(nn.BatchNorm1d(hidden_dim))
|
|
65
|
+
layers.append(nn.GELU())
|
|
66
|
+
for _ in range(nlayers - 2):
|
|
67
|
+
layers.append(nn.Linear(hidden_dim, hidden_dim, bias=bias))
|
|
68
|
+
if use_bn:
|
|
69
|
+
layers.append(nn.BatchNorm1d(hidden_dim))
|
|
70
|
+
layers.append(nn.GELU())
|
|
71
|
+
layers.append(nn.Linear(hidden_dim, bottleneck_dim, bias=bias))
|
|
72
|
+
return nn.Sequential(*layers)
|
defm/layers/drop_path.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Copyright (c) 2026, ETH Zurich, Manthan Patel
|
|
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
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
7
|
+
#
|
|
8
|
+
# This source code is licensed under the Apache License, Version 2.0
|
|
9
|
+
# found in the LICENSE file in the root directory of this source tree.
|
|
10
|
+
|
|
11
|
+
# References:
|
|
12
|
+
# https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
|
|
13
|
+
# https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/drop.py
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from torch import nn
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def drop_path(x, drop_prob: float = 0.0, training: bool = False):
|
|
20
|
+
if drop_prob == 0.0 or not training:
|
|
21
|
+
return x
|
|
22
|
+
keep_prob = 1 - drop_prob
|
|
23
|
+
shape = (x.shape[0],) + (1,) * (
|
|
24
|
+
x.ndim - 1
|
|
25
|
+
) # work with diff dim tensors, not just 2D ConvNets
|
|
26
|
+
random_tensor = x.new_empty(shape).bernoulli_(keep_prob)
|
|
27
|
+
if keep_prob > 0.0:
|
|
28
|
+
random_tensor.div_(keep_prob)
|
|
29
|
+
output = x * random_tensor
|
|
30
|
+
return output
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class DropPath(nn.Module):
|
|
34
|
+
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks)."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, drop_prob=None):
|
|
37
|
+
super(DropPath, self).__init__()
|
|
38
|
+
self.drop_prob = drop_prob
|
|
39
|
+
|
|
40
|
+
def forward(self, x):
|
|
41
|
+
return drop_path(x, self.drop_prob, self.training)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Copyright (c) 2026, ETH Zurich, Manthan Patel
|
|
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
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
7
|
+
#
|
|
8
|
+
# This source code is licensed under the Apache License, Version 2.0
|
|
9
|
+
# found in the LICENSE file in the root directory of this source tree.
|
|
10
|
+
|
|
11
|
+
# Modified from: https://github.com/huggingface/pytorch-image-models/blob/main/timm/models/vision_transformer.py#L103-L110
|
|
12
|
+
|
|
13
|
+
from typing import Union
|
|
14
|
+
|
|
15
|
+
import torch
|
|
16
|
+
from torch import Tensor
|
|
17
|
+
from torch import nn
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LayerScale(nn.Module):
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
dim: int,
|
|
24
|
+
init_values: Union[float, Tensor] = 1e-5,
|
|
25
|
+
inplace: bool = False,
|
|
26
|
+
) -> None:
|
|
27
|
+
super().__init__()
|
|
28
|
+
self.inplace = inplace
|
|
29
|
+
self.gamma = nn.Parameter(init_values * torch.ones(dim))
|
|
30
|
+
|
|
31
|
+
def forward(self, x: Tensor) -> Tensor:
|
|
32
|
+
return x.mul_(self.gamma) if self.inplace else x * self.gamma
|