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 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,3 @@
1
+ arch: efficientnet_b0
2
+ out_channels: 64
3
+
@@ -0,0 +1,3 @@
1
+ arch: efficientnet_b2
2
+ out_channels: 128
3
+
@@ -0,0 +1,3 @@
1
+ arch: efficientnet_b4
2
+ out_channels: 256
3
+
@@ -0,0 +1,3 @@
1
+ arch: efficientnet_b6
2
+ out_channels: 256
3
+
@@ -0,0 +1,3 @@
1
+ arch: regnet_y_1_6gf
2
+ out_channels: 256
3
+
@@ -0,0 +1,3 @@
1
+ arch: regnet_y_400mf
2
+ out_channels: 64
3
+
@@ -0,0 +1,3 @@
1
+ arch: regnet_y_800mf
2
+ out_channels: 128
3
+
@@ -0,0 +1,3 @@
1
+ arch: resnet18
2
+ out_channels: 128
3
+
@@ -0,0 +1,3 @@
1
+ arch: resnet34
2
+ out_channels: 256
3
+
@@ -0,0 +1,3 @@
1
+ arch: resnet50
2
+ out_channels: 256
3
+
@@ -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
@@ -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
@@ -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
@@ -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)
@@ -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