hyper-models 0.1.0__tar.gz → 0.3.0__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.
- {hyper_models-0.1.0 → hyper_models-0.3.0}/PKG-INFO +85 -10
- hyper_models-0.3.0/README.md +170 -0
- {hyper_models-0.1.0 → hyper_models-0.3.0}/pyproject.toml +12 -3
- hyper_models-0.3.0/src/hyper3_clip/__init__.py +3 -0
- hyper_models-0.3.0/src/hyper3_clip/models/__init__.py +3 -0
- hyper_models-0.3.0/src/hyper3_clip/models/encoders.py +181 -0
- hyper_models-0.3.0/src/hyper3_clip/models/experimental.py +691 -0
- hyper_models-0.3.0/src/hyper3_clip/models/himo.py +54 -0
- hyper_models-0.3.0/src/hyper3_clip/models/hyper3_clip.py +1032 -0
- hyper_models-0.3.0/src/hyper3_clip/models/lorentz.py +285 -0
- hyper_models-0.3.0/src/hyper3_clip/models/losses.py +1546 -0
- hyper_models-0.3.0/src/hyper3_clip/models/objectives.py +653 -0
- hyper_models-0.3.0/src/hyper3_clip/models/tren.py +282 -0
- hyper_models-0.3.0/src/hyper3_clip/training/__init__.py +1 -0
- hyper_models-0.3.0/src/hyper3_clip/training/distributed.py +153 -0
- {hyper_models-0.1.0 → hyper_models-0.3.0}/src/hyper_models/__init__.py +6 -4
- hyper_models-0.3.0/src/hyper_models/loader.py +48 -0
- hyper_models-0.3.0/src/hyper_models/loaders.py +84 -0
- hyper_models-0.3.0/src/hyper_models/registry.py +145 -0
- hyper_models-0.3.0/src/hyper_models/torch_models.py +328 -0
- hyper_models-0.1.0/README.md +0 -103
- hyper_models-0.1.0/src/hyper_models/loader.py +0 -45
- hyper_models-0.1.0/src/hyper_models/registry.py +0 -78
- {hyper_models-0.1.0 → hyper_models-0.3.0}/.gitignore +0 -0
- {hyper_models-0.1.0 → hyper_models-0.3.0}/LICENSE +0 -0
- {hyper_models-0.1.0 → hyper_models-0.3.0}/src/hyper_models/models.py +0 -0
- {hyper_models-0.1.0 → hyper_models-0.3.0}/src/hyper_models/preprocessing.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyper-models
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A model zoo for non-Euclidean embedding models (hyperbolic, spherical, product manifolds)
|
|
5
5
|
Project-URL: Homepage, https://github.com/Hyper3Labs/hyper-models
|
|
6
6
|
Project-URL: Repository, https://github.com/Hyper3Labs/hyper-models
|
|
7
7
|
Project-URL: Documentation, https://github.com/Hyper3Labs/hyper-models#readme
|
|
8
8
|
Project-URL: Issues, https://github.com/Hyper3Labs/hyper-models/issues
|
|
9
|
-
Author:
|
|
9
|
+
Author: hyper3labs
|
|
10
10
|
License: MIT
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Keywords: clip,embeddings,hyperbolic,model-zoo,non-euclidean,onnx
|
|
@@ -27,6 +27,14 @@ Requires-Dist: pillow>=10.0
|
|
|
27
27
|
Provides-Extra: dev
|
|
28
28
|
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
29
29
|
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
30
|
+
Provides-Extra: ml
|
|
31
|
+
Requires-Dist: protobuf>=4.25.0; extra == 'ml'
|
|
32
|
+
Requires-Dist: pyyaml>=6.0.0; extra == 'ml'
|
|
33
|
+
Requires-Dist: safetensors>=0.4.0; extra == 'ml'
|
|
34
|
+
Requires-Dist: sentencepiece>=0.2.0; extra == 'ml'
|
|
35
|
+
Requires-Dist: timm>=1.0.0; extra == 'ml'
|
|
36
|
+
Requires-Dist: torch>=2.9.1; extra == 'ml'
|
|
37
|
+
Requires-Dist: transformers<6.0,>=5.0.0; extra == 'ml'
|
|
30
38
|
Description-Content-Type: text/markdown
|
|
31
39
|
|
|
32
40
|
# hyper-models
|
|
@@ -51,13 +59,22 @@ Description-Content-Type: text/markdown
|
|
|
51
59
|
## Why?
|
|
52
60
|
|
|
53
61
|
- **Standardized access** to non-Euclidean embedding models
|
|
54
|
-
- **
|
|
62
|
+
- **One catalog surface**: model names map to internal loaders such as ONNX or optional torch-backed runtimes
|
|
55
63
|
- **Simple API** — `load()` and `encode_images()`
|
|
56
64
|
|
|
57
65
|
## Installation
|
|
58
66
|
|
|
59
67
|
```bash
|
|
60
|
-
pip install hyper-models
|
|
68
|
+
uv pip install hyper-models
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This base install is the simple path: it stays **torch-free** and is enough for
|
|
72
|
+
ONNX-backed catalog entries such as HyCoCLIP and MERU.
|
|
73
|
+
|
|
74
|
+
For torch-backed checkpoints (for example UNCHA and Hyper3-CLIP):
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
uv pip install "hyper-models[ml]"
|
|
61
78
|
```
|
|
62
79
|
|
|
63
80
|
## Usage
|
|
@@ -68,7 +85,11 @@ from PIL import Image
|
|
|
68
85
|
|
|
69
86
|
# List available models
|
|
70
87
|
hyper_models.list_models()
|
|
71
|
-
# ['hycoclip-vit-s', 'hycoclip-vit-b', 'meru-vit-s', 'meru-vit-b']
|
|
88
|
+
# ['hycoclip-vit-s', 'hycoclip-vit-b', 'meru-vit-s', 'meru-vit-b', 'uncha-vit-s', 'uncha-vit-b', 'hyper3-clip-v0.5']
|
|
89
|
+
|
|
90
|
+
# Inspect supported internal loader kinds
|
|
91
|
+
hyper_models.list_loaders()
|
|
92
|
+
# ['hyper3-clip-torch', 'onnx', 'uncha-image-torch']
|
|
72
93
|
|
|
73
94
|
# Load model (auto-downloads from Hugging Face Hub)
|
|
74
95
|
model = hyper_models.load("hycoclip-vit-s")
|
|
@@ -82,6 +103,7 @@ embeddings = model.encode_images(images) # (1, 513) ndarray
|
|
|
82
103
|
# Get model info
|
|
83
104
|
info = hyper_models.get_model_info("hycoclip-vit-s")
|
|
84
105
|
info.hub_id # 'mnm-matin/hyperbolic-clip'
|
|
106
|
+
info.loader # 'onnx'
|
|
85
107
|
info.license # 'CC-BY-NC'
|
|
86
108
|
|
|
87
109
|
# Low-level: preprocess images yourself
|
|
@@ -89,6 +111,54 @@ batch = hyper_models.preprocess_images(images) # (B, 3, 224, 224)
|
|
|
89
111
|
embeddings = model.encode(batch)
|
|
90
112
|
```
|
|
91
113
|
|
|
114
|
+
### Architecture
|
|
115
|
+
|
|
116
|
+
`hyper-models` is intended to be a timm-like catalog for non-Euclidean models.
|
|
117
|
+
|
|
118
|
+
- The public abstraction is the catalog entry name, for example `hycoclip-vit-s`.
|
|
119
|
+
- Each entry declares metadata such as geometry, dimensionality, artifact path,
|
|
120
|
+
and an internal loader kind.
|
|
121
|
+
- Internal loaders may differ by model family:
|
|
122
|
+
- `onnx` for exported, torch-free runtimes
|
|
123
|
+
- `uncha-image-torch` for raw checkpoints that need a PyTorch image runtime
|
|
124
|
+
- `hyper3-clip-torch` for Hyper3-CLIP safetensors checkpoints
|
|
125
|
+
|
|
126
|
+
This keeps callers on one stable API:
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
model = hyper_models.load("hycoclip-vit-s")
|
|
130
|
+
model = hyper_models.load("uncha-vit-b")
|
|
131
|
+
model = hyper_models.load("hyper3-clip-v0.5")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Callers do not need to know which internal loader is used, except for optional
|
|
135
|
+
dependency installation when choosing entries that need `hyper-models[ml]`.
|
|
136
|
+
|
|
137
|
+
### HyperView integration
|
|
138
|
+
|
|
139
|
+
HyperView auto-detects `hyper-models` names and routes them to the `hyper-models` provider.
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
import hyperview as hv
|
|
143
|
+
|
|
144
|
+
dataset = hv.Dataset.from_huggingface(
|
|
145
|
+
name="demo",
|
|
146
|
+
hf_dataset="uoft-cs/cifar10",
|
|
147
|
+
split="train",
|
|
148
|
+
image_key="img",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Uses provider='hyper-models' automatically.
|
|
152
|
+
space_key = dataset.compute_embeddings(model="uncha-vit-b")
|
|
153
|
+
layout_key = dataset.compute_visualization(space_key=space_key, layout="poincare")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
HyperView's simple path remains torch-free. If you use the default ONNX-backed
|
|
157
|
+
`hyper-models` entries or the default `embed-anything` provider, HyperView does
|
|
158
|
+
not need PyTorch. PyTorch is only needed when you explicitly select a
|
|
159
|
+
torch-backed catalog entry such as `uncha-vit-s`, `uncha-vit-b`, or
|
|
160
|
+
`hyper3-clip-v0.5`.
|
|
161
|
+
|
|
92
162
|
## Models
|
|
93
163
|
|
|
94
164
|
### Hyperbolic
|
|
@@ -99,16 +169,21 @@ embeddings = model.encode(batch)
|
|
|
99
169
|
| `hycoclip-vit-b` | [](https://huggingface.co/mnm-matin/hyperbolic-clip/tree/main/hycoclip-vit-b) | [ICLR 2025](https://arxiv.org/abs/2410.06912) | [PalAvik/hycoclip](https://github.com/PalAvik/hycoclip) |
|
|
100
170
|
| `meru-vit-s` | [](https://huggingface.co/mnm-matin/hyperbolic-clip/tree/main/meru-vit-s) | [ICML 2023](https://arxiv.org/abs/2304.09172) | [facebookresearch/meru](https://github.com/facebookresearch/meru) |
|
|
101
171
|
| `meru-vit-b` | [](https://huggingface.co/mnm-matin/hyperbolic-clip/tree/main/meru-vit-b) | [ICML 2023](https://arxiv.org/abs/2304.09172) | [facebookresearch/meru](https://github.com/facebookresearch/meru) |
|
|
172
|
+
| `uncha-vit-s` | [](https://huggingface.co/hayeonkim/uncha/blob/main/uncha_vit_s.pth) | [CVPR 2026](https://arxiv.org/abs/2603.22042) | [jeeit17/UNCHA](https://github.com/jeeit17/UNCHA) |
|
|
173
|
+
| `uncha-vit-b` | [](https://huggingface.co/hayeonkim/uncha/blob/main/uncha_vit_b.pth) | [CVPR 2026](https://arxiv.org/abs/2603.22042) | [jeeit17/UNCHA](https://github.com/jeeit17/UNCHA) |
|
|
174
|
+
| `hyper3-clip-v0.5` | [](https://huggingface.co/hyper3labs/hyper3-clip-v0.5) | — | [Hyper3Labs/hyper3-clip](https://github.com/Hyper3Labs/hyper3-clip) |
|
|
102
175
|
| `hyp-vit` | — | [CVPR 2022](https://arxiv.org/abs/2203.10833) | [htdt/hyp_metric](https://github.com/htdt/hyp_metric) |
|
|
103
176
|
| `hie` | — | [CVPR 2020](https://arxiv.org/abs/1904.02239) | [leymir/hyperbolic-image-embeddings](https://github.com/leymir/hyperbolic-image-embeddings) |
|
|
104
177
|
| `hcnn` | — | [ICLR 2024](https://openreview.net/forum?id=ekz1hN5QNh) | [kschwethelm/HyperbolicCV](https://github.com/kschwethelm/HyperbolicCV) |
|
|
105
178
|
|
|
106
|
-
###
|
|
179
|
+
### Hyperspherical
|
|
180
|
+
|
|
181
|
+
| Model | Available | Paper | Code |
|
|
182
|
+
|------------------|:---------:|-------|------|
|
|
183
|
+
| `megadescriptor` (via timm) | [](https://huggingface.co/BVRA/MegaDescriptor-L-384) | [WACV 2024](https://openaccess.thecvf.com/content/WACV2024/papers/Cermak_WildlifeDatasets_An_Open-Source_Toolkit_for_Animal_Re-Identification_WACV_2024_paper.pdf) | [WildlifeDatasets/wildlife-datasets](https://github.com/WildlifeDatasets/wildlife-datasets) |
|
|
184
|
+
| `sphereface` | — | [CVPR 2017](https://arxiv.org/abs/1704.08063) | [wy1iu/sphereface](https://github.com/wy1iu/sphereface) |
|
|
185
|
+
| `arcface` | — | [CVPR 2019](https://arxiv.org/abs/1801.07698) | [deepinsight/insightface](https://github.com/deepinsight/insightface) |
|
|
107
186
|
|
|
108
|
-
| Model | Available | Paper | Code |
|
|
109
|
-
|-------|:---------:|-------|------|
|
|
110
|
-
| `sphereface` | — | [CVPR 2017](https://arxiv.org/abs/1704.08063) | [wy1iu/sphereface](https://github.com/wy1iu/sphereface) |
|
|
111
|
-
| `arcface` | — | [CVPR 2019](https://arxiv.org/abs/1801.07698) | [deepinsight/insightface](https://github.com/deepinsight/insightface) |
|
|
112
187
|
|
|
113
188
|
### Product Manifolds
|
|
114
189
|
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# hyper-models
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>A model zoo for non-Euclidean embedding models</strong>
|
|
5
|
+
<br>
|
|
6
|
+
<em>Hyperbolic · Spherical · Product Manifolds</em>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://huggingface.co/mnm-matin/hyperbolic-clip">
|
|
11
|
+
<img src="https://img.shields.io/badge/🤗_Models-hyperbolic--clip-orange" alt="Hugging Face">
|
|
12
|
+
</a>
|
|
13
|
+
<a href="LICENSE">
|
|
14
|
+
<img src="https://img.shields.io/badge/License-MIT-blue" alt="License: MIT">
|
|
15
|
+
</a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Why?
|
|
21
|
+
|
|
22
|
+
- **Standardized access** to non-Euclidean embedding models
|
|
23
|
+
- **One catalog surface**: model names map to internal loaders such as ONNX or optional torch-backed runtimes
|
|
24
|
+
- **Simple API** — `load()` and `encode_images()`
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
uv pip install hyper-models
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This base install is the simple path: it stays **torch-free** and is enough for
|
|
33
|
+
ONNX-backed catalog entries such as HyCoCLIP and MERU.
|
|
34
|
+
|
|
35
|
+
For torch-backed checkpoints (for example UNCHA and Hyper3-CLIP):
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
uv pip install "hyper-models[ml]"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import hyper_models
|
|
45
|
+
from PIL import Image
|
|
46
|
+
|
|
47
|
+
# List available models
|
|
48
|
+
hyper_models.list_models()
|
|
49
|
+
# ['hycoclip-vit-s', 'hycoclip-vit-b', 'meru-vit-s', 'meru-vit-b', 'uncha-vit-s', 'uncha-vit-b', 'hyper3-clip-v0.5']
|
|
50
|
+
|
|
51
|
+
# Inspect supported internal loader kinds
|
|
52
|
+
hyper_models.list_loaders()
|
|
53
|
+
# ['hyper3-clip-torch', 'onnx', 'uncha-image-torch']
|
|
54
|
+
|
|
55
|
+
# Load model (auto-downloads from Hugging Face Hub)
|
|
56
|
+
model = hyper_models.load("hycoclip-vit-s")
|
|
57
|
+
model.geometry # 'hyperboloid'
|
|
58
|
+
model.dim # 513
|
|
59
|
+
|
|
60
|
+
# Encode PIL images
|
|
61
|
+
images = [Image.open("image.jpg")]
|
|
62
|
+
embeddings = model.encode_images(images) # (1, 513) ndarray
|
|
63
|
+
|
|
64
|
+
# Get model info
|
|
65
|
+
info = hyper_models.get_model_info("hycoclip-vit-s")
|
|
66
|
+
info.hub_id # 'mnm-matin/hyperbolic-clip'
|
|
67
|
+
info.loader # 'onnx'
|
|
68
|
+
info.license # 'CC-BY-NC'
|
|
69
|
+
|
|
70
|
+
# Low-level: preprocess images yourself
|
|
71
|
+
batch = hyper_models.preprocess_images(images) # (B, 3, 224, 224)
|
|
72
|
+
embeddings = model.encode(batch)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Architecture
|
|
76
|
+
|
|
77
|
+
`hyper-models` is intended to be a timm-like catalog for non-Euclidean models.
|
|
78
|
+
|
|
79
|
+
- The public abstraction is the catalog entry name, for example `hycoclip-vit-s`.
|
|
80
|
+
- Each entry declares metadata such as geometry, dimensionality, artifact path,
|
|
81
|
+
and an internal loader kind.
|
|
82
|
+
- Internal loaders may differ by model family:
|
|
83
|
+
- `onnx` for exported, torch-free runtimes
|
|
84
|
+
- `uncha-image-torch` for raw checkpoints that need a PyTorch image runtime
|
|
85
|
+
- `hyper3-clip-torch` for Hyper3-CLIP safetensors checkpoints
|
|
86
|
+
|
|
87
|
+
This keeps callers on one stable API:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
model = hyper_models.load("hycoclip-vit-s")
|
|
91
|
+
model = hyper_models.load("uncha-vit-b")
|
|
92
|
+
model = hyper_models.load("hyper3-clip-v0.5")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Callers do not need to know which internal loader is used, except for optional
|
|
96
|
+
dependency installation when choosing entries that need `hyper-models[ml]`.
|
|
97
|
+
|
|
98
|
+
### HyperView integration
|
|
99
|
+
|
|
100
|
+
HyperView auto-detects `hyper-models` names and routes them to the `hyper-models` provider.
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
import hyperview as hv
|
|
104
|
+
|
|
105
|
+
dataset = hv.Dataset.from_huggingface(
|
|
106
|
+
name="demo",
|
|
107
|
+
hf_dataset="uoft-cs/cifar10",
|
|
108
|
+
split="train",
|
|
109
|
+
image_key="img",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Uses provider='hyper-models' automatically.
|
|
113
|
+
space_key = dataset.compute_embeddings(model="uncha-vit-b")
|
|
114
|
+
layout_key = dataset.compute_visualization(space_key=space_key, layout="poincare")
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
HyperView's simple path remains torch-free. If you use the default ONNX-backed
|
|
118
|
+
`hyper-models` entries or the default `embed-anything` provider, HyperView does
|
|
119
|
+
not need PyTorch. PyTorch is only needed when you explicitly select a
|
|
120
|
+
torch-backed catalog entry such as `uncha-vit-s`, `uncha-vit-b`, or
|
|
121
|
+
`hyper3-clip-v0.5`.
|
|
122
|
+
|
|
123
|
+
## Models
|
|
124
|
+
|
|
125
|
+
### Hyperbolic
|
|
126
|
+
|
|
127
|
+
| Model | Available | Paper | Code |
|
|
128
|
+
|-------|:---------:|-------|------|
|
|
129
|
+
| `hycoclip-vit-s` | [](https://huggingface.co/mnm-matin/hyperbolic-clip/tree/main/hycoclip-vit-s) | [ICLR 2025](https://arxiv.org/abs/2410.06912) | [PalAvik/hycoclip](https://github.com/PalAvik/hycoclip) |
|
|
130
|
+
| `hycoclip-vit-b` | [](https://huggingface.co/mnm-matin/hyperbolic-clip/tree/main/hycoclip-vit-b) | [ICLR 2025](https://arxiv.org/abs/2410.06912) | [PalAvik/hycoclip](https://github.com/PalAvik/hycoclip) |
|
|
131
|
+
| `meru-vit-s` | [](https://huggingface.co/mnm-matin/hyperbolic-clip/tree/main/meru-vit-s) | [ICML 2023](https://arxiv.org/abs/2304.09172) | [facebookresearch/meru](https://github.com/facebookresearch/meru) |
|
|
132
|
+
| `meru-vit-b` | [](https://huggingface.co/mnm-matin/hyperbolic-clip/tree/main/meru-vit-b) | [ICML 2023](https://arxiv.org/abs/2304.09172) | [facebookresearch/meru](https://github.com/facebookresearch/meru) |
|
|
133
|
+
| `uncha-vit-s` | [](https://huggingface.co/hayeonkim/uncha/blob/main/uncha_vit_s.pth) | [CVPR 2026](https://arxiv.org/abs/2603.22042) | [jeeit17/UNCHA](https://github.com/jeeit17/UNCHA) |
|
|
134
|
+
| `uncha-vit-b` | [](https://huggingface.co/hayeonkim/uncha/blob/main/uncha_vit_b.pth) | [CVPR 2026](https://arxiv.org/abs/2603.22042) | [jeeit17/UNCHA](https://github.com/jeeit17/UNCHA) |
|
|
135
|
+
| `hyper3-clip-v0.5` | [](https://huggingface.co/hyper3labs/hyper3-clip-v0.5) | — | [Hyper3Labs/hyper3-clip](https://github.com/Hyper3Labs/hyper3-clip) |
|
|
136
|
+
| `hyp-vit` | — | [CVPR 2022](https://arxiv.org/abs/2203.10833) | [htdt/hyp_metric](https://github.com/htdt/hyp_metric) |
|
|
137
|
+
| `hie` | — | [CVPR 2020](https://arxiv.org/abs/1904.02239) | [leymir/hyperbolic-image-embeddings](https://github.com/leymir/hyperbolic-image-embeddings) |
|
|
138
|
+
| `hcnn` | — | [ICLR 2024](https://openreview.net/forum?id=ekz1hN5QNh) | [kschwethelm/HyperbolicCV](https://github.com/kschwethelm/HyperbolicCV) |
|
|
139
|
+
|
|
140
|
+
### Hyperspherical
|
|
141
|
+
|
|
142
|
+
| Model | Available | Paper | Code |
|
|
143
|
+
|------------------|:---------:|-------|------|
|
|
144
|
+
| `megadescriptor` (via timm) | [](https://huggingface.co/BVRA/MegaDescriptor-L-384) | [WACV 2024](https://openaccess.thecvf.com/content/WACV2024/papers/Cermak_WildlifeDatasets_An_Open-Source_Toolkit_for_Animal_Re-Identification_WACV_2024_paper.pdf) | [WildlifeDatasets/wildlife-datasets](https://github.com/WildlifeDatasets/wildlife-datasets) |
|
|
145
|
+
| `sphereface` | — | [CVPR 2017](https://arxiv.org/abs/1704.08063) | [wy1iu/sphereface](https://github.com/wy1iu/sphereface) |
|
|
146
|
+
| `arcface` | — | [CVPR 2019](https://arxiv.org/abs/1801.07698) | [deepinsight/insightface](https://github.com/deepinsight/insightface) |
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
### Product Manifolds
|
|
150
|
+
|
|
151
|
+
| Model | Available | Paper | Code |
|
|
152
|
+
|-------|:---------:|-------|------|
|
|
153
|
+
| `hyperbolics` | — | [ICLR 2019](https://openreview.net/forum?id=HJxeWnCcF7) | [HazyResearch/hyperbolics](https://github.com/HazyResearch/hyperbolics) |
|
|
154
|
+
|
|
155
|
+
## Export Tooling
|
|
156
|
+
|
|
157
|
+
This repo also contains tooling to export PyTorch models to ONNX:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
cd export/hycoclip
|
|
161
|
+
uv run python export_onnx.py --checkpoint model.pth --onnx model.onnx
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
See [export/hycoclip/README.md](export/hycoclip/README.md) for details.
|
|
165
|
+
|
|
166
|
+
## References
|
|
167
|
+
|
|
168
|
+
- [HyCoCLIP](https://github.com/PalAvik/hycoclip)
|
|
169
|
+
- [MERU](https://github.com/facebookresearch/meru)
|
|
170
|
+
- [geoopt](https://github.com/geoopt/geoopt)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "hyper-models"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "A model zoo for non-Euclidean embedding models (hyperbolic, spherical, product manifolds)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
7
7
|
requires-python = ">=3.10"
|
|
8
|
-
authors = [{ name = "
|
|
8
|
+
authors = [{ name = "hyper3labs" }]
|
|
9
9
|
keywords = ["embeddings", "hyperbolic", "onnx", "clip", "model-zoo", "non-euclidean"]
|
|
10
10
|
classifiers = [
|
|
11
11
|
"Development Status :: 3 - Alpha",
|
|
@@ -30,6 +30,15 @@ dev = [
|
|
|
30
30
|
"pytest>=7.0",
|
|
31
31
|
"ruff>=0.1",
|
|
32
32
|
]
|
|
33
|
+
ml = [
|
|
34
|
+
"torch>=2.9.1",
|
|
35
|
+
"timm>=1.0.0",
|
|
36
|
+
"transformers>=5.0.0,<6.0",
|
|
37
|
+
"safetensors>=0.4.0",
|
|
38
|
+
"pyyaml>=6.0.0",
|
|
39
|
+
"sentencepiece>=0.2.0",
|
|
40
|
+
"protobuf>=4.25.0",
|
|
41
|
+
]
|
|
33
42
|
|
|
34
43
|
[project.urls]
|
|
35
44
|
Homepage = "https://github.com/Hyper3Labs/hyper-models"
|
|
@@ -42,7 +51,7 @@ requires = ["hatchling"]
|
|
|
42
51
|
build-backend = "hatchling.build"
|
|
43
52
|
|
|
44
53
|
[tool.hatch.build.targets.wheel]
|
|
45
|
-
packages = ["src/hyper_models"]
|
|
54
|
+
packages = ["src/hyper_models", "src/hyper3_clip"]
|
|
46
55
|
|
|
47
56
|
[tool.hatch.build.targets.sdist]
|
|
48
57
|
include = ["/src", "/README.md", "/LICENSE"]
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import timm
|
|
4
|
+
import torch
|
|
5
|
+
from torch import nn
|
|
6
|
+
from transformers import (
|
|
7
|
+
AutoConfig,
|
|
8
|
+
AutoModel,
|
|
9
|
+
AutoTokenizer,
|
|
10
|
+
CLIPTextConfig,
|
|
11
|
+
CLIPTextModel,
|
|
12
|
+
CLIPTextModelWithProjection,
|
|
13
|
+
CLIPVisionConfig,
|
|
14
|
+
CLIPVisionModel,
|
|
15
|
+
CLIPVisionModelWithProjection,
|
|
16
|
+
SiglipTextConfig,
|
|
17
|
+
SiglipTextModel,
|
|
18
|
+
SiglipVisionConfig,
|
|
19
|
+
SiglipVisionModel,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class VisionEncoder(nn.Module):
|
|
24
|
+
def __init__(self, backbone_name: str, pretrained: bool = True) -> None:
|
|
25
|
+
super().__init__()
|
|
26
|
+
self.kind = "timm"
|
|
27
|
+
if backbone_name.startswith("hf_clip_projected:"):
|
|
28
|
+
self.kind = "hf_clip_projected"
|
|
29
|
+
model_name = backbone_name.removeprefix("hf_clip_projected:")
|
|
30
|
+
self.backbone = (
|
|
31
|
+
CLIPVisionModelWithProjection.from_pretrained(model_name)
|
|
32
|
+
if pretrained
|
|
33
|
+
else CLIPVisionModelWithProjection(CLIPVisionConfig.from_pretrained(model_name))
|
|
34
|
+
)
|
|
35
|
+
self.output_dim = self.backbone.config.projection_dim
|
|
36
|
+
elif backbone_name.startswith("hf_clip:"):
|
|
37
|
+
self.kind = "hf_vision"
|
|
38
|
+
model_name = backbone_name.removeprefix("hf_clip:")
|
|
39
|
+
self.backbone = (
|
|
40
|
+
CLIPVisionModel.from_pretrained(model_name)
|
|
41
|
+
if pretrained
|
|
42
|
+
else CLIPVisionModel(CLIPVisionConfig.from_pretrained(model_name))
|
|
43
|
+
)
|
|
44
|
+
self.output_dim = self.backbone.config.hidden_size
|
|
45
|
+
elif backbone_name.startswith("hf_siglip:"):
|
|
46
|
+
self.kind = "hf_vision"
|
|
47
|
+
model_name = backbone_name.removeprefix("hf_siglip:")
|
|
48
|
+
self.backbone = (
|
|
49
|
+
SiglipVisionModel.from_pretrained(model_name)
|
|
50
|
+
if pretrained
|
|
51
|
+
else SiglipVisionModel(SiglipVisionConfig.from_pretrained(model_name))
|
|
52
|
+
)
|
|
53
|
+
self.output_dim = self.backbone.config.hidden_size
|
|
54
|
+
else:
|
|
55
|
+
self.backbone = timm.create_model(
|
|
56
|
+
backbone_name,
|
|
57
|
+
pretrained=pretrained,
|
|
58
|
+
num_classes=0,
|
|
59
|
+
global_pool="avg",
|
|
60
|
+
)
|
|
61
|
+
self.output_dim = self.backbone.num_features
|
|
62
|
+
|
|
63
|
+
def forward(self, image: torch.Tensor) -> torch.Tensor:
|
|
64
|
+
if self.kind == "hf_clip_projected":
|
|
65
|
+
return self.backbone(pixel_values=image).image_embeds
|
|
66
|
+
if self.kind == "hf_vision":
|
|
67
|
+
out = self.backbone(pixel_values=image)
|
|
68
|
+
if hasattr(out, "pooler_output") and out.pooler_output is not None:
|
|
69
|
+
return out.pooler_output
|
|
70
|
+
return out.last_hidden_state[:, 0]
|
|
71
|
+
return self.backbone(image)
|
|
72
|
+
|
|
73
|
+
def forward_with_tokens(self, image: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
|
|
74
|
+
if self.kind == "hf_clip_projected":
|
|
75
|
+
out = self.backbone(pixel_values=image)
|
|
76
|
+
tokens = getattr(out, "last_hidden_state", None)
|
|
77
|
+
if tokens is None and hasattr(out, "vision_model_output"):
|
|
78
|
+
tokens = out.vision_model_output.last_hidden_state
|
|
79
|
+
if tokens is None:
|
|
80
|
+
raise RuntimeError("Projected CLIP vision output did not include patch tokens")
|
|
81
|
+
return out.image_embeds, tokens
|
|
82
|
+
if self.kind == "hf_vision":
|
|
83
|
+
out = self.backbone(pixel_values=image)
|
|
84
|
+
if hasattr(out, "pooler_output") and out.pooler_output is not None:
|
|
85
|
+
pooled = out.pooler_output
|
|
86
|
+
else:
|
|
87
|
+
pooled = out.last_hidden_state[:, 0]
|
|
88
|
+
return pooled, out.last_hidden_state
|
|
89
|
+
|
|
90
|
+
if not hasattr(self.backbone, "forward_features"):
|
|
91
|
+
pooled = self.backbone(image)
|
|
92
|
+
return pooled, pooled[:, None, :]
|
|
93
|
+
features = self.backbone.forward_features(image)
|
|
94
|
+
if hasattr(self.backbone, "forward_head"):
|
|
95
|
+
pooled = self.backbone.forward_head(features, pre_logits=False)
|
|
96
|
+
else:
|
|
97
|
+
pooled = self.backbone(image)
|
|
98
|
+
return pooled, _tokens_from_features(features)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class TextEncoder(nn.Module):
|
|
102
|
+
def __init__(self, model_name: str, pretrained: bool = True, pooling: str = "auto") -> None:
|
|
103
|
+
super().__init__()
|
|
104
|
+
if pooling not in {"auto", "pooler", "cls", "mean"}:
|
|
105
|
+
raise ValueError(
|
|
106
|
+
f"Unsupported text pooling {pooling!r}; expected auto, pooler, cls, or mean"
|
|
107
|
+
)
|
|
108
|
+
self.kind = "hf_text"
|
|
109
|
+
self.pooling = pooling
|
|
110
|
+
tokenizer_name = model_name.removeprefix("hf_clip_projected:")
|
|
111
|
+
self.tokenizer = AutoTokenizer.from_pretrained(tokenizer_name)
|
|
112
|
+
model_name_lower = model_name.lower()
|
|
113
|
+
if model_name.startswith("hf_clip_projected:"):
|
|
114
|
+
self.kind = "hf_clip_projected"
|
|
115
|
+
projected_model_name = model_name.removeprefix("hf_clip_projected:")
|
|
116
|
+
if pretrained:
|
|
117
|
+
self.backbone = CLIPTextModelWithProjection.from_pretrained(projected_model_name)
|
|
118
|
+
else:
|
|
119
|
+
self.backbone = CLIPTextModelWithProjection(
|
|
120
|
+
CLIPTextConfig.from_pretrained(projected_model_name)
|
|
121
|
+
)
|
|
122
|
+
self.output_dim = self.backbone.config.projection_dim
|
|
123
|
+
elif "siglip" in model_name_lower:
|
|
124
|
+
if pretrained:
|
|
125
|
+
self.backbone = SiglipTextModel.from_pretrained(model_name)
|
|
126
|
+
else:
|
|
127
|
+
self.backbone = SiglipTextModel(SiglipTextConfig.from_pretrained(model_name))
|
|
128
|
+
self.output_dim = self.backbone.config.hidden_size
|
|
129
|
+
elif "clip" in model_name_lower:
|
|
130
|
+
if pretrained:
|
|
131
|
+
self.backbone = CLIPTextModel.from_pretrained(model_name)
|
|
132
|
+
else:
|
|
133
|
+
self.backbone = CLIPTextModel(CLIPTextConfig.from_pretrained(model_name))
|
|
134
|
+
self.output_dim = self.backbone.config.hidden_size
|
|
135
|
+
else:
|
|
136
|
+
if pretrained:
|
|
137
|
+
self.backbone = AutoModel.from_pretrained(model_name)
|
|
138
|
+
else:
|
|
139
|
+
self.backbone = AutoModel.from_config(AutoConfig.from_pretrained(model_name))
|
|
140
|
+
hidden_size = getattr(self.backbone.config, "hidden_size", None)
|
|
141
|
+
if hidden_size is None:
|
|
142
|
+
raise ValueError(f"Unsupported text model config for {model_name}")
|
|
143
|
+
self.output_dim = hidden_size
|
|
144
|
+
|
|
145
|
+
def forward(self, input_ids: torch.Tensor, attention_mask: torch.Tensor) -> torch.Tensor:
|
|
146
|
+
out = self.backbone(input_ids=input_ids, attention_mask=attention_mask)
|
|
147
|
+
if self.kind == "hf_clip_projected":
|
|
148
|
+
return out.text_embeds
|
|
149
|
+
if self.pooling == "mean":
|
|
150
|
+
mask = attention_mask.to(dtype=out.last_hidden_state.dtype).unsqueeze(-1)
|
|
151
|
+
summed = (out.last_hidden_state * mask).sum(dim=1)
|
|
152
|
+
denom = mask.sum(dim=1).clamp_min(1.0)
|
|
153
|
+
return summed / denom
|
|
154
|
+
if (
|
|
155
|
+
self.pooling in {"auto", "pooler"}
|
|
156
|
+
and hasattr(out, "pooler_output")
|
|
157
|
+
and out.pooler_output is not None
|
|
158
|
+
):
|
|
159
|
+
return out.pooler_output
|
|
160
|
+
return out.last_hidden_state[:, 0]
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _tokens_from_features(features: torch.Tensor | dict | tuple | list) -> torch.Tensor:
|
|
164
|
+
if isinstance(features, dict):
|
|
165
|
+
for key in ("x", "last_hidden_state", "features"):
|
|
166
|
+
if key in features:
|
|
167
|
+
features = features[key]
|
|
168
|
+
break
|
|
169
|
+
else:
|
|
170
|
+
features = next(iter(features.values()))
|
|
171
|
+
if isinstance(features, tuple | list):
|
|
172
|
+
features = features[0]
|
|
173
|
+
if not torch.is_tensor(features):
|
|
174
|
+
raise TypeError(f"Expected tensor features, got {type(features)!r}")
|
|
175
|
+
if features.ndim == 4:
|
|
176
|
+
return features.flatten(2).transpose(1, 2)
|
|
177
|
+
if features.ndim == 3:
|
|
178
|
+
return features
|
|
179
|
+
if features.ndim == 2:
|
|
180
|
+
return features[:, None, :]
|
|
181
|
+
raise ValueError(f"Unsupported feature tensor shape {tuple(features.shape)}")
|