Audio Classification
ONNX
English
end-of-turn-detection
turn-taking
voice-agents
speech
from-scratch
Eval Results (legacy)
turnwave / README.md
Nikhil-09's picture
Upload README.md with huggingface_hub
61ff982 verified
|
Raw
History Blame Contribute Delete
5.61 kB
---
license: apache-2.0
license_name: mixed-per-model
license_link: LICENSE
library_name: onnx
pipeline_tag: audio-classification
language:
- en
tags:
- end-of-turn-detection
- turn-taking
- voice-agents
- speech
- onnx
- from-scratch
datasets:
- pipecat-ai/smart-turn-data-v3.2-train
- Scicom-intl/semantic-vad-eot
- li2017dailydialog/daily_dialog
metrics:
- roc_auc
- average_precision
model-index:
- name: TurnWave
results:
- task:
type: audio-classification
name: End-of-turn detection
dataset:
type: livekit/eot-bench-data
name: eot-bench (English)
split: validation
metrics:
- type: roc_auc
value: 0.77
name: AUC
- type: average_precision
value: 0.602
name: Average precision
- type: false_cutoff_rate
value: 42.1
name: False cutoffs @300ms latency budget (%)
- type: false_cutoff_rate
value: 17.2
name: False cutoffs @600ms latency budget (%)
---
# TurnWave β€” end-of-turn detection for voice agents
Decides whether a caller has **finished speaking** or is only pausing, so a voice
agent neither interrupts them nor leaves an awkward silence. It replaces the fixed
300–700 ms silence timeout most pipelines still use.
**Trained from scratch β€” no pretrained weights anywhere.** A causal transformer
(RoPE, RMSNorm, SwiGLU) over the transcript tail, and a CNN over log-mel
spectrograms for prosody. Even the log-mel front end is hand-built on `torch.stft`,
so there is no torchaudio or librosa dependency.
## Benchmark
Scored by [LiveKit's eot-bench](https://github.com/livekit/eot-bench) harness on
real human-to-agent conversation, using their code and published baselines. Lower
is better; **bold marks the best per column.**
| model | false cutoffs @300 ms ↓ | @600 ms ↓ | latency @5% cutoff ↓ |
|---|---|---|---|
| VAD baseline | 55.6% | 21.7% | 1600 ms |
| **TurnWave audio branch (this model)** | 42.1% | 17.2% | 1195 ms |
| SmartTurn v3.2 | 35.2% | 14.8% | 1051 ms |
| LiveKit Turn Detector v1 | **9.9%** | **4.5%** | **543 ms** |
TurnWave beats the VAD baseline on every metric the harness reports.
## Models in this repo
| file | licence | training data |
|---|---|---|
| `audio_eot_v2.onnx` | apache-2.0 | trained on smart-turn conversational clips |
| `audio_eot.onnx` | cc-by-4.0 | Phase 4; trained on semantic-vad-eot (CC BY 4.0) |
| `text_eot.int8.onnx` | cc-by-nc-sa-4.0 | trained on DailyDialog (CC BY-NC-SA 4.0) β€” non-commercial |
| `fusion_eot_v2.onnx` | cc-by-nc-sa-4.0 | fusion head retrained on conversational clips; contains the DailyDialog text branch, so it inherits its terms |
Each model's licence follows its training data, so they differ. `audio_eot_v2` is
the one the benchmark above measures and the one to use.
## Usage
```python
from huggingface_hub import hf_hub_download
from turnwave.infer import TurnDetector # pip install git+https://github.com/Nikhils-G/turnwave
detector = TurnDetector(hf_hub_download("Nikhil-09/turnwave", "audio_eot_v2.onnx"))
if detector.predict(audio=wav_16k) > 0.5:
respond()
```
16 kHz mono. The model reads the last 2 seconds ending at the decision point, which
sits 0.2 s into the pause β€” where a live agent decides, and where eot-bench scores.
| model | variant | CPU latency | size |
|---|---|---|---|
| audio_eot_v2 | fp32 | 4.81 ms | 14.0 MB |
| audio_eot | fp32 | 4.52 ms | 14.0 MB |
| text_eot | int8 | 5.02 ms | 7.2 MB |
| fusion_eot_v2 | fp32 | 38.19 ms | 42.4 MB |
INT8 is not applied blindly: dynamic quantization rewrites MatMul, so it speeds up
the transformer and *slows down* the conv-heavy branches. Each model ships whichever
variant measured faster.
## What this project found
The first version of this model scored **AP 0.945** on its own held-out test set and
**AUC 0.563** on eot-bench β€” barely above random. The policy sweep chose thresholds
of 0.0 and 1.0, meaning *ignore the model entirely*.
The cause was the training corpus, not the architecture. It derived from a dataset
whose own card declares `task_categories: [text-to-speech]` β€” read speech, whose
pauses are reading hesitations rather than conversational turn-yields. The model had
learned *"has this sentence finished being read aloud."*
Retraining on conversational data, changing nothing else, lifted AUC to **0.770**.
The in-domain score could never have revealed this; only a benchmark on data we did
not build could.
## Limitations
- **English only.** Other languages are in the training data but untested here.
- **Behind the production models**, and not a fair comparison: SmartTurn starts from
a pretrained Whisper encoder, LiveKit's is a fine-tuned 0.5B LLM distilled from a
7B teacher. This is 3.49M parameters from random initialisation.
- **Fusion wins in-domain, not on the benchmark β€” measured, not guessed.** The
fused model reaches AP 0.959 vs 0.905 audio-only on held-out conversational
clips (Whisper transcripts), but on eot-bench the text branch scores AUC 0.485 β€”
chance β€” on real human-agent speech, so fusion there is the audio branch diluted
by noise (AUC 0.751 vs 0.770). Until a text branch trained on real spoken
dialogue exists, `audio_eot_v2` is the model to deploy.
- **Non-commercial models included.** The text and fusion models derive from
DailyDialog (CC BY-NC-SA 4.0). Only the audio branches are permissively licensed.
Code, training scripts, and the full write-up: **https://github.com/Nikhils-G/turnwave**