mnm-matin commited on
Commit
207c208
·
verified ·
1 Parent(s): 2a4217e

Upload Hyper3-CLIP beta ViT-B scratch checkpoint

Browse files
.hfignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ checkpoint_step_500000.pt
2
+ *.tmp
3
+ *.partial
0_Hyper3CLIP/config.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_config_file": "config.yaml",
3
+ "weights_file": "model.safetensors",
4
+ "image_size": 224,
5
+ "max_text_length": 77,
6
+ "normalize_output": true
7
+ }
LICENSE ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ OpenMDW License Agreement, version 1.0 (OpenMDW-1.0)
2
+
3
+ By exercising rights granted to you under this agreement, you accept and agree
4
+ to its terms.
5
+
6
+ As used in this agreement, "Model Materials" means the materials provided to
7
+ you under this agreement, consisting of: (1) one or more machine learning
8
+ models (including architecture and parameters); and (2) all related artifacts
9
+ (including associated data, documentation and software) that are provided to
10
+ you hereunder.
11
+
12
+ Subject to your compliance with this agreement, permission is hereby granted,
13
+ free of charge, to deal in the Model Materials without restriction, including
14
+ under all copyright, patent, database, and trade secret rights included or
15
+ embodied therein.
16
+
17
+ If you distribute any portion of the Model Materials, you shall retain in your
18
+ distribution (1) a copy of this agreement, and (2) all copyright notices and
19
+ other notices of origin included in the Model Materials that are applicable to
20
+ your distribution.
21
+
22
+ If you file, maintain, or voluntarily participate in a lawsuit against any
23
+ person or entity asserting that the Model Materials directly or indirectly
24
+ infringe any patent, then all rights and grants made to you hereunder are
25
+ terminated, unless that lawsuit was in response to a corresponding lawsuit
26
+ first brought against you.
27
+
28
+ This agreement does not impose any restrictions or obligations with respect to
29
+ any use, modification, or sharing of any outputs generated by using the Model
30
+ Materials.
31
+
32
+ THE MODEL MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
33
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34
+ FITNESS FOR A PARTICULAR PURPOSE, TITLE, NONINFRINGEMENT, ACCURACY, OR THE
35
+ ABSENCE OF LATENT OR OTHER DEFECTS OR ERRORS, WHETHER OR NOT DISCOVERABLE, ALL
36
+ TO THE GREATEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW.
37
+
38
+ YOU ARE SOLELY RESPONSIBLE FOR (1) CLEARING RIGHTS OF OTHER PERSONS THAT MAY
39
+ APPLY TO THE MODEL MATERIALS OR ANY USE THEREOF, INCLUDING WITHOUT LIMITATION
40
+ ANY PERSON'S COPYRIGHTS OR OTHER RIGHTS INCLUDED OR EMBODIED IN THE MODEL
41
+ MATERIALS; (2) OBTAINING ANY NECESSARY CONSENTS, PERMISSIONS OR OTHER RIGHTS
42
+ REQUIRED FOR ANY USE OF THE MODEL MATERIALS; OR (3) PERFORMING ANY DUE
43
+ DILIGENCE OR UNDERTAKING ANY OTHER INVESTIGATIONS INTO THE MODEL MATERIALS OR
44
+ ANYTHING INCORPORATED OR EMBODIED THEREIN.
45
+
46
+ IN NO EVENT SHALL THE PROVIDERS OF THE MODEL MATERIALS BE LIABLE FOR ANY CLAIM,
47
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
48
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MODEL MATERIALS, THE
49
+ USE THEREOF OR OTHER DEALINGS THEREIN.
NOTICE ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Hyper3-CLIP beta
2
+ Copyright hyper³labs.
3
+
4
+ Hyper3-CLIP beta was created by hyper³labs.
5
+
6
+ This notice identifies the original source of the model materials. Redistributions
7
+ of this model or derivative model materials should preserve this notice, the
8
+ accompanying LICENSE file, and the original model card when practical.
9
+
10
+ hyper³labs and Hyper3-CLIP are names associated with hyper³labs. No trademark
11
+ license is granted. Modified or derived checkpoints must not use the hyper³labs
12
+ or Hyper3-CLIP names in a way that suggests they are official hyper³labs releases
13
+ or endorsed by hyper³labs.
14
+
15
+ Please cite and link to the original hyper³labs model repository when publishing
16
+ benchmarks, papers, derivative checkpoints, or public demos based on this model.
README.md ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: openmdw-1.0
3
+ library_name: sentence-transformers
4
+ pipeline_tag: feature-extraction
5
+ tags:
6
+ - vision-language
7
+ - multimodal
8
+ - image-text-retrieval
9
+ - hyperbolic-embeddings
10
+ - clip
11
+ - sentence-transformers
12
+ - transformers
13
+ - haystack
14
+ - safetensors
15
+ - research
16
+ - scratch-training
17
+ ---
18
+
19
+ # Hyper3-CLIP beta
20
+
21
+ Hyper3-CLIP beta is an open-weight hyperbolic vision-language checkpoint from
22
+ hyper³labs. It places image and text representations in a Lorentz space and was
23
+ trained with compositional entailment constraints for hierarchy-sensitive
24
+ image-text retrieval.
25
+
26
+ This beta release is intended as an open baseline and research artifact.
27
+
28
+ ## Model
29
+
30
+ - Architecture: ViT-B scale vision-language model
31
+ - Vision backbone: `vit_base_patch16_224`
32
+ - Text backbone architecture/tokenizer: `openai/clip-vit-base-patch32`
33
+ - Embedding dimension: 512
34
+ - Training steps: 500,000
35
+ - Global batch size: 768
36
+ - Weights artifact: `model.safetensors`
37
+
38
+ The original full training checkpoint included optimizer, scheduler, AMP scaler,
39
+ RNG state, config, and step metadata. This repository publishes the weights-only
40
+ `model.safetensors` artifact for inference and downstream research from the
41
+ ViT-B scratch training run.
42
+
43
+ ## Quick Start: Sentence Transformers
44
+
45
+ The default way to use this checkpoint is through Sentence Transformers. The
46
+ adapter in this repository returns 512-dimensional L2-normalized tangent-space
47
+ embeddings for standard cosine/dot-product vector stores.
48
+
49
+ Install the runtime dependencies:
50
+
51
+ ```bash
52
+ pip install "sentence-transformers>=5.5.1" timm safetensors pyyaml Pillow
53
+ ```
54
+
55
+ If you are using the gated Hugging Face repository from a fresh machine, accept
56
+ access on the model page and set `HF_TOKEN`.
57
+
58
+ ```python
59
+ from PIL import Image
60
+ from sentence_transformers import SentenceTransformer
61
+
62
+ model = SentenceTransformer("hyper3labs/hyper3-clip-beta", trust_remote_code=True)
63
+
64
+ image_embedding = model.encode([Image.open("/path/to/image.jpg")], normalize_embeddings=True)
65
+ text_embedding = model.encode(["machined metal part"], normalize_embeddings=True)
66
+ ```
67
+
68
+ ## Transformers
69
+
70
+ ```python
71
+ from PIL import Image
72
+ import torch
73
+ from transformers import AutoModel, AutoTokenizer
74
+
75
+ model = AutoModel.from_pretrained("hyper3labs/hyper3-clip-beta", trust_remote_code=True).eval()
76
+ tokenizer = AutoTokenizer.from_pretrained("openai/clip-vit-base-patch32")
77
+
78
+ image = model.preprocess_image(Image.open("/path/to/image.jpg")).unsqueeze(0)
79
+ text = tokenizer(
80
+ ["machined metal part"],
81
+ padding=True,
82
+ truncation=True,
83
+ max_length=model.config.max_text_length,
84
+ return_tensors="pt",
85
+ )
86
+
87
+ with torch.no_grad():
88
+ outputs = model(
89
+ pixel_values=image,
90
+ input_ids=text["input_ids"],
91
+ attention_mask=text["attention_mask"],
92
+ )
93
+
94
+ image_embedding = outputs.image_embeds
95
+ text_embedding = outputs.text_embeds
96
+ ```
97
+
98
+ <details>
99
+ <summary>Haystack image retrieval pipeline</summary>
100
+
101
+ For indexing images in a Haystack retrieval pipeline, use
102
+ `SentenceTransformersDocumentImageEmbedder` with image paths in
103
+ `Document.meta["file_path"]`, paired with `SentenceTransformersTextEmbedder` for
104
+ text queries.
105
+
106
+ ```bash
107
+ pip install "haystack-ai>=2.30.1" "sentence-transformers>=5.5.1" timm safetensors pyyaml Pillow
108
+ ```
109
+
110
+ ```python
111
+ from haystack import Document
112
+ from haystack.components.embedders import SentenceTransformersTextEmbedder
113
+ from haystack.components.embedders.image import SentenceTransformersDocumentImageEmbedder
114
+
115
+ model_id = "hyper3labs/hyper3-clip-beta"
116
+
117
+ documents = [
118
+ Document(
119
+ content="front view of a machined metal part",
120
+ meta={"file_path": "/path/to/image.jpg"},
121
+ )
122
+ ]
123
+
124
+ image_embedder = SentenceTransformersDocumentImageEmbedder(
125
+ model=model_id,
126
+ trust_remote_code=True,
127
+ batch_size=8,
128
+ normalize_embeddings=True,
129
+ )
130
+ documents = image_embedder.run(documents=documents)["documents"]
131
+
132
+ text_embedder = SentenceTransformersTextEmbedder(
133
+ model=model_id,
134
+ trust_remote_code=True,
135
+ normalize_embeddings=True,
136
+ )
137
+ query_embedding = text_embedder.run("machined metal part")["embedding"]
138
+ ```
139
+
140
+ </details>
141
+
142
+ ## Evaluation
143
+
144
+ The numbers below use the official evaluator convention for R@10. Higher is
145
+ better except for TIE and LCA.
146
+
147
+ | Model | Comparable setting | ImageNet top-1 | COCO text R@10 | COCO image R@10 | Flickr text R@10 | Flickr image R@10 | TIE | LCA | Jaccard | H-Prec | H-Rec |
148
+ |---|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
149
+ | MERU-B/16 | same-family baseline | 40.1 | 82.0 | 68.6 | 96.2 | 90.0 | 3.630 | 2.220 | 0.780 | 0.850 | 0.850 |
150
+ | HyCoCLIP-B/16 | official checkpoint | 45.8 | 82.0 | 69.3 | 95.4 | 90.3 | 3.172 | 2.047 | 0.814 | 0.874 | 0.874 |
151
+ | UNCHA-B/16 | official checkpoint | 48.8 | 82.6 | 71.0 | 95.9 | 91.2 | 2.945 | 1.961 | 0.828 | 0.883 | 0.884 |
152
+ | PHyCLIP-B/16 | related reported result | 44.4 | 80.4 | 68.7 | 95.6 | 89.9 | 3.285 | 2.088 | 0.807 | 0.868 | 0.868 |
153
+ | Hyper3-CLIP beta | this release | 48.5 | 84.0 | 72.8 | 97.5 | 92.4 | 2.972 | 1.986 | 0.828 | 0.882 | 0.883 |
154
+
155
+ Raw evaluation files are included:
156
+
157
+ - `eval_coco_karpathy_final.json`
158
+ - `eval_flickr30k_final.json`
159
+ - `eval_imagenet_final.json`
160
+ - `eval_hycoclip_uncha_intersection_final.json`
161
+
162
+ ## License And Attribution
163
+
164
+ The model materials in this repository are released under OpenMDW-1.0. See
165
+ `LICENSE`.
166
+
167
+ Redistributions should preserve `NOTICE`, `LICENSE`, and the original model card
168
+ when practical. Modified or derived checkpoints should use a distinct name and
169
+ must not imply endorsement by hyper³labs.
170
+
171
+ Please cite and link to the original hyper³labs model repository when publishing
172
+ benchmarks, papers, derivative checkpoints, or public demos based on this model.
173
+
174
+ ## Intended Use
175
+
176
+ This release is intended for:
177
+
178
+ - hierarchy-sensitive image-text retrieval research
179
+ - zero-shot and retrieval evaluation
180
+ - multimodal embedding baselines
181
+ - downstream experiments with hyperbolic representation learning
182
+
183
+ This model has not been validated for safety-critical use.
184
+
185
+ ## Citation
186
+
187
+ If you use Hyper3-CLIP beta, cite the original model repository and hyper³labs.
SHA256SUMS ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 127022078882a067d3729185679645a173b7831f22454e02f1d48bf4caca454e ./.hfignore
2
+ 00858698a701445ca5e8e0a753832834d605ae5ce0fc029cc1f4b41e8bf334be ./0_Hyper3CLIP/config.json
3
+ 22d5488faff30b2e34e934779d2a6f033712ec6d1eaaee7e774e9cbe0c3c7587 ./LICENSE
4
+ bc06204c8f542252f901c42929aa964a670f65837d961c947e2b404e9ada57e9 ./NOTICE
5
+ 2524472f3efc94e5215beac98fdc8b3e7ff1296e948e0259a3fe09ac44b43041 ./README.md
6
+ 88e0cd5e7a6f13394196662fda3291058ddd5b738d8c57a3c05f635c366ae9fd ./config.json
7
+ 9639dadcd1152dc059ff7b158b3d9a87395ea9ef99917f0e254b79f93fe72efa ./config.yaml
8
+ bc149f4539e0fabd8d4392324f8d4e3d13a3bea0c41cc504349c51be3215bac2 ./config_sentence_transformers.json
9
+ 7ee8d05bf0e690b40930f561c51ada9d54badc82602f279e9e1222fc51d89c0e ./configuration_hyper3_clip.py
10
+ d4ec8249d963614c6bea43812b71157b5d01be7aeee23ea828d77fca8bb26314 ./eval_coco_karpathy_final.json
11
+ 4bff22e2f2ea6a5451847577f05238002888ad2670be8a4f788203879dd2bd1d ./eval_flickr30k_final.json
12
+ 589fd4d0711b266aa77255737d767f184006fac03f65a69ed1a7632deee812b0 ./eval_hycoclip_uncha_intersection_final.json
13
+ b6a68d90cae001ccc422220d371d717d30cce6d993e8c45f36a6a8c7c2dd38b3 ./eval_imagenet_final.json
14
+ 13b30904fbfdcfad9b968ba1bd5853fe5de0d3bf0dccfa9d3baab0f82c6ea5f4 ./hyper3_clip_sentence_transformers.py
15
+ c86353fda268117808086af62808f54896c1ce954f9a653cafa84b97172effd2 ./metadata.json
16
+ b199fc5574ed85df39c3b0de0d8b5a3e998fcddd29dd181125dd2274aae98555 ./model.safetensors
17
+ 605bda2e201ef962387cac2ed64d8735b82b29da57c24ded54ced5f9e3ece04f ./modeling_hyper3_clip.py
18
+ 14c3e2e85d941bac307a35600d1ad8377fddd9cde85b0d706c19a623b9d311d2 ./modules.json
19
+ 8ace6250857b88214db892908ee8b85d80da96dd9f7020be8bfd4de327326e31 ./requirements.txt
config.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "hyper3_clip",
3
+ "architectures": [
4
+ "Hyper3CLIPModel"
5
+ ],
6
+ "auto_map": {
7
+ "AutoConfig": "configuration_hyper3_clip.Hyper3CLIPConfig",
8
+ "AutoModel": "modeling_hyper3_clip.Hyper3CLIPModel"
9
+ },
10
+ "vision_backbone": "vit_base_patch16_224",
11
+ "text_model_name": "openai/clip-vit-base-patch32",
12
+ "embed_dim": 512,
13
+ "curv_init": 1.0,
14
+ "learn_curv": true,
15
+ "image_size": 224,
16
+ "max_text_length": 77,
17
+ "torch_dtype": "float32",
18
+ "transformers_version": "4.49.0"
19
+ }
config.yaml ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ project:
2
+ name: hyper3-clip
3
+ experiment: hyper3_clip_vit_b_8xh100_full
4
+ seed: 7
5
+ output_dir: /sc/projects/sci-aisc/matin.mahmood/runs/hyper3_clip_vit_b_8xh100_full
6
+ model:
7
+ vision_backbone: vit_base_patch16_224
8
+ text_model_name: openai/clip-vit-base-patch32
9
+ embed_dim: 512
10
+ curv_init: 1.0
11
+ learn_curv: true
12
+ entail_weight: 0.2
13
+ inter_aperture_scale: 0.7
14
+ intra_aperture_scale: 1.2
15
+ training:
16
+ total_steps: 500000
17
+ global_batch_size: 768
18
+ grad_accum_steps: 1
19
+ num_workers: 8
20
+ lr: 0.0005
21
+ weight_decay: 0.2
22
+ betas:
23
+ - 0.9
24
+ - 0.98
25
+ warmup_steps: 4000
26
+ log_interval: 20
27
+ ckpt_interval: 10000
28
+ amp: true
29
+ max_grad_norm: 1.0
30
+ resume: true
31
+ optimizer:
32
+ no_decay_params:
33
+ - logit_scale
34
+ - visual_alpha
35
+ - textual_alpha
36
+ - log_curv
37
+ data:
38
+ type: processed_grit
39
+ part_sampling: random_one
40
+ tarfiles:
41
+ - /sc/projects/sci-aisc/matin.mahmood/datasets/hycoclip/train/GRIT/processed/*.tar
42
+ shuffle_buffer: 4000
43
+ image_size: 224
44
+ max_text_length: 77
45
+ num_workers: 8
config_sentence_transformers.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "__version__": {
3
+ "sentence_transformers": "5.5.1"
4
+ },
5
+ "model_type": "SentenceTransformer",
6
+ "similarity_fn_name": "cosine"
7
+ }
configuration_hyper3_clip.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from transformers import PretrainedConfig
4
+
5
+
6
+ class Hyper3CLIPConfig(PretrainedConfig):
7
+ model_type = "hyper3_clip"
8
+
9
+ def __init__(
10
+ self,
11
+ vision_backbone: str = "vit_base_patch16_224",
12
+ text_model_name: str = "openai/clip-vit-base-patch32",
13
+ embed_dim: int = 512,
14
+ curv_init: float = 1.0,
15
+ learn_curv: bool = True,
16
+ image_size: int = 224,
17
+ max_text_length: int = 77,
18
+ **kwargs,
19
+ ) -> None:
20
+ super().__init__(**kwargs)
21
+ self.vision_backbone = vision_backbone
22
+ self.text_model_name = text_model_name
23
+ self.embed_dim = int(embed_dim)
24
+ self.curv_init = float(curv_init)
25
+ self.learn_curv = bool(learn_curv)
26
+ self.image_size = int(image_size)
27
+ self.max_text_length = int(max_text_length)
eval_coco_karpathy_final.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "checkpoint": "/sc/projects/sci-aisc/matin.mahmood/runs/hyper3_clip_vit_b_8xh100_full/checkpoint_final.pt",
3
+ "config": "configs/eval_common_coco_karpathy_8xh100_full_final.yaml",
4
+ "model_config": "configs/hyper3_clip_vit_b_8xh100_full.yaml",
5
+ "results": {
6
+ "coco_karpathy_retrieval": {
7
+ "i2t_r1": 50.260000000000005,
8
+ "i2t_r10": 83.96000000000001,
9
+ "i2t_r5": 76.02,
10
+ "image_to_text_r1": 0.5026,
11
+ "image_to_text_r10": 0.8396,
12
+ "image_to_text_r5": 0.7602,
13
+ "t2i_r1": 36.93722486495972,
14
+ "t2i_r10": 73.15473556518555,
15
+ "t2i_r5": 62.64294385910034,
16
+ "text_to_image_r1": 0.36937224864959717,
17
+ "text_to_image_r10": 0.7315473556518555,
18
+ "text_to_image_r5": 0.6264294385910034
19
+ }
20
+ }
21
+ }
eval_flickr30k_final.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "checkpoint": "/sc/projects/sci-aisc/matin.mahmood/runs/hyper3_clip_vit_b_8xh100_full/checkpoint_final.pt",
3
+ "config": "configs/eval_common_flickr30k_8xh100_full_final.yaml",
4
+ "model_config": "configs/hyper3_clip_vit_b_8xh100_full.yaml",
5
+ "results": {
6
+ "flickr30k_retrieval": {
7
+ "i2t_r1": 79.0,
8
+ "i2t_r10": 97.5,
9
+ "i2t_r5": 94.5,
10
+ "image_to_text_r1": 0.79,
11
+ "image_to_text_r10": 0.975,
12
+ "image_to_text_r5": 0.945,
13
+ "t2i_r1": 64.80000019073486,
14
+ "t2i_r10": 92.35999584197998,
15
+ "t2i_r5": 86.79999709129333,
16
+ "text_to_image_r1": 0.6480000019073486,
17
+ "text_to_image_r10": 0.9235999584197998,
18
+ "text_to_image_r5": 0.8679999709129333
19
+ }
20
+ }
21
+ }
eval_hycoclip_uncha_intersection_final.json ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "checkpoint": "/sc/projects/sci-aisc/matin.mahmood/runs/hyper3_clip_vit_b_8xh100_full/checkpoint_final.pt",
3
+ "config": "configs/eval_hycoclip_uncha_intersection_8xh100_full_final.yaml",
4
+ "model_config": "configs/hyper3_clip_vit_b_8xh100_full.yaml",
5
+ "results": {
6
+ "coco_retrieval": {
7
+ "i2t_r1": 50.3,
8
+ "i2t_r10": 84.0,
9
+ "i2t_r5": 75.86,
10
+ "image_to_text_r1": 0.503,
11
+ "image_to_text_r10": 0.84,
12
+ "image_to_text_r5": 0.7586,
13
+ "t2i_r1": 35.67602038383484,
14
+ "t2i_r10": 72.81522154808044,
15
+ "t2i_r5": 62.113213539123535,
16
+ "text_to_image_r1": 0.3567602038383484,
17
+ "text_to_image_r10": 0.7281522154808044,
18
+ "text_to_image_r5": 0.6211321353912354
19
+ },
20
+ "flickr30k_retrieval": {
21
+ "i2t_r1": 79.0,
22
+ "i2t_r10": 97.5,
23
+ "i2t_r5": 94.5,
24
+ "image_to_text_r1": 0.79,
25
+ "image_to_text_r10": 0.975,
26
+ "image_to_text_r5": 0.945,
27
+ "t2i_r1": 64.80000019073486,
28
+ "t2i_r10": 92.35999584197998,
29
+ "t2i_r5": 86.79999709129333,
30
+ "text_to_image_r1": 0.6480000019073486,
31
+ "text_to_image_r10": 0.9235999584197998,
32
+ "text_to_image_r5": 0.8679999709129333
33
+ },
34
+ "imagenet_hierarchical": {
35
+ "hierarchical_precision": 0.8819191582306588,
36
+ "hierarchical_recall": 0.8834472284937281,
37
+ "jaccard": 0.8284612410570121,
38
+ "lca": 1.98578,
39
+ "tie": 2.97216
40
+ },
41
+ "imagenet_zero_shot": {
42
+ "mean_per_class_acc_pct": 48.496,
43
+ "top1": 0.48496,
44
+ "top1_pct": 48.496
45
+ }
46
+ }
47
+ }
eval_imagenet_final.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "checkpoint": "/sc/projects/sci-aisc/matin.mahmood/runs/hyper3_clip_vit_b_8xh100_full/checkpoint_final.pt",
3
+ "config": "configs/eval_common_imagenet_8xh100_full_final.yaml",
4
+ "model_config": "configs/hyper3_clip_vit_b_8xh100_full.yaml",
5
+ "results": {
6
+ "imagenet_zero_shot": {
7
+ "mean_per_class_acc_pct": 48.496,
8
+ "top1": 0.48496,
9
+ "top1_pct": 48.496
10
+ }
11
+ }
12
+ }
hyper3_clip_sentence_transformers.py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ import numpy as np
8
+ import timm
9
+ import torch
10
+ import torch.nn.functional as F
11
+ from PIL import Image
12
+ from safetensors.torch import load_file
13
+ from sentence_transformers.base.modules.module import Module
14
+ from torch import nn
15
+ from transformers import AutoTokenizer, CLIPTextConfig, CLIPTextModel
16
+
17
+ try:
18
+ import yaml
19
+ except ImportError as exc: # pragma: no cover - produces a clear error for missing deps.
20
+ raise ImportError("Hyper3-CLIP requires pyyaml to load config.yaml") from exc
21
+
22
+
23
+ IMAGENET_MEAN = torch.tensor([0.485, 0.456, 0.406], dtype=torch.float32).view(3, 1, 1)
24
+ IMAGENET_STD = torch.tensor([0.229, 0.224, 0.225], dtype=torch.float32).view(3, 1, 1)
25
+
26
+
27
+ class Hyper3CLIPSentenceTransformerModule(Module):
28
+ """Sentence Transformers module for the Hyper3-CLIP beta checkpoint."""
29
+
30
+ config_keys = [
31
+ "model_config_file",
32
+ "weights_file",
33
+ "image_size",
34
+ "max_text_length",
35
+ "normalize_output",
36
+ ]
37
+ modalities = ["text", "image"]
38
+
39
+ def __init__(
40
+ self,
41
+ model_config: dict[str, Any],
42
+ weights_path: str | None = None,
43
+ model_config_file: str = "config.yaml",
44
+ weights_file: str = "model.safetensors",
45
+ image_size: int = 224,
46
+ max_text_length: int = 77,
47
+ normalize_output: bool = True,
48
+ ) -> None:
49
+ super().__init__()
50
+ self.model_config_file = model_config_file
51
+ self.weights_file = weights_file
52
+ self.image_size = int(image_size)
53
+ self.max_text_length = int(max_text_length)
54
+ self.normalize_output = bool(normalize_output)
55
+
56
+ model_args = dict(model_config["model"])
57
+ data_args = model_config.get("data", {})
58
+ self.image_size = int(data_args.get("image_size", self.image_size))
59
+ self.max_text_length = int(data_args.get("max_text_length", self.max_text_length))
60
+
61
+ self.model = _Hyper3CLIPInference(
62
+ vision_backbone=model_args["vision_backbone"],
63
+ text_model_name=model_args["text_model_name"],
64
+ embed_dim=int(model_args["embed_dim"]),
65
+ curv_init=float(model_args.get("curv_init", 1.0)),
66
+ learn_curv=bool(model_args.get("learn_curv", True)),
67
+ )
68
+ if weights_path is not None:
69
+ state_dict = load_file(weights_path, device="cpu")
70
+ self.model.load_state_dict(state_dict, strict=True)
71
+ self.model.eval()
72
+ self.tokenizer = self.model.tokenizer
73
+
74
+ @classmethod
75
+ def load(
76
+ cls,
77
+ model_name_or_path: str,
78
+ subfolder: str = "",
79
+ token: bool | str | None = None,
80
+ cache_folder: str | None = None,
81
+ revision: str | None = None,
82
+ local_files_only: bool = False,
83
+ **kwargs: Any,
84
+ ) -> "Hyper3CLIPSentenceTransformerModule":
85
+ config = cls.load_config(
86
+ model_name_or_path,
87
+ subfolder=subfolder,
88
+ token=token,
89
+ cache_folder=cache_folder,
90
+ revision=revision,
91
+ local_files_only=local_files_only,
92
+ )
93
+ model_config_file = config.get("model_config_file", "config.yaml")
94
+ weights_file = config.get("weights_file", "model.safetensors")
95
+ model_config_path = cls.load_file_path(
96
+ model_name_or_path,
97
+ model_config_file,
98
+ subfolder="",
99
+ token=token,
100
+ cache_folder=cache_folder,
101
+ revision=revision,
102
+ local_files_only=local_files_only,
103
+ )
104
+ weights_path = cls.load_file_path(
105
+ model_name_or_path,
106
+ weights_file,
107
+ subfolder="",
108
+ token=token,
109
+ cache_folder=cache_folder,
110
+ revision=revision,
111
+ local_files_only=local_files_only,
112
+ )
113
+ if model_config_path is None:
114
+ raise FileNotFoundError(f"Could not find {model_config_file!r} in {model_name_or_path!r}")
115
+ if weights_path is None:
116
+ raise FileNotFoundError(f"Could not find {weights_file!r} in {model_name_or_path!r}")
117
+
118
+ with open(model_config_path, encoding="utf-8") as f:
119
+ model_config = yaml.safe_load(f)
120
+
121
+ return cls(
122
+ model_config=model_config,
123
+ weights_path=weights_path,
124
+ model_config_file=model_config_file,
125
+ weights_file=weights_file,
126
+ image_size=int(config.get("image_size", 224)),
127
+ max_text_length=int(config.get("max_text_length", 77)),
128
+ normalize_output=bool(config.get("normalize_output", True)),
129
+ )
130
+
131
+ def preprocess(
132
+ self,
133
+ inputs: list[Any],
134
+ prompt: str | None = None,
135
+ **kwargs: Any,
136
+ ) -> dict[str, torch.Tensor | str]:
137
+ if not inputs:
138
+ return {}
139
+
140
+ if all(_is_image(item) for item in inputs):
141
+ images = torch.stack([self._preprocess_image(item) for item in inputs])
142
+ return {"pixel_values": images, "modality": "image"}
143
+
144
+ if all(isinstance(item, str) for item in inputs):
145
+ texts = [f"{prompt or ''}{item}" for item in inputs]
146
+ tokens = self.tokenizer(
147
+ texts,
148
+ padding=True,
149
+ truncation=True,
150
+ max_length=self.max_text_length,
151
+ return_tensors="pt",
152
+ )
153
+ return {
154
+ "input_ids": tokens["input_ids"],
155
+ "attention_mask": tokens["attention_mask"],
156
+ "modality": "text",
157
+ }
158
+
159
+ raise TypeError("Hyper3-CLIP beta supports batches containing only PIL images or only text strings.")
160
+
161
+ def tokenize(self, texts: list[Any], **kwargs: Any) -> dict[str, torch.Tensor | str]:
162
+ return self.preprocess(texts, **kwargs)
163
+
164
+ def forward(self, features: dict[str, torch.Tensor | Any], **kwargs: Any) -> dict[str, torch.Tensor | Any]:
165
+ if "pixel_values" in features:
166
+ embeddings = self.model.encode_image(features["pixel_values"], project=False)
167
+ elif "input_ids" in features and "attention_mask" in features:
168
+ embeddings = self.model.encode_text(features["input_ids"], features["attention_mask"], project=False)
169
+ else:
170
+ raise ValueError("Expected either pixel_values or input_ids/attention_mask in features.")
171
+
172
+ embeddings = embeddings.float()
173
+ if self.normalize_output:
174
+ embeddings = F.normalize(embeddings, p=2, dim=-1)
175
+ features["sentence_embedding"] = embeddings
176
+ return features
177
+
178
+ def get_embedding_dimension(self) -> int:
179
+ return int(self.model.embed_dim)
180
+
181
+ def save(self, output_path: str, *args: Any, safe_serialization: bool = True, **kwargs: Any) -> None:
182
+ output_dir = Path(output_path)
183
+ output_dir.mkdir(parents=True, exist_ok=True)
184
+ with open(output_dir / self.config_file_name, "w", encoding="utf-8") as f:
185
+ json.dump(self.get_config_dict(), f, indent=2)
186
+
187
+ def _preprocess_image(self, image: Any) -> torch.Tensor:
188
+ if isinstance(image, np.ndarray):
189
+ image = Image.fromarray(image)
190
+ elif torch.is_tensor(image):
191
+ array = image.detach().cpu()
192
+ if array.ndim == 3 and array.shape[0] in {1, 3, 4}:
193
+ array = array.permute(1, 2, 0)
194
+ image = Image.fromarray(array.numpy())
195
+ if not isinstance(image, Image.Image):
196
+ raise TypeError(f"Expected PIL image, numpy array, or torch tensor, got {type(image)!r}")
197
+
198
+ resampling = getattr(Image, "Resampling", Image).BICUBIC
199
+ image = image.convert("RGB").resize((self.image_size, self.image_size), resampling)
200
+ array = np.asarray(image, dtype=np.float32) / 255.0
201
+ tensor = torch.from_numpy(array).permute(2, 0, 1)
202
+ return (tensor - IMAGENET_MEAN) / IMAGENET_STD
203
+
204
+
205
+ class _Hyper3CLIPInference(nn.Module):
206
+ def __init__(
207
+ self,
208
+ vision_backbone: str,
209
+ text_model_name: str,
210
+ embed_dim: int,
211
+ curv_init: float,
212
+ learn_curv: bool,
213
+ ) -> None:
214
+ super().__init__()
215
+ self.vision_encoder = _VisionEncoder(vision_backbone)
216
+ self.text_encoder = _TextEncoder(text_model_name)
217
+ self.tokenizer = self.text_encoder.tokenizer
218
+ self.embed_dim = int(embed_dim)
219
+ self.image_proj = nn.Linear(self.vision_encoder.output_dim, self.embed_dim)
220
+ self.text_proj = nn.Linear(self.text_encoder.output_dim, self.embed_dim)
221
+ self.logit_scale = nn.Parameter(torch.tensor(1 / 0.07).log())
222
+ self.visual_alpha = nn.Parameter(torch.full((), self.embed_dim**-0.5).log())
223
+ self.textual_alpha = nn.Parameter(torch.full((), self.embed_dim**-0.5).log())
224
+ self.log_curv = nn.Parameter(torch.full((), curv_init).log(), requires_grad=learn_curv)
225
+
226
+ def encode_image(self, image: torch.Tensor, project: bool = False) -> torch.Tensor:
227
+ feats = self.image_proj(self.vision_encoder(image))
228
+ if project:
229
+ feats = feats * self.visual_alpha.exp().float()
230
+ return feats
231
+
232
+ def encode_text(self, input_ids: torch.Tensor, attention_mask: torch.Tensor, project: bool = False) -> torch.Tensor:
233
+ feats = self.text_proj(self.text_encoder(input_ids=input_ids, attention_mask=attention_mask))
234
+ if project:
235
+ feats = feats * self.textual_alpha.exp().float()
236
+ return feats
237
+
238
+
239
+ class _VisionEncoder(nn.Module):
240
+ def __init__(self, backbone_name: str) -> None:
241
+ super().__init__()
242
+ self.backbone = timm.create_model(backbone_name, pretrained=False, num_classes=0, global_pool="avg")
243
+ self.output_dim = self.backbone.num_features
244
+
245
+ def forward(self, image: torch.Tensor) -> torch.Tensor:
246
+ return self.backbone(image)
247
+
248
+
249
+ class _TextEncoder(nn.Module):
250
+ def __init__(self, model_name: str) -> None:
251
+ super().__init__()
252
+ self.tokenizer = AutoTokenizer.from_pretrained(model_name)
253
+ self.backbone = CLIPTextModel(CLIPTextConfig.from_pretrained(model_name))
254
+ self.output_dim = self.backbone.config.hidden_size
255
+
256
+ def forward(self, input_ids: torch.Tensor, attention_mask: torch.Tensor) -> torch.Tensor:
257
+ out = self.backbone(input_ids=input_ids, attention_mask=attention_mask)
258
+ if hasattr(out, "pooler_output") and out.pooler_output is not None:
259
+ return out.pooler_output
260
+ return out.last_hidden_state[:, 0]
261
+
262
+
263
+ def _is_image(item: Any) -> bool:
264
+ return isinstance(item, Image.Image) or isinstance(item, np.ndarray) or torch.is_tensor(item)
metadata.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "end_time": "2026-05-06T10:53:23+00:00",
3
+ "env": {
4
+ "hostname": "gx08",
5
+ "rank": "0",
6
+ "world_size": "8"
7
+ },
8
+ "experiment_name": "hyper3-clip",
9
+ "final_step": 500000,
10
+ "job": {
11
+ "gpus": "0,1,2,3,4,5,6,7",
12
+ "node_list": "gx08",
13
+ "num_nodes": "1",
14
+ "partition": "aisc-batch",
15
+ "slurm_job_id": "1916964"
16
+ },
17
+ "run_id": "hyper3_clip_vit_b_8xh100_full",
18
+ "start_time": "2026-05-05T09:57:08+00:00",
19
+ "status": "completed",
20
+ "tags": {
21
+ "data": "processed_grit",
22
+ "model": "vit_base_patch16_224"
23
+ }
24
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b199fc5574ed85df39c3b0de0d8b5a3e998fcddd29dd181125dd2274aae98555
3
+ size 598529912
modeling_hyper3_clip.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any
5
+
6
+ import numpy as np
7
+ import timm
8
+ import torch
9
+ import torch.nn.functional as F
10
+ from PIL import Image
11
+ from torch import nn
12
+ from transformers import CLIPTextConfig, CLIPTextModel, PreTrainedModel
13
+ from transformers.modeling_outputs import ModelOutput
14
+
15
+ from .configuration_hyper3_clip import Hyper3CLIPConfig
16
+
17
+
18
+ IMAGENET_MEAN = torch.tensor([0.485, 0.456, 0.406], dtype=torch.float32).view(3, 1, 1)
19
+ IMAGENET_STD = torch.tensor([0.229, 0.224, 0.225], dtype=torch.float32).view(3, 1, 1)
20
+
21
+
22
+ @dataclass
23
+ class Hyper3CLIPOutput(ModelOutput):
24
+ image_embeds: torch.FloatTensor | None = None
25
+ text_embeds: torch.FloatTensor | None = None
26
+
27
+
28
+ class Hyper3CLIPModel(PreTrainedModel):
29
+ config_class = Hyper3CLIPConfig
30
+ main_input_name = "pixel_values"
31
+ supports_gradient_checkpointing = False
32
+
33
+ def __init__(self, config: Hyper3CLIPConfig) -> None:
34
+ super().__init__(config)
35
+ self.vision_encoder = _VisionEncoder(config.vision_backbone)
36
+ self.text_encoder = _TextEncoder(config.text_model_name)
37
+ self.embed_dim = int(config.embed_dim)
38
+ self.image_proj = nn.Linear(self.vision_encoder.output_dim, self.embed_dim)
39
+ self.text_proj = nn.Linear(self.text_encoder.output_dim, self.embed_dim)
40
+ self.logit_scale = nn.Parameter(torch.tensor(1 / 0.07).log())
41
+ self.visual_alpha = nn.Parameter(torch.full((), self.embed_dim**-0.5).log())
42
+ self.textual_alpha = nn.Parameter(torch.full((), self.embed_dim**-0.5).log())
43
+ self.log_curv = nn.Parameter(torch.full((), config.curv_init).log(), requires_grad=config.learn_curv)
44
+
45
+ def encode_image(self, pixel_values: torch.Tensor, normalize: bool = True) -> torch.Tensor:
46
+ embeddings = self.image_proj(self.vision_encoder(pixel_values))
47
+ return F.normalize(embeddings.float(), p=2, dim=-1) if normalize else embeddings
48
+
49
+ def encode_text(
50
+ self,
51
+ input_ids: torch.Tensor,
52
+ attention_mask: torch.Tensor,
53
+ normalize: bool = True,
54
+ ) -> torch.Tensor:
55
+ embeddings = self.text_proj(self.text_encoder(input_ids=input_ids, attention_mask=attention_mask))
56
+ return F.normalize(embeddings.float(), p=2, dim=-1) if normalize else embeddings
57
+
58
+ def preprocess_image(self, image: Any) -> torch.Tensor:
59
+ return _preprocess_image(image, image_size=self.config.image_size)
60
+
61
+ def forward(
62
+ self,
63
+ pixel_values: torch.Tensor | None = None,
64
+ input_ids: torch.Tensor | None = None,
65
+ attention_mask: torch.Tensor | None = None,
66
+ normalize: bool = True,
67
+ return_dict: bool | None = None,
68
+ ) -> Hyper3CLIPOutput | tuple[torch.Tensor | None, torch.Tensor | None]:
69
+ image_embeds = self.encode_image(pixel_values, normalize=normalize) if pixel_values is not None else None
70
+ text_embeds = None
71
+ if input_ids is not None:
72
+ if attention_mask is None:
73
+ attention_mask = torch.ones_like(input_ids)
74
+ text_embeds = self.encode_text(input_ids, attention_mask, normalize=normalize)
75
+
76
+ if return_dict is False:
77
+ return image_embeds, text_embeds
78
+ return Hyper3CLIPOutput(image_embeds=image_embeds, text_embeds=text_embeds)
79
+
80
+
81
+ class _VisionEncoder(nn.Module):
82
+ def __init__(self, backbone_name: str) -> None:
83
+ super().__init__()
84
+ self.backbone = timm.create_model(backbone_name, pretrained=False, num_classes=0, global_pool="avg")
85
+ self.output_dim = self.backbone.num_features
86
+
87
+ def forward(self, image: torch.Tensor) -> torch.Tensor:
88
+ return self.backbone(image)
89
+
90
+
91
+ class _TextEncoder(nn.Module):
92
+ def __init__(self, model_name: str) -> None:
93
+ super().__init__()
94
+ self.backbone = CLIPTextModel(CLIPTextConfig.from_pretrained(model_name))
95
+ self.output_dim = self.backbone.config.hidden_size
96
+
97
+ def forward(self, input_ids: torch.Tensor, attention_mask: torch.Tensor) -> torch.Tensor:
98
+ out = self.backbone(input_ids=input_ids, attention_mask=attention_mask)
99
+ if hasattr(out, "pooler_output") and out.pooler_output is not None:
100
+ return out.pooler_output
101
+ return out.last_hidden_state[:, 0]
102
+
103
+
104
+ def _preprocess_image(image: Any, image_size: int = 224) -> torch.Tensor:
105
+ if isinstance(image, np.ndarray):
106
+ image = Image.fromarray(image)
107
+ elif torch.is_tensor(image):
108
+ array = image.detach().cpu()
109
+ if array.ndim == 3 and array.shape[0] in {1, 3, 4}:
110
+ array = array.permute(1, 2, 0)
111
+ image = Image.fromarray(array.numpy())
112
+ if not isinstance(image, Image.Image):
113
+ raise TypeError(f"Expected PIL image, numpy array, or torch tensor, got {type(image)!r}")
114
+
115
+ resampling = getattr(Image, "Resampling", Image).BICUBIC
116
+ image = image.convert("RGB").resize((image_size, image_size), resampling)
117
+ array = np.asarray(image, dtype=np.float32) / 255.0
118
+ tensor = torch.from_numpy(array).permute(2, 0, 1)
119
+ return (tensor - IMAGENET_MEAN) / IMAGENET_STD
modules.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "idx": 0,
4
+ "name": "0_Hyper3CLIP",
5
+ "path": "0_Hyper3CLIP",
6
+ "type": "hyper3_clip_sentence_transformers.Hyper3CLIPSentenceTransformerModule"
7
+ }
8
+ ]
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ sentence-transformers>=5.5.1
2
+ haystack-ai>=2.30.1
3
+ transformers>=4.49.0
4
+ timm>=1.0.0
5
+ safetensors>=0.4.0
6
+ pyyaml>=6.0.0
7
+ Pillow>=10.0.0