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.
- {equimo-0.2.2 → equimo-0.2.4}/Equimo.egg-info/PKG-INFO +28 -10
- {equimo-0.2.2 → equimo-0.2.4}/Equimo.egg-info/SOURCES.txt +1 -3
- {equimo-0.2.2 → equimo-0.2.4}/Equimo.egg-info/requires.txt +2 -0
- {equimo-0.2.2 → equimo-0.2.4}/PKG-INFO +28 -10
- {equimo-0.2.2 → equimo-0.2.4}/README.md +25 -9
- equimo-0.2.4/equimo/__init__.py +1 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/io.py +21 -4
- equimo-0.2.4/equimo/layers/activation.py +33 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/models/vit.py +4 -2
- {equimo-0.2.2 → equimo-0.2.4}/pyproject.toml +4 -1
- equimo-0.2.2/equimo/converters/__init__.py +0 -1
- equimo-0.2.2/equimo/converters/dinov2.py +0 -69
- equimo-0.2.2/equimo/converters/utils.py +0 -152
- equimo-0.2.2/equimo/layers/__init__.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/Equimo.egg-info/dependency_links.txt +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/Equimo.egg-info/top_level.txt +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/LICENSE.md +0 -0
- {equimo-0.2.2/equimo → equimo-0.2.4/equimo/layers}/__init__.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/attention.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/convolution.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/downsample.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/dropout.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/ffn.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/generic.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/mamba.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/norm.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/patch.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/posemb.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/sharing.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/layers/squeeze_excite.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/models/__init__.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/models/emamodel.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/models/fastervit.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/models/mlla.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/models/partialformer.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/models/shvit.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/models/vssd.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/ops/scan.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/equimo/utils.py +0 -0
- {equimo-0.2.2 → equimo-0.2.4}/setup.cfg +0 -0
- {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.
|
|
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
|
-
|
|
145
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: Equimo
|
|
3
|
-
Version: 0.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
|
-
|
|
145
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|