Stop teaching machines a fixed menu of sounds. Teach them the relationship between what a recording sounds like and what a human would say about it — and the menu disappears.
You have shipped a sound classifier. It is a good one: a convolutional network trained on fifty environmental sound classes, 82% accurate on the held-out fold, running in 40 ms on a phone. Dog barking, rain, chainsaw, crying baby, church bells — fifty categories, learned from thousands of labelled clips.
Then a product manager walks over and says: "Can it flag smoke alarms?"
Smoke alarm is not one of your fifty. And here is the part that should bother you: your model cannot even tell you that it does not know. Ask it about a smoke alarm recording and it will return a probability distribution over fifty categories that sum to one. It will pick something. It will pick it confidently.
Let us make that concrete before we make it abstract. Suppose the final layer produces three raw scores — call them logits, the unnormalised numbers that come out just before the softmax — on a smoke alarm clip:
Softmax exponentiates each and divides by the total: e1.2 = 3.320, e0.4 = 1.492, e0.9 = 2.460. The sum is 7.272. Divide:
The clip contains no dog. The model was never able to say otherwise. The softmax denominator is the sum over the classes the model knows, so the probabilities are conditional on an assumption that is false: that one of my classes is the answer. There is no slot for "none of the above", because a slot is exactly what the architecture cannot grow.
Walk the tensor shapes and the prison becomes visible. A typical audio classifier takes a spectrogram, runs it through an encoder to get a single vector h of size D (say 2048), and then does exactly one more thing:
Everything interesting lives in h. The encoder has learned something rich — texture, onset sharpness, harmonicity, room reverberation. But the only way to ask a question of that richness is through W, and W has exactly C rows, fixed at training time, one per category someone decided on months ago.
C is not a hyperparameter you can nudge at inference. It is the shape of a weight tensor. Change C and you change the model's identity: new parameters, new optimiser state, new data requirement, new evaluation. The vocabulary of the model is welded shut.
| Question you want to ask | Fixed-head classifier | What it costs |
|---|---|---|
| "Is there a dog barking?" | Yes — class 3 exists | Free |
| "Is there a smoke alarm?" | Impossible — no row | Annotate + retrain + redeploy |
| "Is there a small dog barking, indoors?" | Impossible — the label space has no compositional structure | Define a new taxonomy from scratch |
| "Rank these 50 clips by 'sounds like an emergency'" | Impossible — that is not a class | A whole new project |
| "None of the above?" | Cannot be expressed — softmax sums to 1 over known classes | Threshold hacks that do not transfer |
Look at row three. The label space is not just small, it is flat. "Small dog barking indoors" is not a point some distance from "dog barking" — it is nowhere at all, because the one-hot label space has no geometry. Every class is equidistant from every other. A dog bark and a wolf howl are exactly as unrelated, to the loss function, as a dog bark and a trombone.
The obvious escape is to stop needing labels at all. Self-supervised learning (SSL) — pretraining on unlabelled audio with a pretext task like masked prediction or contrastive augmentation — does exactly that, and the paper credits it fairly: SSL "pretrains models with unlabeled audio, avoiding the limited supervision of learning from class labels."
But read the next sentence carefully, because it is the hinge of this whole paper: "However, SSL excludes semantic knowledge from natural language." An SSL model learns what audio looks like from the inside. It learns nothing about what any of it means to a person. And when you deploy it, you still bolt a fixed classification head on top and train it with class labels. Same prison, one floor up.
The paper calls this third option "a middle path between both approaches" and notes, in 2022, that it is "underexplored" for audio. Computer vision had already walked it: CLIP, Florence, ALIGN. In audio, the closest prior work — Wav2CLIP and AudioCLIP — distilled from CLIP and trained "with audio and class labels from AudioSet instead of audio and natural language." Close, but still label-shaped. Chapter 1 is about why that difference matters more than it looks.
Before any machinery, play with the failure. The simulation below runs the same three-second clip through two models. On the left is a fixed-head classifier with a menu of ten classes. On the right is an open-vocabulary model that scores whatever text you give it. Add a class the fixed head has never seen and watch what each one does.
Pick the sound that is actually playing, then add or remove candidate labels. The fixed head can only redistribute mass over its ten frozen rows — it has no way to represent an absent class, and no way to abstain. The open-vocabulary model scores every label you type, including ones nobody trained on.
Three things to notice while you play. First, the fixed head's bars always sum to 100% — that is not confidence, it is arithmetic. Second, adding a class to the open-vocabulary model costs one forward pass of a text encoder and nothing else: no gradient step, no data, no redeploy. Third, the open model happily scores a phrase — "a small dog barking indoors" — which the fixed head could never have as a row.
Before accepting a new paradigm, it is worth being sure the old one is genuinely stuck. Here are the three fixes an engineer reaches for, in order, and where each one breaks.
Escape 1: just train on more classes. Go from 50 to 527 (AudioSet) or 200 (FSD50K). This helps and it does not solve anything, because the problem is not the size of the menu, it is that there is a menu. Someone still has to enumerate the categories in advance, and the annotation cost is now 500 times larger. The 528th sound still cannot be asked about.
Escape 2: use a hierarchy. AudioSet ships an ontology — "Animal → Domestic animals → Dog → Bark". Now "smoke alarm" can inherit from "Alarm". This buys real generalisation, and it buys it at a brutal price: the hierarchy is hand-built, it is finite, and it encodes exactly one way of carving up sound. "A small dog barking indoors" is not a node in anyone's ontology, and it never will be, because the space of useful descriptions is compositional and unbounded.
Escape 3: threshold for open-set rejection. If the maximum probability is below some θ, say "unknown". This is the most sophisticated of the three and it fails in an instructive way. Return to the smoke alarm example: the softmax gave (0.457, 0.205, 0.338). Set θ = 0.5 and the model correctly abstains. Now feed it a genuine dog bark whose logits are (2.4, 0.4, 0.9):
Fine so far. But now feed it a wolf howl — also absent from the taxonomy, and genuinely similar to a dog. Its logits might be (2.1, 0.3, 1.0), giving a maximum probability of 0.681 — comfortably above threshold, and confidently wrong. The threshold cannot distinguish "unknown but far from everything" from "unknown but close to something." The failure mode is worst exactly where it matters most: on near-neighbours of known classes.
All three escapes share a root cause: they try to fix the model's uncertainty when the actual deficiency is its vocabulary. CLAP does not improve the calibration of a fixed head. It removes the head.
The phrase gets abused, so pin it down. Zero-shot in this paper means: the model performs a classification task with no training stage for that task at all. No fine-tuning, no linear probe, not even one labelled example. You supply the class names as text at inference time and read off similarities.
The paper is explicit: "Zero-shot requires no training stage so there are no predefined categories." That is a stronger claim than "generalises well." It means the deployment story changes shape — a new category is a string, not a project.
| Setup | Labelled examples of the target task | Gradient steps at deployment | Can add a class at 3am? |
|---|---|---|---|
| Supervised | Thousands | Full training run | No |
| Linear probe on SSL features | Hundreds to thousands | Head only | No |
| Few-shot | 1–16 per class | Some | Sort of |
| Zero-shot (CLAP) | Zero | None | Yes — type the string |
Hold on to the honesty, though, because this lesson will not oversell. CLAP's zero-shot numbers are spectacular on sound events and close to random on keyword spotting. Chapter 6 shows every number, including the embarrassing ones, and Chapter 8 explains why they are exactly the numbers you should have predicted from the training data.
The locked menu is not a mistake anyone made. It is the accumulated shape of two decades of sensible decisions, and knowing the sequence makes the break feel earned rather than arbitrary.
| Era | What supervision looked like | What it unlocked | What stayed locked |
|---|---|---|---|
| Feature engineering (1990s–2000s) | Hand-designed features (MFCCs, spectral centroid) plus a GMM or SVM per class | Speech recognition, speaker ID, first environmental sound systems | Features were fixed by a human; each task needed its own pipeline |
| Benchmark challenges (DCASE, from 2013) | Curated, labelled datasets with a fixed taxonomy per challenge | Comparable results, real progress, a research community | The taxonomy became the definition of the problem |
| Deep supervised (2015–2020) | Spectrograms as images, CNNs, softmax over C classes. AudioSet's 2M clips and 527 classes | Large-scale transfer: PANNs, YAMNet — encoders you could reuse | The reusable part was the encoder. The head was still per-task and still fixed |
| Self-supervised (2020–2022) | No labels during pretraining; masked or contrastive pretext tasks | Freedom from annotation at pretraining time | Still a fixed head at fine-tuning time. And no language at all |
| Natural-language supervision (2022–) | Audio paired with a human sentence | The head disappears; the taxonomy is chosen at inference | Only what people write about — which Chapter 6 will measure precisely |
Read down the last column. Each era freed one thing and left the interface alone. AudioSet's 527 classes made the menu bigger; self-supervision made the menu cheaper to build; neither removed it. That is the specific act CLAP performs.
The introduction states them explicitly, and it is worth holding all three separately because they are usually collapsed into one.
Contribution 2 is the one that changes how systems are built; contribution 3 is what makes 2 believable. Contribution 1 is, honestly, the least novel part — the architecture is CLIP's, and the paper says so. Novelty and importance are not the same axis, and this paper is a clean example of the gap.
Chapter 0 ended with a promise: replace the label with a sentence. That sounds like a small edit — a richer annotation format. It is not. It changes what the loss function can possibly teach, and it changes what the model can possibly be asked at inference. This chapter is about why.
Here are four real captions from CLAP's training data, sampled in the paper's own appendix table:
Now compress each into one integer from a taxonomy. "Music." "Siren." "Bird." "Speech." Look at what evaporates.
| Information in the caption | Survives the label? | What it would have taught |
|---|---|---|
| Multiple sources present at once | No (single label) | Scene composition, source separation priors |
| Temporal order ("before violins join") | No | That sound has structure in time, not just identity |
| Counting ("honked twice", "a child") | No | Repetition, cardinality |
| Distance / perspective ("nearby", "far away") | No | Loudness and reverberation carry spatial meaning |
| Manner ("wailing", "repeatedly", "flying down and landing") | No | Fine-grained acoustic dynamics |
| The recording gear ("Olympus LS-14") | No | Nothing useful — genuine caption noise, and CLAP eats it anyway |
| Category identity | Yes | Exactly the one bit of the taxonomy |
This is worth slowing down for, because it is the load-bearing idea of the whole paradigm. Take the one-hot targets for three classes:
Compute the distance between any two: the squared Euclidean distance is 2 for every pair. Dog-to-wolf: 2. Dog-to-trombone: 2. The loss function is structurally incapable of knowing that a dog and a wolf are related. If the model confuses a wolf howl for a dog bark, it is penalised exactly as hard as if it had said trombone.
Now take the caption route. "A dog barking in a yard" and "a wolf howling in the distance" are two token sequences that a language encoder maps to two vectors that are genuinely close, because they share a, in, the animal-vocalisation frame, and the outdoor-scene frame. The target for the audio is no longer an arbitrary corner of a simplex; it is a point in a space where semantic neighbours are geometric neighbours.
That is the entire trick. The text encoder donates its geometry to the audio encoder. Everything else — the contrastive loss, the projections, the temperature — is plumbing to make the donation happen.
The same clip, supervised two ways. Click any word in the caption to see which acoustic evidence it commits the model to explain. Toggle to label mode and watch the evidence collapse to a single point — and watch the target geometry collapse with it.
The paper did not commission a new dataset. It assembled one out of four public corpora, and the assembly is itself instructive:
| Dataset | Pairs | Unique audios | Unique captions | How the text was obtained |
|---|---|---|---|---|
| FSD50K | 36,796 | 36,796 | 36,796 | Title + description from Freesound metadata, concatenated. The class label was deliberately ignored. |
| ClothoV2 | 29,646 | 5,929 | 29,646 | 5 human captions per clip → 5 pairs per clip |
| AudioCaps | 44,292 | 44,292 | 44,292 | 1 crowdsourced caption per 10-second AudioSet clip |
| MACS | 17,276 | 3,930 | 17,276 | Multiple annotators per clip → multiple pairs per clip |
| Total | 128,010 | 90,947 | 128,010 | Four datasets, one shared idea |
Two details in that table are easy to skim past and both are load-bearing.
Pairs exceed unique audios by 41%. 128,010 pairs over 90,947 clips. ClothoV2 contributes five captions for each of 5,929 clips; MACS multiplies similarly. So the same audio appears repeatedly with different descriptions. That is free regularisation: the model is being told, five ways, that one waveform has many valid verbalisations. It cannot latch onto one phrasing. It is nudged toward the meaning that all five share.
FSD50K's class label was thrown away on purpose. FSD50K ships with clean sound-event labels — the paper's own Table 1 evaluates on them. And the authors chose the messy human-written title and description instead. That is a bet: that noisy natural language beats clean categories as a training signal. Chapter 8 will show you the one place that bet failed spectacularly.
"Captions are richer" deserves an actual number rather than a gesture. Information theory gives one.
A label drawn uniformly from C categories carries log2(C) bits. For ESC50's fifty classes that is 5.64 bits. For FSD50K's 200 classes, 7.64 bits — and even that is optimistic, because real label distributions are skewed, which lowers the entropy further.
A caption is a sequence of words. If you model English at roughly 8–10 bits per word for content-bearing text (Shannon's classic estimate for English is about 1 bit per character after redundancy, so a 5-letter word plus a space runs about 6 bits, and audio captions are less predictable than newspaper prose), then a 15-word caption carries on the order of a hundred bits. Two orders of magnitude more than the label.
| Supervision | Rough information content | Structured? |
|---|---|---|
| One-hot over 50 classes | ≈ 5.6 bits | No — all pairs equidistant |
| One-hot over 527 classes (AudioSet) | ≈ 9.0 bits | Ontology helps, but is hand-built and finite |
| Multi-label over 200 classes | up to 200 bits, in practice a handful | Slightly — co-occurrence carries some structure |
| A 15-word caption | on the order of 100 bits | Yes — a pretrained language geometry comes free with it |
But the bit count is the less important half. The label's 5.6 bits are unstructured: they identify a corner of a simplex and say nothing about which corners are near which. The caption's hundred bits arrive already embedded in a space where synonyms are close, negations are far, and compositional phrases land between their parts. That structure is what CLAP is really harvesting.
python — constructing CLAP's 128,010 pairspairs = [] # FSD50K: 36,796 clips. The class label is available and DELIBERATELY IGNORED. for clip in fsd50k_train_and_val: caption = clip.title + ". " + clip.description # raw Freesound metadata pairs.append((clip.audio, caption)) # 1 pair per clip # ClothoV2: 5,929 clips, each with 5 human captions -> 5 pairs each. for clip in clotho_v2: for caption in clip.captions: # len == 5 pairs.append((clip.audio, caption)) # 29,646 total # AudioCaps: 44,292 ten-second AudioSet clips, 1 crowdsourced caption each. # MACS: 3,930 clips captioned by multiple annotators -> 17,276 pairs. len(pairs) # 128,010 len({p[0] for p in pairs}) # 90,947 unique clips <- 41% more pairs than audio len({p[1] for p in pairs}) # 128,010 unique captions <- every caption is distinct
Look at the last two lines together. Every caption is unique; the clips are not. That asymmetry is a design decision with a consequence we will meet again in Chapter 8: at large batch sizes, two entries in the same batch can be the same audio with different captions, and the contrastive loss will insist they are negatives of each other.
Two audio models had already borrowed CLIP's machinery by 2022, and understanding why CLAP is not just a third one clarifies the contribution.
| Model | Text signal it learns from | Relationship to CLIP | Consequence |
|---|---|---|---|
| Wav2CLIP | AudioSet class labels, routed through CLIP's frozen text encoder | Distils from CLIP; audio is aligned to CLIP's existing image-text space | Inherits CLIP's vocabulary but is trained on label-shaped text |
| AudioCLIP | AudioSet class labels, in a tri-modal image/text/audio setup | Extends CLIP with a third tower | Same: the supervision is a taxonomy wearing a sentence's clothes |
| CLAP | Free-form human captions describing what the clip sounds like | Same recipe as CLIP, trained from scratch on audio-text | Learns the full distribution of ways people describe sound |
The paper is measured about the prior work — "the performance was promising" — but names the gap precisely: those models are "trained with audio and class labels from AudioSet instead of audio and natural language," so "it is yet to be explored how natural language can benefit flexibility and generalization to new classes and tasks."
Why does training on label-text hurt? Because of a distribution mismatch that will come back in Chapter 5. If every training text is a bare category name, the text encoder's useful region is a small island of noun phrases. Ask it about "a small dog barking indoors" and you are off the island. If instead the training text is real sentences, the model has seen the whole continent, and the class names at inference are the odd case — which, delightfully, is exactly the problem the prompt templates of Chapter 5 exist to fix.
The phrase is loose enough to cover several different things, and CLAP means exactly one of them. Pin the boundaries.
| Not this | Why it is different |
|---|---|
| Text as an auxiliary task — predict a caption from audio with a decoder | That is audio captioning: a generative objective producing one output. CLAP's objective is discriminative and produces a space. Captioning models cannot rank arbitrary candidate strings cheaply |
| Labels written as words — "dog_barking" turned into "dog barking" | The vocabulary is still a closed taxonomy. This is Wav2CLIP and AudioCLIP, and Chapter 6 shows the 12-point gap it leaves on the table |
| Text-conditioned generation — produce audio from a prompt | The opposite direction. It consumes a joint space rather than building one — and, as Chapter 9 notes, several such systems consume CLAP's |
| Instruction tuning on audio — answer questions about a clip | Requires a language model, not a language encoder. That arrives two years later and is built on top of this |
| This: pair a clip with a free-form description and learn a shared space contrastively | Discriminative, dual-encoder, and the description is whatever a human happened to write |
The narrowness is the point. CLAP does one thing: it makes audio and its description land in the same place. Everything in the rest of this lesson is a consequence of that single commitment.
Take the paper's own samples and ask, of each, what the model is being asked to solve.
| Caption | The task it silently sets |
|---|---|
| "An insect buzzing in the foreground as birds chirp in the background" | Figure-ground separation. The model must represent two simultaneous sources and their relative prominence — from a single pooled vector |
| "Church bells chime loudly and repeatedly" | Intensity and periodicity. "Loudly" is an absolute-level claim the log compression partly erases; "repeatedly" is a temporal-structure claim clip-level pooling erases entirely |
| "Water dripping in a cave or underground temple" | Reverberation as a scene cue. The source is a drip; the room is inferred from the tail. This is the acoustic-scene skill that gives TUT2017 its 25% headroom in Chapter 6 |
| "Beating drum getting faster than children voices and clapping then adult male voice" | Three sources, a tempo change, and an ordering. Roughly half of this is unlearnable for a bag-of-events model — and it is still better supervision than the label "drum" |
Two of the four demand things the architecture provably cannot represent. That is not a flaw in the data; it is how the ceiling gets set. The model extracts what it can and treats the rest as noise, which is exactly why the resulting embedding is a good event detector and a poor sequence model.
Before the plumbing chapters, here is the whole model in a sentence you should be able to reconstruct by Chapter 4: encode the audio, encode the caption, project both into one shared space, and train so that each clip's own caption is the nearest text in the batch and every other caption is far.
Chapter 1 gave us pairs: a clip and a sentence. The sentence is easy — text encoders eat text. The clip is not. A microphone hands you a list of numbers describing air pressure over time, and a convolutional network wants a two-dimensional array with meaningful local structure. Getting from one to the other is a sequence of deliberate, lossy decisions, and the paper specifies every one of them in three lines of Section 3.2. This chapter unpacks those three lines completely, because if you cannot state the shape of the tensor entering the audio encoder, you do not understand the model.
Here is the paper's own specification, verbatim in substance: log Mel spectrogram, sampling rate 44.1 kHz, hop size 320, window size 1024, 64 Mel bins in the range 50–8000 Hz, each clip randomly truncated to a continuous 5-second segment or padded if shorter.
At 44.1 kHz, five seconds of audio is 5 × 44,100 = 220,500 floating-point numbers. That is a one-dimensional array. You could hand it straight to a network — some models do — but for a convolutional encoder it is a bad substrate for three reasons.
The features are non-local in time. A 440 Hz tone is a pattern that repeats every 100 samples. To notice it, a filter needs a receptive field spanning hundreds of samples. To notice a 50 Hz rumble it needs 880. Pitch information lives at scales that a small convolution kernel cannot see.
Phase is mostly nuisance. Shift the whole waveform by one sample and every number changes, while the sound is perceptually identical. You would be asking the network to learn shift-invariance from scratch, using capacity you would rather spend elsewhere.
It is enormous. 220,500 numbers per clip, times a batch, times 40 epochs.
So we transform: chop time into overlapping frames, take the frequency content of each frame, and stack the results into an image-like array where one axis is time and the other is frequency. Convolutions are good at images. That array is the spectrogram.
The window length is how many consecutive samples go into one frequency analysis; the hop is how far you slide before the next one. CLAP uses 1024 and 320.
| Quantity | In samples | In time at 44.1 kHz | Why this value |
|---|---|---|---|
| Window | 1024 | 23.22 ms | Long enough that a periodic sound completes several cycles (a 100 Hz tone fits 2.3 cycles); short enough that the sound does not change much within it — the "locally stationary" assumption |
| Hop | 320 | 7.26 ms | Fine enough to place a transient (a click, a consonant, a drum hit) to within about 7 ms; coarse enough to keep the tensor small |
| Overlap | 704 | 15.96 ms | 68.75% overlap. Neighbouring frames share most of their samples, so an event straddling a boundary is never lost |
| Frames in 5 s | 1 + ⌊220,500 / 320⌋ = 690 | This is T, the time axis of the tensor | |
Notice the tension the two numbers resolve. Make the window longer and you get sharper frequency resolution but you smear transients — a sudden bark becomes a blur. Make it shorter and onsets get crisp but pitch turns to mush. This is the time-frequency uncertainty tradeoff, and 23 ms is the conventional compromise for general audio.
Then hop is chosen separately and smaller, which is the part beginners often miss. Overlap does not improve the frequency resolution of any single frame — that is set by the window. What it buys is temporal sampling density: the analysis is re-run every 7.26 ms so that whatever the window sees, it sees at many alignments.
Each 1024-sample frame goes through a discrete Fourier transform, yielding 1024 complex coefficients. Real signals have conjugate-symmetric spectra, so only 1024/2 + 1 = 513 of them are independent. Take their magnitudes and discard phase.
Each of those 513 bins covers 44,100 / 1024 = 43.07 Hz, uniformly. That uniformity is the problem. Uniform spacing means bin 1 (43 Hz) to bin 2 (86 Hz) is one octave — a gigantic perceptual leap — while bin 400 (17,226 Hz) to bin 401 (17,269 Hz) is a difference no human can hear. The representation spends most of its dimensions on distinctions nobody perceives.
The Mel scale is an empirical mapping from frequency in hertz to perceived pitch, built so that equal steps on the Mel axis sound like equal pitch steps. The standard form:
Let us actually run CLAP's numbers rather than admire the formula. The band is 50 to 8000 Hz with 64 filters.
| Step | Computation | Result |
|---|---|---|
| Low edge in Mel | 2595 · log10(1 + 50/700) = 2595 · log10(1.0714) | 77.75 mel |
| High edge in Mel | 2595 · log10(1 + 8000/700) = 2595 · log10(12.4286) | 2840.02 mel |
| Span | 2840.02 − 77.75 | 2762.27 mel |
| Spacing (64 filters need 66 edge points, so 65 gaps) | 2762.27 / 65 | 42.50 mel per step |
| First filter, in hertz | edges at 50.0 and 108.7 Hz | 58.7 Hz wide |
| Last filter, in hertz | edges at 7368.0 and 8000.0 Hz | 632.0 Hz wide |
The last filter is 10.8 times wider than the first. That single ratio is the entire content of the Mel warp: fine resolution where hearing is fine, coarse where it is coarse. Each of the 64 outputs is a weighted sum of the 513 FFT magnitudes, with triangular weights over that filter's band. 513 numbers become 64, and the ones we kept are aligned with perception.
Mel energies span an enormous dynamic range: a loud transient can be a million times the energy of a quiet background. Feed that to a network and the loud bins dominate every gradient. So take the logarithm.
Two things happen, and both are useful. First, dynamic range compression: six orders of magnitude become a span of about 14 in natural log, comfortable for a network. Second, and more elegant, gain becomes an offset. Record the same sound twice as loud and every energy is multiplied by 4; in the log domain every value gains a constant log 4. A network with any translation tolerance — or a batch-norm layer — can shrug that off. Volume stops being a confound.
This also mirrors hearing: perceived loudness is roughly logarithmic in intensity, which is exactly why we measure it in decibels.
This is the paper's own notation from Section 2.1: "Let the processed audio be Xa such that Xa ∈ RF×T where F are the number of spectral components (e.g. Mel bins) and T are the number of time bins." Now you can fill in both letters: F = 64, T = 690.
That is 44,160 floats, down from 220,500 raw samples — a 5.0× compression, and every surviving number is perceptually meaningful. In a batch of N pairs the audio tensor is (N, 64, 690), or (N, 1, 64, 690) once you add the channel axis a 2-D convolution expects.
Top: the pressure wave. Bottom: the log-Mel spectrogram CLAP's encoder actually sees. Change the window, hop, and Mel-bin count and watch the tensor shape recompute live. The paper's settings are marked. Push the window down to 256 and watch pitch structure dissolve into vertical smears; push it to 4096 and watch a transient spread across time.
"Longer window, sharper frequency, blurrier time" is easy to say and worth quantifying, because the numbers explain why 1024 and not 256 or 4096.
| Window (samples) | Duration | FFT bin width | Frames in 5 s at hop 320 | What it does well / badly |
|---|---|---|---|---|
| 256 | 5.8 ms | 172.3 Hz | 690 | Onsets are razor sharp. Pitch is unusable — a 172 Hz bin cannot separate a bass note from the one above it |
| 512 | 11.6 ms | 86.1 Hz | 690 | Common for speech, where phonemes are short |
| 1024 | 23.2 ms | 43.07 Hz | 690 | CLAP's choice. Resolves musical pitch above ~90 Hz while keeping events crisp |
| 2048 | 46.4 ms | 21.5 Hz | 690 | Beautiful harmonics; a handclap smears across two frames |
| 4096 | 92.9 ms | 10.8 Hz | 690 | Music-analysis territory. Environmental transients are destroyed |
Two things to notice in that table. First, the frame count does not change with the window — it is set entirely by the hop. Window and hop are genuinely independent knobs, controlling resolution and sampling density respectively. Second, the product of duration and bin width is constant at about 1.0: 5.8 ms × 172.3 Hz ≈ 1, 92.9 ms × 10.8 Hz ≈ 1. That is the time-frequency uncertainty principle, and no amount of engineering escapes it. You can only choose where to spend it.
The filterbank is often treated as a black box. It is not; it is 64 triangles. Build filter 0 by hand.
Its three edge frequencies, from the Mel spacing computed above, are 50.0, 78.8, and 108.7 Hz. Convert each to an FFT bin index by dividing by the 43.07 Hz bin width:
The triangle rises from bin 1 to its peak near bin 1.83 and falls to bin 2.52. With only two or three FFT bins underneath it, the weights come out roughly 0.63 on bin 1 and 0.37 on bin 2. Compare filter 63, spanning 7368 to 8000 Hz: bins 171 through 186, sixteen bins wide, with a smooth triangular ramp over all of them.
That contrast is the Mel warp made concrete. Low filters are starved — barely more than one FFT bin each — while high filters average sixteen. If you wanted better low-frequency resolution you would need a longer window, and you would pay for it in time smearing. The filterbank does not create resolution; it allocates the resolution the FFT gave you.
Learned front ends exist and work — SincNet, LEAF, and the convolutional stems of wav2vec-style models all take samples directly. So the log-Mel choice is a decision, not a law. Weigh it honestly:
| Fixed log-Mel (CLAP) | Learned front end | |
|---|---|---|
| Parameters | Zero — the filterbank is a constant matrix | Thousands to millions, trained |
| Data needed | None — it encodes psychoacoustics for free | Substantial; the front end must be learned from scratch |
| Inductive bias | Strong and usually correct: log frequency, log amplitude, phase discarded | Weak — can discover better filters, or waste capacity rediscovering the Mel scale |
| Transferability | Total — every audio model speaks log-Mel | Tied to the model it was trained with |
| Ceiling | Capped by what the filterbank preserves — and it discards phase and everything above 8 kHz | Higher, given enough data |
With 128,010 pairs, the strong prior wins. CLAP has nowhere near enough data to learn a front end; it barely has enough to learn an alignment. The log-Mel front end is 44,160 free, perceptually-organised numbers, and that is exactly the deal a small-data project should take.
First by hand, on one frame, so nothing is magic:
the arithmetic for ONE frame# frame 0 = samples 0..1023 of a 220,500-sample clip x = signal[0:1024] # 1024 floats x = x * hann_window # taper the edges so the FFT sees no cliff X = fft(x) # 1024 complex numbers mag = abs(X[0:513]) # keep the non-redundant half, drop phase power = mag ** 2 # 513 energies, one per 43.07 Hz bin # mel filter 0 covers 50.0 -> 108.7 Hz, i.e. FFT bins 1..2 (43.07 Hz each) # with triangular weights peaking at 78.8 Hz: mel[0] = 0.63*power[1] + 0.37*power[2] # mel filter 63 covers 7368 -> 8000 Hz, i.e. FFT bins 171..186 -- 16 bins wide mel[63] = sum(w[i] * power[i] for i in range(171, 187)) logmel = np.log(mel + 1e-10) # epsilon so log(0) is finite # repeat for frames 1..689, hopping 320 samples each time -> (64, 690)
Then the honest loop, which is what the hand version generalises to:
python — numpy, from scratchimport numpy as np SR, WIN, HOP, N_MEL, FMIN, FMAX = 44100, 1024, 320, 64, 50, 8000 def hz_to_mel(f): return 2595 * np.log10(1 + f / 700) def mel_to_hz(m): return 700 * (10 ** (m / 2595) - 1) def mel_filterbank(): edges = np.linspace(hz_to_mel(FMIN), hz_to_mel(FMAX), N_MEL + 2) # 66 points hz = mel_to_hz(edges) # 50.0 ... 8000.0 bins = np.floor((WIN + 1) * hz / SR).astype(int) fb = np.zeros((N_MEL, WIN // 2 + 1)) # (64, 513) for m in range(N_MEL): l, c, r = bins[m], bins[m + 1], bins[m + 2] fb[m, l:c] = np.linspace(0, 1, c - l) # triangle up fb[m, c:r] = np.linspace(1, 0, r - c) # triangle down return fb def log_mel(sig): # sig: (220500,) float32 win = np.hanning(WIN) n = 1 + (len(sig) - WIN) // HOP S = np.stack([np.abs(np.fft.rfft(sig[i*HOP : i*HOP+WIN] * win)) ** 2 for i in range(n)], axis=1) # (513, T) return np.log(mel_filterbank() @ S + 1e-10) # (64, T)
And the one-liner you would actually ship:
python — torchaudio, the one-linerimport torchaudio.transforms as T front_end = torch.nn.Sequential( T.MelSpectrogram(sample_rate=44100, n_fft=1024, win_length=1024, hop_length=320, f_min=50, f_max=8000, n_mels=64), T.AmplitudeToDB()) X_a = front_end(waveform) # (B, 64, 690) <- the paper's X_a, exactly
"During training, each audio clip is randomly truncated to a continuous segment of 5 secs, or padded if shorter." Two clauses, two consequences.
Random truncation is augmentation and it is also label noise. ClothoV2 clips run 15 to 30 seconds and their captions describe the whole clip. Take a random 5-second window of "a bow playing a stringed instrument in a one note tone repeatedly before violins join to create the melody" and there is a good chance your window contains only the bow, or only the violins. The caption is then partly wrong for the crop you are training on. Over 40 epochs the crop re-randomises, so on average the model sees every part of the clip paired with the full caption — the noise averages toward a weak-labelling signal rather than a systematic bias. But the per-step gradient is noisier than the pairing suggests, and this is one reason a large batch matters (Chapter 8).
Padding is worse than it looks, on exactly the tasks that failed. The paper never mentions this, but run the arithmetic on the downstream datasets and a pattern jumps out:
| Task dataset | Clip duration | Fraction of the 5 s input that is real audio | CLAP zero-shot |
|---|---|---|---|
| ESC50 | 5 s | 100% | 0.826 |
| UrbanSound8K | up to 4 s | 80% or less | 0.7324 |
| Vocal Sound | 5 s | 100% | 0.4945 |
| Mridangam Stroke / Tonic | 0.81 s | 16% | 0.3447 / 0.1965 |
| Speech Commands V2 | 1 s | 20% | 0.1063 (random is 0.083) |
This is a correlation on a handful of points, not a proof, and Chapter 8 will give a much stronger explanation for the speech failures — the training captions simply do not describe speech content. But note the mechanism, because it is real: a CNN14 that global-pools over the time axis is averaging over 690 frames of which 552 are silence for a Speech Commands clip. Whatever evidence exists is diluted five-fold before the classifier ever sees it.
We have Xa ∈ R64×690 and a caption string. Section 2.1 of the paper compresses the next stage into two equations. This chapter expands them into every tensor shape, every parameter count, and every design decision behind them — because "two encoders and two projections" is a diagram, and a diagram is not understanding.
Where, in the paper's words, X̂a ∈ RN×V are the audio representations of dimensionality V, and X̂t ∈ RN×U the text representations of dimensionality U. N is the batch size. From Section 3.2 we can name V and U: V = 2048 and U = 768.
The audio encoder fa is CNN14, taken from PANNs (reference [23] in the paper). The paper gives its vital statistics: 80.8 million parameters, embedding size 2048, and — the important part — "pretrained with 2M audio clips from AudioSet."
Read that last clause slowly. CLAP does not learn to hear from 128k captioned clips. It starts from an encoder that already learned to hear from two million labelled AudioSet clips, and then re-purposes that ear to speak to language. The 128k pairs are not building an audio front end; they are building a bridge. This is why 128k pairs is enough where CLIP needed 400M — CLIP's vision tower was trained from scratch.
The paper also states its own reason for the choice: CNN14 was picked "to provide a fair comparison to previous SoTA models." PANNs, Wav2CLIP, and AudioCLIP all use it, so any difference in the results is attributable to the training signal, not to a fancier encoder. That is good experimental hygiene and worth copying.
CNN14's internals come from the PANNs paper rather than this one, but they matter for tracing shapes. It is six convolutional blocks; each block is two 3×3 convolutions with batch norm and ReLU, followed by 2×2 average pooling. Channels double each block. Then a global pooling over what remains, and a fully-connected layer to the 2048-dim embedding:
| Stage | Tensor shape (one clip) | What just happened |
|---|---|---|
| Input log-Mel | (1, 64, 690) | Chapter 2's tensor, with a channel axis |
| Block 1 (64 ch) | (64, 32, 345) | Local time-frequency edges; both axes halved |
| Block 2 (128 ch) | (128, 16, 172) | Onsets, harmonic stacks |
| Block 3 (256 ch) | (256, 8, 86) | Textures spanning ~60 ms |
| Block 4 (512 ch) | (512, 4, 43) | Event-scale patterns |
| Block 5 (1024 ch) | (1024, 2, 21) | Frequency axis nearly collapsed |
| Block 6 (2048 ch) | (2048, 1, 10) | Ten coarse time steps, 2048 feature maps |
| Global pooling + FC | (2048,) | One vector for the whole clip. This is X̂a |
Sit with the last row. Every temporal detail — "the horn honked twice", "the violins joined after the bow" — is pooled into a single 2048-vector. A clip-level embedding cannot, in principle, distinguish "dog then siren" from "siren then dog". CLAP is a bag-of-events model. That limitation is invisible in the ESC50 number and very visible if you ever try to use CLAP for temporal localisation, and it is precisely what later work (Chapter 9) attacks.
The text encoder ft is BERT base uncased via HuggingFace: 110 million parameters. Two implementation details from Section 3.2 deserve attention.
"We limited the max text sequence length to 100 chars for computational efficiency." Not 100 tokens — 100 characters. Roughly 20 English words. Check that against the paper's own sample captions in Table 7 and you find genuine casualties: "A bow playing a stringed instrument in a one note tone repeatedly before violins join to create the melody" is 106 characters, so BERT never sees "the melody". "Leaves falling in a forest near a pond. Recorded in October 2017 in a German forest using a Zoom H2n." is 101 characters. Two of the twelve sampled captions get clipped — about 17%. In those cases the tail of the sentence, which is often where the second sound source appears, is discarded.
"The [CLS] token from the final layer of BERT is used as the text embedding with a size of 768." BERT prepends a special [CLS] token to every input; its final-layer vector is conventionally used as a whole-sequence summary, because self-attention lets it read every other token. So a 20-word caption collapses to one 768-dim vector, by exactly the same logic that collapsed 690 frames to one audio vector. Both towers are summarisers.
There is another way to compare audio and text: let them attend to each other. Feed the 690 audio frames and the 20 text tokens into a joint transformer and let every token look at every frame. That is a cross-attention or fusion model, and it is strictly more expressive. CLAP does not do it. Understanding why is understanding the family.
| Dual encoder (CLAP, CLIP) | Cross-attention fusion | |
|---|---|---|
| Comparing one clip to C classes | 1 audio pass + C text passes (cached) + C dot products | C full joint forward passes — nothing can be cached |
| ESC50 zero-shot on 2,000 clips, 50 classes | 2,000 audio passes + 50 text passes, once | 100,000 joint passes |
| Can precompute a searchable index? | Yes — embeddings are independent of the query | No — the score exists only for a specific pair |
| Expressiveness | Limited to what one vector per modality can carry | Can model fine-grained token-to-frame correspondence |
| Training signal per batch | N2 comparisons from 2N forward passes | N comparisons from N forward passes — no free negatives |
The last row is decisive and often overlooked. The contrastive trick of Chapter 4 — getting N2−N negatives for free — only works because the two towers are independent. If scoring a pair required a joint forward pass, computing the full similarity matrix would cost N2 passes instead of 2N, and a batch of 768 would need 589,824 of them. Contrastive pretraining at scale is not merely compatible with the dual-encoder design; it is enabled by it.
Equation (2) is the entire "joint multimodal space." Two learnable linear maps, both landing in d = 1024:
| Map | Shape | Weight parameters | Role |
|---|---|---|---|
| La | 2048 → 1024 | 2,097,152 | Compress the audio embedding into the shared space |
| Lt | 768 → 1024 | 786,432 | Expand the text embedding into the shared space |
| Projection total | 2,883,584 (2.88M) | 1.49% of the model's ~193.7M parameters | |
| Encoders | 190.8M (80.8 + 110) | The other 98.5% | |
Two questions worth asking here, both of which the paper leaves implicit.
Why linear and not an MLP? A linear map can rotate, scale, shear, and project — it can re-express the encoder's geometry in new coordinates, but it cannot fold or tear it. Neighbours stay neighbours. That is exactly what you want when your goal is to align two existing geometries rather than invent a new one. An MLP could align the training pairs more tightly while distorting the space in ways that do not generalise to unseen class names — and unseen class names are the entire product.
Why does text go up (768 → 1024) while audio comes down (2048 → 1024)? Because d is a choice about the shared space, not about either tower. Meeting in the middle keeps both projections cheap. Note the asymmetry it creates: La must discard information (2048 dims into 1024), while Lt cannot possibly add any (a 768-dim input can only ever span a 768-dim subspace of the 1024-dim output). The text embeddings live on a lower-dimensional sheet inside the shared space, and the audio embeddings must learn to land on that sheet.
Click any stage to see what enters, what leaves, and what it costs in parameters. The freeze toggles show which parts receive gradient in each of the paper's four training regimes — blue means gradient flows, grey means the weights are locked. Watch the parameter counter at the bottom.
python — the whole architectureimport torch, torch.nn as nn from transformers import AutoModel, AutoTokenizer class CLAP(nn.Module): def __init__(self, d=1024): super().__init__() self.audio_enc = cnn14_pretrained_on_audioset() # 80.8M, out 2048 self.text_enc = AutoModel.from_pretrained("bert-base-uncased") # 110M, out 768 self.tok = AutoTokenizer.from_pretrained("bert-base-uncased") self.La = nn.Linear(2048, d) # 2,097,152 weights self.Lt = nn.Linear(768, d) # 786,432 weights # temperature: stored in log space so it stays positive under SGD self.logit_scale = nn.Parameter(torch.tensor(np.log(1 / 0.007))) def encode_audio(self, x): # x: (N, 64, 690) log-mel h = self.audio_enc(x) # (N, 2048) <- X_hat_a e = self.La(h) # (N, 1024) <- E_a return e / e.norm(dim=-1, keepdim=True) # unit sphere def encode_text(self, captions): # list[str], truncated to 100 chars b = self.tok([c[:100] for c in captions], padding=True, return_tensors="pt") h = self.text_enc(**b).last_hidden_state[:, 0] # [CLS] -> (N, 768) e = self.Lt(h) # (N, 1024) <- E_t return e / e.norm(dim=-1, keepdim=True)
Note the norm calls. The paper's Equation (3) writes a bare dot product, but a dot product between unnormalised vectors mixes "do these mean the same thing" with "how long are these vectors" — and vector length has no semantics here. Normalising to the unit sphere makes every entry of the similarity matrix a cosine in [−1, 1], which is what makes a single global temperature meaningful. Chapter 4 shows exactly why that matters.
Equation (3) writes a bare dot product. Every implementation normalises first. Here is the argument, in three steps.
Write the dot product in polar form: Et · Ea = ‖Et‖ · ‖Ea‖ · cosθ. Only the angle carries semantics — two embeddings mean the same thing when they point the same way, regardless of length. The two magnitudes are free parameters the model can inflate at will.
Now watch what the loss does with that freedom. The loss wants the diagonal large relative to its row. Doubling ‖Ea,i‖ doubles every entry in column i — useless for a column softmax, but it inflates the diagonal in row i relative to that row's other entries, which are unchanged. So the cheapest way to lower the loss is not to learn better semantics; it is to make the vectors of easy examples longer. The model discovers a shortcut, and length starts encoding confidence instead of meaning.
Normalising kills the shortcut. Every embedding sits on the unit sphere, every entry of C is exactly a cosine in [−1, 1], and the only way to raise the diagonal is to rotate. Two further benefits fall out: the temperature is now comparable across the whole matrix (a single τ is meaningful only if the raw scale is fixed), and cosine similarity at inference is just a dot product, so zero-shot needs no per-query normalisation.
A model is defined as much by its behaviour on bad inputs as on good ones. Trace each degradation through the shapes.
| Degraded input | What the towers produce | What zero-shot then does |
|---|---|---|
| Silence (a 5-second zero vector) | The log-Mel is a constant floor (log of the epsilon). CNN14 still emits a well-defined 2048-vector; La still emits a unit vector | Ranks all classes by their similarity to the "silence direction". Confident nonsense — there is no null output |
| A 0.8-second clip (Mridangam) | Padded to 5 s; 84% of the 690 frames are silence. Global pooling averages the real evidence with the padding | Evidence diluted roughly 6×. Compare Mridangam Tonic's 0.1965 against a 0.1667 chance level |
| A 30-second clip (ClothoV2) | A random 5-second crop. The caption may describe events outside the crop | At training time this is label noise; at inference it means the prediction depends on which window you took |
| An empty caption | BERT sees just [CLS] and [SEP]; the [CLS] vector is the model's prior over "no content" | A single fixed direction that every clip is compared against — effectively a constant added to one column |
| A caption longer than 100 characters | Truncated. The tail — often the second sound source — is gone | Systematically biases the text embedding toward whatever is described first |
| Audio sampled at 16 kHz | Resampled to 44.1 kHz; the 8–22 kHz band is empty, but the Mel filterbank already stops at 8 kHz | Almost no effect — an accidental robustness of the low ceiling |
| Quantity | Value | Where it comes from |
|---|---|---|
| Parameters, total | ~193.7M | 80.8M + 110M + 2.88M |
| Parameters actually joining the modalities | 2.88M (1.49%) | The two projections |
| Audio tensor per clip | 64 × 690 = 44,160 floats | Chapter 2 |
| Embedding stored per clip | 1024 floats = 4 KB (fp32) | Ea |
| Similarity matrix per training step | N × N — 589,824 entries at N = 768 | Equation (3) |
| Zero-shot classifier for 50 classes | 50 × 1024 floats = 200 KB, computed once | Chapter 5 |
| Setting | Value | Comment |
|---|---|---|
| Regime | Both encoders unfrozen | The best of the four options in Table 2 |
| Epochs | 40 | Over 128,010 pairs |
| Optimiser | Adam | Reference [26] |
| Learning rate | 10−3, reduced on plateau by 10−1, patience 10 | A high LR for a 190M-parameter model — the projections need to move fast |
| Hardware | PyTorch DDP, 16GB V100, scaling from 8 to 24 GPUs | The GPU count is set by the batch size (Chapter 8) |
| Temperature τ | Learnable, initialised to 0.007 | Scaled logits clipped to a maximum of 100 |
Compare the two supervised evaluation setups the paper also runs, because they clarify what "the encoder learned" means. Supervised Feature Extraction freezes CLAP and trains 1 or 3 fully-connected layers on top (LR 10−3, 30 epochs) — that measures representation quality. Supervised Finetune unfreezes the audio encoder along with the attached layers (LR 10−4, 30 epochs) — that measures ceiling performance. The paper notes it did no hyperparameter grid search "due to computation constraints," which makes the numbers in Chapter 6 conservative rather than cherry-picked.
We have two vectors per pair: Ea from the clip, Et from its caption, both 1024-dimensional, both on the unit sphere. Nothing yet forces them to be near each other. This chapter builds the objective that does — and it builds it by hand, on a batch of three, with every intermediate number written down.
This is the load-bearing chapter of the paper. If you can reconstruct the 3×3 example below on a whiteboard, you can implement CLAP.
The naive move: maximise the similarity between each clip and its own caption.
Run gradient descent on that for five minutes and you get a model with a perfect score and zero value. It maps every clip and every caption to the same point on the sphere. Similarity 1.0 for all pairs, matched or not. This failure has a name — representational collapse — and it happens because the objective only ever says "closer", never "not that one."
The fix has to introduce a comparison. A pairing is only meaningful if it beats alternatives. So the objective must be: this caption should match this clip more than the other captions do.
The paper says exactly this, in the sentence right after Equation (3): "The similarity matrix C ∈ RN×N has N correct pairs in the diagonal and N2−N incorrect pairs in the off-diagonal."
Shapes first, always. Et is (N, d) and Ea is (N, d), so EaT is (d, N) and the product is (N, N). Entry C[i][j] is a scaled inner product between text i and audio j.
| Object | Shape | Meaning of one element |
|---|---|---|
| Et | (N, 1024) | Row i = the unit-length embedding of caption i |
| EaT | (1024, N) | Column j = the unit-length embedding of clip j |
| Et · EaT | (N, N) | Cosine similarity between caption i and clip j, in [−1, 1] |
| C | (N, N) | The same, scaled by τ — these are the logits |
| diagonal of C | (N,) | The N true pairings — what we want to be largest |
Since both E's are L2-normalised, each raw entry is a cosine: 1 means "same direction", 0 means "unrelated", −1 means "opposite". That bounded range is exactly what makes a single global temperature sensible.
Section 3.2 says: "The temperature parameter τ is learnable and initialised to 0.007. To prevent training instability, the logits scaled by τ are clipped to a maximum value of 100."
Take Equation (3) literally with τ = 0.007 and multiply your cosines by 0.007. Every logit now lies in [−0.007, 0.007]. Softmax over a batch of 768 such logits is indistinguishable from uniform: every probability is about 1/768, the loss sits at ln(768) = 6.644 forever, and the gradients are microscopic. The model cannot learn. Also, no product of a number in [−1,1] with 0.007 will ever approach 100, so the stated clipping would never fire.
Both problems vanish under the convention CLIP established and CLAP inherits: 0.007 is the temperature, and the multiplier applied to the cosines is its reciprocal, 1/0.007 = 142.86. Now the numbers all make sense at once:
| Reading of Equation (3) | Logit range for cosines in [−1, 1] | Does clipping at 100 ever fire? | Can the model learn? |
|---|---|---|---|
| multiply by 0.007 | [−0.007, 0.007] | Never | No — softmax is flat, loss pinned at ln N |
| multiply by 1/0.007 = 142.86 | [−142.86, 142.86] | Yes — exactly what it is protecting against | Yes |
This is reasoning about the paper, not a quotation from it — but the arithmetic only closes one way, and the reference implementations of both CLIP and CLAP store a learnable logit_scale initialised to log(1/0.007) and exponentiate it.
And the clip? Once the model is right — diagonal larger than every off-diagonal entry in its row — the loss decreases monotonically as the scale grows. Gradient descent notices. Left alone, the learnable scale runs away, logits explode, exp() overflows in mixed precision, and training dies. Clipping the scaled logits at 100 is a hard ceiling on that runaway. It is a numerical guardrail, not a modelling choice.
with, in the paper's notation, ℓk = (1/N) ∑i log diag(softmax(C)) taken "along text and audio axes respectively."
Unpack that in three moves.
Move 1 — softmax along an axis turns a similarity row into a choice. Take row i of C: the similarities between caption i and all N clips. Softmax it and you have a probability distribution over "which clip does this caption describe?" The right answer is clip i, the diagonal entry.
Move 2 — the diagonal is the label, so no labels are needed. Cross-entropy with a one-hot target reduces to −log(probability of the correct class). The correct class for row i is index i. The "labels" are literally [0, 1, 2, …, N−1]. That is the whole reason this is called self-supervised.
Move 3 — do it both ways. Softmax along rows answers "given this caption, which clip?" (text-to-audio retrieval). Softmax along columns answers "given this clip, which caption?" (audio-to-text retrieval). These are different questions with different answers, and optimising only one leaves the other free to be bad. Averaging the two gives the symmetric loss.
Everything above becomes concrete now. To make the arithmetic doable on paper we shrink the shared space from d = 1024 to d = 2 and the batch from N = 768 to N = 3. Nothing else changes — the equations are identical.
Our toy space has two axes: horizontal is impulsive / percussive energy, vertical is sustained / tonal energy. Three pairs:
| i | Audio clip | Its caption |
|---|---|---|
| 1 | a dog barking | "a dog barks" |
| 2 | knocking on a wooden door | "knocking on a wooden door" |
| 3 | a violin holding one note | "a violin plays a long note" |
Step 0 — normalise. The projections La and Lt output arbitrary-length vectors; we put them on the unit circle. Do one by hand so the step is not a black box. Suppose Lt emits (3.984, 0.348) for caption 1:
Doing that for all six gives our batch:
| Vector | Components | Reading |
|---|---|---|
| Ea,1 (dog audio) | (0.966, 0.259) | Strongly impulsive |
| Ea,2 (knock audio) | (0.766, 0.643) | Impulsive, some resonance from the wood |
| Ea,3 (violin audio) | (−0.174, 0.985) | Almost purely tonal |
| Et,1 ("a dog barks") | (0.996, 0.087) | Impulsive |
| Et,2 ("knocking on a wooden door") | (0.574, 0.819) | Between the two axes |
| Et,3 ("a violin plays a long note") | (−0.342, 0.940) | Tonal |
Step 1 — the raw similarity matrix. Nine dot products. Here are three of them written out in full; the rest follow identically.
All nine, with rows indexed by text and columns by audio:
| Ĉ = EtEaT | audio 1 (dog) | audio 2 (knock) | audio 3 (violin) |
|---|---|---|---|
| text 1 "a dog barks" | 0.985 | 0.819 | −0.088 |
| text 2 "knocking on a wooden door" | 0.767 | 0.966 | 0.707 |
| text 3 "a violin plays a long note" | −0.087 | 0.342 | 0.985 |
Before scaling, read the structure. The diagonal is the largest entry in every row and every column — this batch is already mostly right. But look at cell (1,2): 0.819. Caption "a dog barks" is nearly as close to the knock audio as to the dog audio. Both are impulsive. That is our hard negative, and it will dominate the loss.
Step 2 — scale to logits. The real model multiplies by about 143, which would saturate this well-behaved toy instantly. For hand arithmetic take a teaching scale of s = 4, and we will study the real value in a moment. C = 4 · Ĉ:
| C = 4 Ĉ | audio 1 | audio 2 | audio 3 |
|---|---|---|---|
| text 1 | 3.940 | 3.276 | −0.352 |
| text 2 | 3.068 | 3.864 | 2.828 |
| text 3 | −0.348 | 1.368 | 3.940 |
Step 3 — softmax along the text axis (rows). Exponentiate each entry, then divide by the row sum.
| Row | exp of each entry | Row sum | Probabilities | −log of the diagonal |
|---|---|---|---|---|
| text 1 | e3.940=51.42, e3.276=26.47, e−0.352=0.703 | 78.59 | (0.654, 0.337, 0.009) | −ln 0.654 = 0.424 |
| text 2 | e3.068=21.50, e3.864=47.66, e2.828=16.91 | 86.07 | (0.250, 0.554, 0.196) | −ln 0.554 = 0.591 |
| text 3 | e−0.348=0.706, e1.368=3.928, e3.940=51.42 | 56.05 | (0.013, 0.070, 0.917) | −ln 0.917 = 0.086 |
Step 4 — softmax along the audio axis (columns). Same matrix, same numbers, different normalisation direction. Column 1 asks: given the dog audio, which of the three captions describes it?
| Column | exp of each entry (going down) | Column sum | Probabilities | −log of the diagonal |
|---|---|---|---|---|
| audio 1 (dog) | 51.42, 21.50, 0.706 | 73.63 | (0.698, 0.292, 0.010) | −ln 0.698 = 0.359 |
| audio 2 (knock) | 26.47, 47.66, 3.928 | 78.06 | (0.339, 0.611, 0.050) | −ln 0.611 = 0.493 |
| audio 3 (violin) | 0.703, 16.91, 51.42 | 69.03 | (0.010, 0.245, 0.745) | −ln 0.745 = 0.295 |
Step 5 — the symmetric loss.
Compare to the guessing baseline ln 3 = 1.099. The batch is learning.
Take the same Ĉ and vary the scale s. Nothing about the model changes — only how sharply we read its similarities.
| Scale s | Row 1 probabilities | L | Regime |
|---|---|---|---|
| 1 | (0.457, 0.387, 0.156) | 0.790 | Mush. The correct answer barely beats the wrong ones. Gradients are weak and undiscriminating — the model is told everything is roughly fine |
| 4 | (0.654, 0.337, 0.009) | 0.375 | Learnable. Clear preference, meaningful residual pressure on the hard negative |
| 20 | (0.964, 0.036, 0.000) | 0.041 | Sharp. Only the hardest negative still matters |
| 142.86 (the paper's) | (1.000, 0.000, 0.000) | ≈ 0 | Saturated — for this toy. Real 1024-dim embeddings at initialisation have cosine gaps near 0.03, not 0.17, so s = 143 lands them squarely in the learnable regime. The scale must be read together with the dimension |
That last row is the single most common confusion about CLIP-family temperatures, so state it plainly: a temperature is not "sharp" or "soft" in the abstract. It is sharp or soft relative to the typical spread of your similarities, and that spread shrinks as 1/√d.
The full similarity matrix, live. Change the batch size to add negatives; change the scale to sharpen the softmax; switch the normalisation axis to see the two retrieval directions disagree. Then press Train and watch gradient descent rotate the toy embeddings until the diagonal wins — the diagonal brightening is CLAP learning. The N = 3 setting reproduces the hand-worked numbers above exactly.
Press Show collapse to see the failure from the top of this chapter play out: with only the diagonal in the objective, every embedding migrates to one point, the whole matrix turns bright, and the model has learned nothing. The off-diagonal is not a nuisance term. It is the entire supervision.
The loss of the fixed 3×3 batch as a function of the logit scale, with the guessing baseline ln 3 marked. Drag through the mush, the learnable band, and the saturated tail. Notice the curve is monotonically decreasing once the diagonal wins — which is why a learnable scale runs away and why the paper clips it.
Hand arithmetic first — the exact steps you just did, transcribed:
the arithmetic, for row 1 of the 3x3 batchlogits = [ 4*0.985 , 4*0.819 , 4*(-0.088) ] = [ 3.940 , 3.276 , -0.352 ] exps = [ 51.42 , 26.47 , 0.703 ] sum = 78.59 probs = [ 51.42/78.59 , 26.47/78.59 , 0.703/78.59 ] = [ 0.654 , 0.337 , 0.009 ] loss_1 = -ln(0.654) = 0.424 # the diagonal entry is index 0 for row 0 # repeat for rows 2,3 -> 0.591, 0.086 ; average -> l_text = 0.367 # repeat down the COLUMNS -> 0.359, 0.493, 0.295 ; l_audio = 0.382 # L = 0.5 * (0.367 + 0.382) = 0.375
Then step-by-step numpy, which is the same thing with loops:
python — numpy, every intermediate exposedimport numpy as np Ea = np.array([[ 0.966, 0.259], [ 0.766, 0.643], [-0.174, 0.985]]) # (3, 2) Et = np.array([[ 0.996, 0.087], [ 0.574, 0.819], [-0.342, 0.940]]) # (3, 2) Ea = Ea / np.linalg.norm(Ea, axis=1, keepdims=True) # unit sphere Et = Et / np.linalg.norm(Et, axis=1, keepdims=True) s = 4.0 # the real model uses 1/0.007 = 142.86 C = s * Et @ Ea.T # (3, 3) <- Equation (3) C = np.minimum(C, 100.0) # the paper's stability clip def softmax(x, axis): x = x - x.max(axis=axis, keepdims=True) # subtract max: exp never overflows e = np.exp(x) return e / e.sum(axis=axis, keepdims=True) P_text = softmax(C, axis=1) # each ROW sums to 1: caption -> which clip? P_audio = softmax(C, axis=0) # each COL sums to 1: clip -> which caption? l_text = -np.log(np.diag(P_text )).mean() # 0.367 l_audio = -np.log(np.diag(P_audio)).mean() # 0.382 L = 0.5 * (l_text + l_audio) # 0.375 <- Equation (4) print(L, "vs guessing baseline", np.log(len(Ea))) # 0.375 vs 1.0986
And the one-liner you would actually train with. The insight that makes it a one-liner: cross-entropy against the identity is cross-entropy against the index vector [0, 1, …, N−1].
python — PyTorch, production formimport torch.nn.functional as F logits = model.logit_scale.exp().clamp(max=100) * E_t @ E_a.T # (N, N) targets = torch.arange(len(logits), device=logits.device) # [0,1,...,N-1] loss = 0.5 * (F.cross_entropy(logits, targets) + # rows: text -> audio F.cross_entropy(logits.T, targets)) # cols: audio -> text
Three lines. logits.T is the entire content of "symmetric". torch.arange is the entire content of "no labels needed".
Differentiate the row term for pair i and the structure is beautifully simple. Writing pij for the softmax probability that caption i matches clip j:
Read that as a tug-of-war. The first term pulls caption i toward its own clip. The second pushes it away from a probability-weighted average of all clips in the batch — so the clips the model currently finds confusable pull hardest. In our batch, p12 = 0.337, so the knock audio exerts a third of the repulsion budget, while the violin at p13 = 0.009 exerts almost none.
Two consequences follow immediately, and both reappear in Chapter 8. Larger batches mean more candidates in that weighted average, so a harder and more informative push. And the temperature multiplies the whole gradient by s, which is a second, sneakier reason the learnable scale wants to grow.
| Failure | What you see in the logs | Cause and fix |
|---|---|---|
| Loss pinned at ln N | Flat at 6.644 for batch 768, from step one, forever | The temperature is being multiplied rather than inverted — logits are in [−0.007, 0.007] and softmax is uniform. Use 1/τ |
| Loss collapses to zero in the first epoch | L → 0.001, but zero-shot accuracy stays at chance | A shortcut: the batch is sorted, or all clips in a batch come from one dataset with a distinguishing artefact. Shuffle across sources |
| NaN after a few hundred steps | Loss becomes NaN; the learnable scale has grown without bound | Exactly what the clip at 100 exists to prevent. Clamp the scaled logits, and clamp logit_scale itself |
| ℓtext and ℓaudio diverge badly | One term keeps dropping, the other stalls | Usually duplicate items on one side — the same caption or the same audio appearing twice in a batch, making one direction unsatisfiable. Deduplicate when sampling (Chapter 8) |
Training is over. You have two encoders and two projections. You have no classifier. Someone hands you 2,000 five-second clips and asks you to sort them into fifty environmental categories — categories that appear nowhere in your training objective.
Section 2.2 of the paper answers in three sentences, and this chapter is the unpacking of those three sentences into something you could implement in an afternoon.
Return to Chapter 0's prison. A classifier's last layer is z = W h with W ∈ RC×D, one learned row per class. Now write CLAP's zero-shot computation the same way:
These are the same equation. In both cases a matrix of shape (C, d) multiplies a vector of shape (d,) to give C logits. The only difference — and it is the whole paper — is where the matrix comes from.
| Fixed-head classifier | CLAP zero-shot | |
|---|---|---|
| The classifier matrix | W: learned parameters | Etext: the output of a function applied to strings |
| To add a class | Add a row, get labelled data, retrain | Run one string through BERT and the text projection |
| Cost of a new class | Days to weeks | Milliseconds |
| Class geometry | Arbitrary; learned independently | Inherited from language — "wolf howling" lands near "dog barking" |
| Who chooses the taxonomy | Whoever labelled the training set | Whoever types the strings, at inference |
Same 2-D toy space as Chapter 4 (horizontal = impulsive, vertical = tonal). One test clip — a dog barking, embedded at Ea = (0.966, 0.259). Four candidate classes, each turned into a prompted caption and embedded:
| Class | Prompted text | Etext row | cosine with Ea |
|---|---|---|---|
| dog | "this is a sound of a dog barking" | (0.985, 0.174) | (0.985)(0.966)+(0.174)(0.259) = 0.996 |
| rooster | "this is a sound of a rooster crowing" | (0.707, 0.707) | (0.707)(0.966)+(0.707)(0.259) = 0.866 |
| rain | "this is a sound of rain falling" | (0.174, 0.985) | (0.174)(0.966)+(0.985)(0.259) = 0.423 |
| violin | "this is a sound of a violin playing" | (−0.342, 0.940) | (−0.342)(0.966)+(0.940)(0.259) = −0.087 |
Multiply by the logit scale (s = 4 again, for readable arithmetic) and softmax:
| Class | logit = 4 × cosine | exp(logit) | probability |
|---|---|---|---|
| dog | 3.985 | 53.77 | 0.585 |
| rooster | 3.464 | 31.95 | 0.348 |
| rain | 1.690 | 5.42 | 0.059 |
| violin | −0.349 | 0.71 | 0.008 |
| sum of exps | 91.85 | 1.000 | |
Prediction: dog, 58.5% confident. And look at the runner-up: rooster, at 34.8%. Nothing told the model that dogs and roosters are both impulsive animal vocalisations — the language geometry did. A one-hot classifier's second-place guess carries no such information.
The multilabel case is different and the paper is careful about it. FSD50K and AudioSet clips can contain several events at once, so "which one class" is the wrong question. There you apply a sigmoid to each logit independently:
On the same logits that gives 0.982, 0.970, 0.844, 0.414 — no longer summing to one, because "dog" being present says nothing about whether "rain" is also present. Which is exactly why FSD50K and AudioSet are scored with mAP rather than accuracy in Table 1: they are ranking problems, not choice problems.
| Operation | How often | Cost |
|---|---|---|
| Embed the C class prompts | Once, ever — cache the result | C forward passes of a 110M BERT. For ESC50, 50 of them |
| Store Etext | Once | 50 × 1024 floats = 200 KB for all of ESC50 |
| Embed a test clip | Per clip | One CNN14 forward pass |
| Classify | Per clip | One (50, 1024) × (1024,) matrix-vector product — 51,200 multiply-adds, microseconds |
| Add a 51st class at runtime | Whenever you like | One BERT forward pass. No gradients, no data, no redeploy |
Pick the clip that is playing, then switch candidate labels on and off — including labels no classifier was ever trained on. The bars show cosine similarity; the ring shows the softmax over whichever labels are active. Change the prompt template and watch every text embedding move. Turn off the true class to see what the model does when the answer is not on the menu.
Two experiments worth running in that widget before moving on. First, turn off the correct label and watch the probabilities redistribute over the survivors — the model is still forced to sum to one, so zero-shot inherits Chapter 0's abstention problem unless you threshold the raw cosine. Second, add the deliberately unusual candidate and see it rank sensibly anyway. That is generalisation to a class that exists only as a string.
There is a distribution mismatch hiding in the procedure, and Section 4.4 names it: "The training data of CLAP consists of natural language captions containing one or more sentences. However, the vast majority of datasets have class labels defined by a few words — 'dog barking' and 'sneezing'. Using single words instead of language description affects how Zero-Shot learning transfers."
The text encoder spent 40 epochs reading full sentences. At inference you hand it the fragment "dog". That fragment lands in a region of the text space the model rarely visited. The fix is a prompt template: wrap the class name in a sentence shaped like the training data. The paper's default is "This is a sound of [class label]".
Here is Table 3, complete, and it does not say what you expect:
| Prompt | ESC50 zero-shot accuracy | Relative to the bare label |
|---|---|---|
| "i can hear [class label]" | 0.7860 | −2.6 points — worse than no template at all |
| "this is an audio of [class label]" | 0.8005 | −1.15 points — also worse |
| "[class label]" (no template) | 0.8120 | baseline |
| "this is [class label]" | 0.8135 | +0.15 |
| "this is a sound of [class label]" | 0.8260 | +1.4 |
One more honest note. The paper writes that prompting "leads to a 5% acc increase." The table's own span is 0.826 − 0.786 = 4.0 points, and against the more natural baseline of the bare class label it is 1.4 points. The effect is real and free, but it is a percentage point or two, not a transformation. Do not let a rounded claim in prose overwrite a table you can read.
Table 3, drawn to scale from the bare-label baseline so the two negative templates are visible as negative. The dashed line is the no-template accuracy; the band at the top is reported human performance on ESC50 (81%). Hover targets are the bars — tap one to see the prompt in full.
Section 3.3 records a detail most readers skim: "The prompt was kept the same for all the domains except three."
| Domain | Prompt used | Why the default fails |
|---|---|---|
| Emotion Recognition (CREMA-D, RAVDESS) | "this person is feeling [class label]" | "A sound of angry" is not English. Emotion is a property of a speaker, not a category of sound event — the prompt must supply the missing subject |
| Keyword Spotting (Speech Commands) | the keyword alone | The target is a literal utterance, not a description of one. "This is a sound of yes" describes a recording; the class is the word "yes" itself |
| Speaker Counting (LibriCount) | "[number between 0 - 10] persons speaking" | The class is a count. It only becomes a description when attached to a noun phrase |
These three exceptions are not tuning noise; they are a map of where the caption distribution runs out. Captions describe what happened acoustically. They rarely annotate a speaker's emotional state, almost never transcribe words, and essentially never count talkers. In Chapter 6 those same three domains produce CLAP's three worst zero-shot numbers. The prompts were the first warning.
Nothing in Chapter 5 says the class list must be short and the audio list long. Swap which axis you loop over and the same model does something else entirely.
| Task | Fixed side | Ranked side | What you get |
|---|---|---|---|
| Zero-shot classification | One clip | C class prompts | "This clip is a dog barking" |
| Text-to-audio retrieval | One sentence | N clips in an archive | "Here are the 20 clips most like 'glass breaking on tile'" |
| Audio-to-text retrieval | One clip | A caption pool | The nearest human description — a crude captioner |
| Audio-to-audio search | One clip | N clips | "More like this" — the text tower is not even used |
| Dataset curation | A description of what you want | Millions of clips | Filter a corpus by a sentence instead of a label |
All five are the same two lines of code with the loops exchanged. That is what "joint embedding space" actually buys, and it is why CLAP's descendants show up inside data pipelines as often as inside classifiers.
Softmax forces a choice. FSD50K clips genuinely contain several events, so the paper switches to sigmoid there — and the switch has consequences worth walking.
With the same four logits (3.985, 3.464, 1.690, −0.349), sigmoid gives:
Every score is high, including "rain" at 0.844 and "violin" at 0.414 — and their sum is 3.21, not 1. That is not a bug; independence is the point. But it exposes a real difficulty: to turn these into predictions you need a threshold, and cosine similarities between unit vectors are bunched. Because raw cosines rarely go below zero for related concepts, σ(s·cos) sits above 0.5 for almost everything, and the useful threshold depends on both s and the dataset.
This is exactly why FSD50K and AudioSet are scored with mAP — mean average precision — rather than accuracy. mAP evaluates the ranking and never asks you to pick a threshold. When you see a mAP number, read it as "how well does the model order the classes", not "how often is it right".
the arithmetic, one clip against four classescos = [ 0.996 , 0.866 , 0.423 , -0.087 ] # dot products, both sides unit-norm logits = 4 * cos = [ 3.985 , 3.464 , 1.690 , -0.349 ] exps = [ 53.77 , 31.95 , 5.42 , 0.71 ] sum = 91.85 probs = [ 0.585 , 0.348 , 0.059 , 0.008 ] # multiclass: softmax sigmoid = [ 0.982 , 0.970 , 0.844 , 0.414 ] # multilabel: independent prediction = argmax = class 0 = "dog"
python — the whole zero-shot classifierimport numpy as np, torch # ---- ONCE, at "deployment". No gradients anywhere in this file. ---- classes = ["dog", "rooster", "rain", "violin", "chainsaw"] # type anything prompts = [f"this is a sound of {c}" for c in classes] with torch.no_grad(): E_text = model.encode_text(prompts) # (C, 1024), unit-norm, CACHE THIS # ---- PER CLIP ---- with torch.no_grad(): E_a = model.encode_audio(log_mel(clip)) # (1, 1024), unit-norm cos = E_text @ E_a.T # (C, 1) <- cosine similarities logits = model.logit_scale.exp() * cos probs = logits.softmax(dim=0) # multiclass / binary scores = torch.sigmoid(logits) # multilabel (FSD50K, AudioSet) print(classes[probs.argmax()]) # adding a 6th class at 3am: E_text = torch.cat([E_text, model.encode_text(["this is a sound of a smoke alarm"])]) # done. that is the entire deployment story.
python — the one-liner, for the whole test set at oncepreds = (model.encode_audio(X) @ model.encode_text(prompts).T).argmax(-1) # (N,1024) @ (1024,C) -> (N,C) -> (N,) ... the temperature cancels under argmax
That last comment is worth a beat. Under argmax the temperature is irrelevant — scaling every logit by the same positive constant cannot change which is largest. Temperature matters for the loss during training and for calibrated probabilities at inference. It does not matter for accuracy. Many people tune it hoping otherwise.
Every claim so far has been mechanical: this is the tensor, this is the loss, this is the inference procedure. Now the empirical question. A model trained on 128,010 captioned clips, with no classifier and no task-specific training, is pointed at sixteen benchmarks it has never seen. What happens?
The answer is not uniform, and the non-uniformity is the most instructive part. This chapter shows every number, including the ones that make CLAP look bad, because the shape of the failures is what tells you what the model actually learned.
Five rows, and confusing them is the fastest way to misread the paper:
| Row | What it is | Trained on the target task? |
|---|---|---|
| Random | Chance level, essentially 1/C for a C-way task | — |
| Benchmark (ZS) | Best zero-shot result in the prior literature (Wav2CLIP, AudioCLIP) | No |
| CLAP (ZS) | CLAP, zero-shot, prompted class names | No |
| Benchmark (Best) | Best supervised result in the literature | Yes, fully |
| CLAP (Best) | Best of CLAP's supervised setups (feature extraction or finetune) | Yes |
Rows 3 and 4 are not comparable in fairness — one had labelled data for the task and one did not. That asymmetry is exactly what makes some of the numbers below remarkable.
Metrics differ by task, and the caption of Table 1 is precise about it: DCASE17 employs F1, FSD50K and AudioSet employ mAP, everything else uses accuracy. Higher is better everywhere. Random also differs by task, because it is set by the number of classes: 1/50 = 0.02 for ESC50, 1/2 = 0.5 for GTZAN music-versus-speech, and under 0.0018 for AudioSet's 527 multi-label classes.
Comparing 0.826 on ESC50 with 0.3024 on FSD50K is meaningless unless you know they measure different things.
| Metric | Definition | Used for | Why it was chosen there |
|---|---|---|---|
| Accuracy | Fraction of clips whose argmax class is correct | Twelve of the sixteen tasks | Single-label, forced choice — every clip has exactly one right answer |
| mAP | For each class, compute average precision over the ranked clip list; average across classes | FSD50K (200 classes), AudioSet (527) | Multi-label: several classes can be true at once, so "the argmax" is not the question. mAP scores the ranking and needs no threshold |
| F1 | Harmonic mean of precision and recall at a chosen operating point | DCASE17 Task 4 | Detection with sparse positives — accuracy would be dominated by the many true negatives |
Three practical consequences. mAP numbers look low compared to accuracy numbers and are not worse for it — 0.30 mAP over 200 classes is a strong result. F1 depends on a threshold, so the number carries an unstated operating-point choice. And chance level differs wildly: 0.02 for ESC50, under 0.0018 for AudioSet, 0.5 for a binary task. Always locate the chance level before reacting to a score.
| Domain / dataset | Classes | Random | Bench (ZS) | CLAP (ZS) | Bench (Best) | CLAP (Best) |
|---|---|---|---|---|---|---|
| SEC — ESC50 | 50 | 0.02 | 0.6940 | 0.826 | 0.9715 | 0.9670 |
| SEC — FSD50K (mAP) | 200 | <0.005 | 0.0302 | 0.3024 | 0.641 | 0.5859 |
| SEC — UrbanSound8K | 10 | 0.05* | 0.6531 | 0.7324 | 0.907 | 0.8796 |
| SEC — DCASE17 Task4 (F1) | 17 | 0.1* | — | 0.30 | 0.646 | 0.5938 |
| SEC — AudioSet (mAP) | 527 | <0.0018 | — | 0.058 | 0.471 | — |
| Music — GTZAN Music vs Speech | 2 | 0.5 | — | 1.000 | 0.992 | 1.000 |
| Music — GTZAN Genres | 10 | 0.1 | — | 0.252 | 0.883 | 0.9130 |
| Music — Mridangam Stroke | 10 | 0.1 | — | 0.3447 | 0.975 | 0.9794 |
| Music — Mridangam Tonic | 6 | 0.1667 | — | 0.1965 | 0.942 | 0.9534 |
| Instrument — Beijing Opera | 4 | 0.25 | — | 0.4746 | 0.975 | 0.9026 |
| Scene — TUT2017 | 15 | 0.06 | — | 0.2963 | 0.843 | 0.7463 |
| Emotion — CREMA-D | 6 | 0.1667 | — | 0.1784 | 0.752 | 0.6834 |
| Emotion — RAVDESS | 8 | 0.125 | — | 0.1599 | 0.8182 | 0.6436 |
| Keyword — Speech Commands | 12 | 0.083 | — | 0.1063 | 0.987 | 0.9683 |
| Vocal Sound | 6 | 0.1667 | — | 0.4945 | 0.905 | 0.9795 |
| Speaker Counting — LibriCount | 11 | 0.090 | — | 0.1788 | 0.785 | 0.7783 |
Bold = CLAP is state of the art in its row's setting. Italic = CLAP is barely above chance. * The paper's random row lists 0.05 and 0.1 for the DCASE17/US8K pair; the values matching class counts are 1/17 ≈ 0.059 and 1/10 = 0.1. Dashes mean the paper reports no number in that cell.
ESC50: 82.6% zero-shot. Fifty environmental sound classes, five-second clips. The prior zero-shot best was AudioCLIP at 69.40%, so this is an absolute 12-point jump on a benchmark that had been competitively worked. And the paper flags the comparison that gets quoted everywhere: reported human accuracy on ESC50 is 81%. A model with no classifier, told the class names in English at inference time, beats the humans who defined the categories.
GTZAN Music vs Speech: 100% zero-shot. Perfect. And the supervised literature benchmark is 99.2%. Zero-shot beat fully supervised. This is a two-class task on 30-second clips, so it is the easiest cell in the table — but it also demonstrates that when the distinction is one the caption corpus talks about constantly ("a man speaks" versus "music plays"), the language prior is simply sufficient.
FSD50K: 30.24 mAP zero-shot, against Wav2CLIP's 3.02. An absolute 27-point mAP gain, the largest relative jump in the table. FSD50K is multi-label over 200 classes, and the paper notes CLAP's training data includes FSD50K clips — but paired with their free-text titles and descriptions, with the class labels deliberately ignored. So the model saw the audio and a human sentence about it, and then reconstructed a 200-way taxonomy it was never shown.
UrbanSound8K: 73.24% vs AudioCLIP's 65.31%. An 8-point absolute gain on ten urban sound classes.
The paper is honest here and so is this lesson: "CLAP (ZS) performed better than random on all downstream tasks and achieved good to slightly better than random on some music and speech-related tasks."
| Task | Random | CLAP (ZS) | Absolute lift | Verdict |
|---|---|---|---|---|
| CREMA-D (emotion) | 0.1667 | 0.1784 | +1.2 points | Statistically alive, practically useless |
| Mridangam Tonic | 0.1667 | 0.1965 | +3.0 points | Nearly chance |
| RAVDESS (emotion) | 0.125 | 0.1599 | +3.5 points | Nearly chance |
| Speech Commands (keywords) | 0.083 | 0.1063 | +2.3 points | Nearly chance |
| AudioSet (mAP) | <0.0018 | 0.058 | +5.6 points mAP | 32× random, but 8× below the supervised benchmark of 0.471 |
Every task, every row of Table 1. Switch between absolute scores and headroom-captured to see the ordering change. Filter by domain to watch the pattern separate: sound events at the top, speech at the bottom. The dashed line on each bar group is chance for that task.
Zero-shot is the headline, but the paper also asks a quieter question: is CLAP's audio representation good, independent of the zero-shot trick? Two setups answer it. Feature extraction freezes CLAP and trains 1 or 3 fully-connected layers on top. Finetune unfreezes the audio encoder along with those layers. CLAP (Best) is whichever won.
Result: state of the art on five datasets — GTZAN Music vs Speech (100%), GTZAN Genres (91.30%), Mridangam Stroke (97.94%), Mridangam Tonic (95.34%), and Vocal Sound (97.95%). Elsewhere it trails the literature by at most seven points, with the worst case being RAVDESS emotion at 64.36% against a 81.82% benchmark.
Table 6 puts CLAP head to head with the standard audio representation models. A few comparisons worth carrying:
| Task | YAMNet | OpenL3 | Wav2CLIP | PANNs | Wav2Vec2 | CLAP (S) | CLAP (F) |
|---|---|---|---|---|---|---|---|
| ESC50 | 0.8375 | 0.7823 | 0.9085 | 0.9310 | — | 0.8389 | 0.9670 |
| Speech Commands | 0.4104 | 0.7634 | 0.3466 | 0.6182 | 0.8785 | 0.3708 | 0.9683 |
| CREMA-D | 0.453 | 0.550 | 0.512 | 0.555 | 0.6562 | 0.2830 | 0.6834 |
| Mridangam Tonic | 0.9369 | 0.8289 | 0.8244 | 0.8283 | 0.6391 | — | 0.9534 |
| Vocal Sound | 0.8411 | — | — | — | — | — | 0.9795 |
Look at the Speech Commands row carefully, because it settles an important question. Frozen CLAP features score 0.3708 — worse than OpenL3 and far worse than Wav2Vec2's 0.8785. But finetuned CLAP reaches 0.9683, the best in the row. So the audio encoder is perfectly capable of keyword spotting once gradients are allowed to reshape it. What CLAP's frozen space lacks is not capacity; it is that the contrastive objective never had a reason to encode which word was spoken. Nothing in 128k sound captions ever said "the person said 'left'."
The headroom ordering is not a mystery once you ask, for each domain, would a person writing a caption about this clip mention the thing being classified?
| Domain | Would a caption mention it? | Headroom captured |
|---|---|---|
| Sound events (ESC50, US8K, FSD50K) | Yes — this is literally what audio captions are about | 30–82% |
| Music vs speech | Yes — "a man speaks", "music plays" are among the commonest caption phrases | 100% |
| Acoustic scene (TUT2017) | Often — "in a park", "on a busy street" appear in captions | 25% |
| Vocal sounds (cough, sneeze, laughter) | Yes — these are events, and captions name them | 39% |
| Instrument identity (Beijing Opera) | Sometimes — "a drum", "a stringed instrument" appear; specific percussion names rarely | 30% |
| Music genre | Rarely — captions say "music plays", not "this is bebop" | 17% |
| Speaker counting | Occasionally — "two people talking" exists, but 0-to-10 precision does not | 10% |
| Percussion stroke / tonic | Almost never — requires trained musical vocabulary | 27% / 4% |
| Emotion | Essentially never — captions describe what happened, not how the speaker felt | 1–4% |
| Keyword spotting | Never — captions do not transcribe | 2.5% |
Read the two columns together and the model stops looking mysterious. CLAP's zero-shot ability on a task is roughly the frequency with which that task's distinction appears in written descriptions of sound. It is not a general audio understanding system; it is a system that understands audio through the vocabulary humans use to write about it.
Notice the Mridangam pair, which is a clean natural experiment. Stroke classification captures 27% of the headroom; tonic classification captures 3.6%. Both are 5-fold tasks on the same 0.81-second clips from the same instrument. The difference is that a stroke is a percussive event with a distinct timbre — the kind of thing captions describe — while a tonic is a pitch, which requires naming a note. Same audio, same encoder, same prompt template; the gap is entirely in whether language talks about it.
82.6% zero-shot on ESC50, above 81% human accuracy, is the line that got CLAP quoted. Two caveats keep it honest.
It is a comparison between different things. The human number comes from crowdworkers listening once and choosing among fifty categories, with human failure modes: attention, memory for the category list, unfamiliarity with some sounds. The model is not "better at hearing"; it is better at this particular forced-choice protocol.
The supervised ceiling is still higher. CLAP's own supervised finetune reaches 96.70% on ESC50, and the literature benchmark is 97.15%. Zero-shot bought you 82.6% for zero labelled examples. That is an extraordinary price-performance point, not an accuracy record.
Table 1 is well constructed, which makes it a good place to practise the habits that catch badly constructed ones.
CLAP has two pretrained encoders and a training budget. You can update both, one, or neither. That is four experiments, and the paper ran all four. The result is the most quietly consequential finding in the paper, because it tells you what to build next.
Before you read the table, commit to a prediction. Which encoder do you expect to matter more? Most people say audio — the audio side is the one being taught something new, the text side already speaks English. Write your guess down.
| Audio encoder | Text encoder | Avg. zero-shot across all 16 tasks | ESC50 accuracy |
|---|---|---|---|
| frozen | frozen | 0.2809 | 0.5555 |
| trainable | frozen | 0.2818 | 0.6415 |
| frozen | trainable | 0.3109 | 0.7631 |
| trainable | trainable | 0.3265 | 0.826 |
Two of those rows are unsurprising. Both frozen is worst; both trainable is best; the paper calls this "expected because unfreezing both encoders allows them to learn the multimodal information from the pairs."
The middle two rows are the finding. Isolate the gains:
| What you unfreeze (starting from both frozen) | Gain in avg. ZS | Gain on ESC50 |
|---|---|---|
| Only the audio encoder (80.8M parameters) | +0.0009 | +8.6 points |
| Only the text encoder (110M parameters) | +0.0300 | +20.8 points |
| Ratio, text gain over audio gain | 33× | 2.4× |
| Both (for reference) | +0.0456 | +27.1 points |
Unfreezing the text encoder is worth thirty-three times as much as unfreezing the audio encoder, averaged across the sixteen tasks. The paper's own reaction is candid: "Surprisingly, unfreezing the text encoder performed better than unfreezing the audio encoder. Our intuition was that unfreezing the audio encoder would enable learning beyond the SEC coming from the pretrained AudioSet information."
The paper does not explain the asymmetry beyond noting the parallel to CLIP. Here are three mechanisms that fit the evidence, flagged as analysis rather than as claims from the paper.
1. Asymmetric domain shift. CNN14 was pretrained on 2M AudioSet clips — the same kind of audio, labelled with the same kind of sound-event concepts. Its features arrive nearly on-task. BERT was pretrained on Wikipedia and BookCorpus, where nobody writes "an insect buzzing in the foreground as birds chirp in the background." Audio captioning is a distinctive, narrow, oddly-structured genre. The text encoder has much further to travel, so it gains more from being allowed to travel.
2. The alignment burden falls where the geometry is coarser. Both towers must be brought into one space, and something has to bend. If the audio geometry is already a good match for sound-event structure, the cheaper solution is to bend the text geometry toward it. Gradient descent will find whichever adaptation lowers the loss for less movement.
3. Forgetting risk is asymmetric. 128,010 pairs is a small dataset for a 80.8M-parameter encoder that learned from 2M clips. Unfreezing the audio tower risks overwriting the AudioSet knowledge with something narrower — and the frozen-audio rows suggest that knowledge is worth a great deal. The text tower has less to lose: BERT's general language ability is not what CLAP needs, only its ability to place caption-like sentences sensibly.
Click any cell of the grid to select a training regime. Blue means gradients flow, grey means locked. The bars show both metrics from Table 2, with the both-frozen row as the reference line so the gains are legible. Watch the average-ZS bar barely move when only the audio tower is unfrozen.
Freezing is not the same as removing. A frozen encoder still runs forward, still produces its 2048- or 768-dimensional vector, and still contributes to the loss. What changes is that requires_grad is off for its parameters, so the optimiser never updates them. Concretely:
python — the four regimes of Table 2def set_regime(model, train_audio, train_text): for p in model.audio_enc.parameters(): p.requires_grad = train_audio for p in model.text_enc.parameters(): p.requires_grad = train_text # the two projections and the temperature ALWAYS train — in all four rows. for p in [*model.La.parameters(), *model.Lt.parameters()]: p.requires_grad = True model.logit_scale.requires_grad = True # row 1 of Table 2: 2.88M trainable of 193.68M -> ESC50 55.55% # row 2: 83.68M trainable -> ESC50 64.15% # row 3: 112.88M trainable -> ESC50 76.31% # row 4: 193.68M trainable -> ESC50 82.60%
That comment block is the whole ablation, and it exposes something the prose glides over: the projections train in every row. So "both frozen" is not "no learning" — it is "learning restricted to a linear map between two fixed geometries." 55.55% on a 50-way task is what a rotation is worth.
Now the gradient story, which explains the asymmetry mechanically. The loss reaches the audio encoder only through La, and reaches the text encoder only through Lt. Both paths are equally short. What differs is what the gradient asks for at each end:
The paper connects its finding to reference [27] — Zhai et al., LiT: Zero-Shot Transfer with Locked-image Text Tuning — noting "a similar insight was found for CLIP models in Computer Vision." LiT's headline is that locking the image tower and tuning only the text tower gives better zero-shot transfer than tuning both, precisely because a strong pretrained vision encoder should not be disturbed by a noisy image-text corpus.
Note the difference in conclusion, though, because it is easy to over-read. LiT found locked-image tuning best. CLAP found both-unfrozen best, with text-unfrozen the strongest single choice. Same direction, different endpoint — most plausibly because CLAP's 128k pairs are clean human captions rather than web alt-text, so overwriting the audio tower is less dangerous than overwriting a vision tower with internet noise.
Unpack the engineering consequence. If the audio tower does not need to change much, then the audio tower is a swappable component. Take any strong pretrained audio encoder — PANNs, an Audio Spectrogram Transformer, HTSAT, a self-supervised speech model — bolt on a linear projection, train against captions with a language tower that is allowed to move, and you have converted a fixed-taxonomy classifier into an open-vocabulary one.
That is exactly what the field then did. Every CLAP successor in Chapter 9 is, structurally, this recipe with a different audio tower.
One number in Table 2 deserves suspicion, and reading it carefully is good practice. "Avg. ZS score" is the mean of CLAP's zero-shot performance across all sixteen downstream tasks. But those tasks use different metrics (accuracy, mAP, F1) with different chance levels (0.0018 to 0.5). Averaging them is averaging apples, oranges, and a percentage of a percentage.
Concretely: GTZAN Music vs Speech contributes 1.000 to that mean while its chance level is 0.5, and AudioSet contributes 0.058 while its chance level is under 0.002. A tenth of the average is being supplied by a two-class task that any model solves. So the absolute values 0.2809 and 0.3265 do not mean "28% accurate" in any interpretable sense.
What survives the objection is the comparison. All four rows aggregate the same sixteen tasks the same way, so the ordering and the relative gaps are meaningful even though the level is not. The 33× asymmetry is a ratio of two differences computed under an identical aggregation, and it is robust to the aggregation being crude. The ESC50 column, which is a single clean accuracy on a single task, tells the same story at 2.4×.
| Your situation | What Table 2 recommends |
|---|---|
| Strong domain audio encoder, small caption set | Freeze audio, train text and both projections. Cheapest, and Table 2's second-best row |
| Plenty of clean pairs, plenty of compute | Unfreeze both — the paper's configuration |
| Very small or noisy caption set | Freeze both, train only the projections. Still worth 55.55% on ESC50 for 2.88M parameters |
| You want to swap in a new audio encoder next quarter | Keep audio frozen so the projection absorbs the interface, and your text tower stays reusable |
Table 2 has four rows because there are two binary switches. But the interesting design space has more dimensions, and naming the missing experiments sharpens what the four rows actually established.
| Experiment not run | What it would have settled |
|---|---|
| Freeze the audio tower but train its last block only | Whether the 8.6-point ESC50 gain from unfreezing audio comes from deep re-representation or a shallow recalibration. If a last-block-only run recovered most of it, the audio tower is even more swappable than claimed |
| A nonlinear projection with both encoders frozen | Whether the 55.55% frozen-frozen result is limited by the linearity of the map or by the encoders' geometry. This is the single cheapest missing experiment in the paper |
| A randomly initialised audio tower, trained | How much of CLAP is AudioSet pretraining versus captions. Given 128k pairs, almost certainly a lot — but "almost certainly" is not a measurement |
| A randomly initialised text tower, trained | Whether BERT's language prior matters, or whether any encoder that maps strings consistently would do. The zero-shot transfer to unseen class names says the prior matters; nothing quantifies it |
| Freezing schedules — freeze audio for 20 epochs, then release | Whether the forgetting hypothesis (mechanism 3) is right. If staged unfreezing beat both-from-scratch, that would be strong evidence |
Two of these five would have been nearly free — the nonlinear-projection run and the staged unfreeze reuse the same code and the same data. Their absence is a reminder that a four-page paper reports the experiments that fit, not the experiments that exist.
python — the "audio encoder of choice" recipe# Table 2 row 3: freeze audio, train text + both projections. # Second-best zero-shot, ~42% of the trainable parameters, and your # audio tower stays swappable next quarter. audio_enc = load_any_pretrained_audio_encoder() # PANNs, AST, BEATs, HTSAT... for p in audio_enc.parameters(): p.requires_grad = False audio_enc.eval() # also freezes BatchNorm statistics — # forgetting this silently trains the encoder text_enc = AutoModel.from_pretrained("bert-base-uncased") # trainable La, Lt = nn.Linear(audio_dim, 1024), nn.Linear(768, 1024) opt = torch.optim.Adam([ {"params": text_enc.parameters(), "lr": 1e-5}, # gentle on the pretrained tower {"params": [*La.parameters(), *Lt.parameters(), logit_scale], "lr": 1e-3}, ]) # with the audio tower frozen you can also PRECOMPUTE every E_a once and # train on cached vectors — an order-of-magnitude cheaper per epoch.
That last comment is the underrated payoff. A frozen audio tower means the expensive forward pass happens once, offline, for the whole corpus. Training then reads 1024-float vectors off disk. On a single GPU that turns a multi-day run into an afternoon — and Table 2 says you give up 0.0156 of average zero-shot to get it.
The main text of a four-page conference paper is a highlight reel. The interesting failures live in the appendix, in a footnote, or in a number that does not quite match its prose. CLAP has three appendix findings that matter more than most of Section 4, and a handful of smaller discrepancies worth cataloguing. This chapter reads the quiet parts out loud.
Appendix C.2, in full, is the most useful paragraph in the paper. AudioSet has 2M clips. The authors tried to turn them into pairs by "constructing the text description with the title and the class label(s)." Adding about 1.7M such pairs to the existing 128k produced this:
| Task | 128,010 curated pairs | +1.7M AudioSet pairs (1.83M total) | Change |
|---|---|---|---|
| ESC50 | 0.8260 | 0.6715 | −15.45 points |
| UrbanSound8K | 0.7324 | 0.7093 | −2.31 points |
| Speech Commands V2 | 0.10 | 0.15 | +5 points |
Fourteen times more data, and the flagship number dropped by fifteen points. In an era whose default assumption is that more pairs are always better, this is a result worth memorising.
The authors diagnose it precisely: "In AudioSet, often the YouTube titles and descriptions do not describe the acoustic content of the video segment under consideration but instead describe the video as a whole."
Picture the failure. A ten-second AudioSet segment contains a dog barking. Its parent video is titled "Ep. 42 — Our Trip to Portugal (Vlog)". The constructed caption describes a travel vlog. The clip contains a bark.
Now do the arithmetic on the mixture. After the addition, 1.7M of 1.828M pairs — 93% — come from the noisy source. The curated captions are outnumbered thirteen to one. The model is not learning "sound events plus some noise"; it is primarily learning "YouTube video titles, with an occasional real caption."
And the one improvement is just as diagnostic. Speech Commands went from 10% to 15%. The authors' guess: "Perhaps due to the large amount of audio containing speech in AudioSet." Even bad captions attached to speech-heavy audio taught the model something about speech — because the base corpus taught it essentially nothing. A signal from noise is still a signal when your baseline is zero.
Two whispers, one canvas. Dilution mode sweeps the fraction of noisy AudioSet-style pairs in the mixture, anchored at the paper's two measured endpoints (0% and 93%) — the curve between them is an interpolation, not a measurement, and is drawn dashed to say so. Batch mode plots the appendix's batch-size story alongside two quantities the paper does not plot: total optimiser steps, and the expected number of same-audio collisions per batch.
Chapter 6 established the pattern: sound events near the human ceiling, speech near chance. The paper offers one cause. There are at least five, and they all point the same way.
| # | Cause | Source | Effect on speech tasks |
|---|---|---|---|
| 1 | Captions describe acoustic events, not linguistic content. No training caption ever transcribes a word or names an emotion | The paper, Section 4.2 | Decisive — the concept is simply absent from the supervision |
| 2 | The Mel filterbank stops at 8 kHz, discarding the sibilance band | Section 3.2 + Chapter 2 here | Removes cues that separate "s" from "sh" and one voice from another |
| 3 | 1-second Speech Commands clips are padded to 5 seconds — 80% silence | Section 3.2 + our arithmetic | Dilutes the evidence fivefold before clip-level pooling |
| 4 | CNN14 pools 690 frames into one vector, destroying phonetic order | Chapter 3 here | "cat" and "tack" have similar bags of phonemes |
| 5 | Three of the four speech-adjacent domains needed a custom prompt because the default was ungrammatical | Section 3.3 | A symptom, not a cause — but a loud one |
Cause 1 dominates, and we know it dominates because of the Table 6 control from Chapter 6: finetuned CLAP reaches 96.83% on Speech Commands, the best number in that row. Causes 2 through 4 would still apply after finetuning. So they cannot be what is limiting the frozen model. The bottleneck really is that the contrastive objective was never given a reason to encode which word was spoken.
The paper's own forecast is reasonable and, in hindsight, correct: "We posit that as we increase training data and increase human speech-based captioning, CLAP's performance on speech datasets will increase." That is precisely the arc from CLAP to speech-aware audio-language models (Chapter 9).
Appendix C.1 reports a batch-size study: 4 to 24 GPUs, batch sizes 32 to 768, measuring average zero-shot performance across the downstream tasks. Larger batches helped — until 768, where performance dropped. The authors write: "This might be an anamoly in an increasing zero-shot performance trend. We leave the larger batch size investigation to future work."
Fair enough for a four-page paper. But two mechanisms predict exactly that drop, and both are computable from numbers the paper already gives.
Mechanism A: the learning rate was not rescaled, so the big-batch run got 4% of the updates. Section 3.2 fixes the learning rate at 10−3 and the schedule at 40 epochs. Epochs are fixed, so the number of optimiser steps falls linearly with batch size:
| Batch size | Steps per epoch (128,010 pairs) | Total steps over 40 epochs | Relative to batch 32 |
|---|---|---|---|
| 32 | 4,000 | 160,013 | 100% |
| 128 | 1,000 | 40,003 | 25% |
| 512 | 250 | 10,001 | 6.3% |
| 768 | 167 | 6,667 | 4.2% |
With a fixed learning rate, the batch-768 model took one twenty-fourth as many gradient steps as the batch-32 model. Standard practice would scale the learning rate with batch size (linearly, or by the square root) to compensate. Nothing in the paper says that was done. The "anomaly" may be a model that simply had not finished training.
Mechanism B: false negatives grow with N2, and this corpus has duplicate audio by design. Recall Chapter 1: 128,010 pairs cover only 90,947 unique clips, because ClothoV2 supplies five captions per clip and MACS supplies several. So two entries in a batch can be the same audio with different captions — and the loss will insist that caption i matches clip i and not clip j, when clips i and j are byte-identical. That is an unsatisfiable constraint, and it actively penalises correct behaviour.
How often? Count the colliding pairs in the corpus — 5,929 ClothoV2 clips contribute C(5,2) = 10 collisions each, MACS contributes roughly 7.5 per clip over 3,930 clips — giving about 88,800 colliding pairs out of the C(128010, 2) ≈ 8.19 billion possible. The probability that a random pair of batch entries collides is about 1.08 × 10−5, and a batch of N contains C(N,2) pairs, so:
| Batch size N | Pairs in the batch | Expected same-audio collisions per batch |
|---|---|---|
| 32 | 496 | 0.005 |
| 128 | 8,128 | 0.09 |
| 512 | 130,816 | 1.4 |
| 768 | 294,528 | 3.2 |
At batch 32 a collision essentially never happens. At batch 768 there are about three per batch, every batch, for all 40 epochs. Each one is a contradiction the optimiser cannot resolve, and it is aimed at exactly the captions the corpus most wants the model to treat as equivalent.
Any zero-shot claim invites the same challenge: how much of the evaluation did the model already see? Trace the corpora and the answer is more interesting than a simple yes or no.
| Downstream task | Relationship to CLAP's training data | Does it undermine the claim? |
|---|---|---|
| FSD50K | Direct — 36,796 FSD50K train/val clips are in the training set, paired with their titles and descriptions. The paper says so plainly, and evaluates on the FSD50K test split | No test-clip leakage, but the domain and recording style are seen. The 30.24 mAP should be read as "in-domain zero-shot" |
| AudioSet | Indirect — AudioCaps is built from AudioSet clips, so 44,292 AudioSet segments are in training, captioned | Notably, CLAP scores its worst headroom here (5.6%). If seeing the audio were sufficient, this cell would be strong. It is not — which is itself evidence that captions, not audio exposure, drive the zero-shot ability |
| ESC50, US8K, TUT2017, GTZAN, Mridangam, CREMA-D, RAVDESS, Speech Commands, Vocal Sound, LibriCount, DCASE17, Beijing Opera | No stated overlap | These are the clean cases — and ESC50, the headline, is one of them |
Some gaps are not errors, just consequences of the format. Naming them is part of reading a paper honestly, and each one is a question you would have to answer yourself to reproduce the work.
| Missing | Why it matters |
|---|---|
| Error bars, seeds, or repeated runs | Is the 82.6% ±0.3 or ±3? Several of the near-chance numbers (CREMA-D at +1.2 points over random) could plausibly be run-to-run noise |
| Whether embeddings are L2-normalised | Equation (3) implies not; the temperature's behaviour implies yes. This changes the implementation materially (Chapter 3) |
| Which of the two supervised setups won each task | Table 1's "CLAP (Best)" is a max over feature-extraction and finetune. Table 6 disambiguates for some tasks, not all |
| The batch size of the main model | Appendix C.1 studies 32 to 768, but the headline model's batch size is never stated — and the appendix says 768 was worse |
| Actual values in the batch-size figure | Figure 2 shows a trend; no numbers are given in the text, so the drop at 768 cannot be quantified |
| Whether prompts were tuned on test data | Table 3's five prompts are evaluated on ESC50 and the best is selected. If that selection used the same folds reported in Table 1, the 82.6% carries a small optimistic bias |
None of these invalidate the paper. All of them are the difference between "I read the result" and "I could rebuild it." The last one is the only one with teeth, and even there the effect is bounded by Table 3's own spread — at most 1.4 points against the bare-label baseline.
| Where | What is written | What is actually the case | Consequence |
|---|---|---|---|
| Section 3.2 | "hop size of 320 secs, window size 1024 secs" | Those are samples: 7.26 ms and 23.22 ms at 44.1 kHz | Harmless typo, total confusion for a careful reader |
| Equation (3) + Section 3.2 | C = τ(EtEaT) with τ = 0.007, logits clipped at 100 | The multiplier is 1/τ ≈ 142.86; 0.007 is the temperature. Only then can the clip ever fire | Implement it literally and the model cannot learn (Chapter 4) |
| Equation (4) | ℓk = (1/N)∑ log diag(softmax(C)) | Implementations minimise the negative log-likelihood | Sign error if transcribed directly |
| Section 3.2 | "max text sequence length to 100 chars" | Characters, not tokens — about 20 words. Two of the twelve captions the paper itself samples exceed it | ~17% of captions lose their tail, often the second sound source |
| Section 4.4 | Prompts give "a 5% acc increase" | Table 3 spans 4.0 points worst-to-best, and 1.4 points versus the bare class label | The effect is real and free, but small |
| Conclusion | Training data "is at least 0.001% smaller" than comparable vision models | 128,010 versus CLIP's 400M is 0.032%, i.e. about 1/3125 | The garbled figure undersells a genuinely striking fact |
| Table 1 vs Table 5 | DCASE17 random listed as 0.05; US8K as 0.1 | DCASE17 has 17 classes (1/17 ≈ 0.059); US8K has 10 (1/10 = 0.1) | Column alignment in the typeset table repays care |
The AudioSet result is reported as a single before-and-after. What would turn it from an anecdote into a law? Four experiments, none of which needs new data.
| Experiment | What it distinguishes |
|---|---|
| Add only 128k AudioSet pairs (a 50/50 mixture) rather than 1.7M | Whether noise is harmful per pair or only when it dominates the mixture. If a 50/50 mix is fine, the problem is dilution, not toxicity, and upsampling the clean set fixes it |
| Add 1.7M AudioSet pairs but weight their loss at 0.1 | The same question from the optimisation side. Cheap, and standard practice for noisy web data |
| Filter AudioSet pairs by a CLAP-score threshold from the 128k-trained model | Whether a model trained on clean pairs can identify which noisy pairs are worth keeping — a bootstrapping loop that later work does use |
| Replace AudioSet titles with class-label templates ("this is a sound of dog, bark") | Whether the harm comes from wrong text or merely label-shaped text. Chapter 1 predicts the latter would be mediocre but not destructive |
The paper runs none of these, and one sentence explains why: the four-page format. But notice how much the single reported number still constrains the space. Whatever the mechanism, it is strong enough that a 14× increase in data lost 15 points — and no amount of "more data is better" intuition survives contact with that.
If you set out to rebuild CLAP from the paper alone, here is what you know, what you must infer, and what you must guess.
| Item | Status | Value or inference |
|---|---|---|
| Sampling rate, window, hop, Mel bins, band | Stated | 44.1 kHz, 1024, 320, 64, 50–8000 Hz (units corrected) |
| Crop length and padding | Stated | Random continuous 5 s; pad if shorter |
| Encoders and dimensions | Stated | CNN14 (80.8M, 2048), BERT base uncased (110M, [CLS] 768), d = 1024 |
| Text truncation | Stated | 100 characters |
| Optimiser, LR, schedule, epochs | Stated | Adam, 10−3, plateau ×10−1 patience 10, 40 epochs |
| Temperature handling | Must infer | Multiplier = 1/τ from log-space parameter; clamp scaled logits at 100 |
| L2 normalisation | Must infer | Yes — otherwise the temperature and the clip do not cohere |
| Loss sign | Must infer | Negative log-likelihood |
| Batch size of the headline model | Must guess | Somewhere in 32–768; the appendix says 768 was worse, so likely 256–512 |
| Weight decay, warmup, gradient clipping | Must guess | Unstated. Defaults are the only option |
| Which supervised setup produced each CLAP (Best) | Partly stated | Table 6 splits (S) and (F) for most tasks |
Five stated, three inferable from internal consistency, two genuine guesses. That is a reasonably reproducible paper by 2022 standards — and the three inferable items are precisely the ones this lesson spent Chapters 3 and 4 deriving, because getting them wrong means getting a model that does not train at all.
CLAP is a four-page ICASSP-style paper with a 128k-pair dataset and two off-the-shelf encoders. Its influence is out of all proportion to its size, for a reason worth naming: it did not introduce a technique, it relocated an interface. After CLAP, the natural way to ask an audio model a question is to type the question.
| Direction | What CLAP contributed | Where it went |
|---|---|---|
| Open-vocabulary audio classification | The whole recipe: dual encoder, shared space, prompted class names | Standard practice. Every later audio-text model reports zero-shot ESC50 |
| Scaled successors | The demonstration that the recipe works at 128k pairs | LAION-CLAP (2023) swapped in an HTSAT audio tower and RoBERTa text tower, trained on far more pairs, and added keyword-to-caption augmentation — the direct answer to Chapter 8's data-quality problem. Microsoft's own 2023 follow-up moved to an HTSAT encoder and a larger caption corpus |
| Text-to-audio generation | A text encoder whose embeddings are acoustically grounded | Diffusion-based audio generators condition on CLAP-style text embeddings, and the "CLAP score" — cosine similarity between the generated audio and the prompt — became a standard automatic metric for whether generated audio matches its prompt |
| Audio-text retrieval | A shared space where a sentence can query a sound archive | Sound-library search, dataset curation, and caption-based filtering of training corpora |
| Audio language models | The audio-encoder-plus-projection pattern | Later systems keep the audio tower and the projection but replace the text tower with a full LLM, turning retrieval into conversation. See Qwen2-Audio and our audio LLMs lesson |
| Swappable audio towers | Chapter 7's finding: the text tower carries the alignment burden | Made it cheap to try every new audio encoder — AST, PaSST, Audio-MAE, BEATs — inside the same contrastive frame |
Where CLAP sits. Upstream: the encoders and the idea it borrowed. Downstream: what the recipe became. Tap any node to see what it contributed and what it fixed about its parent.
| Limitation | Root cause (which chapter) | What later work did about it |
|---|---|---|
| No temporal order — "dog then siren" ≈ "siren then dog" | Clip-level pooling to one vector (Ch 3) | Frame-level or token-level audio encoders feeding sequence models |
| No localisation — cannot say when an event occurred | Same pooling | Sound event detection heads; frame-wise contrastive variants |
| Near-chance on speech content and emotion | Captions never describe linguistic content (Ch 6, Ch 8) | Speech-aware corpora and speech encoders alongside the audio tower |
| Fixed 5-second window; long clips get a random crop | Section 3.2 (Ch 2) | Feature fusion over long inputs; variable-length encoders |
| Cannot generate text — it scores captions, it does not write them | Dual-encoder by design (Ch 3) | Audio captioning models and audio LLMs with a decoder |
| Cannot abstain — softmax over candidates always sums to one | Ch 0's problem, inherited (Ch 5) | Threshold on raw cosine; open-set calibration |
| Performance depends on batch size and on caption quality | Contrastive negatives come from the batch (Ch 4, Ch 8) | Queue-based negatives; LLM-generated captions |
| 8 kHz spectral ceiling | Section 3.2 (Ch 2) | Full-band front ends in successors |
| CLIP | Wav2CLIP | AudioCLIP | CLAP | SSL audio (wav2vec 2.0, BEATs) | |
|---|---|---|---|---|---|
| Modalities | image + text | audio + CLIP space | image + text + audio | audio + text | audio only |
| Text supervision | web alt-text | AudioSet class labels | AudioSet class labels | free-form human captions | none |
| Pairs | 400M | ~2M (label pairs) | ~2M (label pairs) | 128,010 | unlabelled hours |
| Zero-shot open vocabulary? | yes | partial — inherits CLIP's | partial | yes | no — needs a fixed head |
| ESC50 zero-shot | — | — | 0.6940 | 0.826 | not applicable |
| Trained from scratch? | both towers | audio only | audio only | neither — both towers pretrained | encoder only |
| Main limitation | data scale required | label-shaped text | label-shaped text | caption coverage: no speech content | no semantics from language |
The row that carries the argument is "text supervision". Everything else in the CLAP column is borrowed, off the shelf, or smaller than the alternatives. Change only that row and the ESC50 number moves 12 points.
| Term | One line | First appeared |
|---|---|---|
| Logits | Unnormalised scores just before a softmax or sigmoid | Ch 0 |
| Zero-shot | Performing a task with no labelled examples and no gradient steps for it | Ch 0 |
| Self-supervised learning | Pretraining on unlabelled data with a pretext task | Ch 0 |
| Spectrogram | Time on one axis, frequency on the other, magnitude as the value | Ch 2 |
| Window / hop | How many samples per frequency analysis; how far you slide between analyses | Ch 2 |
| Mel scale | A frequency warp so equal steps sound like equal pitch steps | Ch 2 |
| [CLS] token | BERT's prepended summary token; its final vector represents the sequence | Ch 3 |
| Dual encoder | Two independent towers compared only by a dot product — no cross-attention | Ch 3 |
| Representational collapse | Everything maps to one point because the objective never says "not that one" | Ch 4 |
| Hard negative | A wrong pair the model currently rates highly — where nearly all the gradient goes | Ch 4 |
| Temperature | The scalar setting how sharp the softmax over similarities is; here its reciprocal multiplies the cosines | Ch 4 |
| Prompt template | A sentence frame wrapping a class name so it lands where captions live | Ch 5 |
| mAP | Mean average precision — a threshold-free ranking metric for multi-label tasks | Ch 6 |
| Symbol | Meaning | Shape / value in CLAP |
|---|---|---|
| Xa | Processed audio: the log-Mel spectrogram | RF×T = R64×690 per clip |
| F, T | Number of spectral components (Mel bins) and time bins | 64 and about 690 |
| Xt | The text of the caption | String, truncated to 100 characters |
| N | Batch size — also the number of negatives per anchor, minus one | 32 to 768 in the appendix study |
| fa, ft | Audio and text encoders | CNN14 (80.8M) and BERT base uncased (110M) |
| X̂a, X̂t | Encoder outputs, before the shared space | RN×V and RN×U, V = 2048, U = 768 |
| La, Lt | Learnable linear projections into the joint space | 2048→1024 and 768→1024; 2.88M parameters together |
| Ea, Et | The joint multimodal embeddings | RN×d, d = 1024, L2-normalised in practice |
| τ | Temperature; the logits are scaled by its reciprocal | Learnable, initialised 0.007, so the multiplier starts at 142.86 |
| C | Similarity matrix — the logits | RN×N; N correct pairs on the diagonal, N2−N incorrect off it; clipped at 100 |
| ℓtext, ℓaudio | Cross-entropy along the text axis (rows) and audio axis (columns) | Scalars; the guessing baseline for each is ln N |
| L | The symmetric loss | Scalar; 0.375 for the worked 3×3 batch at scale 4 |
| C (in Chapter 5) | Number of candidate classes at inference — unrelated to the matrix C | 50 for ESC50, whatever you type |
| Etext | Stacked embeddings of the C prompted class names — the zero-shot classifier matrix | RC×d; 200 KB for ESC50 |
The four equations, in order.
| Number | What it is |
|---|---|
| 128,010 | Training pairs, from FSD50K + ClothoV2 + AudioCaps + MACS — over 90,947 unique clips |
| 82.6% | ESC50 zero-shot, beating AudioCLIP's 69.40% and reported human accuracy of 81% |
| 73.24% / 30.24 mAP | UrbanSound8K and FSD50K zero-shot — the latter a 27-point mAP gain over Wav2CLIP |
| 100% | GTZAN Music vs Speech zero-shot — above the 99.2% supervised benchmark |
| 16 tasks / 8 domains | The evaluation suite; better than random on all of them |
| 5 datasets | Where CLAP's supervised setups set state of the art |
| 0.2809 → 0.3265 | Average zero-shot, both encoders frozen versus both trainable |
| 55.55% | ESC50 with both encoders frozen — the projections alone |
| 82.6 → 67.15 | ESC50 after adding 1.7M noisy AudioSet pairs |
| 1024 / 2048 / 768 | Shared space, audio embedding, text embedding dimensions |
| 0.007 | The initial temperature; the logit multiplier is its reciprocal, 142.86 |
| If you want… | Go to |
|---|---|
| The vision original this borrowed from | CLIP and contrastive vision-language |
| The mechanics of contrastive objectives in general | Contrastive learning |
| The audio front end in far more depth | Audio representations and cepstrum and MFCC |
| The audio tower CLAP borrowed | PANNs / CNN14 |
| Transformer audio encoders that replaced CNN14 | AST, PaSST, Audio-MAE, BEATs |
| What happens when audio becomes tokens | AudioLM and neural audio codecs |
| Speech at scale, and audio inside an LLM | Whisper, Qwen2-Audio, audio LLMs |
| The classical era CLAP replaced | Classical audio classification |
Everything in this lesson fits into one file. Here is the checklist, with the decision that matters at each step.
| Step | What to do | The decision that matters |
|---|---|---|
| 1. Pairs | Grab ClothoV2 (5 captions per clip) or a few thousand of your own captioned clips | Quality over quantity — Chapter 8. A thousand honest captions beat a hundred thousand video titles |
| 2. Front end | torchaudio.transforms.MelSpectrogram then AmplitudeToDB | Match your domain's band. Do not inherit an 8 kHz ceiling if you care about speech |
| 3. Audio tower | Any pretrained encoder — PANNs, AST, BEATs. Freeze it | Chapter 7 says this costs you little and saves you most of the compute |
| 4. Text tower | A sentence encoder. Unfreeze it | This is where the alignment happens. Thirty-three times the return |
| 5. Projections | Two nn.Linear layers into d = 512 or 1024, then L2-normalise | Linear, not MLP. Keep the geometry, do not re-learn it |
| 6. Temperature | nn.Parameter(log(1/0.007)), exponentiate, clamp the logits at 100 | Calibrate to your d: the random-cosine spread is 1/√d |
| 7. Loss | The three-line symmetric cross-entropy from Chapter 4 | Log ℓtext and ℓaudio separately; their gap is a real diagnostic |
| 8. Batch | As large as memory allows — and deduplicate by audio when sampling | Negatives come from the batch. Same-clip collisions are unsatisfiable constraints |
| 9. Sanity check | Confirm the loss drops below ln N in the first epoch | If it sits at ln N, your temperature is wrong — almost always the 0.007-versus-142.86 trap |
| 10. Evaluate | ESC50 zero-shot with "this is a sound of [class]" | It is five lines of code and the community's shared yardstick |
nn.Linear layers, and a thousand captioned clips. Three lines of loss. You will have a working open-vocabulary sound classifier by the end of a weekend — and the 82.6% will stop being a number you read.Without scrolling up: (1) write the shape of Xa and justify both numbers from the sampling rate, window, hop, and Mel settings; (2) write Equations (3) and (4) and say what the diagonal means; (3) compute the loss of a 2×2 batch whose cosines are 0.9 on the diagonal and 0.4 off it, at scale 4; (4) explain why unfreezing the text encoder mattered 33× more than the audio encoder; (5) explain why 1.7M extra pairs dropped ESC50 by 15 points. If any of the five stalls, its chapter is one tap away.