DEV Community

Aydin Adnan
Aydin Adnan

Posted on

How i got a 2,118-parameter VAD to almost beat Silero

2,118 parameters. 2.1 KB of INT8 weights. 44k MACs per inference. a tiny 200 ms causal VAD designed for edge deployment.

and on a fresh AVA-Speech subset, it does not beat Silero on accuracy. that is the honest result.

This is PulseVAD, a strictly causal voice activity detector that runs on a Cortex-M. here's the full build: the architecture, the training recipe, the silent bias bug, and what the benchmark actually proves.

Why build another VAD

Silero-VAD is the default answer and it's genuinely good. it is also hundreds of thousands of parameters, over a megabyte of model weights, and built for runtimes much larger than the tiny-memory targets PulseVAD is aimed at. on a desktop nobody cares. on a Cortex-M0+ with 32 KB of SRAM, the deployment problem is different.

kiloVAD (arXiv:2607.25870, INTERSPEECH 2026) proved you can go absurdly small: 2,052 parameters, depthwise-separable CNN, no recurrence. exactly what i wanted to study, except the public checkpoint is not the same commercially permissive package i wanted to ship.

so the plan: reimplement the kiloVAD-style backbone from scratch, train my own teacher, and make the pipeline reproducible. the architecture is a faithful small-CNN design, not a novelty claim. the actual engineering work is in data, labels, pruning, calibration, quantization, and deployment.

The model, stage by stage

everything happens on a 200 ms causal window. 3,200 samples at 16 kHz mono in, P(speech) out. no future audio is used.

  1. pre-emphasis (0.97) + waveform normalization. microphone gain matters less.
  2. 64-bin log-mel, 25 ms hann window, 10 ms hop, 21 frames. then per-bin normalization across the window, so the model focuses on spectral shape instead of absolute loudness.
  3. 1x1 conv adapter, 64 -> 128. the fixed mel interface stays separate from the internal channel width.
  4. depthwise k=11 + pointwise. local temporal patterns.
  5. two 1x1 projections, 128 -> 64 -> 64. cheap channel mixing.
  6. residual block k=17, two depthwise-separable sub-blocks with a 1x1 skip.
  7. dilated depthwise k=29, dilation 2. the receptive field covers the 200 ms context without recurrence or hidden state.
  8. pointwise back to 128, global average pool over time.
  9. linear 128 -> 2, softmax. done.

conv + batchnorm + relu, nothing exotic. the shipped package contains ONNX, TorchScript, a state dict, and a C header of int8_t arrays. the model is small. the frontend still has a cost: an FFT and mel filterbank must exist somewhere in the target system.

The part nobody tells you: you can't train 2.1k params directly

i tried. a 2,118-parameter net trained from scratch on noisy audio collapses into bad basins. tiny models do not have much room to discover a good representation from random initialization.

so the real pipeline is teacher-first:

1. train an 81k teacher. use LibriSpeech with MUSAN and DNS noise, synthetic wind, and simulated room impulse responses.

2. generate training labels. use a Silero-VAD-based hysteresis labeller over the training corpus, rasterized to a strict 10 ms grid. this is pseudo-labeling, not human ground truth. that distinction matters when interpreting the final accuracy.

3. add multilingual and noisy examples. Common Voice, MLS, VoxLingua107, and noise corpora broaden the acoustic coverage. the goal is not to claim universal language invariance without per-language confidence intervals; the goal is to avoid building an English-only detector by accident.

4. structured pruning. DepGraph keeps coupled depthwise, pointwise, batchnorm, and residual dimensions consistent. the final shipped model lands at 2,118 parameters.

5. distillation. train the pruned student with the frozen teacher using KL divergence plus cross-entropy. this recovers much of what pruning removed.

then it went wrong.

The silent bias trap

first eval of the distilled student: clean speech looked great. then pure noise. the detector fired far too often. completely unusable as an always-on gate.

the cause is boring in hindsight. a speech-heavy training distribution creates a prior toward speech. on clean audio the acoustic evidence dominates. on pure noise there is no evidence, so the prior wins and the model hallucinates speech.

this is the kind of bug that ships. AUC and a test-set F1 can look respectable while an always-on product still wakes up constantly. measure false alarms on negative-only audio separately.

the fix is bias calibration. a constant shift in the speech score preserves ranking, so ROC-AUC stays unchanged. the operating point does not stay unchanged. that is why the calibration must be reported with the exact threshold, negative set, and raw predictions.

on the repository’s original internal comparison, the calibrated PulseVAD row reported fewer false alarms than the cited kiloVAD row on its selected noise and music sets. those values are useful engineering results, but they are not a universal noise benchmark and they are not the same as FPR at 95% TPR.

What the metric name must say

A pure-noise set contains only negative examples. TPR is undefined there. therefore this label is wrong:

pure noise FPR@95

The valid label is:

false-positive rate on pure-noise windows at a declared fixed threshold

If the operating point is selected to reach 95% speech recall, that threshold must be selected using a separate validation set containing speech and non-speech. it must then be frozen before evaluating the negative test set.

Quantization

PulseVAD uses post-training INT8 quantization with batchnorm folding, symmetric per-channel weight scaling, and per-tensor activation scaling. the checked-in INT8 ONNX graph is 26,787 bytes; the FP32 graph is 11,968 bytes. the weight payload and the complete deployable graph are different numbers, so both should be reported.

The repository’s FP32 and INT8 independent results are close. that supports the claim that this quantization configuration did not create a large accuracy loss on the tested corpus. it does not prove losslessness on every microphone, runtime, or hardware target.

The benchmark that matters

The first independent smoke test used FSDD speech recordings and ESC-50 environmental recordings. it is useful, but it has clip-level source labels rather than dense human speech boundaries.

The stronger test used official AVA-Speech labels and audio from a stratified subset of 12 source videos. AVA-Speech is movie audio with dense human interval labels for NO_SPEECH, CLEAN_SPEECH, SPEECH_WITH_MUSIC, and SPEECH_WITH_NOISE. the subset contains 54,000 complete non-overlapping 200 ms windows:

  • 15,446 clean-speech windows
  • 5,929 speech-with-music windows
  • 9,873 speech-with-noise windows
  • 22,752 no-speech windows

All models received the same 16 kHz mono windows. PulseVAD and kiloVAD used their respective official feature frontends. Silero state was reset at each independent 200 ms window. WebRTC was scored using 10 ms frames aggregated to the same 200 ms decision.

model parameters ROC-AUC best F1 FPR @ 95% TPR no-speech positive rate @ 0.5
Silero VAD 6.2.1 hundreds of thousands 0.8947 0.8442 0.6939 0.31%
kiloVAD checkpoint 2,052 0.8752 0.8234 0.7366 43.26%
PulseVAD FP32 2,118 0.8742 0.8213 0.7150 7.89%
PulseVAD INT8 2,118 0.8726 0.8201 0.7264 7.16%
WebRTC mode 3 non-neural 0.7874 0.7889 1.0000 15.30%
WebRTC mode 1 non-neural 0.7155 0.7850 0.7028 62.24%

The result is clear. on this AVA-Speech subset, Silero wins overall accuracy, F1, and default-threshold no-speech rejection. PulseVAD is close to kiloVAD and remains much cleaner than kiloVAD at the 0.5 operating point. neither tiny model beats Silero on this dense movie-audio test.

At threshold 0.5, the speech-positive rates were:

model clean speech speech + music speech + noise no speech
PulseVAD INT8 74.51% 62.34% 65.35% 7.16%
kiloVAD 87.87% 87.67% 88.46% 43.26%
Silero 59.99% 37.31% 43.75% 0.31%

These condition rates are diagnostic rates, not recalls, because each condition is single-class. the model’s threshold must be chosen for the product’s tradeoff between misses and false alarms.

On latency claims

The article’s original 0.73 ms number is a local CPU measurement, not a universal latency guarantee. the exact CPU, thread count, warm-up, batch size, preprocessing inclusion, and repetition statistics must be published.

The fresh benchmark runtimes are also not an apples-to-apples deployment ranking: PulseVAD and kiloVAD were evaluated in batches, while Silero was evaluated sequentially. batch-amortized milliseconds per window are useful for throughput, but they are not single-window end-to-end latency.

For an embedded claim, benchmark the actual target. report peak RAM, flash, frontend cost, operator implementation, clock rate, real-time factor, and p50/p95/p99 latency.

Honest weak spots

  • accuracy on difficult real audio. the AVA-Speech subset shows Silero ahead of both tiny models in AUC and F1.
  • default false alarms. PulseVAD’s no-speech rate is 7.16% on the AVA subset at threshold 0.5, versus 0.31% for Silero. threshold calibration and product-level hysteresis matter.
  • music. PulseVAD detects only 62.34% of speech-with-music windows at threshold 0.5 in this subset. music is a difficult distractor for a 2k-parameter model.
  • noisy speech. PulseVAD detects 65.35% of speech-with-noise windows at threshold 0.5 in this subset. this is not a universal far-field or noisy-call guarantee.
  • 200 ms granularity. Silero supports much smaller streaming chunks. if you need 30 ms word-boundary decisions, PulseVAD’s input buffer is real latency.
  • frontend cost. tiny weights do not mean zero system cost. FFT, mel filtering, normalization, buffers, and runtime kernels still consume memory and compute.
  • benchmark scope. the AVA result here is a 12-video stratified subset, not the complete approximately-40-hour release. the complete release should be run before making a publication-grade claim.

Takeaway

the model is not the hard part. the architecture is a compact stack of depthwise convolutions. the hard part is the surrounding evaluation: clean labels, unseen speakers and domains, calibrated thresholds, negative-only false-alarm tests, and target-device measurements.

PulseVAD’s defensible advantage is deployment efficiency. it is tiny, causal, and practical to package for constrained hardware. the current evidence does not support saying it universally beats Silero on noise or accuracy. on the AVA-Speech subset, Silero is the accuracy winner. PulseVAD is the resource-efficiency contender.

that is still a useful product position. just make the claim match the evidence.

repo: github.com/AydinAdnan/PulseVAD.

pip install pulsevad. ships as ONNX, TorchScript, and a C header.

Top comments (0)