pulsevad 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pulsevad-0.1.0/.claude-flow/policy/state.json +52 -0
- pulsevad-0.1.0/.github/workflows/publish.yml +26 -0
- pulsevad-0.1.0/.gitignore +24 -0
- pulsevad-0.1.0/.python-version +1 -0
- pulsevad-0.1.0/ATTRIBUTION.md +30 -0
- pulsevad-0.1.0/LICENSE +21 -0
- pulsevad-0.1.0/PKG-INFO +19 -0
- pulsevad-0.1.0/README.md +170 -0
- pulsevad-0.1.0/REPRODUCE.md +110 -0
- pulsevad-0.1.0/data/cache/.gitkeep +0 -0
- pulsevad-0.1.0/data/labels/.gitkeep +0 -0
- pulsevad-0.1.0/data/raw/.gitkeep +0 -0
- pulsevad-0.1.0/data/runs/.gitkeep +0 -0
- pulsevad-0.1.0/docs/assets/comparison_graph.png +0 -0
- pulsevad-0.1.0/docs/assets/multilingual_graph.png +0 -0
- pulsevad-0.1.0/modal_app.py +934 -0
- pulsevad-0.1.0/pulsevad/__init__.py +24 -0
- pulsevad-0.1.0/pulsevad/augment.py +77 -0
- pulsevad-0.1.0/pulsevad/build_cache.py +417 -0
- pulsevad-0.1.0/pulsevad/data/__init__.py +1 -0
- pulsevad-0.1.0/pulsevad/data/pulsevad_2.1k.jit +0 -0
- pulsevad-0.1.0/pulsevad/data/pulsevad_2.1k.onnx +0 -0
- pulsevad-0.1.0/pulsevad/data/pulsevad_2.1k.pth +0 -0
- pulsevad-0.1.0/pulsevad/data/pulsevad_2.1k_int8.onnx +0 -0
- pulsevad-0.1.0/pulsevad/data/pulsevad_teacher_81k.onnx +0 -0
- pulsevad-0.1.0/pulsevad/data/pulsevad_teacher_81k.pth +0 -0
- pulsevad-0.1.0/pulsevad/data/pulsevad_weights.h +183 -0
- pulsevad-0.1.0/pulsevad/download_data.py +101 -0
- pulsevad-0.1.0/pulsevad/eval.py +272 -0
- pulsevad-0.1.0/pulsevad/export_onnx.py +78 -0
- pulsevad-0.1.0/pulsevad/frontend.py +76 -0
- pulsevad-0.1.0/pulsevad/label_corpus.py +113 -0
- pulsevad-0.1.0/pulsevad/model.py +122 -0
- pulsevad-0.1.0/pulsevad/prune.py +198 -0
- pulsevad-0.1.0/pulsevad/quantize.py +245 -0
- pulsevad-0.1.0/pulsevad/scheduler.py +45 -0
- pulsevad-0.1.0/pulsevad/train.py +164 -0
- pulsevad-0.1.0/pulsevad/utils_vad.py +186 -0
- pulsevad-0.1.0/pyproject.toml +32 -0
- pulsevad-0.1.0/scripts/plot_comparison.py +130 -0
- pulsevad-0.1.0/tests/test_dataset.py +241 -0
- pulsevad-0.1.0/tests/test_env.py +25 -0
- pulsevad-0.1.0/tests/test_eval.py +101 -0
- pulsevad-0.1.0/tests/test_export.py +101 -0
- pulsevad-0.1.0/tests/test_frontend.py +84 -0
- pulsevad-0.1.0/tests/test_model.py +94 -0
- pulsevad-0.1.0/tests/test_prune.py +58 -0
- pulsevad-0.1.0/tests/test_train.py +122 -0
- pulsevad-0.1.0/uv.lock +2574 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"mode": "legacy",
|
|
4
|
+
"rules": [],
|
|
5
|
+
"budgets": [],
|
|
6
|
+
"usage": [],
|
|
7
|
+
"approvals": [],
|
|
8
|
+
"receipts": [
|
|
9
|
+
{
|
|
10
|
+
"payload": {
|
|
11
|
+
"receiptId": "sha256:eb3c2b2101ffe1b64c3bda8261a941075647bf8718d86d9ee27e86c59b7e2b91",
|
|
12
|
+
"previousReceiptHash": null,
|
|
13
|
+
"sequence": 0,
|
|
14
|
+
"issuedAt": 1788261872543,
|
|
15
|
+
"request": {
|
|
16
|
+
"identity": {
|
|
17
|
+
"id": "legacy-cli",
|
|
18
|
+
"type": "legacy"
|
|
19
|
+
},
|
|
20
|
+
"action": {
|
|
21
|
+
"type": "mcp.tool.call",
|
|
22
|
+
"resource": "hooks_session-end",
|
|
23
|
+
"tool": "hooks_session-end",
|
|
24
|
+
"server": "ruflo",
|
|
25
|
+
"network": false,
|
|
26
|
+
"destructive": false
|
|
27
|
+
},
|
|
28
|
+
"context": {
|
|
29
|
+
"metadata": {
|
|
30
|
+
"inputDigest": "sha256:9ddccf94f2e2940231b45b399e38a0221ef77ec3a79b1a042acf0e2e4aa01c36"
|
|
31
|
+
},
|
|
32
|
+
"now": 1788261872543
|
|
33
|
+
},
|
|
34
|
+
"requestId": "1a79a4fa-6deb-4cc4-b3fc-e7b9dffe9a52"
|
|
35
|
+
},
|
|
36
|
+
"decision": {
|
|
37
|
+
"requestId": "1a79a4fa-6deb-4cc4-b3fc-e7b9dffe9a52",
|
|
38
|
+
"outcome": "allowed",
|
|
39
|
+
"enforcedOutcome": "allowed",
|
|
40
|
+
"mode": "legacy",
|
|
41
|
+
"reason": "legacy-default-allow",
|
|
42
|
+
"matchedRules": [],
|
|
43
|
+
"obligations": []
|
|
44
|
+
},
|
|
45
|
+
"policyHash": "sha256:00b63b2f746eb8b9095bbd5e1dbae4ddd74fd92a6740b72f59f4069fbfda4737"
|
|
46
|
+
},
|
|
47
|
+
"hash": "sha256:0c1ec83309dd286666d559ab58a7e3a0ef8cc07aa720bf2563a0099cacf3d9f9"
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"migratedAt": 1788261872543,
|
|
51
|
+
"migratedFrom": "pre-ADR-324; capabilities=none-detected"
|
|
52
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
pypi-publish:
|
|
10
|
+
name: Upload release to PyPI
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write # MANDATORY for PyPI Trusted Publishing
|
|
14
|
+
contents: read
|
|
15
|
+
steps:
|
|
16
|
+
- name: Check out repository
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v3
|
|
21
|
+
|
|
22
|
+
- name: Build package distributions
|
|
23
|
+
run: uv build
|
|
24
|
+
|
|
25
|
+
- name: Publish to PyPI
|
|
26
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.pyc
|
|
3
|
+
data/raw/*
|
|
4
|
+
data/labels/*
|
|
5
|
+
data/cache/*
|
|
6
|
+
*.pth
|
|
7
|
+
*.onnx
|
|
8
|
+
*.tflite
|
|
9
|
+
.venv/
|
|
10
|
+
dist/
|
|
11
|
+
build/
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
!data/raw/.gitkeep
|
|
14
|
+
!data/labels/.gitkeep
|
|
15
|
+
!data/cache/.gitkeep
|
|
16
|
+
.agents/
|
|
17
|
+
data/runs/*
|
|
18
|
+
!data/runs/.gitkeep
|
|
19
|
+
specs/
|
|
20
|
+
*.pdf
|
|
21
|
+
*build_plan*
|
|
22
|
+
!pulsevad/data/*
|
|
23
|
+
!pulsevad/data/**
|
|
24
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.11
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Attribution
|
|
2
|
+
|
|
3
|
+
PulseVAD is trained exclusively on commercially clean, permissively licensed datasets and
|
|
4
|
+
tools. This document acknowledges the sources used.
|
|
5
|
+
|
|
6
|
+
## Datasets
|
|
7
|
+
|
|
8
|
+
- **LibriSpeech train-clean-100** — CC BY 4.0 — Vassil Panayotov, Guoguo Chen, Daniel Povey,
|
|
9
|
+
Sanjeev Khudanpur. Primary speech corpus.
|
|
10
|
+
- **Common Voice** — CC0 (Public Domain) — Mozilla. Multilingual speech expansion.
|
|
11
|
+
- **Multilingual LibriSpeech (MLS)** — CC BY 4.0. Multilingual speech expansion.
|
|
12
|
+
- **VoxLingua107** — CC BY 4.0. Multilingual speech expansion.
|
|
13
|
+
- **MUSAN (Noise / Music)** — CC BY 4.0 — David Snyder, Guoguo Chen, Daniel Povey. Noise
|
|
14
|
+
augmentation and background noise.
|
|
15
|
+
- **DNS Challenge Noise (Interspeech 2020)** — CC BY 4.0 / CC0 subset — Chandan K. A. Reddy et
|
|
16
|
+
al. Acoustic interference augmentation.
|
|
17
|
+
- **Synthetic Wind Noise (Mirabilii 2022)** — Open source algorithm, synthesized locally.
|
|
18
|
+
Airflow simulation at -5 dB SNR.
|
|
19
|
+
|
|
20
|
+
## Tools
|
|
21
|
+
|
|
22
|
+
- **Silero-VAD (v5/v6)** — MIT License — Silero Team. Used only as an automated self-labeling
|
|
23
|
+
engine; no Silero-released labeled dataset or weights are used or shipped.
|
|
24
|
+
|
|
25
|
+
## Explicitly Excluded (Non-Commercial, Do NOT Use)
|
|
26
|
+
|
|
27
|
+
- Silero Released Labeled Dataset — CC BY-NC-SA 4.0 — never downloaded or used.
|
|
28
|
+
- LibriVAD Dataset — CC BY-NC-SA 4.0 — never downloaded or used.
|
|
29
|
+
- Official kiloVAD Checkpoints — CC BY-NC 4.0 — referenced only as a numerical unit-test
|
|
30
|
+
reference, never shipped or trained upon.
|
pulsevad-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aydin Adnan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pulsevad-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pulsevad
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Ultra-tiny streaming VAD (kiloVAD clean-room reimplementation)
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: matplotlib>=3.8.0
|
|
8
|
+
Requires-Dist: modal>=1.5.5
|
|
9
|
+
Requires-Dist: numpy<2.0.0,>=1.24.0
|
|
10
|
+
Requires-Dist: onnx>=1.15.0
|
|
11
|
+
Requires-Dist: onnxruntime>=1.16.0
|
|
12
|
+
Requires-Dist: pyroomacoustics>=0.7.5
|
|
13
|
+
Requires-Dist: scikit-learn>=1.3.0
|
|
14
|
+
Requires-Dist: scipy>=1.11.0
|
|
15
|
+
Requires-Dist: soundfile>=0.12.1
|
|
16
|
+
Requires-Dist: torch-pruning>=1.4.0
|
|
17
|
+
Requires-Dist: torch>=2.1.0
|
|
18
|
+
Requires-Dist: torchaudio>=2.1.0
|
|
19
|
+
Requires-Dist: tqdm>=4.66.0
|
pulsevad-0.1.0/README.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# pulsevad
|
|
2
|
+
|
|
3
|
+
2,118 parameters. 2.1 KB as INT8. strictly causal. zero future context. runs on microcontrollers that choke on silero.
|
|
4
|
+
|
|
5
|
+
an ultra-compact, commercially clean voice activity detector built from scratch, inspired by the kiloVAD architecture in [arXiv:2607.25870v1](https://arxiv.org/abs/2607.25870) (*"VAD to the Bone: Ultra-Tiny Speech Activity Detection for Edge Deployment"*, INTERSPEECH 2026).
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## installation
|
|
12
|
+
|
|
13
|
+
### pip / uv
|
|
14
|
+
```bash
|
|
15
|
+
# coming soon
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
to run or reproduce the training, pruning, and cloud evaluation pipeline on Modal yourself, see [REPRODUCE.md](REPRODUCE.md).
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## what this actually is
|
|
23
|
+
|
|
24
|
+
if you've ever tried running a modern deep learning voice activity detector on a real embedded target (think an ARM Cortex-M0+ or M4 with 32 KB of RAM), you know the options suck. silero is fantastic for servers and desktop apps, but it weighs 545,000 parameters (~2.2 MB) and demands millions of MACs per inference. other tiny models in academic papers either rely on non-causal lookahead (cheating by looking 600 ms into the future), require exotic activation functions that don't exist in CMSIS-NN, or use non-commercial research licenses.
|
|
25
|
+
|
|
26
|
+
pulsevad takes raw 16 kHz mono audio, computes a 64-channel log-mel spectrogram over a 200 ms causal window, and runs a depthwise-separable 1D CNN pruned down to **2,118 parameters**.
|
|
27
|
+
|
|
28
|
+
quantized with round-to-nearest INT8, the entire weight payload is **2.1 KB**. it ships as a single drop-in C header (`pulsevad_weights.h`) and standard ONNX graphs.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## why building this was hell (and how it actually works)
|
|
33
|
+
|
|
34
|
+
you cannot train a 2,118 parameter network from scratch on noisy audio. it gets stuck in terrible local minima and predicts pure noise 100% of the time.
|
|
35
|
+
|
|
36
|
+
here is how we got it to work:
|
|
37
|
+
|
|
38
|
+
1. **the 81k teacher**: we first trained an 81,090-parameter CNN backbone on LibriSpeech augmented with synthetic room impulse responses (pyroomacoustics), synthetic wind profiles, and heavy background noise from MUSAN at -10 dB to +10 dB SNR.
|
|
39
|
+
2. **commercially clean self-labeling**: instead of using non-commercial academic labels (like LibriVAD or AVA CC-BY-NC splits), we labeled 28,539 LibriSpeech files using Silero-VAD under an MIT license, using a 0.50/0.35 hysteresis state machine quantized to a strict 10 ms grid. 100% permissive commercial data only.
|
|
40
|
+
3. **DepGraph structured pruning**: uniform pruning collapses at sub-3k params. we used dependency-graph magnitude pruning to identify coupled channel dependencies across depthwise and pointwise layers, carving out the exact 2.1k channel spec.
|
|
41
|
+
4. **knowledge distillation**: we fine-tuned the 2.1k student under the frozen 81k teacher using KL divergence with temperature scaling and cosine learning rate decay.
|
|
42
|
+
5. **the silent bias trap**: LibriSpeech is ~78% active speech. a distilled model naturally inherits a positive prior (+1.4 logit shift), which causes it to trigger on pure air conditioning noise (yielding a disastrous 25% false positive rate on pure noise). we implemented quantile-based prior bias calibration to shift the linear classifier bias, crushing pure-noise FPR to **3.5%** while keeping ROC-AUC strictly invariant.
|
|
43
|
+
6. **post-training quantization (PTQ)**: batchnorm layers were mathematically folded into conv weights before calibration. using symmetric per-channel weight scaling and per-tensor activation scaling, the AUC gap between FP32 and INT8 is under **0.001**.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## cross-vad benchmark: size, compute & latency
|
|
48
|
+
|
|
49
|
+
we benchmarked pulsevad against standard embedded baselines and measured silero-vad v5 on identical causal 200 ms audio chunks:
|
|
50
|
+
|
|
51
|
+
| model | params | footprint | MACs / 200 ms | input latency | commercial license |
|
|
52
|
+
| :--- | :---: | :---: | :---: | :---: | :---: |
|
|
53
|
+
| **PulseVAD (81k teacher)** *(measured)* | 81,090 | 324 KB FP32 / 81 KB INT8 | 1.66M | 200 ms | **YES (MIT)** |
|
|
54
|
+
| **PulseVAD (2.1k ship)** *(measured)* | **2,118** | **8.5 KB FP32 / 2.1 KB INT8** | **44,000** | **200 ms** | **YES (MIT)** |
|
|
55
|
+
| **Silero-VAD (v6)** *(measured)* | 309,000 | ~1.2 MB | >8M | 32 ms | YES (MIT) |
|
|
56
|
+
| **Silero-VAD (v5)** *(measured)* | 545,000 | ~2.2 MB | >10M | 32 ms | YES (MIT) |
|
|
57
|
+
| **MarbleNet** *(measured)* | 91,000 | ~370 KB | >2.0M | 630 ms | non-commercial (NS) |
|
|
58
|
+
| **AtomicVAD** *[cited]* | 300 | ~1.2 KB | 6,000 | 630 ms | non-commercial (custom GGCU) |
|
|
59
|
+
| **TinyVAD** *[cited]* | 11,600 | n/a | ~80,000 | 630 ms | non-causal (87.5% lookahead) |
|
|
60
|
+
| **ResectNet** *[cited]* | 4,500 | n/a | n/a | 200 ms | non-commercial |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## acoustic evaluation: measured AUC on held-out test sets
|
|
65
|
+
|
|
66
|
+
evaluated causally with 0% overlap across 2,000 audio windows per category on the exact same audio:
|
|
67
|
+
|
|
68
|
+
| category | PulseVAD 2.1k INT8 | PulseVAD 2.1k FP32 | PulseVAD 81k Teacher | Silero-VAD v6 (measured) | Silero-VAD v5 (measured) | MarbleNet (measured) |
|
|
69
|
+
| :--- | :---: | :---: | :---: | :---: | :---: | :---: |
|
|
70
|
+
| **clean speech** | 0.976 | 0.977 | **0.989** | 0.988 | 0.990 | 0.970 |
|
|
71
|
+
| **windy / reverb** | **0.938** | 0.937 | **0.985** | 0.934 | 0.960 | 0.910 |
|
|
72
|
+
| **DNS synthetic noise** | **0.891** | 0.891 | **0.943** | 0.873 | 0.913 | 0.903 |
|
|
73
|
+
| **speech + noise (0-20 dB)** | 0.903 | 0.904 | **0.966** | 0.916 | 0.944 | 0.924 |
|
|
74
|
+
| **pure noise (FPR@95, gate <5%)**| **0.035 (3.5%)** | 0.040 (4.0%) | 0.040 (4.0%) | 0.004 (0.4%) | 0.017 (1.7%) | 0.024 (2.4%) |
|
|
75
|
+
|
|
76
|
+
*silero v5 and v6 were streamed in 512-sample causal chunks with hidden states reset per window. marblenet was evaluated using its 80-channel mel frontend in 11-frame output aggregations.*
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## multilingual & indian language benchmark
|
|
81
|
+
|
|
82
|
+
we tested generalization on in-the-wild speech across 10 diverse languages from Google FLEURS mixed with realistic MUSAN noise at 0 to 20 dB SNR. this includes 4 major Indian languages (Hindi, Tamil, Telugu, Bengali):
|
|
83
|
+
|
|
84
|
+

|
|
85
|
+
|
|
86
|
+
| language | PulseVAD 2.1k INT8 | PulseVAD 2.1k FP32 | PulseVAD 81k Teacher | Silero-VAD v5 (545k) |
|
|
87
|
+
| :--- | :---: | :---: | :---: | :---: |
|
|
88
|
+
| **English (US)** | **0.882** | 0.882 | 0.899 | 0.725 |
|
|
89
|
+
| **Spanish (LatAm)** | **0.866** | 0.866 | 0.884 | 0.846 |
|
|
90
|
+
| **French** | **0.819** | 0.820 | 0.876 | 0.812 |
|
|
91
|
+
| **German** | **0.854** | 0.855 | 0.872 | 0.819 |
|
|
92
|
+
| **Mandarin Chinese** | **0.826** | 0.826 | 0.864 | 0.777 |
|
|
93
|
+
| **Japanese** | 0.779 | 0.777 | **0.815** | 0.794 |
|
|
94
|
+
| **Hindi (India)** | **0.872** | 0.872 | 0.923 | 0.857 |
|
|
95
|
+
| **Tamil (India)** | **0.881** | 0.882 | 0.926 | 0.879 |
|
|
96
|
+
| **Telugu (India)** | **0.903** | 0.904 | 0.924 | 0.873 |
|
|
97
|
+
| **Bengali (India)** | 0.843 | 0.843 | **0.884** | 0.845 |
|
|
98
|
+
| **Macro Average** | **0.852** | **0.853** | **0.887** | **0.823** |
|
|
99
|
+
|
|
100
|
+
in noisy speech conditions, pulsevad's mel frontend and dilated convolutions beat silero v5 across 8 out of 10 languages, outperforming it on average by **+0.029 AUC** while using **257x fewer parameters**.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## real talk: advantages vs disadvantages
|
|
105
|
+
|
|
106
|
+
no model is magic. here is the honest breakdown of when you should use pulsevad and when you should not.
|
|
107
|
+
|
|
108
|
+
### advantages
|
|
109
|
+
- **runs anywhere**: 2.1 KB fits in L1 cache or tiny MCU SRAM without external DRAM.
|
|
110
|
+
- **zero dependencies**: pure C array weights (`pulsevad_weights.h`) or standard ONNX. no PyTorch, no heavy runtime, no recurrent state tensors to track.
|
|
111
|
+
- **tough on noise**: holds up remarkably well against wind, reverberation, and babble noise because it was trained with aggressive augmentations.
|
|
112
|
+
- **100% commercially permissive**: clean MIT license with no non-commercial viral traps.
|
|
113
|
+
- **strictly causal**: 0 ms lookahead. what happens in the future stays in the future.
|
|
114
|
+
|
|
115
|
+
### disadvantages
|
|
116
|
+
- **200 ms window granularity**: silero streams in 32 ms sub-chunks. if you need instantaneous 30 ms word-boundary cuts for live transcription, pulsevad's 200 ms input buffer has higher initial buffering latency.
|
|
117
|
+
- **clean speech ceiling**: on pristine studio speech with zero background noise, silero's hundreds of thousands of parameters give it a higher ceiling (0.988–0.990 vs 0.976 AUC).
|
|
118
|
+
- **requires mel frontend**: pulsevad expects 64 log-mel bins. you need an FFT + mel filterbank implementation on your target device (though standard CMSIS-DSP covers this easily).
|
|
119
|
+
|
|
120
|
+
### head-to-head: vs marblenet & silero (v5 vs v6)
|
|
121
|
+
- **vs MarbleNet (91k params)**: MarbleNet was NVIDIA's lightweight VAD for NeMo. at 91,000 parameters and >2M MACs, it incurs 630 ms input latency and carries a non-commercial license. PulseVAD 2.1k is **43x smaller**, **3.1x lower latency**, and beats MarbleNet on clean speech (**0.976 vs 0.970 AUC**) and windy audio (**0.938 vs 0.910 AUC**).
|
|
122
|
+
- **vs Silero v5 (545k) & v6 (309k)**: Silero v6 trimmed parameters from 545k to 309k (~1.2 MB). while both Silero versions excel on clean studio audio (0.988–0.990 AUC), PulseVAD 2.1k INT8 beats Silero v6 on windy audio (**0.938 vs 0.934**) and DNS synthetic noise (**0.891 vs 0.873**) while being **145x smaller** than v6 and **257x smaller** than v5.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## under the hood
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
16 kHz mono audio (3,200 samples = 200 ms)
|
|
130
|
+
│
|
|
131
|
+
▼
|
|
132
|
+
[ pre-emphasis (0.97) + z-norm ]
|
|
133
|
+
│
|
|
134
|
+
▼
|
|
135
|
+
[ 64-bin log-mel filterbank (21 frames x 64 bins) ]
|
|
136
|
+
│
|
|
137
|
+
▼
|
|
138
|
+
[ conv1d adapter: 64 -> 16 ]
|
|
139
|
+
│
|
|
140
|
+
▼
|
|
141
|
+
[ depthwise-separable block 1: k=11, ch=16 ]
|
|
142
|
+
│
|
|
143
|
+
▼
|
|
144
|
+
[ depthwise-separable block 2: k=17, ch=16 ]
|
|
145
|
+
│
|
|
146
|
+
▼
|
|
147
|
+
[ dilated depthwise block 3: k=29, dilation=2, ch=16 ]
|
|
148
|
+
│
|
|
149
|
+
▼
|
|
150
|
+
[ global average pooling -> linear (16 -> 2) ]
|
|
151
|
+
│
|
|
152
|
+
▼
|
|
153
|
+
[ calibrated prior bias shift (-1.40) ] -> P(speech)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## references & attribution
|
|
159
|
+
|
|
160
|
+
- **kiloVAD Paper**: Stephen Bauer, Sheila Seidel, Shanza Iftikhar, Scott Veidenheimer, Gorkem Ulkar. *"VAD to the Bone: Ultra-Tiny Speech Activity Detection for Edge Deployment"*, [arXiv:2607.25870v1](https://arxiv.org/abs/2607.25870), INTERSPEECH 2026. (inspiration for ultra-tiny CNN VAD and structural pruning targets).
|
|
161
|
+
- **Silero-VAD**: [snakers4/silero-vad](https://github.com/snakers4/silero-vad) (MIT License) used as the teacher state-machine labeling tool and evaluation baseline.
|
|
162
|
+
- **Google FLEURS**: [google/fleurs](https://huggingface.co/datasets/google/fleurs) for multilingual speech evaluation.
|
|
163
|
+
- **LibriSpeech & OpenSLR**: Vassil Panayotov et al., [OpenSLR 12](https://www.openslr.org/12/) (CC BY 4.0).
|
|
164
|
+
- **MUSAN Corpus**: David Snyder et al., [OpenSLR 17](https://www.openslr.org/17/) (CC BY 4.0).
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## license
|
|
169
|
+
|
|
170
|
+
MIT License. see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# reproduce pulsevad end-to-end
|
|
2
|
+
|
|
3
|
+
this guide documents how to reproduce the entire pulsevad training, pruning, quantization, export, and cross-vad benchmarking pipeline on modal.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## prerequisites
|
|
8
|
+
|
|
9
|
+
1. install `uv` (fast python package manager):
|
|
10
|
+
```bash
|
|
11
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
12
|
+
```
|
|
13
|
+
2. clone this repo and sync dependencies:
|
|
14
|
+
```bash
|
|
15
|
+
git clone https://github.com/AydinAdnan/PulseVAD.git
|
|
16
|
+
cd PulseVAD
|
|
17
|
+
uv sync
|
|
18
|
+
```
|
|
19
|
+
3. authenticate with modal:
|
|
20
|
+
```bash
|
|
21
|
+
uv run modal setup
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## pipeline execution steps
|
|
27
|
+
|
|
28
|
+
### 1. download training corpora
|
|
29
|
+
pulls librispeech dev-clean (speech) and musan (noise, music, speech) onto the `pulsevad-data` cloud volume:
|
|
30
|
+
```bash
|
|
31
|
+
uv run modal run modal_app.py::download
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. generate teacher speech labels
|
|
35
|
+
labels audio with silero vad hysteresis state machine into canonical 10 ms grid manifests:
|
|
36
|
+
```bash
|
|
37
|
+
uv run modal run modal_app.py::label
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 3. synthesize augmented window cache
|
|
41
|
+
generates 3.4m augmented 200 ms windows with noise mixing, simulated rirs, and noise-only rejection samples:
|
|
42
|
+
```bash
|
|
43
|
+
uv run modal run modal_app.py::build_cache --seed 0
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 4. train unpruned teacher model (81k params)
|
|
47
|
+
trains the unpruned 81k cnn teacher on the augmented cache for 40 epochs with cosine annealing:
|
|
48
|
+
```bash
|
|
49
|
+
uv run modal run modal_app.py::train --seed 0
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 5. structured pruning & self-distillation (2.1k student)
|
|
53
|
+
applies depgraph magnitude pruning to collapse channels to the exact 2,118 parameter spec and runs 8-epoch self-distillation with kl divergence:
|
|
54
|
+
```bash
|
|
55
|
+
uv run modal run modal_app.py::distill --seed 0
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 6. batchnorm folding, int8 quantization & onnx / c export
|
|
59
|
+
folds batchnorm into conv weights, runs rtn int8 quantization with activation calibration, verifies the <= 0.002 auc gap, and exports onnx pairs and `pulsevad_weights.h`:
|
|
60
|
+
```bash
|
|
61
|
+
uv run modal run modal_app.py::export --seed 0
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 7. cache audio for competitor evaluation
|
|
65
|
+
regenerates the 5 held-out evaluation sets (clean, windy, dns synthetic, speech+noise, pure noise) with raw audio preserved so all models score identical windows:
|
|
66
|
+
```bash
|
|
67
|
+
uv run modal run modal_app.py::cache_eval_audio
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 8. run all evaluations in parallel
|
|
71
|
+
```bash
|
|
72
|
+
# terminal 1: held-out evaluation on pulsevad models (teacher, 2.1k fp32, 2.1k int8)
|
|
73
|
+
uv run modal run modal_app.py::eval_heldout --seed 0
|
|
74
|
+
|
|
75
|
+
# terminal 2: score silero-vad v5 on the exact same 200 ms windows
|
|
76
|
+
uv run modal run modal_app.py::eval_competitors
|
|
77
|
+
|
|
78
|
+
# terminal 3: score marblenet on the exact same windows (nemo container)
|
|
79
|
+
uv run modal run modal_app.py::eval_marblenet
|
|
80
|
+
|
|
81
|
+
# terminal 4: multilingual benchmark across 10 global & indian languages (fleurs)
|
|
82
|
+
uv run modal run modal_app.py::eval_multilingual
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 9. assemble cross-vad benchmark comparison
|
|
86
|
+
aggregates measured rows and cited competitor numbers into the final comparison artifacts:
|
|
87
|
+
```bash
|
|
88
|
+
uv run modal run modal_app.py::build_comparison --seed 0
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 10. download local artifacts
|
|
92
|
+
pull the final reports, c header, onnx graphs, and benchmark tables to your local workspace:
|
|
93
|
+
```bash
|
|
94
|
+
uv run modal volume get --force pulsevad-data runs/pruned_seed_0/comparison.md ./data/runs/pruned_seed_0/comparison.md
|
|
95
|
+
uv run modal volume get --force pulsevad-data runs/pruned_seed_0/comparison.json ./data/runs/pruned_seed_0/comparison.json
|
|
96
|
+
uv run modal volume get --force pulsevad-data runs/pruned_seed_0/heldout_report.json ./data/runs/pruned_seed_0/heldout_report.json
|
|
97
|
+
uv run modal volume get --force pulsevad-data runs/pruned_seed_0/competitor_report.json ./data/runs/pruned_seed_0/competitor_report.json
|
|
98
|
+
uv run modal volume get --force pulsevad-data runs/pruned_seed_0/multilingual_report.json ./data/runs/pruned_seed_0/multilingual_report.json
|
|
99
|
+
uv run modal volume get --force pulsevad-data runs/pruned_seed_0/pulsevad_weights.h ./data/runs/pruned_seed_0/pulsevad_weights.h
|
|
100
|
+
uv run modal volume get --force pulsevad-data runs/pruned_seed_0/pulsevad_2.1k.onnx ./data/runs/pruned_seed_0/pulsevad_2.1k.onnx
|
|
101
|
+
uv run modal volume get --force pulsevad-data runs/pruned_seed_0/pulsevad_2.1k_int8.onnx ./data/runs/pruned_seed_0/pulsevad_2.1k_int8.onnx
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 11. generate visual benchmark plots
|
|
105
|
+
```bash
|
|
106
|
+
uv run python scripts/plot_comparison.py
|
|
107
|
+
```
|
|
108
|
+
generates:
|
|
109
|
+
- `data/runs/pruned_seed_0/comparison_graph.png`
|
|
110
|
+
- `data/runs/pruned_seed_0/multilingual_graph.png`
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
Binary file
|
|
Binary file
|