File size: 13,583 Bytes
52a34a3
 
2bf352b
 
 
 
 
 
 
 
 
 
 
52a34a3
2bf352b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
---
license: cc-by-4.0
library_name: pytorch
pipeline_tag: feature-extraction
tags:
  - audio-text-retrieval
  - contrastive-learning
  - clip
  - openclip
  - audio-spectrogram-transformer
  - cross-modal-alignment
  - multimodal-embeddings
base_model: MIT/ast-finetuned-audioset-10-10-0.4593
---

# Edgebind

Audio-into-CLIP alignment for text–image–audio retrieval. Edgebind maps audio into the
existing embedding space of a frozen CLIP model instead of training a joint encoder
from scratch, so audio, text and images become directly comparable by cosine
similarity in one 512-d space.

Research artifact for **"Edgebind: Towards Edge-Compatible Audio-into-CLIP Alignment
for Text-Image-Audio Retrieval"** (CAISc 2026).

> **Note on the `datasets` metadata field.** This card intentionally omits it. Clotho
> v2.1 has no canonical publisher-owned dataset repository on the Hub β€” a search
> returns only third-party mirrors and the unrelated ClothoAQA task. The training code
> downloads Clotho directly from Zenodo, so tagging a mirror would misstate
> provenance. The authoritative source is linked in prose under
> [Training data](#training-data).

## Model description

| Component | Detail |
|---|---|
| Text & image encoders | OpenCLIP ViT-B/32, `hf-hub:laion/CLIP-ViT-B-32-laion2B-s34B-b79K` β€” **fully frozen**, never updated |
| Audio encoder | Audio Spectrogram Transformer, [`MIT/ast-finetuned-audioset-10-10-0.4593`](https://huggingface.co/MIT/ast-finetuned-audioset-10-10-0.4593) |
| AST frozen | Patch embeddings, position embeddings, encoder layers 0–8 |
| AST trained | Encoder layers 9, 10, 11 and the final layernorm |
| Pooling | Mean of the first two AST output tokens (CLS and distillation) |
| Projection head | `Linear(768, 1024) β†’ LayerNorm(1024) β†’ ReLU β†’ Dropout(0.3) β†’ Linear(1024, 512)` |
| Temperature | Learnable `logit_scale`, initialized to `log(1 / 0.07)` |
| Output | 512-d, L2-normalized, shared with CLIP text and image embeddings |
| Objective | Symmetric InfoNCE (cross-entropy over in-batch negatives in both directions, averaged) |
| Training data | Clotho v2.1, development split |

Only the AST upper layers, the projection head and the temperature receive gradients.
Because the CLIP towers are untouched, text and image embeddings produced by this model
are identical to stock OpenCLIP ViT-B/32 β€” the alignment is carried entirely by the
audio branch.

The pooling choice is worth stating explicitly, since it is easy to assume otherwise:
audio features are the **average of tokens 0 and 1**, not the CLS token alone. Using
CLS alone is a separate ablation in the training notebook.

## Intended use and limitations

Intended use: text-to-audio and audio-to-text retrieval over a local corpus you
control β€” embed a set of audio clips once, then rank them against free-text queries by
cosine similarity. Because the CLIP image tower is frozen and shared, images can be
embedded into the same space and searched with the same text queries.

**This is a research prototype, not a production model.** Specifically:

- **Single dataset.** Trained and validated only on Clotho v2.1, which is small
  (a few thousand clips) and skewed toward everyday environmental and ambient sound.
  Behaviour on speech, music, or domain-specific audio is uncharacterized.
- **Single training run, no seed variance.** One run of 20 epochs. The training code
  sets no random seed, and caption sampling, crop offsets and SpecAugment masks are all
  stochastic, so run-to-run variance has not been measured. Treat any single reported
  number as one sample, not a mean.
- **No CPU, quantized, or on-device benchmarking.** The model was trained and run under
  CUDA mixed precision on a single NVIDIA T4. Latency, memory, and accuracy under CPU
  inference, quantization, distillation, or mobile/embedded runtimes have **not** been
  evaluated. The released checkpoint is unquantized fp32. Despite "edge-compatible" in
  the paper title β€” which refers to the design motivation for reusing a frozen
  ViT-B/32 backbone β€” nothing here establishes that this model is edge-ready or
  deployable on constrained hardware. Do not treat it as such.
- **Fixed 10.24 s window.** Audio is cropped or zero-padded to exactly 163,840 samples
  at 16 kHz. Longer recordings are truncated, not chunked; content outside the window
  is invisible to the model.
- **Inherited bias.** The text and image behaviour is entirely that of
  LAION-2B-trained OpenCLIP ViT-B/32 and carries its biases unchanged.

Out of scope: audio captioning or generation (there is no decoder), speaker or speech
recognition, and any safety-, surveillance-, or identity-related classification.

## How to use

The checkpoint is a `state_dict` for the composite module defined in the training
notebook, so you must reconstruct that module β€” CLIP submodule included β€” before
loading. The snippet below mirrors the notebook's own model definition and its
evaluation-time preprocessing path.

```python
import numpy as np
import torch
import torch.nn as nn
import torchaudio
import torchaudio.transforms as T
from huggingface_hub import hf_hub_download
from transformers import ASTModel, AutoProcessor
import open_clip

MODEL_NAME = "hf-hub:laion/CLIP-ViT-B-32-laion2B-s34B-b79K"
AST_NAME = "MIT/ast-finetuned-audioset-10-10-0.4593"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"


class OpenCLIP_AST_Model(nn.Module):
    def __init__(self, embedding_dim=512):
        super().__init__()
        self.ast = ASTModel.from_pretrained(AST_NAME)
        self.clip_model, _, self.image_preprocess = open_clip.create_model_and_transforms(
            MODEL_NAME, pretrained=None
        )
        self.audio_projection = nn.Sequential(
            nn.Linear(self.ast.config.hidden_size, 1024),
            nn.LayerNorm(1024),
            nn.ReLU(),
            nn.Dropout(0.3),
            nn.Linear(1024, embedding_dim),
        )
        self.logit_scale = nn.Parameter(torch.ones([]) * np.log(1 / 0.07))

    def forward_audio(self, input_values):
        out = self.ast(input_values)
        # Mean of CLS + distillation tokens, matching training.
        feats = (out.last_hidden_state[:, 0] + out.last_hidden_state[:, 1]) / 2
        return self.audio_projection(feats)


weights = hf_hub_download("harryfrz/edgebind", "edgebind_v1.1")

model = OpenCLIP_AST_Model().to(DEVICE)
# The notebook loads with a plain torch.load. On torch >= 2.6 the weights_only=True
# default is appropriate for a pure tensor state_dict; pass weights_only=False if
# your torch version raises on it.
model.load_state_dict(torch.load(weights, map_location=DEVICE))
model.eval()

ast_processor = AutoProcessor.from_pretrained(AST_NAME)
tokenizer = open_clip.get_tokenizer(MODEL_NAME)

SAMPLE_RATE = 16000
TARGET_SAMPLES = 163840  # 10.24 s at 16 kHz


def embed_audio(path):
    waveform, sr = torchaudio.load(path)
    waveform = waveform.mean(dim=0) if waveform.shape[0] > 1 else waveform.squeeze(0)
    if sr != SAMPLE_RATE:
        waveform = T.Resample(sr, SAMPLE_RATE)(waveform)

    n = waveform.shape[0]
    if n < TARGET_SAMPLES:
        waveform = torch.nn.functional.pad(waveform, (0, TARGET_SAMPLES - n))
    elif n > TARGET_SAMPLES:
        start = (n - TARGET_SAMPLES) // 2  # centre crop, as at evaluation time
        waveform = waveform[start:start + TARGET_SAMPLES]

    inputs = ast_processor(waveform, sampling_rate=SAMPLE_RATE, return_tensors="pt")
    with torch.no_grad():
        emb = model.forward_audio(inputs["input_values"].to(DEVICE))
    return emb / emb.norm(dim=-1, keepdim=True)


def embed_text(prompts):
    with torch.no_grad():
        emb = model.clip_model.encode_text(tokenizer(prompts).to(DEVICE))
    return emb / emb.norm(dim=-1, keepdim=True)


def embed_image(pil_image):
    with torch.no_grad():
        x = model.image_preprocess(pil_image).unsqueeze(0).to(DEVICE)
        emb = model.clip_model.encode_image(x)
    return emb / emb.norm(dim=-1, keepdim=True)


# --- retrieval over a local corpus ---
corpus = ["clip_a.wav", "clip_b.wav", "clip_c.wav"]
index = torch.cat([embed_audio(p) for p in corpus], dim=0)   # [N, 512]

query = embed_text(["waves hitting the shore"])              # [1, 512]
scores = (query @ index.T)[0]                                # cosine similarity

for rank in scores.argsort(descending=True):
    print(f"{corpus[rank]}  {scores[rank].item():.4f}")
```

`open_clip.create_model_and_transforms(MODEL_NAME, pretrained=None)` still downloads
pretrained weights: the `hf-hub:` prefix resolves the checkpoint from the Hub, and
`pretrained=None` only means no additional named tag is applied. Those weights are
then overwritten by the `clip_model.*` entries in the state dict.

### What the checkpoint file contains

`edgebind_v1.1` (955 MB, no file extension). The training code saves with
`torch.save(model.state_dict(), ...)` and reloads with a strict
`model.load_state_dict(torch.load(path, map_location=device))`. That means:

- It is a **raw `state_dict`**, not a training checkpoint β€” no optimizer state, no
  epoch counter, no scheduler or scaler state, no embedded config or metrics.
- It stores the **complete** module, not only the trained tensors. Key prefixes are
  `ast.*` (the whole AST, frozen layers 0–8 included), `clip_model.*` (the entire
  frozen OpenCLIP ViT-B/32 image *and* text towers), `audio_projection.*`, plus the
  scalar `logit_scale`.
- That is roughly 239 M parameters in fp32 (AST β‰ˆ86 M + CLIP β‰ˆ151 M + projection
  β‰ˆ1.3 M), consistent with the 955 MB file size.
- Loading is strict, so the module must be rebuilt exactly β€” including the CLIP
  submodule, which is why the snippet above instantiates it.

**This description is derived from the notebook's save/load code and the file size, not
from opening the file.** The tensor keys and dtypes have not been enumerated directly.
If you need that confirmed, load it and inspect `.keys()`.

## Evaluation

**No results table is published in this card.** The training notebook was committed
with all cell outputs stripped, and it contains no code that computes R@1/R@5/R@10,
MedR, MeanR or mAP@10, no CLAP baseline, and no frozen-AST ablation. Transcribing
metrics from any other source would not be verifiable against this repository, so none
are reproduced here.

For the reported text-to-audio and audio-to-text results on the Clotho v2.1 evaluation
split, and the baseline and ablation comparisons, see the CAISc 2026 paper.

The notebook does include a qualitative check β€” top-3 retrieval for five hardcoded
prompts β€” and one ablation, CLS-only pooling, described below.

## Training details

### Training data

Clotho v2.1, **development** split, obtained from Zenodo record
[4783391](https://zenodo.org/records/4783391). The training code downloads
`clotho_captions_development.csv` and `clotho_audio_development.7z` from that record
directly. Each audio file carries five human-written captions; one is sampled at random
per example per epoch during training.

### Preprocessing

- Downmix to mono, resample to 16 kHz.
- Crop or zero-pad to 163,840 samples (10.24 s): **random** crop during training,
  **centre** crop at evaluation.
- Log-mel features via `AutoProcessor` for `MIT/ast-finetuned-audioset-10-10-0.4593`.
- SpecAugment, training only: `FrequencyMasking(freq_mask_param=24)` and
  `TimeMasking(time_mask_param=40)`.

### Hyperparameters

| Setting | Value |
|---|---|
| Optimizer | AdamW, `weight_decay=0.05` |
| Epochs | 20 |
| Batch size | 64 (gradient accumulation steps = 1, so effective batch = 64) |
| LR β€” AST layers 9–11 | 5e-6 |
| LR β€” AST final layernorm | 5e-6 |
| LR β€” projection head | 2e-4 |
| LR β€” logit scale | 2e-4 |
| Schedule | `CosineAnnealingLR`, `eta_min=1e-6`, stepped per optimizer step |
| Precision | CUDA mixed precision (`torch.amp.autocast` + `GradScaler`) |
| Loader | `drop_last=True`, `num_workers=4` |

### Compute

Single NVIDIA T4. The training cell's recorded execution window spans **50 min 25 s**
for all 20 epochs plus audio-embedding export. That figure comes from notebook cell
execution timestamps, not from a printed training log.

### Ablation included in the code

**CLS-only pooling** β€” identical in every other respect, but uses
`last_hidden_state[:, 0]` instead of the mean of tokens 0 and 1. It trains to a
separate checkpoint. No metrics for it are present in the notebook.

## A note on naming

Some artifacts and in-code comments use the internal name **"Sage-Embed"** (for
example, `# Verified Sage-Embed v1.1 train/freeze configuration`). This refers to the
same model as Edgebind. The `v1.1` suffix on the checkpoint filename corresponds to
that internal versioning.

## Links

- **Code:** https://github.com/harryfrzz/edgebind
- **Paper:** "Edgebind: Towards Edge-Compatible Audio-into-CLIP Alignment for
  Text-Image-Audio Retrieval", CAISc 2026
- **Training data:** Clotho v2.1 β€” https://zenodo.org/records/4783391
- **Base audio model:** https://huggingface.co/MIT/ast-finetuned-audioset-10-10-0.4593
- **Base CLIP model:** https://huggingface.co/laion/CLIP-ViT-B-32-laion2B-s34B-b79K

## Citation

```bibtex
@inproceedings{edgebind2026,
  title     = {Edgebind: Towards Edge-Compatible Audio-into-CLIP Alignment for
               Text-Image-Audio Retrieval},
  author    = {Harikrishna C},
  booktitle = {CAISc},
  year      = {2026}
}
```

The repository records no full author list, DOI, or page numbers; the author field
above is taken from commit metadata and should be completed before use.

## License

CC BY 4.0.