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.
Files changed (34) hide show
  1. {equimo-0.1.2 → equimo-0.1.3a2}/Equimo.egg-info/PKG-INFO +1 -1
  2. {equimo-0.1.2 → equimo-0.1.3a2}/PKG-INFO +1 -1
  3. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/ffn.py +127 -0
  4. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/vit.py +43 -8
  5. {equimo-0.1.2 → equimo-0.1.3a2}/pyproject.toml +1 -1
  6. {equimo-0.1.2 → equimo-0.1.3a2}/Equimo.egg-info/SOURCES.txt +0 -0
  7. {equimo-0.1.2 → equimo-0.1.3a2}/Equimo.egg-info/dependency_links.txt +0 -0
  8. {equimo-0.1.2 → equimo-0.1.3a2}/Equimo.egg-info/requires.txt +0 -0
  9. {equimo-0.1.2 → equimo-0.1.3a2}/Equimo.egg-info/top_level.txt +0 -0
  10. {equimo-0.1.2 → equimo-0.1.3a2}/LICENSE.md +0 -0
  11. {equimo-0.1.2 → equimo-0.1.3a2}/README.md +0 -0
  12. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/__init__.py +0 -0
  13. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/__init__.py +0 -0
  14. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/attention.py +0 -0
  15. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/convolution.py +0 -0
  16. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/downsample.py +0 -0
  17. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/dropout.py +0 -0
  18. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/generic.py +0 -0
  19. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/mamba.py +0 -0
  20. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/norm.py +0 -0
  21. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/patch.py +0 -0
  22. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/posemb.py +0 -0
  23. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/sharing.py +0 -0
  24. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/layers/squeeze_excite.py +0 -0
  25. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/__init__.py +0 -0
  26. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/emamodel.py +0 -0
  27. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/fastervit.py +0 -0
  28. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/mlla.py +0 -0
  29. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/partialformer.py +0 -0
  30. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/shvit.py +0 -0
  31. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/models/vssd.py +0 -0
  32. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/ops/scan.py +0 -0
  33. {equimo-0.1.2 → equimo-0.1.3a2}/equimo/utils.py +0 -0
  34. {equimo-0.1.2 → equimo-0.1.3a2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Equimo
3
- Version: 0.1.2
3
+ Version: 0.1.3a2
4
4
  Summary: Implementation of popular vision models in Jax
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Equimo
3
- Version: 0.1.2
3
+ Version: 0.1.3a2
4
4
  Summary: Implementation of popular vision models in Jax
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -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
- key_posdrop, *block_subkeys = jr.split(key, len(self.blocks) + 1)
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "Equimo"
3
- version = "0.1.2"
3
+ version = "0.1.3a2"
4
4
  description = "Implementation of popular vision models in Jax"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
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