CoolFace
Modelpublic

laion/vocal-burst-detector-x3

sourceHugging Facecc-by-4.0updated 15d agoView on Hugging Face
0likes
Model Card

vocal-burst-detector-x3

### Which file do I want? `production/` — start here. The laion/voiceclap-large-v2 (3584-d, frozen) head, 5 initialisations averaged in probability space, 19 classes = 18 bursts + `no_burst`, with bs_score.py (importable scorer, unchanged API), per_class_recall.json (read it before you read any hit rate) and the full confusion matrices. `commercial/` is the same recipe on laion/voiceclap-commercial (768-d): cheaper, and — see below — the more selective rejecter. ablations/ holds the variants that explain every number here. training_extras/ carries the provenance of the new negatives. laion/vocal-burst-detector-x2 is untouched; this repository is its successor for re-scoring the laion-tts-annotated-v1 corpus.

What changed against x2, and why — three findings

x2 was used to re-classify the 63.8 M burst spans that laion/vocalburst-locator v2 + the old vocal-burst-detector-v2 had stored in laion-tts-annotated-v1. The result was that 70 % of all spans came back as `Sharp Inhale` (Mediathek: 81 %), and that the two commonest old labels — Ahem and Low Mumble, ~26 % of the old events each — were not in x2's 16-class vocabulary. The obvious reading was "x2 cannot name the two commonest sounds; add them". Three measurements turned that reading around (all in training_extras/ and the report this card summarises):

  1. 1.The old detector invented its two commonest labels. In the 3,598 real utterances that carry both the old spans and a blind whole-clip gemini-3.8-flash annotation, a Gemini event overlaps an old Ahem span in 16.5 % of cases and an old Low Mumble span in 7.3 % — against 86.5 % for old Chuckle spans and 91.5 % for old Sharp Inhale spans. A level dip (≤ −10 dB) does not change this (15.2 % / 5.0 %). Where an event does overlap, it is almost never a throat-clear or a mumble (6 and 1 of ~1,250). Those spans are locator false alarms mid-speech, given the old detector's two fallback names.
  2. 2.x2 never rejects them. On the old spans in the corpus, x2's P(no_burst) is 0.000 at the median and ≥ 0.5 on 0.03–0.09 % of them, whatever the level contrast. Its no_burst training rows were ≥ 0.5 s windows of speech or silence, ≥ 0.5 s clear of every annotation; a 0.2–0.5 s cut inside running speech is something it never saw as "nothing", so it picks the nearest burst class, and for a short breathy speech fragment that is Sharp Inhale. A calibration problem of the reject output, not a vocabulary problem.
  3. 3.A real throat-clear is not what x2 calls `Sharp Inhale` either. On Gemini's own Ahem segments, x2 says Chuckle (real, 50 %) or Humming / Affirmative Grunt / Chuckle (DramaBox); on Low Mumble it says Humming. So the two classes are missing from the vocabulary — but adding them alone would have forced the same non-events into another class.

x3 therefore does two things: it adds `Ahem` and `Low Mumble` (Gemini labels Clears Throat and Throat-clearing folded into Ahem, Whispered Mumble into Low Mumble; Cough deliberately not folded in — a cough is a different sound), and it adds a third `no_burst` sub-type, `fp_span`: old-detector spans labelled Ahem / Low Mumble that no Gemini event overlaps — 1,115 from the real utterances and 1,200 mined the same way on DramaBox (locator v2 + old detector on 35,224 clips, so that fp_span is not learnable as "is real audio"). The negative mix per epoch is 0.55 speech / 0.20 silence / 0.25 fp_span (x2: 0.75 / 0.25). Everything else is x2's recipe verbatim: one grouped split (real by speaker, DramaBox by prompt), split seed 0, five initialisations, 120 epochs, 3584 → 256 → 19 (768 → 256 → 19), dropout 0.3, 25 held-out per class.

The two new classes are 96–97 % DramaBox material. Gemini's real-audio half has 44 Ahem and 14 Low Mumble segments (x2's rule of ≥ 100 per half would have admitted neither); they were admitted on the pooled count (1,099 / 418). Their real-audio recall below is measured on those few rows and flagged ⚠ accordingly. Re-annotating old Ahem / Low Mumble spans to get more real examples was costed and dropped: finding 1 puts the expected yield at ~1 %.

The number this repository exists for: rejection of the false-alarm spans

no_burst recall on held-out rows by sub-type (fp_span rows are speaker-/prompt-disjoint from training; x2 never trained on any fp_span row, so all 2,315 of them are fair for x2 — it rejects 0.2 %):

sub-typenx2 (large-v2)**x3 `production/`**x2 (commercial)**x3 `commercial/`**
real / fp_span600 %97 %2 %92 %
dramabox / fp_span260 %96 %0 %92 %
real / speech77799 %99 %97 %96 %
real / silence2556 %44 %36 %36 %
dramabox / speech89100 %99 %99 %99 %
dramabox / silence4992 %88 %84 %82 %

On the 1,967 stored spans of 3,200 validation clips (emolia + podcast, never seen by either head; */validation_spans_x2_vs_x3.md), gate P(no_burst) ≥ 0.5:

old labelnx2 rejects**x3 `production/` rejects****x3 `commercial/` rejects**
Low Mumble10020.0 %97.2 %89.4 %
Ahem7010.0 %97.4 %76.9 %
Surprised Gasp540.0 %74.1 %16.7 %
Wistful Sigh310.0 %74.2 %29.0 %
Contented Sigh240.0 %50.0 %4.2 %
Exhausted Groan280.0 %50.0 %0.0 %
Childlike Giggle440.0 %22.7 %9.1 %
Chuckle380.0 %18.4 %0.0 %
Breathy Giggle350.0 %37.1 %0.0 %
all19670.0 %90.7 %74.2 %

Sharp Inhale as top-1 goes from 70.2 % of spans (x2) to 1.2 % (large-v2) / 3.4 % (commercial). The rejection is flat across level-dip bands (≤ −20 dB: 94 %; > 0 dB: 91 %), i.e. it is not a level detector.

Is it rejecting the right spans? Verified against unverified

All 5,143 old spans of the 3,598 Gemini utterances, split by whether a Gemini event overlaps the span (*/rejection_verified_vs_unverified.md). "Unseen" = the utterance contributed no fp_span training row.

spansnx2**x3 `production/`****x3 `commercial/`**
unverified (no Gemini event), old Ahem/Low Mumble11150.1 %98.7 % (training rows)88.0 %
unverified, other old labels, unseen utterances4820.0 %40.7 %6.2 %
verified (a Gemini event overlaps), unseen utterances29960.0 %13.7 %2.6 %
verified Chuckle5400.0 %20.6 %3.1 %
verified Scream1640.0 %6.7 %0.6 %
verified Sharp Inhale1050.0 %21.0 %6.7 %

So the two heads trade differently: `production/` rejects more, including 14 % of verified bursts at gate 0.5; `commercial/` rejects almost only the `Ahem`/`Low Mumble`-type false alarms and keeps 97 % of verified bursts. P(no_burst) is delivered ungated, and the gate moves the trade-off (ablations/gate_sweep.json):

gate on `P(no_burst)`production: verified rejectedproduction: old `Ahem`/`LM` rejected (validation)commercial: verifiedcommercial: old `Ahem`/`LM`
0.513.7 %97.3 %2.6 %84.3 %
0.710.0 %95.1 %1.6 %69.3 %
0.95.9 %91.1 %0.6 %40.8 %
0.954.7 %86.6 %0.4 %26.4 %

A reasonable default for production/ on corpus spans is a gate of 0.9; for commercial/, 0.5.

What the new negatives cost on burst classification

The old classes are the same 16 and go through the same admission rule; what changes their numbers is (a) two more classes to confuse with and (b) the fp_span negatives. Both were isolated (ablations/, large-v2, same split seed, x2 re-run reproduces x2's release exactly at 0.4659):

variant (large-v2)real, balanced, same rows as x2real, all held-outdramabox, all held-out
x2 (17 classes, no fp_span)referencereferencereference
17 classes + fp_span−7.3 pts (init 5–9: −5.2)+0.4+0.3
19 classes, no fp_span−1.7−0.1−1.1
x3 = 19 classes + `fp_span`−6.3−0.1+2.9
x3 with fp_span at 10 % of the negative draws−8.5−0.2+2.5
initialisation noise (17 classes, init 5–9 vs 0–4)−0.9−0.2+1.8

The balanced real-audio loss comes from the fp_span negatives, is consistent across initialisations, does not shrink when the mix share is cut, and lands on Frustrated Groan, Breathy Giggle, Exhausted Groan, Affirmative Grunt and Sharp Inhale — which move into their neighbour class (groan ↔ groan, giggle → chuckle), not into no_burst or the new classes. On the prior-weighted held-out sets (the rows that look like the corpus) nothing is lost. Across five split seeds (cross-source table below) the 17-class mean on real audio is unchanged (0.454 vs 0.449) and −2.7 pts on DramaBox, so the single-split −6 is partly that split.

If your use is grading generated bursts by class rather than cleaning corpus spans, the variant without fp_span negatives ships as ablations/large-v2_19cls_no_fp_span/ (same 19 classes).

Per class, x2 → x3

large-v2 (production/)

Strict recall on x3's held-out rows (n, x3, family), and the x2 → x3 change on the rows held out by both splits (n_c, x2_c → x3_c; neither head trained on them). ⚠ = fewer than 30 held-out rows in one source.

classreal nx3familyn_cx2_c → x3_cdbox nx3familyn_cx2_c → x3_c
Affirmative Grunt ⚠2966 %69 %2572 % → 60 %2673 %73 %1753 % → 59 %
Ahem ⚠ new2623 %23 %——2763 %63 %——
Breathy Giggle3429 %79 %2458 % → 25 %3063 %93 %1889 % → 67 %
Chuckle5377 %85 %3073 % → 77 %4665 %80 %2181 % → 81 %
Deep Breath5533 %65 %4229 % → 33 %5871 %91 %2467 % → 71 %
Exasperated Sigh6920 %32 %3517 % → 14 %4943 %59 %2060 % → 50 %
Exhausted Groan ⚠3352 %73 %1776 % → 53 %2631 %69 %2030 % → 30 %
Frustrated Groan ⚠3330 %64 %2568 % → 32 %2662 %73 %1974 % → 68 %
Heavy Breathing ⚠3424 %71 %2433 % → 25 %2544 %88 %1822 % → 33 %
Humming3751 %76 %3158 % → 48 %3959 %85 %2361 % → 61 %
Low Mumble ⚠ new140 %21 %——2528 %68 %——
Panting ⚠3972 %87 %3165 % → 68 %2560 %64 %1040 % → 30 %
Relief Sigh398 %46 %3010 % → 3 %3743 %76 %2748 % → 44 %
Scream ⚠3773 %73 %2868 % → 71 %2793 %93 %2496 % → 92 %
Sharp Inhale5273 %81 %3789 % → 73 %4166 %83 %2060 % → 65 %
Soft Hum ⚠2532 %60 %2241 % → 27 %2722 %59 %2433 % → 21 %
Wistful Sigh ⚠2619 %35 %2015 % → 15 %2733 %44 %2536 % → 36 %
Yawn4542 %42 %3444 % → 41 %3964 %64 %2250 % → 64 %
no_burst86297 %97 %82893 % → 97 %16495 %95 %7070 % → 94 %
held-out set (large-v2)nx3 exactx3 familycommon rowsx2 → x3 on them
real, all held-out154273.2 %81.6 %128377.9 % → 77.8 %
real, balanced 25/class46444.4 %63.4 %31751.4 % → 45.1 %
dramabox, all held-out76464.3 %79.1 %40259.0 % → 61.9 %
dramabox, balanced 25/class47556.8 %75.6 %26154.8 % → 55.2 %
`no_burst` sub-type (large-v2, held-out)nx2 recallx2 mean P(no_burst)x3 recallx3 mean P(no_burst)
real/speech77799 %0.98499 %0.974
real/silence2556 %0.43344 %0.328
real/fp_span600 %0.00297 %0.967
dramabox/speech89100 %0.99099 %0.985
dramabox/silence4992 %0.86888 %0.830
dramabox/fp_span260 %0.00196 %0.948
commercial (commercial/)

Strict recall on x3's held-out rows (n, x3, family), and the x2 → x3 change on the rows held out by both splits (n_c, x2_c → x3_c; neither head trained on them). ⚠ = fewer than 30 held-out rows in one source.

classreal nx3familyn_cx2_c → x3_cdbox nx3familyn_cx2_c → x3_c
Affirmative Grunt ⚠2945 %45 %2552 % → 40 %2669 %73 %1765 % → 53 %
Ahem ⚠ new2619 %19 %——2770 %70 %——
Breathy Giggle3435 %71 %2433 % → 38 %3073 %90 %1889 % → 89 %
Chuckle5362 %72 %3067 % → 63 %4657 %70 %2176 % → 71 %
Deep Breath555 %55 %427 % → 7 %5853 %79 %2450 % → 50 %
Exasperated Sigh699 %16 %359 % → 11 %4929 %45 %2050 % → 35 %
Exhausted Groan ⚠3315 %58 %1735 % → 18 %2619 %46 %2015 % → 15 %
Frustrated Groan ⚠3342 %58 %2560 % → 44 %2662 %69 %1968 % → 68 %
Heavy Breathing ⚠3412 %71 %2412 % → 12 %2568 %92 %1872 % → 67 %
Humming3714 %46 %3119 % → 13 %3941 %67 %2343 % → 43 %
Low Mumble ⚠ new1414 %14 %——2548 %60 %——
Panting ⚠3972 %82 %3177 % → 71 %2564 %72 %1050 % → 50 %
Relief Sigh3910 %41 %307 % → 7 %3727 %62 %2730 % → 26 %
Scream ⚠3784 %84 %2889 % → 86 %2781 %81 %2479 % → 83 %
Sharp Inhale5254 %60 %3765 % → 54 %4144 %59 %2055 % → 45 %
Soft Hum ⚠2544 %44 %2236 % → 36 %2719 %52 %2442 % → 12 %
Wistful Sigh ⚠2612 %23 %2010 % → 10 %2744 %48 %2556 % → 48 %
Yawn4520 %20 %3432 % → 24 %3944 %44 %2236 % → 36 %
no_burst86294 %94 %82891 % → 94 %16493 %93 %7067 % → 91 %
held-out set (commercial)nx3 exactx3 familycommon rowsx2 → x3 on them
real, all held-out154266.7 %74.6 %128371.9 % → 72.8 %
real, balanced 25/class46434.5 %52.4 %31739.4 % → 36.3 %
dramabox, all held-out76458.6 %70.9 %40256.2 % → 56.0 %
dramabox, balanced 25/class47552.4 %66.5 %26155.2 % → 51.7 %
`no_burst` sub-type (commercial, held-out)nx2 recallx2 mean P(no_burst)x3 recallx3 mean P(no_burst)
real/speech77797 %0.93396 %0.914
real/silence2536 %0.19436 %0.180
real/fp_span602 %0.03292 %0.792
dramabox/speech8999 %0.95599 %0.935
dramabox/silence4984 %0.41182 %0.352
dramabox/fp_span260 %0.01492 %0.772

Cross-source 2×2 (five split seeds, balanced 25/class, one head per seed and arm)

Trained on the left, tested on the right; chance = 5.3 % over 19 classes (x2: 5.9 % over 17). The "17-class mean" column averages x3's per-class recall over the 16 old classes + no_burst so it is comparable to x2's number in the same row.

cellx3 large-v2 (19)x3, 17-class meanx2 large-v2 (17)`Ahem``Low Mumble`x3 commercial (19)x3, 17-class meanx2 commercial (17)`Ahem``Low Mumble`
real → real0.422 ± 0.0110.4510.456 ± 0.0180.150.000.373 ± 0.0160.4020.434 ± 0.0040.080.00
real → dramabox0.400 ± 0.0240.4200.406 ± 0.0190.470.000.315 ± 0.0340.3510.342 ± 0.0170.020.00
dramabox → real0.342 ± 0.0130.3530.357 ± 0.0150.310.060.320 ± 0.0210.3280.344 ± 0.0100.320.10
dramabox → dramabox0.545 ± 0.0140.5580.560 ± 0.0200.610.260.514 ± 0.0120.5110.504 ± 0.0030.670.41
both → real0.430 ± 0.0070.4540.449 ± 0.0230.250.040.344 ± 0.0130.3620.383 ± 0.0140.190.09
both → dramabox0.536 ± 0.0070.5490.576 ± 0.0120.620.240.517 ± 0.0110.5120.512 ± 0.0160.720.38

The real → arms have 19 real Ahem and no real Low Mumble training rows (all 14 are held out), so their Ahem/Low Mumble cells measure DramaBox-only or near-zero training. `Ahem` transfers from DramaBox to real speech at 0.25–0.31 (25 real rows per seed; chance 0.05). `Low Mumble` does not (≤ 0.10) and should be read as a DramaBox-only class until real examples exist.

How to call it

python
import sys; sys.path.insert(0, "<dir containing bs_score.py>")   # ships in production/
from bs_score import ProductionBurstScorer

sc  = ProductionBurstScorer(device="cuda", ckpt_dir="<.../production>")
out = sc.classify(cuts16)                    # spans you already cut: list[np.float32 @16 kHz]
# each: {"label", "prob", "labels": top-3, "family", "no_burst_prob"} -- apply your own gate
out = sc.bursts(wavs16, no_burst_gate=0.9)   # locate with laion/vocalburst-locator v2, then name

Same API and output shape as x2; the checkpoint carries classes (19, no_burst last), family, label_merge, extra_classes and neg_mix. commercial/ uses bs_vcsmall.py for the encoder and the same checkpoint format. Families: Ahem → throat, Low Mumble → hum.

What it cannot do

  • —Its labels have never been checked by a human. Every training and test label is one model's opinion (gemini-3.8-flash, blind, whole utterances). The fp_span negatives are defined as "Gemini heard nothing there"; a quiet real breath that Gemini did not annotate is, by that standard, a non-event, and production/ will reject some real breaths (14 % of verified spans at gate 0.5, 6 % at 0.9).
  • —`Low Mumble` is a DramaBox class. 14 real examples in total, real recall 0.00–0.14.
  • —It is a classifier, not a locator. It labels a span something else proposed. On the 3,598 annotated utterances the old locator finds 42 of 44 Gemini Ahem events and 13 of 14 Low Mumble events — but only 23 % of Gemini's Sharp Inhale events and 47 % of Deep Breath.
  • —Half its training data is TTS output (DramaBox, LTX-2 Community Licence not assessed), and the two new classes almost entirely so.

Files

  • —production/ — large-v2 head: vocal_burst_mlp_prod_s{0..4}.pt, bs_score.py, bs_vclap.py, bs_det.py, prod_report.json, per_class_recall.{json,csv}, cross_source_2x2.json, validation_spans_x2_vs_x3.{md,json}, rejection_verified_vs_unverified.{md,json}.
  • —commercial/ — the same for the 768-d head, bs_vcsmall.py.
  • —ablations/ — large-v2_19cls_no_fp_span/ (heads + reports), large-v2_17cls_with_fp_span/, large-v2_17cls_no_fp_span_x2_rerun/, *_init5-9/, large-v2_19cls_fp_mix_0.10/ (reports), gate_sweep.json.
  • —training_extras/ — fp_span_negatives.jsonl (2,315 rows: parent clip, span, old label, level dip, why), old_spans_vs_gemini_3598_utterances.json (finding 1), x2_on_gemini_ahem_lowmumble_segments.json (finding 3).
  • —GROUPS.md, vocal_burst_groups.json — the 23-group scheme, unchanged from x2.