Equimo 0.1.2__tar.gz → 0.1.3a2__tar.gz
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.
- {equimo-0.1.2 → equimo-0.1.3a2}/Equimo.egg-info/PKG-INFO +1 -1
- {equimo-0.1.2 → equimo-0.1.3a2}/PKG-INFO +1 -1
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/ffn.py +127 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/vit.py +43 -8
- {equimo-0.1.2 → equimo-0.1.3a2}/pyproject.toml +1 -1
- {equimo-0.1.2 → equimo-0.1.3a2}/Equimo.egg-info/SOURCES.txt +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/Equimo.egg-info/dependency_links.txt +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/Equimo.egg-info/requires.txt +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/Equimo.egg-info/top_level.txt +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/LICENSE.md +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/README.md +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/__init__.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/__init__.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/attention.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/convolution.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/downsample.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/dropout.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/generic.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/mamba.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/norm.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/patch.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/posemb.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/sharing.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/squeeze_excite.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/__init__.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/emamodel.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/fastervit.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/mlla.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/partialformer.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/shvit.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/vssd.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/ops/scan.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/equimo/utils.py +0 -0
- {equimo-0.1.2 → equimo-0.1.3a2}/setup.cfg +0 -0
|
@@ -9,6 +9,133 @@ from jaxtyping import Array, Float, PRNGKeyArray
|
|
|
9
9
|
from equimo.layers.dropout import Dropout
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
class WeightNormLinear(eqx.Module):
|
|
13
|
+
"""Linear layer with weight normalization.
|
|
14
|
+
|
|
15
|
+
Implements weight normalization as described in "Weight Normalization: A Simple
|
|
16
|
+
Reparameterization to Accelerate Training of Deep Neural Networks". The weights
|
|
17
|
+
are parameterized as w = g * (v/||v||), where g is a scalar and v is a vector.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
weight_v: The weight vector v to be normalized
|
|
21
|
+
weight_g: The scalar multiplier g for controlling the output scale
|
|
22
|
+
|
|
23
|
+
References:
|
|
24
|
+
[1] https://arxiv.org/abs/1602.07868
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
weight_v: jnp.ndarray
|
|
28
|
+
weight_g: jnp.ndarray
|
|
29
|
+
|
|
30
|
+
def __init__(self, in_features: int, out_features: int, key: PRNGKeyArray):
|
|
31
|
+
self.weight_v = eqx.nn.Linear(
|
|
32
|
+
in_features, out_features, use_bias=False, key=key
|
|
33
|
+
).weight
|
|
34
|
+
self.weight_g = jnp.ones((out_features, 1))
|
|
35
|
+
|
|
36
|
+
def __call__(self, x):
|
|
37
|
+
"""Apply weight normalized linear transformation.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
x: Input tensor
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Transformed tensor using normalized weights
|
|
44
|
+
"""
|
|
45
|
+
v_norm = jnp.linalg.norm(self.weight_v, ord=2, axis=1, keepdims=True)
|
|
46
|
+
normalized_v = self.weight_v / v_norm
|
|
47
|
+
weight = self.weight_g * normalized_v
|
|
48
|
+
|
|
49
|
+
out = x @ weight.T
|
|
50
|
+
return out
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class DINOHead(eqx.Module):
|
|
54
|
+
"""Multi-layer Perceptron (MLP) head used to train DINOv2 models.
|
|
55
|
+
|
|
56
|
+
Implements the projection head architecture from DINOv2 self-supervised learning.
|
|
57
|
+
Features multiple fully connected layers with activation, followed by L2
|
|
58
|
+
normalization and a weight-normalized final layer.
|
|
59
|
+
|
|
60
|
+
The architecture follows:
|
|
61
|
+
input -> fc1 -> act -> fc2 -> act -> fc3 -> act -> L2norm -> weight_norm_linear
|
|
62
|
+
|
|
63
|
+
Attributes:
|
|
64
|
+
fc1: First linear layer projecting to hidden dimension
|
|
65
|
+
fc2: Second linear layer maintaining hidden dimension
|
|
66
|
+
fc3: Third linear layer projecting to bottleneck dimension
|
|
67
|
+
last: Final weight-normalized linear layer
|
|
68
|
+
act_layer: Activation function used between layers
|
|
69
|
+
|
|
70
|
+
References:
|
|
71
|
+
[1] https://arxiv.org/abs/2304.07193
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
fc1: eqx.nn.Linear
|
|
75
|
+
fc2: eqx.nn.Linear
|
|
76
|
+
fc3: eqx.nn.Linear
|
|
77
|
+
last: WeightNormLinear
|
|
78
|
+
act_layer: Callable
|
|
79
|
+
|
|
80
|
+
def __init__(
|
|
81
|
+
self,
|
|
82
|
+
in_features: int,
|
|
83
|
+
out_features: int,
|
|
84
|
+
*,
|
|
85
|
+
hidden_dim=2048,
|
|
86
|
+
bottleneck_dim=256,
|
|
87
|
+
key: PRNGKeyArray,
|
|
88
|
+
act_layer: Callable = jax.nn.gelu,
|
|
89
|
+
**kwargs,
|
|
90
|
+
):
|
|
91
|
+
"""Initialize the DINOv2 projection head.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
in_features: Number of input features
|
|
95
|
+
out_features: Number of output features
|
|
96
|
+
hidden_dim: Dimension of hidden layers (default: 2048)
|
|
97
|
+
bottleneck_dim: Dimension of bottleneck layer (default: 256)
|
|
98
|
+
key: PRNG key for initialization
|
|
99
|
+
act_layer: Activation function (default: gelu)
|
|
100
|
+
**kwargs: Additional arguments
|
|
101
|
+
"""
|
|
102
|
+
key_fc1, key_fc2, key_fc3, key_last = jr.split(key, 3)
|
|
103
|
+
|
|
104
|
+
self.act_layer = act_layer
|
|
105
|
+
|
|
106
|
+
self.fc1 = eqx.nn.Linear(in_features, hidden_dim, key=key_fc1)
|
|
107
|
+
self.fc2 = eqx.nn.Linear(hidden_dim, hidden_dim, key=key_fc2)
|
|
108
|
+
self.fc3 = eqx.nn.Linear(hidden_dim, bottleneck_dim, key=key_fc3)
|
|
109
|
+
self.last = WeightNormLinear(bottleneck_dim, out_features, key=key_last)
|
|
110
|
+
|
|
111
|
+
def __call__(
|
|
112
|
+
self,
|
|
113
|
+
x: Float[Array, "seqlen dim"],
|
|
114
|
+
enable_dropout: bool,
|
|
115
|
+
key: PRNGKeyArray,
|
|
116
|
+
) -> Float[Array, "seqlen dim"]:
|
|
117
|
+
"""Process input through the DINOv2 projection head.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
x: Input feature tensor
|
|
121
|
+
enable_dropout: Whether to enable dropout (unused in original implementation)
|
|
122
|
+
key: PRNG key for random operations
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Projected and normalized features
|
|
126
|
+
"""
|
|
127
|
+
eps = jnp.where(x.dtype == jnp.float16, 1e-6, 1e-12)
|
|
128
|
+
x = self.act_layer(jax.vmap(self.fc1)(x))
|
|
129
|
+
x = self.act_layer(jax.vmap(self.fc2)(x))
|
|
130
|
+
x = self.act_layer(jax.vmap(self.fc3)(x))
|
|
131
|
+
|
|
132
|
+
x = x / (jnp.linalg.norm(x, ord=2, axis=-1, keepdims=True) + eps)
|
|
133
|
+
|
|
134
|
+
x = self.last(x)
|
|
135
|
+
|
|
136
|
+
return x
|
|
137
|
+
|
|
138
|
+
|
|
12
139
|
class Mlp(eqx.Module):
|
|
13
140
|
"""Multi-layer Perceptron (MLP) module with dropout.
|
|
14
141
|
|
|
@@ -6,7 +6,7 @@ import jax
|
|
|
6
6
|
import jax.numpy as jnp
|
|
7
7
|
import jax.random as jr
|
|
8
8
|
from einops import rearrange
|
|
9
|
-
from jaxtyping import Array, Float, PRNGKeyArray
|
|
9
|
+
from jaxtyping import Array, Float, Int, PRNGKeyArray
|
|
10
10
|
|
|
11
11
|
from equimo.layers.attention import Attention, AttentionBlock
|
|
12
12
|
from equimo.layers.dropout import Dropout
|
|
@@ -148,12 +148,14 @@ class VisionTransformer(eqx.Module):
|
|
|
148
148
|
pos_embed: jnp.ndarray
|
|
149
149
|
cls_token: jnp.ndarray | None
|
|
150
150
|
reg_tokens: jnp.ndarray | None
|
|
151
|
+
mask_token: jnp.ndarray | None
|
|
151
152
|
blocks: List[eqx.Module]
|
|
152
153
|
pos_drop: Dropout
|
|
153
154
|
norm: eqx.Module
|
|
154
155
|
head: eqx.Module
|
|
155
156
|
|
|
156
157
|
dim: int = eqx.field(static=True)
|
|
158
|
+
embed_size: int = eqx.field(static=True)
|
|
157
159
|
num_patches: int = eqx.field(static=True)
|
|
158
160
|
global_pool: str = eqx.field(static=True)
|
|
159
161
|
num_reg_tokens: int = eqx.field(static=True)
|
|
@@ -175,6 +177,7 @@ class VisionTransformer(eqx.Module):
|
|
|
175
177
|
depths: List[int],
|
|
176
178
|
*,
|
|
177
179
|
key: PRNGKeyArray,
|
|
180
|
+
use_mask_token: bool = False,
|
|
178
181
|
repeat: int = 1,
|
|
179
182
|
dynamic_img_size: bool = False,
|
|
180
183
|
dynamic_img_pad: bool = False,
|
|
@@ -217,6 +220,7 @@ class VisionTransformer(eqx.Module):
|
|
|
217
220
|
self.no_embed_class = no_embed_class
|
|
218
221
|
self.pos_embed_reg_tokens = pos_embed_reg_tokens
|
|
219
222
|
self.global_pool = global_pool
|
|
223
|
+
self.embed_size = img_size // patch_size
|
|
220
224
|
|
|
221
225
|
self.patch_embed = PatchEmbedding(
|
|
222
226
|
in_channels,
|
|
@@ -232,6 +236,8 @@ class VisionTransformer(eqx.Module):
|
|
|
232
236
|
self.cls_token = jr.normal(key_cls, (1, dim))
|
|
233
237
|
self.reg_tokens = jr.normal(key_reg, (reg_tokens, dim))
|
|
234
238
|
|
|
239
|
+
self.mask_token = jnp.zeros((1, dim)) if use_mask_token else None
|
|
240
|
+
|
|
235
241
|
if no_embed_class:
|
|
236
242
|
self.embed_len = self.num_patches
|
|
237
243
|
elif self.pos_embed_reg_tokens:
|
|
@@ -390,6 +396,7 @@ class VisionTransformer(eqx.Module):
|
|
|
390
396
|
x: Float[Array, "channels height width"],
|
|
391
397
|
enable_dropout: bool,
|
|
392
398
|
key: PRNGKeyArray,
|
|
399
|
+
mask: Optional[Int[Array, "embed_h embed_w"]] = None,
|
|
393
400
|
) -> Float[Array, "seqlen dim"]:
|
|
394
401
|
"""Extract features from input image.
|
|
395
402
|
|
|
@@ -397,6 +404,7 @@ class VisionTransformer(eqx.Module):
|
|
|
397
404
|
x: Input image tensor
|
|
398
405
|
enable_dropout: Whether to enable dropout during inference
|
|
399
406
|
key: PRNG key for random operations
|
|
407
|
+
mask: optional binary mask of the size of the input after patch embedding
|
|
400
408
|
|
|
401
409
|
Returns:
|
|
402
410
|
Processed feature tensor
|
|
@@ -405,6 +413,16 @@ class VisionTransformer(eqx.Module):
|
|
|
405
413
|
x = self.patch_embed(x)
|
|
406
414
|
x = self.pos_drop(x, inference=not enable_dropout, key=key_posdrop)
|
|
407
415
|
|
|
416
|
+
if mask is not None:
|
|
417
|
+
assert (
|
|
418
|
+
self.mask_token is not None
|
|
419
|
+
), "To use masked forward, init the model with `use_mask_token=True`."
|
|
420
|
+
x = jnp.where(
|
|
421
|
+
rearrange(mask, "h w -> (h w) 1"), x, self.mask_token.astype(x.dtype)
|
|
422
|
+
)
|
|
423
|
+
# TODO: Decompose in multiple fns
|
|
424
|
+
x = self._pos_embed(x, h=self.embed_size, w=self.embed_size)
|
|
425
|
+
|
|
408
426
|
for blk, key_block in zip(self.blocks, block_subkeys):
|
|
409
427
|
x = blk(x, enable_dropout=enable_dropout, key=key_block)
|
|
410
428
|
|
|
@@ -430,13 +448,7 @@ class VisionTransformer(eqx.Module):
|
|
|
430
448
|
- x_norm_patchtokens: Normalized patch tokens
|
|
431
449
|
- x_prenorm: Pre-normalized features
|
|
432
450
|
"""
|
|
433
|
-
|
|
434
|
-
x = self.patch_embed(x)
|
|
435
|
-
x = self.pos_drop(x, inference=not enable_dropout, key=key_posdrop)
|
|
436
|
-
|
|
437
|
-
for blk, key_block in zip(self.blocks, block_subkeys):
|
|
438
|
-
x = blk(x, enable_dropout=enable_dropout, key=key_block)
|
|
439
|
-
|
|
451
|
+
x = self.features(x, enable_dropout=enable_dropout, key=key)
|
|
440
452
|
x_norm = jax.vmap(self.norm)(x)
|
|
441
453
|
|
|
442
454
|
return {
|
|
@@ -474,3 +486,26 @@ class VisionTransformer(eqx.Module):
|
|
|
474
486
|
x = self.head(x)
|
|
475
487
|
|
|
476
488
|
return x
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
#
|
|
492
|
+
# key = jr.PRNGKey(42)
|
|
493
|
+
# img_size = 224
|
|
494
|
+
# patch_size = 14
|
|
495
|
+
#
|
|
496
|
+
# x = jr.normal(key, (3, 224, 224))
|
|
497
|
+
# mask = jr.bernoulli(key, shape=(16, 16)) * 1
|
|
498
|
+
#
|
|
499
|
+
# base_model = VisionTransformer(
|
|
500
|
+
# img_size=img_size,
|
|
501
|
+
# in_channels=3,
|
|
502
|
+
# dim=384,
|
|
503
|
+
# patch_size=patch_size,
|
|
504
|
+
# num_heads=[6],
|
|
505
|
+
# depths=[12],
|
|
506
|
+
# num_classes=0,
|
|
507
|
+
# use_mask_token=True,
|
|
508
|
+
# key=key,
|
|
509
|
+
# )
|
|
510
|
+
# f = base_model.features(x, mask=mask, enable_dropout=True, key=key)
|
|
511
|
+
# self = base_model
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|