Commit ·
d5878b3
0
Parent(s):
Release Indic Smart Turn
Browse files- .gitattributes +36 -0
- README.md +103 -0
- charts.html +89 -0
- config.json +13 -0
- figures/group1_tiny_int8-dark.svg +77 -0
- figures/group1_tiny_int8-light.svg +77 -0
- figures/group2_base_int8-dark.svg +77 -0
- figures/group2_base_int8-light.svg +77 -0
- figures/group3_tiny_fp32-dark.svg +81 -0
- figures/group3_tiny_fp32-light.svg +81 -0
- figures/group4_base_fp32-dark.svg +81 -0
- figures/group4_base_fp32-light.svg +81 -0
- indic-smart-turn-base-fp32.onnx +3 -0
- indic-smart-turn-base-int8.onnx +3 -0
- indic-smart-turn-tiny-fp32.onnx +3 -0
- indic-smart-turn-tiny-int8.onnx +3 -0
- int8_cpu_dependence.md +26 -0
- pause-test.mp4 +3 -0
.gitattributes
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
pause-test.mp4 filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: bsd-2-clause
|
| 3 |
+
language: [en, hi, mr, bn, ta, te, kn, ml, gu, pa, or, as]
|
| 4 |
+
pipeline_tag: audio-classification
|
| 5 |
+
tags: [end-of-turn, turn-detection, voice-agent, pipecat, smart-turn, onnx, indic]
|
| 6 |
+
base_model: openai/whisper-base
|
| 7 |
+
datasets: [adimyth/indic-smart-turn-data, ai4bharat/IndicVoices, santhosh-005/tamil-eot, pipecat-ai/smart-turn-data-v3.2-train]
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
# Indic Smart Turn
|
| 11 |
+
|
| 12 |
+
End-of-turn detection for English and eleven Indian languages: Hindi, Marathi, Bengali, Tamil, Telugu, Kannada, Malayalam, Gujarati, Punjabi, Odia and Assamese. A voice agent has to decide at every pause whether the user has finished speaking. Indic Smart Turn makes that decision from the audio alone and runs on one CPU core in under 50 ms.
|
| 13 |
+
|
| 14 |
+
It is built with the [Pipecat Smart Turn v3](https://github.com/pipecat-ai/smart-turn) recipe from Whisper weights and trained mostly on real Indian phone conversations from IndicVoices and TamilEOT, plus Pipecat's own data. It loads in Pipecat's `LocalSmartTurnAnalyzerV3` with a one-line change.
|
| 15 |
+
|
| 16 |
+
- **Models:** [adimyth/indic-smart-turn](https://huggingface.co/adimyth/indic-smart-turn)
|
| 17 |
+
- **Data:** [adimyth/indic-smart-turn-data](https://huggingface.co/datasets/adimyth/indic-smart-turn-data)
|
| 18 |
+
- **Release post:** [Building Indic Smart Turn](https://adimyth.in/essays/building-indic-smart-turn)
|
| 19 |
+
|
| 20 |
+
<video src="https://huggingface.co/adimyth/indic-smart-turn/resolve/main/pause-test.mp4" controls muted width="100%"></video>
|
| 21 |
+
|
| 22 |
+
## Results
|
| 23 |
+
|
| 24 |
+
Accuracy at the 0.5 threshold on the same speaker-disjoint test clips, with ROC-AUC at the end of each row. Smart Turn v3.2 ships as a whisper-tiny in two files, int8 for CPU and fp32 for GPU, so each group compares with the v3.2 file of the same precision.
|
| 25 |
+
|
| 26 |
+
### Tiny int8 vs Smart Turn v3.2 int8
|
| 27 |
+
|
| 28 |
+
Same encoder size, precision and speed as the shipped CPU file.
|
| 29 |
+
|
| 30 |
+

|
| 31 |
+
|
| 32 |
+
### Base int8 vs Smart Turn v3.2 int8
|
| 33 |
+
|
| 34 |
+

|
| 35 |
+
|
| 36 |
+
### Tiny fp32 vs Smart Turn v3.2 fp32
|
| 37 |
+
|
| 38 |
+

|
| 39 |
+
|
| 40 |
+
### Base fp32 vs Smart Turn v3.2 fp32
|
| 41 |
+
|
| 42 |
+
On the human-validated TamilEOT test it scores 85.9%, close to the paper's whisper-base result of 86.1%.
|
| 43 |
+
|
| 44 |
+

|
| 45 |
+
|
| 46 |
+
int8 results differ slightly between CPU architectures while fp32 results do not, so each int8 chart pairs models scored on one machine. Details: `int8_cpu_dependence.md` in this repo.
|
| 47 |
+
|
| 48 |
+
## How to use it
|
| 49 |
+
|
| 50 |
+
Four files on Hugging Face:
|
| 51 |
+
|
| 52 |
+
- `indic-smart-turn-base-int8.onnx`, recommended on x86 servers
|
| 53 |
+
- `indic-smart-turn-base-fp32.onnx`, for Apple Silicon and GPUs
|
| 54 |
+
- `indic-smart-turn-tiny-int8.onnx`
|
| 55 |
+
- `indic-smart-turn-tiny-fp32.onnx`
|
| 56 |
+
|
| 57 |
+
All take the same input as Smart Turn v3, 8 s of 16 kHz audio as an 80 x 800 log-mel, and return the probability that the turn is complete.
|
| 58 |
+
|
| 59 |
+
### Pipecat
|
| 60 |
+
|
| 61 |
+
```python
|
| 62 |
+
from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3
|
| 63 |
+
|
| 64 |
+
analyzer = LocalSmartTurnAnalyzerV3(smart_turn_model_path="indic-smart-turn-base-int8.onnx")
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
### Directly
|
| 69 |
+
|
| 70 |
+
The model takes an 80 x 800 log-mel of the last 8 seconds of 16 kHz mono audio and returns the probability that the turn is complete. `config.json` records the same contract.
|
| 71 |
+
|
| 72 |
+
Single clip:
|
| 73 |
+
|
| 74 |
+
```python
|
| 75 |
+
import numpy as np, onnxruntime as ort, soundfile as sf
|
| 76 |
+
from transformers import WhisperFeatureExtractor
|
| 77 |
+
|
| 78 |
+
MODEL = "indic-smart-turn-base-int8.onnx"
|
| 79 |
+
fe = WhisperFeatureExtractor(chunk_length=8) # 8 s window, 80 mel bins, 800 frames
|
| 80 |
+
so = ort.SessionOptions(); so.intra_op_num_threads = 1 # one core is enough at batch 1
|
| 81 |
+
session = ort.InferenceSession(MODEL, so, providers=["CPUExecutionProvider"])
|
| 82 |
+
|
| 83 |
+
def last_8s(audio: np.ndarray, sr: int = 16000) -> np.ndarray:
|
| 84 |
+
n = 8 * sr
|
| 85 |
+
return audio[-n:] if len(audio) > n else np.pad(audio, (n - len(audio), 0)) # keep the end, zero-pad the front
|
| 86 |
+
|
| 87 |
+
def turn_complete_prob(audio: np.ndarray) -> float:
|
| 88 |
+
feats = fe(last_8s(audio), sampling_rate=16000, return_tensors="np", padding="max_length",
|
| 89 |
+
max_length=8 * 16000, truncation=True, do_normalize=True).input_features.astype(np.float32)
|
| 90 |
+
return float(session.run(None, {"input_features": feats})[0][0, 0])
|
| 91 |
+
|
| 92 |
+
audio, sr = sf.read("clip.wav", dtype="float32") # 16 kHz mono; resample first if not
|
| 93 |
+
p = turn_complete_prob(audio)
|
| 94 |
+
print("complete" if p > 0.5 else "incomplete", round(p, 3))
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
## License and attribution
|
| 98 |
+
|
| 99 |
+
- **Code and models: BSD 2-Clause**, the same license as Pipecat Smart Turn. Code: [github.com/adimyth/indic-smart-turn](https://github.com/adimyth/indic-smart-turn).
|
| 100 |
+
- **Dataset `adimyth/indic-smart-turn-data`: CC BY 4.0**, because its audio comes from IndicVoices, which is CC BY 4.0. Credit AI4Bharat when you use it.
|
| 101 |
+
- [Pipecat Smart Turn v3](https://github.com/pipecat-ai/smart-turn) by Pipecat / Daily: architecture, trainer and the v3.2 training data (CC BY 4.0).
|
| 102 |
+
- [ai4bharat/IndicVoices](https://huggingface.co/datasets/ai4bharat/IndicVoices) by AI4Bharat: conversational audio (CC BY 4.0).
|
| 103 |
+
- [santhosh-005/tamil-eot](https://huggingface.co/datasets/santhosh-005/tamil-eot): Tamil turn boundaries and the recipe this project follows (CC BY 4.0).
|
charts.html
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<title>Indic Smart Turn Results</title>
|
| 2 |
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap">
|
| 3 |
+
<style>
|
| 4 |
+
/* Layout: one column of four small-multiple bar charts, each self-contained with legend, hover tooltip and a table view. */
|
| 5 |
+
:root{
|
| 6 |
+
--bg:#fcfcfb; --surface:#f3f2ee; --ink:#141412; --ink-2:#55534d; --ink-3:#8a877f; --rule:#dedcd5; --grid:#e9e8e3;
|
| 7 |
+
--ours:#2a78d6; --base:#eb6834; --ours-ink:#1c5cab; --base-ink:#b8461f;
|
| 8 |
+
--font-body:"IBM Plex Sans",system-ui,-apple-system,"Segoe UI",sans-serif; --font-mono:"IBM Plex Mono",ui-monospace,Menlo,monospace;
|
| 9 |
+
}
|
| 10 |
+
@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]){ color-scheme:dark; --bg:#1a1a19; --surface:#232322; --ink:#f3f2ee; --ink-2:#c3c2b7; --ink-3:#8f8d85; --rule:#3a3936; --grid:#2d2d2b; --ours:#3987e5; --base:#d95926; --ours-ink:#86b6ef; --base-ink:#f2a07f; } }
|
| 11 |
+
:root[data-theme="dark"]{ color-scheme:dark; --bg:#1a1a19; --surface:#232322; --ink:#f3f2ee; --ink-2:#c3c2b7; --ink-3:#8f8d85; --rule:#3a3936; --grid:#2d2d2b; --ours:#3987e5; --base:#d95926; --ours-ink:#86b6ef; --base-ink:#f2a07f; }
|
| 12 |
+
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--font-body);font-size:15px;line-height:1.5}
|
| 13 |
+
.wrap{max-width:980px;margin:0 auto;padding-block:32px 56px;padding-inline:20px}
|
| 14 |
+
h1{font-size:26px;font-weight:600;margin:0 0 6px;text-wrap:balance;letter-spacing:-0.01em}
|
| 15 |
+
.lede{color:var(--ink-2);max-width:66ch;margin:0 0 8px}
|
| 16 |
+
.meta{font-family:var(--font-mono);font-size:12px;color:var(--ink-3);letter-spacing:.02em;text-transform:uppercase;margin-bottom:28px}
|
| 17 |
+
.chart{border-top:1px solid var(--rule);padding-block:22px 10px}
|
| 18 |
+
.chart h2{font-size:17px;font-weight:600;margin:0 0 4px;text-wrap:balance}
|
| 19 |
+
.chart .sub{color:var(--ink-2);margin:0 0 12px;max-width:70ch}
|
| 20 |
+
.legend{display:flex;flex-wrap:wrap;gap:18px;font-size:13px;color:var(--ink-2);margin:0 0 10px}
|
| 21 |
+
.legend span{display:inline-flex;align-items:center;gap:8px}
|
| 22 |
+
.legend i{display:inline-block;width:14px;height:10px;border-radius:2px}
|
| 23 |
+
.plot{position:relative}
|
| 24 |
+
svg{display:block;width:100%;height:auto}
|
| 25 |
+
.bar{cursor:default}
|
| 26 |
+
.bar:hover{filter:brightness(1.12)}
|
| 27 |
+
.tip{position:absolute;pointer-events:none;background:var(--ink);color:var(--bg);font-family:var(--font-mono);font-size:12px;padding:6px 9px;border-radius:4px;white-space:nowrap;transform:translate(-50%,-115%);opacity:0;transition:opacity .08s}
|
| 28 |
+
.tip.on{opacity:1}
|
| 29 |
+
.tools{display:flex;gap:10px;align-items:center;margin-top:6px}
|
| 30 |
+
button{font:inherit;font-size:13px;color:var(--ink-2);background:var(--surface);border:1px solid var(--rule);border-radius:4px;padding:5px 10px;cursor:pointer}
|
| 31 |
+
button:hover,button:focus-visible{color:var(--ink);outline:2px solid var(--ours);outline-offset:1px}
|
| 32 |
+
table{border-collapse:collapse;width:100%;margin-top:10px;font-size:13px;font-variant-numeric:tabular-nums}
|
| 33 |
+
th,td{text-align:right;padding:5px 8px;border-bottom:1px solid var(--grid)}
|
| 34 |
+
th:first-child,td:first-child{text-align:left}
|
| 35 |
+
th{color:var(--ink-3);font-weight:500}
|
| 36 |
+
.pos{color:var(--ours-ink)} .neg{color:var(--base-ink)}
|
| 37 |
+
.note{color:var(--ink-3);font-size:12.5px;max-width:70ch;margin-top:24px}
|
| 38 |
+
@media (max-width:520px){ .wrap{padding-inline:16px} h1{font-size:22px} }
|
| 39 |
+
@media (prefers-reduced-motion: reduce){ .tip{transition:none} }
|
| 40 |
+
</style>
|
| 41 |
+
<div class="wrap">
|
| 42 |
+
<h1>Indic Smart Turn versus Smart Turn v3.2</h1>
|
| 43 |
+
<p class="lede">Accuracy per language on the same speaker-disjoint test clips, at the default 0.5 threshold. Hover a bar for the exact value; open the table for the numbers and the difference.</p>
|
| 44 |
+
<div class="meta">11 Indian languages + English · 20,431 test clips · four model groups</div>
|
| 45 |
+
<div id="charts"></div>
|
| 46 |
+
<p class="note">Pipecat ships Smart Turn v3.2 as two whisper-tiny files: int8 for CPU and fp32 for GPU. There is no base-size v3.2, so the base groups are compared with the tiny v3.2 file of the same precision. English, Hindi, Marathi and Bengali rows include Pipecat's own v3.2 test set; Tamil includes the human-validated TamilEOT test set.</p>
|
| 47 |
+
</div>
|
| 48 |
+
<script>
|
| 49 |
+
const DATA = {"group1_tiny_int8": {"title": "Group 1: tiny int8 (8.7 MB) vs Smart Turn v3.2 int8 (same size, same precision)", "ours_label": "Indic Smart Turn tiny int8", "baseline_label": "Smart Turn v3.2 int8 (CPU file, whisper-tiny)", "languages": ["English", "Hindi", "Marathi", "Bengali", "Tamil", "Telugu", "Kannada", "Malayalam", "Gujarati", "Punjabi", "Odia", "Assamese"], "ours": [91.7, 89.4, 84.1, 84.3, 82.1, 85.1, 82.9, 80.9, 80.9, 80.2, 86.4, 80.4], "baseline": [92.9, 84.7, 77.9, 81.2, 70.5, 73.4, 77.7, 66.9, 73.5, 69.2, 74.6, 67.8]}, "group2_base_int8": {"title": "Group 2: base int8 (24 MB) vs Smart Turn v3.2 int8 (same precision; v3.2 has no base size)", "ours_label": "Indic Smart Turn base int8", "baseline_label": "Smart Turn v3.2 int8 (CPU file, whisper-tiny)", "languages": ["English", "Hindi", "Marathi", "Bengali", "Tamil", "Telugu", "Kannada", "Malayalam", "Gujarati", "Punjabi", "Odia", "Assamese"], "ours": [93.9, 91.5, 88.1, 86.8, 86.7, 84.8, 89.1, 85.6, 87.1, 86.5, 87.3, 83.3], "baseline": [94.3, 85.8, 77.7, 81.4, 70.0, 70.9, 74.0, 63.8, 72.5, 69.4, 75.2, 64.9], "ours_auc": [0.988, 0.971, 0.955, 0.936, 0.922, 0.923, 0.92, 0.873, 0.896, 0.892, 0.933, 0.892], "baseline_auc": [0.983, 0.941, 0.865, 0.884, 0.749, 0.766, 0.783, 0.664, 0.765, 0.749, 0.778, 0.691]}, "group3_tiny_fp32": {"title": "Group 3: tiny fp32 (32 MB) vs Smart Turn v3.2 fp32 (same size, same precision)", "ours_label": "Indic Smart Turn tiny fp32", "baseline_label": "Smart Turn v3.2 fp32 (GPU file, whisper-tiny)", "languages": ["English", "Hindi", "Marathi", "Bengali", "Tamil", "Telugu", "Kannada", "Malayalam", "Gujarati", "Punjabi", "Odia", "Assamese"], "ours": [93.7, 90.2, 88.1, 85.4, 83.4, 85.0, 82.3, 81.4, 82.5, 83.1, 84.9, 81.4], "baseline": [94.7, 83.0, 75.8, 75.9, 58.6, 58.1, 56.6, 49.6, 58.4, 56.7, 60.4, 55.4]}, "group4_base_fp32": {"title": "Group 4: base fp32 (81 MB) vs Smart Turn v3.2 fp32 (same precision; v3.2 has no base size)", "ours_label": "Indic Smart Turn base fp32", "baseline_label": "Smart Turn v3.2 fp32 (GPU file, whisper-tiny)", "languages": ["English", "Hindi", "Marathi", "Bengali", "Tamil", "Telugu", "Kannada", "Malayalam", "Gujarati", "Punjabi", "Odia", "Assamese"], "ours": [94.0, 91.4, 88.0, 86.5, 86.4, 85.1, 89.6, 85.4, 86.9, 86.9, 87.3, 83.3], "baseline": [94.7, 83.0, 75.8, 75.9, 58.6, 58.1, 56.6, 49.6, 58.4, 56.7, 60.4, 55.4], "ours_auc": [0.988, 0.97, 0.954, 0.938, 0.921, 0.922, 0.92, 0.876, 0.898, 0.896, 0.935, 0.891], "baseline_auc": [0.986, 0.943, 0.88, 0.893, 0.744, 0.773, 0.795, 0.667, 0.759, 0.748, 0.785, 0.678]}};
|
| 50 |
+
const order = ["group1_tiny_int8","group2_base_int8","group3_tiny_fp32","group4_base_fp32"];
|
| 51 |
+
const subs = {
|
| 52 |
+
group1_tiny_int8: "Same encoder size and precision as the shipped Smart Turn v3.2 CPU file.",
|
| 53 |
+
group2_base_int8: "Weights-only int8; ROC-AUC within 0.005 of fp32 in every language.",
|
| 54 |
+
group3_tiny_fp32: "Compared with the Smart Turn v3.2 GPU file, which is fp32.",
|
| 55 |
+
group4_base_fp32: "On TamilEOT it scores 85.9%, close to the paper's whisper-base result of 86.1%."
|
| 56 |
+
};
|
| 57 |
+
const root = document.getElementById("charts");
|
| 58 |
+
for (const key of order) {
|
| 59 |
+
const d = DATA[key]; const n = d.languages.length;
|
| 60 |
+
const W = 900, L = 96, R = 60, T = 8, rowH = 34, H = T + n*rowH + 30, x0 = 50, x1 = 100;
|
| 61 |
+
const sx = v => L + (v - x0) / (x1 - x0) * (W - L - R);
|
| 62 |
+
let svg = `<svg viewBox="0 0 ${W} ${H}" role="img" aria-label="${d.title}">`;
|
| 63 |
+
for (let g = x0; g <= x1; g += 10) svg += `<line x1="${sx(g)}" x2="${sx(g)}" y1="${T}" y2="${H-26}" stroke="var(--grid)" stroke-width="1"/><text x="${sx(g)}" y="${H-10}" text-anchor="middle" font-size="12" fill="var(--ink-3)" font-family="var(--font-mono)">${g}</text>`;
|
| 64 |
+
d.languages.forEach((lang, i) => {
|
| 65 |
+
const y = T + i*rowH, o = d.ours[i], b = d.baseline[i], delta = (o - b);
|
| 66 |
+
svg += `<text x="${L-10}" y="${y+rowH/2+4}" text-anchor="end" font-size="13" fill="var(--ink)">${lang}</text>`;
|
| 67 |
+
svg += `<rect class="bar" data-tip="${d.ours_label}: ${o.toFixed(1)}%" x="${sx(x0)}" y="${y+4}" width="${sx(o)-sx(x0)}" height="11" rx="2" fill="var(--ours)"/>`;
|
| 68 |
+
svg += `<rect class="bar" data-tip="${d.baseline_label}: ${b.toFixed(1)}%" x="${sx(x0)}" y="${y+18}" width="${sx(b)-sx(x0)}" height="11" rx="2" fill="var(--base)"/>`;
|
| 69 |
+
svg += `<text x="${sx(Math.max(o,b))+8}" y="${y+rowH/2+4}" font-size="12" fill="var(--ink-2)" font-family="var(--font-mono)">${delta>=0?"+":""}${delta.toFixed(1)}</text>`;
|
| 70 |
+
});
|
| 71 |
+
svg += `</svg>`;
|
| 72 |
+
let table = `<table hidden><thead><tr><th>Language</th><th>${d.ours_label}</th><th>${d.baseline_label}</th><th>Difference</th></tr></thead><tbody>`;
|
| 73 |
+
d.languages.forEach((lang,i)=>{ const dl=d.ours[i]-d.baseline[i]; table += `<tr><td>${lang}</td><td>${d.ours[i].toFixed(1)}</td><td>${d.baseline[i].toFixed(1)}</td><td class="${dl>=0?'pos':'neg'}">${dl>=0?'+':''}${dl.toFixed(1)}</td></tr>`; });
|
| 74 |
+
table += `</tbody></table>`;
|
| 75 |
+
const sec = document.createElement("section"); sec.className = "chart";
|
| 76 |
+
sec.innerHTML = `<h2>${d.title}</h2><p class="sub">${subs[key]}</p>
|
| 77 |
+
<div class="legend"><span><i style="background:var(--ours)"></i>${d.ours_label}</span><span><i style="background:var(--base)"></i>${d.baseline_label}</span><span><i style="background:transparent;width:0"></i>Number at the end of each row: difference in points</span></div>
|
| 78 |
+
<div class="plot">${svg}<div class="tip"></div></div>
|
| 79 |
+
<div class="tools"><button type="button" id="btn-${key}" aria-expanded="false">Show table</button></div>${table}`;
|
| 80 |
+
root.appendChild(sec);
|
| 81 |
+
const tip = sec.querySelector(".tip"), plot = sec.querySelector(".plot");
|
| 82 |
+
sec.querySelectorAll(".bar").forEach(bar => {
|
| 83 |
+
bar.addEventListener("mousemove", e => { const r = plot.getBoundingClientRect(); tip.textContent = bar.dataset.tip; tip.style.left = (e.clientX - r.left) + "px"; tip.style.top = (e.clientY - r.top) + "px"; tip.classList.add("on"); });
|
| 84 |
+
bar.addEventListener("mouseleave", () => tip.classList.remove("on"));
|
| 85 |
+
});
|
| 86 |
+
const btn = sec.querySelector("button"), tbl = sec.querySelector("table");
|
| 87 |
+
btn.addEventListener("click", () => { tbl.hidden = !tbl.hidden; btn.textContent = tbl.hidden ? "Show table" : "Hide table"; btn.setAttribute("aria-expanded", String(!tbl.hidden)); });
|
| 88 |
+
}
|
| 89 |
+
</script>
|
config.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architecture": "whisper encoder + attention pooling + MLP head (Pipecat Smart Turn v3 layout)",
|
| 3 |
+
"input": {"name": "input_features", "shape": [-1, 80, 800], "description": "log-mel of the last 8 s of 16 kHz audio, WhisperFeatureExtractor(chunk_length=8), do_normalize=True"},
|
| 4 |
+
"output": {"name": "logits", "shape": [-1, 1], "description": "probability that the user's turn is complete"},
|
| 5 |
+
"threshold": 0.5,
|
| 6 |
+
"languages": ["eng", "hin", "mar", "ben", "tam", "tel", "kan", "mal", "guj", "pan", "ori", "asm"],
|
| 7 |
+
"files": {
|
| 8 |
+
"indic-smart-turn-base-int8.onnx": {"encoder": "whisper-base", "precision": "int8 dynamic (weights)", "size_mb": 24, "recommended": true},
|
| 9 |
+
"indic-smart-turn-base-fp32.onnx": {"encoder": "whisper-base", "precision": "fp32", "size_mb": 81},
|
| 10 |
+
"indic-smart-turn-tiny-int8.onnx": {"encoder": "whisper-tiny", "precision": "int8 static", "size_mb": 8.8},
|
| 11 |
+
"indic-smart-turn-tiny-fp32.onnx": {"encoder": "whisper-tiny", "precision": "fp32", "size_mb": 32}
|
| 12 |
+
}
|
| 13 |
+
}
|
figures/group1_tiny_int8-dark.svg
ADDED
|
|
figures/group1_tiny_int8-light.svg
ADDED
|
|
figures/group2_base_int8-dark.svg
ADDED
|
|
figures/group2_base_int8-light.svg
ADDED
|
|
figures/group3_tiny_fp32-dark.svg
ADDED
|
|
figures/group3_tiny_fp32-light.svg
ADDED
|
|
figures/group4_base_fp32-dark.svg
ADDED
|
|
figures/group4_base_fp32-light.svg
ADDED
|
|
indic-smart-turn-base-fp32.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5f42d53537b8622f4c4916903da2b71f4b50398cc06fd01fdf7522022f817b9d
|
| 3 |
+
size 81328960
|
indic-smart-turn-base-int8.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:98ef8f8e2185ab337bb622cf60cf353c5235d9db91182dd5c56fe7fbda8dc172
|
| 3 |
+
size 23960494
|
indic-smart-turn-tiny-fp32.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4b46c144a7f290388087112934d685460a2afa0b767a5fa5e0a621b6313bd904
|
| 3 |
+
size 32070911
|
indic-smart-turn-tiny-int8.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1cfda3c0935de4eab03e2e02b7643d1382cf98c08245e12f9fb091e10691b3dd
|
| 3 |
+
size 8785144
|
int8_cpu_dependence.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# int8 results depend on the CPU architecture
|
| 2 |
+
|
| 3 |
+
The same ONNX files were scored on the same test clips on two machines: an x86 cloud server (AMD EPYC 7543) and a MacBook (Apple M4 Pro, Arm). Four languages, test split, one evaluator.
|
| 4 |
+
|
| 5 |
+
| Language | Model | Server (x86) | MacBook (Arm) | Difference |
|
| 6 |
+
|---|---|---:|---:|---:|
|
| 7 |
+
| Kannada | Smart Turn v3.2 fp32 | 56.6 / 0.795 | 56.6 / 0.795 | 0 |
|
| 8 |
+
| Kannada | Indic tiny fp32 | 82.3 / 0.876 | 82.3 / 0.876 | 0 |
|
| 9 |
+
| Kannada | Smart Turn v3.2 int8 (static) | 77.7 / 0.770 | 74.0 / 0.783 | -3.7 |
|
| 10 |
+
| Kannada | Indic tiny int8 (static) | 82.9 / 0.878 | 84.2 / 0.895 | +1.3 |
|
| 11 |
+
| Malayalam | Smart Turn v3.2 int8 (static) | 66.9 / 0.657 | 63.8 / 0.664 | -3.1 |
|
| 12 |
+
| Malayalam | Indic tiny int8 (static) | 80.9 / 0.823 | 79.4 / 0.830 | -1.5 |
|
| 13 |
+
| Telugu | Smart Turn v3.2 int8 (static) | 73.4 / 0.750 | 70.9 / 0.766 | -2.5 |
|
| 14 |
+
| Telugu | Indic tiny int8 (static) | 85.1 / 0.898 | 87.9 / 0.900 | +2.8 |
|
| 15 |
+
| Assamese | Smart Turn v3.2 int8 (static) | 67.8 / 0.696 | 64.9 / 0.691 | -2.9 |
|
| 16 |
+
| Assamese | Indic tiny int8 (static) | 80.4 / 0.837 | 81.5 / 0.850 | +1.1 |
|
| 17 |
+
|
| 18 |
+
Every fp32 model gives identical accuracy and AUC on both machines, so the audio decoding and feature extraction are identical. Only the int8 files move. ONNX Runtime uses different int8 kernels on x86 and Arm, with different rounding and saturation, so a quantised model is not the same function on the two architectures. Statically quantised files move by up to 3.7 points on a single language. Over the whole 20,431-clip public test, Smart Turn v3.2 int8 scores 81.7 / 0.894 on the server and 81.9 / 0.902 on the MacBook.
|
| 19 |
+
|
| 20 |
+
What this means for reading the results:
|
| 21 |
+
|
| 22 |
+
- Compare int8 numbers only when both models were scored on the same machine. Every int8 chart in the README pairs models scored on one machine.
|
| 23 |
+
- fp32 numbers can be compared across machines. int8 numbers can differ by about 3 points per language and 1 point overall.
|
| 24 |
+
- On Apple Silicon use the fp32 files: they are faster there, and their results do not depend on the CPU.
|
| 25 |
+
|
| 26 |
+
Raw run for the MacBook side: `python -m indic_turn.eval` on `data/built/{kan,mal,tel,asm}.parquet`, split test.
|
pause-test.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f8df26a4416bb37eaea4d7db30abdb967898feeac447c2ce790ab35b4db84305
|
| 3 |
+
size 2949867
|