harryfrz commited on
Commit
2bf352b
Β·
verified Β·
1 Parent(s): 5fd4e28

Add model card

Browse files
Files changed (1) hide show
  1. README.md +309 -0
README.md CHANGED
@@ -1,3 +1,312 @@
1
  ---
2
  license: cc-by-4.0
 
 
 
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: cc-by-4.0
3
+ library_name: pytorch
4
+ pipeline_tag: feature-extraction
5
+ tags:
6
+ - audio-text-retrieval
7
+ - contrastive-learning
8
+ - clip
9
+ - openclip
10
+ - audio-spectrogram-transformer
11
+ - cross-modal-alignment
12
+ - multimodal-embeddings
13
+ base_model: MIT/ast-finetuned-audioset-10-10-0.4593
14
  ---
15
+
16
+ # Edgebind
17
+
18
+ Audio-into-CLIP alignment for text–image–audio retrieval. Edgebind maps audio into the
19
+ existing embedding space of a frozen CLIP model instead of training a joint encoder
20
+ from scratch, so audio, text and images become directly comparable by cosine
21
+ similarity in one 512-d space.
22
+
23
+ Research artifact for **"Edgebind: Towards Edge-Compatible Audio-into-CLIP Alignment
24
+ for Text-Image-Audio Retrieval"** (CAISc 2026).
25
+
26
+ > **Note on the `datasets` metadata field.** This card intentionally omits it. Clotho
27
+ > v2.1 has no canonical publisher-owned dataset repository on the Hub β€” a search
28
+ > returns only third-party mirrors and the unrelated ClothoAQA task. The training code
29
+ > downloads Clotho directly from Zenodo, so tagging a mirror would misstate
30
+ > provenance. The authoritative source is linked in prose under
31
+ > [Training data](#training-data).
32
+
33
+ ## Model description
34
+
35
+ | Component | Detail |
36
+ |---|---|
37
+ | Text & image encoders | OpenCLIP ViT-B/32, `hf-hub:laion/CLIP-ViT-B-32-laion2B-s34B-b79K` β€” **fully frozen**, never updated |
38
+ | Audio encoder | Audio Spectrogram Transformer, [`MIT/ast-finetuned-audioset-10-10-0.4593`](https://huggingface.co/MIT/ast-finetuned-audioset-10-10-0.4593) |
39
+ | AST frozen | Patch embeddings, position embeddings, encoder layers 0–8 |
40
+ | AST trained | Encoder layers 9, 10, 11 and the final layernorm |
41
+ | Pooling | Mean of the first two AST output tokens (CLS and distillation) |
42
+ | Projection head | `Linear(768, 1024) β†’ LayerNorm(1024) β†’ ReLU β†’ Dropout(0.3) β†’ Linear(1024, 512)` |
43
+ | Temperature | Learnable `logit_scale`, initialized to `log(1 / 0.07)` |
44
+ | Output | 512-d, L2-normalized, shared with CLIP text and image embeddings |
45
+ | Objective | Symmetric InfoNCE (cross-entropy over in-batch negatives in both directions, averaged) |
46
+ | Training data | Clotho v2.1, development split |
47
+
48
+ Only the AST upper layers, the projection head and the temperature receive gradients.
49
+ Because the CLIP towers are untouched, text and image embeddings produced by this model
50
+ are identical to stock OpenCLIP ViT-B/32 β€” the alignment is carried entirely by the
51
+ audio branch.
52
+
53
+ The pooling choice is worth stating explicitly, since it is easy to assume otherwise:
54
+ audio features are the **average of tokens 0 and 1**, not the CLS token alone. Using
55
+ CLS alone is a separate ablation in the training notebook.
56
+
57
+ ## Intended use and limitations
58
+
59
+ Intended use: text-to-audio and audio-to-text retrieval over a local corpus you
60
+ control β€” embed a set of audio clips once, then rank them against free-text queries by
61
+ cosine similarity. Because the CLIP image tower is frozen and shared, images can be
62
+ embedded into the same space and searched with the same text queries.
63
+
64
+ **This is a research prototype, not a production model.** Specifically:
65
+
66
+ - **Single dataset.** Trained and validated only on Clotho v2.1, which is small
67
+ (a few thousand clips) and skewed toward everyday environmental and ambient sound.
68
+ Behaviour on speech, music, or domain-specific audio is uncharacterized.
69
+ - **Single training run, no seed variance.** One run of 20 epochs. The training code
70
+ sets no random seed, and caption sampling, crop offsets and SpecAugment masks are all
71
+ stochastic, so run-to-run variance has not been measured. Treat any single reported
72
+ number as one sample, not a mean.
73
+ - **No CPU, quantized, or on-device benchmarking.** The model was trained and run under
74
+ CUDA mixed precision on a single NVIDIA T4. Latency, memory, and accuracy under CPU
75
+ inference, quantization, distillation, or mobile/embedded runtimes have **not** been
76
+ evaluated. The released checkpoint is unquantized fp32. Despite "edge-compatible" in
77
+ the paper title β€” which refers to the design motivation for reusing a frozen
78
+ ViT-B/32 backbone β€” nothing here establishes that this model is edge-ready or
79
+ deployable on constrained hardware. Do not treat it as such.
80
+ - **Fixed 10.24 s window.** Audio is cropped or zero-padded to exactly 163,840 samples
81
+ at 16 kHz. Longer recordings are truncated, not chunked; content outside the window
82
+ is invisible to the model.
83
+ - **Inherited bias.** The text and image behaviour is entirely that of
84
+ LAION-2B-trained OpenCLIP ViT-B/32 and carries its biases unchanged.
85
+
86
+ Out of scope: audio captioning or generation (there is no decoder), speaker or speech
87
+ recognition, and any safety-, surveillance-, or identity-related classification.
88
+
89
+ ## How to use
90
+
91
+ The checkpoint is a `state_dict` for the composite module defined in the training
92
+ notebook, so you must reconstruct that module β€” CLIP submodule included β€” before
93
+ loading. The snippet below mirrors the notebook's own model definition and its
94
+ evaluation-time preprocessing path.
95
+
96
+ ```python
97
+ import numpy as np
98
+ import torch
99
+ import torch.nn as nn
100
+ import torchaudio
101
+ import torchaudio.transforms as T
102
+ from huggingface_hub import hf_hub_download
103
+ from transformers import ASTModel, AutoProcessor
104
+ import open_clip
105
+
106
+ MODEL_NAME = "hf-hub:laion/CLIP-ViT-B-32-laion2B-s34B-b79K"
107
+ AST_NAME = "MIT/ast-finetuned-audioset-10-10-0.4593"
108
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
109
+
110
+
111
+ class OpenCLIP_AST_Model(nn.Module):
112
+ def __init__(self, embedding_dim=512):
113
+ super().__init__()
114
+ self.ast = ASTModel.from_pretrained(AST_NAME)
115
+ self.clip_model, _, self.image_preprocess = open_clip.create_model_and_transforms(
116
+ MODEL_NAME, pretrained=None
117
+ )
118
+ self.audio_projection = nn.Sequential(
119
+ nn.Linear(self.ast.config.hidden_size, 1024),
120
+ nn.LayerNorm(1024),
121
+ nn.ReLU(),
122
+ nn.Dropout(0.3),
123
+ nn.Linear(1024, embedding_dim),
124
+ )
125
+ self.logit_scale = nn.Parameter(torch.ones([]) * np.log(1 / 0.07))
126
+
127
+ def forward_audio(self, input_values):
128
+ out = self.ast(input_values)
129
+ # Mean of CLS + distillation tokens, matching training.
130
+ feats = (out.last_hidden_state[:, 0] + out.last_hidden_state[:, 1]) / 2
131
+ return self.audio_projection(feats)
132
+
133
+
134
+ weights = hf_hub_download("harryfrz/edgebind", "edgebind_v1.1")
135
+
136
+ model = OpenCLIP_AST_Model().to(DEVICE)
137
+ # The notebook loads with a plain torch.load. On torch >= 2.6 the weights_only=True
138
+ # default is appropriate for a pure tensor state_dict; pass weights_only=False if
139
+ # your torch version raises on it.
140
+ model.load_state_dict(torch.load(weights, map_location=DEVICE))
141
+ model.eval()
142
+
143
+ ast_processor = AutoProcessor.from_pretrained(AST_NAME)
144
+ tokenizer = open_clip.get_tokenizer(MODEL_NAME)
145
+
146
+ SAMPLE_RATE = 16000
147
+ TARGET_SAMPLES = 163840 # 10.24 s at 16 kHz
148
+
149
+
150
+ def embed_audio(path):
151
+ waveform, sr = torchaudio.load(path)
152
+ waveform = waveform.mean(dim=0) if waveform.shape[0] > 1 else waveform.squeeze(0)
153
+ if sr != SAMPLE_RATE:
154
+ waveform = T.Resample(sr, SAMPLE_RATE)(waveform)
155
+
156
+ n = waveform.shape[0]
157
+ if n < TARGET_SAMPLES:
158
+ waveform = torch.nn.functional.pad(waveform, (0, TARGET_SAMPLES - n))
159
+ elif n > TARGET_SAMPLES:
160
+ start = (n - TARGET_SAMPLES) // 2 # centre crop, as at evaluation time
161
+ waveform = waveform[start:start + TARGET_SAMPLES]
162
+
163
+ inputs = ast_processor(waveform, sampling_rate=SAMPLE_RATE, return_tensors="pt")
164
+ with torch.no_grad():
165
+ emb = model.forward_audio(inputs["input_values"].to(DEVICE))
166
+ return emb / emb.norm(dim=-1, keepdim=True)
167
+
168
+
169
+ def embed_text(prompts):
170
+ with torch.no_grad():
171
+ emb = model.clip_model.encode_text(tokenizer(prompts).to(DEVICE))
172
+ return emb / emb.norm(dim=-1, keepdim=True)
173
+
174
+
175
+ def embed_image(pil_image):
176
+ with torch.no_grad():
177
+ x = model.image_preprocess(pil_image).unsqueeze(0).to(DEVICE)
178
+ emb = model.clip_model.encode_image(x)
179
+ return emb / emb.norm(dim=-1, keepdim=True)
180
+
181
+
182
+ # --- retrieval over a local corpus ---
183
+ corpus = ["clip_a.wav", "clip_b.wav", "clip_c.wav"]
184
+ index = torch.cat([embed_audio(p) for p in corpus], dim=0) # [N, 512]
185
+
186
+ query = embed_text(["waves hitting the shore"]) # [1, 512]
187
+ scores = (query @ index.T)[0] # cosine similarity
188
+
189
+ for rank in scores.argsort(descending=True):
190
+ print(f"{corpus[rank]} {scores[rank].item():.4f}")
191
+ ```
192
+
193
+ `open_clip.create_model_and_transforms(MODEL_NAME, pretrained=None)` still downloads
194
+ pretrained weights: the `hf-hub:` prefix resolves the checkpoint from the Hub, and
195
+ `pretrained=None` only means no additional named tag is applied. Those weights are
196
+ then overwritten by the `clip_model.*` entries in the state dict.
197
+
198
+ ### What the checkpoint file contains
199
+
200
+ `edgebind_v1.1` (955 MB, no file extension). The training code saves with
201
+ `torch.save(model.state_dict(), ...)` and reloads with a strict
202
+ `model.load_state_dict(torch.load(path, map_location=device))`. That means:
203
+
204
+ - It is a **raw `state_dict`**, not a training checkpoint β€” no optimizer state, no
205
+ epoch counter, no scheduler or scaler state, no embedded config or metrics.
206
+ - It stores the **complete** module, not only the trained tensors. Key prefixes are
207
+ `ast.*` (the whole AST, frozen layers 0–8 included), `clip_model.*` (the entire
208
+ frozen OpenCLIP ViT-B/32 image *and* text towers), `audio_projection.*`, plus the
209
+ scalar `logit_scale`.
210
+ - That is roughly 239 M parameters in fp32 (AST β‰ˆ86 M + CLIP β‰ˆ151 M + projection
211
+ β‰ˆ1.3 M), consistent with the 955 MB file size.
212
+ - Loading is strict, so the module must be rebuilt exactly β€” including the CLIP
213
+ submodule, which is why the snippet above instantiates it.
214
+
215
+ **This description is derived from the notebook's save/load code and the file size, not
216
+ from opening the file.** The tensor keys and dtypes have not been enumerated directly.
217
+ If you need that confirmed, load it and inspect `.keys()`.
218
+
219
+ ## Evaluation
220
+
221
+ **No results table is published in this card.** The training notebook was committed
222
+ with all cell outputs stripped, and it contains no code that computes R@1/R@5/R@10,
223
+ MedR, MeanR or mAP@10, no CLAP baseline, and no frozen-AST ablation. Transcribing
224
+ metrics from any other source would not be verifiable against this repository, so none
225
+ are reproduced here.
226
+
227
+ For the reported text-to-audio and audio-to-text results on the Clotho v2.1 evaluation
228
+ split, and the baseline and ablation comparisons, see the CAISc 2026 paper.
229
+
230
+ The notebook does include a qualitative check β€” top-3 retrieval for five hardcoded
231
+ prompts β€” and one ablation, CLS-only pooling, described below.
232
+
233
+ ## Training details
234
+
235
+ ### Training data
236
+
237
+ Clotho v2.1, **development** split, obtained from Zenodo record
238
+ [4783391](https://zenodo.org/records/4783391). The training code downloads
239
+ `clotho_captions_development.csv` and `clotho_audio_development.7z` from that record
240
+ directly. Each audio file carries five human-written captions; one is sampled at random
241
+ per example per epoch during training.
242
+
243
+ ### Preprocessing
244
+
245
+ - Downmix to mono, resample to 16 kHz.
246
+ - Crop or zero-pad to 163,840 samples (10.24 s): **random** crop during training,
247
+ **centre** crop at evaluation.
248
+ - Log-mel features via `AutoProcessor` for `MIT/ast-finetuned-audioset-10-10-0.4593`.
249
+ - SpecAugment, training only: `FrequencyMasking(freq_mask_param=24)` and
250
+ `TimeMasking(time_mask_param=40)`.
251
+
252
+ ### Hyperparameters
253
+
254
+ | Setting | Value |
255
+ |---|---|
256
+ | Optimizer | AdamW, `weight_decay=0.05` |
257
+ | Epochs | 20 |
258
+ | Batch size | 64 (gradient accumulation steps = 1, so effective batch = 64) |
259
+ | LR β€” AST layers 9–11 | 5e-6 |
260
+ | LR β€” AST final layernorm | 5e-6 |
261
+ | LR β€” projection head | 2e-4 |
262
+ | LR β€” logit scale | 2e-4 |
263
+ | Schedule | `CosineAnnealingLR`, `eta_min=1e-6`, stepped per optimizer step |
264
+ | Precision | CUDA mixed precision (`torch.amp.autocast` + `GradScaler`) |
265
+ | Loader | `drop_last=True`, `num_workers=4` |
266
+
267
+ ### Compute
268
+
269
+ Single NVIDIA T4. The training cell's recorded execution window spans **50 min 25 s**
270
+ for all 20 epochs plus audio-embedding export. That figure comes from notebook cell
271
+ execution timestamps, not from a printed training log.
272
+
273
+ ### Ablation included in the code
274
+
275
+ **CLS-only pooling** β€” identical in every other respect, but uses
276
+ `last_hidden_state[:, 0]` instead of the mean of tokens 0 and 1. It trains to a
277
+ separate checkpoint. No metrics for it are present in the notebook.
278
+
279
+ ## A note on naming
280
+
281
+ Some artifacts and in-code comments use the internal name **"Sage-Embed"** (for
282
+ example, `# Verified Sage-Embed v1.1 train/freeze configuration`). This refers to the
283
+ same model as Edgebind. The `v1.1` suffix on the checkpoint filename corresponds to
284
+ that internal versioning.
285
+
286
+ ## Links
287
+
288
+ - **Code:** https://github.com/harryfrzz/edgebind
289
+ - **Paper:** "Edgebind: Towards Edge-Compatible Audio-into-CLIP Alignment for
290
+ Text-Image-Audio Retrieval", CAISc 2026
291
+ - **Training data:** Clotho v2.1 β€” https://zenodo.org/records/4783391
292
+ - **Base audio model:** https://huggingface.co/MIT/ast-finetuned-audioset-10-10-0.4593
293
+ - **Base CLIP model:** https://huggingface.co/laion/CLIP-ViT-B-32-laion2B-s34B-b79K
294
+
295
+ ## Citation
296
+
297
+ ```bibtex
298
+ @inproceedings{edgebind2026,
299
+ title = {Edgebind: Towards Edge-Compatible Audio-into-CLIP Alignment for
300
+ Text-Image-Audio Retrieval},
301
+ author = {Harikrishna C},
302
+ booktitle = {CAISc},
303
+ year = {2026}
304
+ }
305
+ ```
306
+
307
+ The repository records no full author list, DOI, or page numbers; the author field
308
+ above is taken from commit metadata and should be completed before use.
309
+
310
+ ## License
311
+
312
+ CC BY 4.0.