Equimo 0.2.2__tar.gz → 0.2.4__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 (41) hide show
  1. {equimo-0.2.2 → equimo-0.2.4}/Equimo.egg-info/PKG-INFO +28 -10
  2. {equimo-0.2.2 → equimo-0.2.4}/Equimo.egg-info/SOURCES.txt +1 -3
  3. {equimo-0.2.2 → equimo-0.2.4}/Equimo.egg-info/requires.txt +2 -0
  4. {equimo-0.2.2 → equimo-0.2.4}/PKG-INFO +28 -10
  5. {equimo-0.2.2 → equimo-0.2.4}/README.md +25 -9
  6. equimo-0.2.4/equimo/__init__.py +1 -0
  7. {equimo-0.2.2 → equimo-0.2.4}/equimo/io.py +21 -4
  8. equimo-0.2.4/equimo/layers/activation.py +33 -0
  9. {equimo-0.2.2 → equimo-0.2.4}/equimo/models/vit.py +4 -2
  10. {equimo-0.2.2 → equimo-0.2.4}/pyproject.toml +4 -1
  11. equimo-0.2.2/equimo/converters/__init__.py +0 -1
  12. equimo-0.2.2/equimo/converters/dinov2.py +0 -69
  13. equimo-0.2.2/equimo/converters/utils.py +0 -152
  14. equimo-0.2.2/equimo/layers/__init__.py +0 -0
  15. {equimo-0.2.2 → equimo-0.2.4}/Equimo.egg-info/dependency_links.txt +0 -0
  16. {equimo-0.2.2 → equimo-0.2.4}/Equimo.egg-info/top_level.txt +0 -0
  17. {equimo-0.2.2 → equimo-0.2.4}/LICENSE.md +0 -0
  18. {equimo-0.2.2/equimo → equimo-0.2.4/equimo/layers}/__init__.py +0 -0
  19. {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/attention.py +0 -0
  20. {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/convolution.py +0 -0
  21. {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/downsample.py +0 -0
  22. {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/dropout.py +0 -0
  23. {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/ffn.py +0 -0
  24. {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/generic.py +0 -0
  25. {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/mamba.py +0 -0
  26. {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/norm.py +0 -0
  27. {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/patch.py +0 -0
  28. {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/posemb.py +0 -0
  29. {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/sharing.py +0 -0
  30. {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/squeeze_excite.py +0 -0
  31. {equimo-0.2.2 → equimo-0.2.4}/equimo/models/__init__.py +0 -0
  32. {equimo-0.2.2 → equimo-0.2.4}/equimo/models/emamodel.py +0 -0
  33. {equimo-0.2.2 → equimo-0.2.4}/equimo/models/fastervit.py +0 -0
  34. {equimo-0.2.2 → equimo-0.2.4}/equimo/models/mlla.py +0 -0
  35. {equimo-0.2.2 → equimo-0.2.4}/equimo/models/partialformer.py +0 -0
  36. {equimo-0.2.2 → equimo-0.2.4}/equimo/models/shvit.py +0 -0
  37. {equimo-0.2.2 → equimo-0.2.4}/equimo/models/vssd.py +0 -0
  38. {equimo-0.2.2 → equimo-0.2.4}/equimo/ops/scan.py +0 -0
  39. {equimo-0.2.2 → equimo-0.2.4}/equimo/utils.py +0 -0
  40. {equimo-0.2.2 → equimo-0.2.4}/setup.cfg +0 -0
  41. {equimo-0.2.2 → equimo-0.2.4}/tests/test_models.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: Equimo
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Implementation of popular vision models in Jax
5
5
  Classifier: Development Status :: 4 - Beta
6
6
  Classifier: Intended Audience :: Developers
@@ -20,8 +20,10 @@ Requires-Dist: jaxlib>=0.4.25
20
20
  Requires-Dist: loguru>=0.7.3
21
21
  Requires-Dist: lz4>=4.4.3
22
22
  Requires-Dist: requests>=2.32.3
23
+ Requires-Dist: semver>=3.0.4
23
24
  Provides-Extra: torch
24
25
  Requires-Dist: torch>=2.6.0; extra == "torch"
26
+ Requires-Dist: timm>=1.0.15; extra == "torch"
25
27
 
26
28
  # Equimo: Modern Vision Models in JAX/Equinox
27
29
 
@@ -54,6 +56,8 @@ pip install -e .
54
56
 
55
57
  ## Implemented Models
56
58
 
59
+ Beyond normal ViT (e.g., dinov2 or siglip), equimo proposes other SotA architectures:
60
+
57
61
  | Model | Paper | Year | Status |
58
62
  | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | --------- |
59
63
  | FasterViT | [FasterViT: Fast Vision Transformers with Hierarchical Attention](https://arxiv.org/abs/2306.06189) | 2023 | ✅ |
@@ -139,19 +143,33 @@ model = load_model(cls="vit", path=Path("path/to/model.tar.lz4"))
139
143
  model = load_model(cls="vit", path=Path("path/to/model/"))
140
144
  ```
141
145
 
146
+ Parameters passed to models can be overridden such as:
147
+
148
+ ```python
149
+ model = load_model(
150
+ cls="vit",
151
+ identifier="siglip2_vitb16_256",
152
+ dynamic_img_size=True, # passed to the VisionTransformer class
153
+ )
154
+ ```
155
+
142
156
  #### List of pretrained models
143
157
 
144
- Currently, only [DinoV2](https://github.com/facebookresearch/dinov2/) have been ported, therefore the following
145
- identifiers are available:
158
+ The following models have pretrained weights available in Equimo:
159
+
160
+ - [DinoV2](https://arxiv.org/abs/2304.07193),
161
+ - [SigLIP2](https://arxiv.org/abs/2502.14786),
162
+ - [TIPS](https://arxiv.org/abs/2410.16512).
163
+
164
+ Model identifiers allow downloading from equimo's [repository on huggingface](https://huggingface.co/poiretclement/equimo/tree/main/models/default)
146
165
 
147
- - `dinov2_vitb14.tar.lz4`
148
- - `dinov2_vitb14_reg.tar.lz4`
149
- - `dinov2_vitl14.tar.lz4`
150
- - `dinov2_vitl14_reg.tar.lz4`
151
- - `dinov2_vits14.tar.lz4`
152
- - `dinov2_vits14_reg.tar.lz4`
166
+ Identifiers are filenames without the extensions, such as:
153
167
 
154
- (`giant` version coming soon)
168
+ - `dinov2_vitb14`
169
+ - `dinov2_vits14_reg`
170
+ - `siglip2_vitl16_512`
171
+ - `siglip2_vitso400m16_384`
172
+ - `tips_vitg14_lr`
155
173
 
156
174
  ## Contributing
157
175
 
@@ -9,10 +9,8 @@ Equimo.egg-info/top_level.txt
9
9
  equimo/__init__.py
10
10
  equimo/io.py
11
11
  equimo/utils.py
12
- equimo/converters/__init__.py
13
- equimo/converters/dinov2.py
14
- equimo/converters/utils.py
15
12
  equimo/layers/__init__.py
13
+ equimo/layers/activation.py
16
14
  equimo/layers/attention.py
17
15
  equimo/layers/convolution.py
18
16
  equimo/layers/downsample.py
@@ -5,6 +5,8 @@ jaxlib>=0.4.25
5
5
  loguru>=0.7.3
6
6
  lz4>=4.4.3
7
7
  requests>=2.32.3
8
+ semver>=3.0.4
8
9
 
9
10
  [torch]
10
11
  torch>=2.6.0
12
+ timm>=1.0.15
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: Equimo
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Implementation of popular vision models in Jax
5
5
  Classifier: Development Status :: 4 - Beta
6
6
  Classifier: Intended Audience :: Developers
@@ -20,8 +20,10 @@ Requires-Dist: jaxlib>=0.4.25
20
20
  Requires-Dist: loguru>=0.7.3
21
21
  Requires-Dist: lz4>=4.4.3
22
22
  Requires-Dist: requests>=2.32.3
23
+ Requires-Dist: semver>=3.0.4
23
24
  Provides-Extra: torch
24
25
  Requires-Dist: torch>=2.6.0; extra == "torch"
26
+ Requires-Dist: timm>=1.0.15; extra == "torch"
25
27
 
26
28
  # Equimo: Modern Vision Models in JAX/Equinox
27
29
 
@@ -54,6 +56,8 @@ pip install -e .
54
56
 
55
57
  ## Implemented Models
56
58
 
59
+ Beyond normal ViT (e.g., dinov2 or siglip), equimo proposes other SotA architectures:
60
+
57
61
  | Model | Paper | Year | Status |
58
62
  | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | --------- |
59
63
  | FasterViT | [FasterViT: Fast Vision Transformers with Hierarchical Attention](https://arxiv.org/abs/2306.06189) | 2023 | ✅ |
@@ -139,19 +143,33 @@ model = load_model(cls="vit", path=Path("path/to/model.tar.lz4"))
139
143
  model = load_model(cls="vit", path=Path("path/to/model/"))
140
144
  ```
141
145
 
146
+ Parameters passed to models can be overridden such as:
147
+
148
+ ```python
149
+ model = load_model(
150
+ cls="vit",
151
+ identifier="siglip2_vitb16_256",
152
+ dynamic_img_size=True, # passed to the VisionTransformer class
153
+ )
154
+ ```
155
+
142
156
  #### List of pretrained models
143
157
 
144
- Currently, only [DinoV2](https://github.com/facebookresearch/dinov2/) have been ported, therefore the following
145
- identifiers are available:
158
+ The following models have pretrained weights available in Equimo:
159
+
160
+ - [DinoV2](https://arxiv.org/abs/2304.07193),
161
+ - [SigLIP2](https://arxiv.org/abs/2502.14786),
162
+ - [TIPS](https://arxiv.org/abs/2410.16512).
163
+
164
+ Model identifiers allow downloading from equimo's [repository on huggingface](https://huggingface.co/poiretclement/equimo/tree/main/models/default)
146
165
 
147
- - `dinov2_vitb14.tar.lz4`
148
- - `dinov2_vitb14_reg.tar.lz4`
149
- - `dinov2_vitl14.tar.lz4`
150
- - `dinov2_vitl14_reg.tar.lz4`
151
- - `dinov2_vits14.tar.lz4`
152
- - `dinov2_vits14_reg.tar.lz4`
166
+ Identifiers are filenames without the extensions, such as:
153
167
 
154
- (`giant` version coming soon)
168
+ - `dinov2_vitb14`
169
+ - `dinov2_vits14_reg`
170
+ - `siglip2_vitl16_512`
171
+ - `siglip2_vitso400m16_384`
172
+ - `tips_vitg14_lr`
155
173
 
156
174
  ## Contributing
157
175
 
@@ -29,6 +29,8 @@ pip install -e .
29
29
 
30
30
  ## Implemented Models
31
31
 
32
+ Beyond normal ViT (e.g., dinov2 or siglip), equimo proposes other SotA architectures:
33
+
32
34
  | Model | Paper | Year | Status |
33
35
  | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | --------- |
34
36
  | FasterViT | [FasterViT: Fast Vision Transformers with Hierarchical Attention](https://arxiv.org/abs/2306.06189) | 2023 | ✅ |
@@ -114,19 +116,33 @@ model = load_model(cls="vit", path=Path("path/to/model.tar.lz4"))
114
116
  model = load_model(cls="vit", path=Path("path/to/model/"))
115
117
  ```
116
118
 
119
+ Parameters passed to models can be overridden such as:
120
+
121
+ ```python
122
+ model = load_model(
123
+ cls="vit",
124
+ identifier="siglip2_vitb16_256",
125
+ dynamic_img_size=True, # passed to the VisionTransformer class
126
+ )
127
+ ```
128
+
117
129
  #### List of pretrained models
118
130
 
119
- Currently, only [DinoV2](https://github.com/facebookresearch/dinov2/) have been ported, therefore the following
120
- identifiers are available:
131
+ The following models have pretrained weights available in Equimo:
132
+
133
+ - [DinoV2](https://arxiv.org/abs/2304.07193),
134
+ - [SigLIP2](https://arxiv.org/abs/2502.14786),
135
+ - [TIPS](https://arxiv.org/abs/2410.16512).
136
+
137
+ Model identifiers allow downloading from equimo's [repository on huggingface](https://huggingface.co/poiretclement/equimo/tree/main/models/default)
121
138
 
122
- - `dinov2_vitb14.tar.lz4`
123
- - `dinov2_vitb14_reg.tar.lz4`
124
- - `dinov2_vitl14.tar.lz4`
125
- - `dinov2_vitl14_reg.tar.lz4`
126
- - `dinov2_vits14.tar.lz4`
127
- - `dinov2_vits14_reg.tar.lz4`
139
+ Identifiers are filenames without the extensions, such as:
128
140
 
129
- (`giant` version coming soon)
141
+ - `dinov2_vitb14`
142
+ - `dinov2_vits14_reg`
143
+ - `siglip2_vitl16_512`
144
+ - `siglip2_vitso400m16_384`
145
+ - `tips_vitg14_lr`
130
146
 
131
147
  ## Contributing
132
148
 
@@ -0,0 +1 @@
1
+ __version__ = "0.2.4"
@@ -8,8 +8,10 @@ import jax
8
8
  import lz4.frame
9
9
  import requests
10
10
  from loguru import logger
11
+ from semver import Version
11
12
 
12
13
  import equimo.models as em
14
+ from equimo import __version__
13
15
 
14
16
  DEFAULT_REPOSITORY_URL = (
15
17
  "https://huggingface.co/poiretclement/equimo/resolve/main/models/default"
@@ -20,7 +22,8 @@ def save_model(
20
22
  path: Path,
21
23
  model: eqx.Module,
22
24
  model_config: dict,
23
- torch_hub_cfg: list[str],
25
+ torch_hub_cfg: list[str] = [],
26
+ timm_cfg: list = [],
24
27
  compression: bool = True,
25
28
  ):
26
29
  """Save an Equinox model with its configuration and metadata to disk.
@@ -31,6 +34,7 @@ def save_model(
31
34
  model (eqx.Module): The Equinox model to be saved.
32
35
  model_config (dict): Configuration dictionary containing model hyperparameters.
33
36
  torch_hub_cfg (list[str]): List of torch hub configuration parameters.
37
+ timm_cfg (list[str]): List of timm configuration parameters.
34
38
  compression (bool, optional): Whether to compress the saved model using LZ4.
35
39
  Defaults to True.
36
40
 
@@ -46,8 +50,10 @@ def save_model(
46
50
  metadata = {
47
51
  "model_config": model_config,
48
52
  "torch_hub_cfg": torch_hub_cfg,
53
+ "timm": timm_cfg,
49
54
  "jax_version": jax.__version__,
50
55
  "equinox_version": eqx.__version__,
56
+ "equimo_version": __version__,
51
57
  }
52
58
 
53
59
  logger.debug(f"Metadata: {metadata}")
@@ -66,6 +72,7 @@ def save_model(
66
72
  eqx.tree_serialise_leaves(tmp_path / "weights.eqx", model)
67
73
 
68
74
  # Create compressed archive
75
+ path.parent.mkdir(parents=True, exist_ok=True)
69
76
  with lz4.frame.open(path, "wb") as f_out:
70
77
  with tarfile.open(fileobj=f_out, mode="w") as tar:
71
78
  tar.add(tmp_path / "metadata.json", arcname="metadata.json")
@@ -100,8 +107,9 @@ def download(identifier: str, repository: str) -> Path:
100
107
 
101
108
  logger.info(f"Downloading {identifier}...")
102
109
 
103
- url = f"{repository}/{identifier}.tar.lz4"
104
- path = Path(f"~/.cache/equimo/{identifier}.tar.lz4").expanduser()
110
+ model = identifier.split("_")[0]
111
+ url = f"{repository}/{model}/{identifier}.tar.lz4"
112
+ path = Path(f"~/.cache/equimo/{model}/{identifier}.tar.lz4").expanduser()
105
113
  path.parent.mkdir(parents=True, exist_ok=True)
106
114
 
107
115
  if path.exists():
@@ -123,6 +131,7 @@ def load_model(
123
131
  identifier: str | None = None,
124
132
  path: Path | None = None,
125
133
  repository: str = DEFAULT_REPOSITORY_URL,
134
+ **model_kwargs,
126
135
  ) -> eqx.Module:
127
136
  """Load an Equinox model from either a local path or remote repository.
128
137
 
@@ -135,6 +144,7 @@ def load_model(
135
144
  Mutually exclusive with identifier. Defaults to None.
136
145
  repository (str, optional): Base URL for model download.
137
146
  Defaults to DEFAULT_REPOSITORY_URL.
147
+ model_kwargs: kwargs passed to model instanciation. Overrides metadatas.
138
148
 
139
149
  Returns:
140
150
  eqx.Module: Loaded and initialized model with weights.
@@ -189,6 +199,12 @@ def load_model(
189
199
 
190
200
  logger.debug(f"Metadata: {metadata}")
191
201
 
202
+ model_eqm_version = metadata.get("equimo_version", "0.2.0")
203
+ if Version.parse(model_eqm_version) > Version.parse(__version__):
204
+ logger.warning(
205
+ f"The model you are importing was packaged with equimo v{model_eqm_version}, but you have equimo v{__version__}. You may face unexpected errors. Please consider updating equimo."
206
+ )
207
+
192
208
  # Class resolution
193
209
  match cls:
194
210
  case "vit":
@@ -207,7 +223,8 @@ def load_model(
207
223
  raise ValueError(f"Unknown model class: {cls}")
208
224
 
209
225
  # Reconstruct model skeleton
210
- model = model_cls(**metadata["model_config"], key=jax.random.PRNGKey(42))
226
+ kwargs = metadata["model_config"] | model_kwargs
227
+ model = model_cls(**kwargs, key=jax.random.PRNGKey(42))
211
228
 
212
229
  # Load weights
213
230
  model = eqx.tree_deserialise_leaves(load_path / "weights.eqx", model)
@@ -0,0 +1,33 @@
1
+ from typing import Callable
2
+ import jax
3
+ from functools import partial
4
+
5
+
6
+ def get_act(activation: str | Callable) -> Callable:
7
+ """Get an activation function from its common name.
8
+
9
+ This is necessary because configs have to be stringified and stored as
10
+ json files to allow (de)serialization.
11
+ """
12
+ if not isinstance(activation, str):
13
+ return activation
14
+
15
+ match activation:
16
+ case "relu":
17
+ return jax.nn.relu
18
+ case "gelu":
19
+ return jax.nn.gelu
20
+ case "exactgelu":
21
+ return partial(jax.nn.gelu, approximate=False)
22
+ case "silu":
23
+ return jax.nn.silu
24
+ case "elu":
25
+ return jax.nn.elu
26
+ case "sigmoid":
27
+ return jax.nn.sigmoid
28
+ case "hard_sigmoid":
29
+ return jax.nn.hard_sigmoid
30
+ case "softmax":
31
+ return jax.nn.softmax
32
+ case _:
33
+ raise ValueError(f"Got an unknown activation string: {activation}")
@@ -8,6 +8,7 @@ import jax.random as jr
8
8
  from einops import rearrange
9
9
  from jaxtyping import Array, Float, Int, PRNGKeyArray
10
10
 
11
+ from equimo.layers.activation import get_act
11
12
  from equimo.layers.attention import (
12
13
  Attention,
13
14
  AttentionBlock,
@@ -190,7 +191,7 @@ class VisionTransformer(eqx.Module):
190
191
  qk_norm: bool = False,
191
192
  attn_drop: float = 0.0,
192
193
  proj_drop: float = 0.0,
193
- act_layer: Callable = jax.nn.gelu,
194
+ act_layer: Callable | str = jax.nn.gelu,
194
195
  attn_layer: str | eqx.Module = Attention,
195
196
  ffn_layer: str | eqx.Module = Mlp,
196
197
  ffn_bias: bool = True,
@@ -222,6 +223,7 @@ class VisionTransformer(eqx.Module):
222
223
  attn_layer = get_attention(attn_layer)
223
224
  ffn_layer = get_ffn(ffn_layer)
224
225
  norm_layer = get_norm(norm_layer)
226
+ act_layer = get_act(act_layer)
225
227
 
226
228
  self.patch_embed = PatchEmbedding(
227
229
  in_channels,
@@ -234,7 +236,7 @@ class VisionTransformer(eqx.Module):
234
236
  key=key_patchemb,
235
237
  )
236
238
  self.num_patches = self.patch_embed.num_patches
237
- self.cls_token = jr.normal(key_cls, (1, dim))
239
+ self.cls_token = jr.normal(key_cls, (1, dim)) if class_token else None
238
240
  self.reg_tokens = (
239
241
  jr.normal(key_reg, (reg_tokens, dim)) if reg_tokens > 0 else None
240
242
  )
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "Equimo"
3
- version = "0.2.2"
3
+ version = "0.2.4"
4
4
  description = "Implementation of popular vision models in Jax"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -12,6 +12,7 @@ dependencies = [
12
12
  "loguru>=0.7.3",
13
13
  "lz4>=4.4.3",
14
14
  "requests>=2.32.3",
15
+ "semver>=3.0.4",
15
16
  ]
16
17
  classifiers = [
17
18
  "Development Status :: 4 - Beta",
@@ -32,6 +33,7 @@ classifiers = [
32
33
  [project.optional-dependencies]
33
34
  torch = [
34
35
  "torch>=2.6.0",
36
+ "timm>=1.0.15",
35
37
  ]
36
38
 
37
39
  [dependency-groups]
@@ -41,3 +43,4 @@ dev = [
41
43
 
42
44
  [tool.setuptools]
43
45
  license-files = []
46
+ packages = ["equimo"]
@@ -1 +0,0 @@
1
-
@@ -1,69 +0,0 @@
1
- from pathlib import Path
2
-
3
- import jax
4
-
5
- import equimo.models as em
6
- from equimo.converters.utils import convert_torch_to_equinox
7
- from equimo.io import load_model, save_model
8
-
9
-
10
- def main():
11
- key = jax.random.PRNGKey(42)
12
- dinov2_vits14_reg_config = {
13
- "img_size": 518,
14
- "in_channels": 3,
15
- "dim": 384,
16
- "patch_size": 14,
17
- "num_heads": [6],
18
- "depths": [12],
19
- "num_classes": 0,
20
- "use_mask_token": True,
21
- "reg_tokens": 4,
22
- "init_values": 1e-5,
23
- "eps": 1e-6,
24
- "dynamic_img_size": True,
25
- }
26
-
27
- dinov2_vits14_reg = em.VisionTransformer(
28
- **dinov2_vits14_reg_config,
29
- key=key,
30
- )
31
-
32
- torch_hub_cfg = ["facebookresearch/dinov2", "dinov2_vits14_reg"]
33
-
34
- replace_cfg = {
35
- "reg_tokens": "register_tokens",
36
- "blocks.0.blocks": "blocks",
37
- ".prenorm.": ".norm1.",
38
- ".norm.": ".norm2.",
39
- }
40
- expand_cfg = {"patch_embed.proj.bias": ["after", 2]}
41
- squeeze_cfg = {
42
- "pos_embed": 0,
43
- "cls_token": 0,
44
- "register_tokens": 0,
45
- }
46
- whitelist = []
47
-
48
- dinov2_vits14_reg = convert_torch_to_equinox(
49
- dinov2_vits14_reg,
50
- torch_hub_cfg,
51
- replace_cfg,
52
- expand_cfg,
53
- squeeze_cfg,
54
- whitelist,
55
- strict=True,
56
- )
57
-
58
- save_model(
59
- Path("~/.cache/equimo/dinov2_vits14_reg").expanduser(),
60
- dinov2_vits14_reg,
61
- dinov2_vits14_reg_config,
62
- torch_hub_cfg,
63
- compression=True,
64
- )
65
-
66
- _ = load_model(
67
- cls="vit",
68
- path=Path("~/.cache/equimo/dinov2_vits14_reg.tar.lz4").expanduser(),
69
- )
@@ -1,152 +0,0 @@
1
- import re
2
- from typing import Dict, Tuple
3
-
4
- import equinox as eqx
5
- import jax
6
- import jax.numpy as jnp
7
- from jax.tree_util import GetAttrKey, SequenceKey
8
- from loguru import logger
9
-
10
-
11
- def stringify_name(path: Tuple) -> str:
12
- stringified = []
13
- for p in path:
14
- if isinstance(p, GetAttrKey):
15
- stringified.append(p.name)
16
- if isinstance(p, SequenceKey):
17
- stringified.append(str(p.idx))
18
- return ".".join(stringified)
19
-
20
-
21
- def expand_torch_tensor(tensor, pos: str, n: int):
22
- padding = [None] * n
23
- match pos:
24
- case "before":
25
- return tensor[*padding, ...]
26
- case "after":
27
- return tensor[..., *padding]
28
- case _:
29
- raise ValueError(
30
- f"Invalid `pos`, expected one of [`before`, `after`], got: {pos}"
31
- )
32
-
33
-
34
- def convert_params_from_torch_hub(
35
- jax_model: eqx.Module,
36
- torch_hub_cfg: list[str],
37
- replace_cfg: Dict[str, str],
38
- expand_cfg: Dict[str, list],
39
- squeeze_cfg: Dict[str, int | None],
40
- whitelist: list[str],
41
- strict: bool = True,
42
- ):
43
- """
44
- Load weights from a torch hub model into an Equinox module.
45
-
46
- Args:
47
- jax_model (eqx.Module): A preexisting Jax model corresponding to the checkpoint to download.
48
- torch_hub_cfg (Tuple[str]): Arguments passed to `torch.hub.load()`.
49
- transpose_whitelist (Set[str]): Parameters to exclude from format conversion.
50
- strict (bool): Whether to crash on missing parameters one of the models.
51
- """
52
- try:
53
- import torch
54
- except:
55
- raise ImportError("`torch` not available")
56
-
57
- # Load the pytorch model from torch hub
58
- torch_model = torch.hub.load(*torch_hub_cfg)
59
- torch_params = dict(torch_model.named_parameters())
60
-
61
- # Extract the parameters from the defined Jax model
62
- jax_params = eqx.filter(jax_model, eqx.is_array)
63
- # _, jax_params, _ = nnx.split(jax_model, nnx.Param, ...)
64
- jax_params_flat, jax_param_pytree = jax.tree_util.tree_flatten_with_path(jax_params)
65
-
66
- torch_params_flat = []
67
- for path, param in jax_params_flat:
68
- # Match the parameters' path of pytorch
69
- param_path = stringify_name(path)
70
- param_path = re.sub(r"\.scale|.kernel", ".weight", param_path)
71
-
72
- for old, new in replace_cfg.items():
73
- param_path = param_path.replace(old, new)
74
-
75
- shape = param.shape
76
-
77
- if param_path not in torch_params:
78
- _msg = f"{param_path} ({shape}) not found in PyTorch model."
79
- if strict:
80
- logger.error(_msg)
81
- raise AttributeError(_msg)
82
-
83
- logger.warning(f"{_msg} Appending `None` to flat param list.")
84
- torch_params_flat.append(None)
85
- continue
86
-
87
- logger.info(f"Converting {param_path}...")
88
- torch_param = torch_params[param_path]
89
-
90
- if param_path in expand_cfg:
91
- torch_param = expand_torch_tensor(torch_param, *expand_cfg[param_path])
92
- if param_path in squeeze_cfg:
93
- torch_param = torch.squeeze(torch_param, dim=squeeze_cfg[param_path])
94
-
95
- if shape != torch_param.shape:
96
- _msg = f"`{param_path}`: expected shape ({shape}) does not match its pytorch implementation ({torch_param.shape})."
97
- logger.error(_msg)
98
- raise ValueError(_msg)
99
-
100
- torch_params_flat.append(jnp.asarray(torch_param.detach().numpy()))
101
- _ = torch_params.pop(param_path)
102
-
103
- loaded_params = jax.tree_util.tree_unflatten(jax_param_pytree, torch_params_flat)
104
-
105
- for path, param in torch_params.items():
106
- logger.warning(
107
- f"PyTorch parameters `{path}` ({param.shape}) were not converted."
108
- )
109
- if strict and path not in whitelist:
110
- _msg = f"The PyTorch model contains parameters ({path}) that do not have a Jax counterpart."
111
- logger.error(_msg)
112
- raise AttributeError(_msg)
113
-
114
- return loaded_params
115
-
116
-
117
- def convert_torch_to_equinox(
118
- jax_model: eqx.Module,
119
- torch_hub_cfg: list[str],
120
- replace_cfg: dict = {},
121
- expand_cfg: dict = {},
122
- squeeze_cfg: dict = {},
123
- whitelist: list[str] = [],
124
- strict: bool = True,
125
- ) -> eqx.Module:
126
- """
127
- Convert a PyTorch model from torch.hub to Equinox format.
128
-
129
- Args:
130
- jax_model: The Equinox model
131
- torch_hub_cfg: [repo, model_name] for torch.hub.load
132
- replace_cfg: Dict of parameter name replacements
133
- expand_cfg: Dict of dimensions to expand
134
- squeeze_cfg: Dict of dimensions to squeeze
135
- whitelist: List of parameters to keep from JAX model
136
- strict: Wether to raise an issue if not all weights are converted
137
-
138
- Returns:
139
- eqx.Module: Converted Equinox model in inference mode
140
- """
141
- dynamic, static = eqx.partition(jax_model, eqx.is_array)
142
- converted_params = convert_params_from_torch_hub(
143
- dynamic,
144
- torch_hub_cfg,
145
- replace_cfg,
146
- expand_cfg,
147
- squeeze_cfg,
148
- whitelist,
149
- strict,
150
- )
151
-
152
- return eqx.nn.inference_mode(eqx.combine(converted_params, static), value=True)
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