tiny-hinglish-turn-detector / SOURCE_README.md
suvradeepp's picture
Publish Tiny Hinglish Turn Detector development preview
0348402 verified
|
Raw
History Blame Contribute Delete
21.6 kB
# Tiny Hinglish Turn Detector
An audio-native, low-latency **HOLD vs END** classifier for voice agents, built
for the Shiprocket Data Scientist challenge. The model is evaluated only at VAD
pause checkpoints: `HOLD` means “keep listening”; `END` means “the agent may
respond.”
The packaged artifact is a development preview: a 151,812-parameter causal
TinyTCN using the most recent four seconds of audio. It exports to a 625,431-byte
FP32 ONNX file and runs waveform-to-probability inference at 1.63 ms p95 on the
measured Apple-arm CPU setup. It does **not** beat the acoustic logistic baseline
on the current IID development split, and it has not been evaluated on the
official test or on verified Hinglish speech. The repository is submission-grade
engineering evidence, not a production-readiness claim.
## Current evidence, at a glance
| Item | Status | What is actually established |
|---|---|---|
| Upstream train snapshot | Partial locally | 1 of 83 Parquet shards audited: 3,265 rows, 6.857 hours |
| Data integrity on that shard | Measured | 3,265/3,265 valid; no exact-audio, ID, or label-conflict duplicates |
| Split integrity | Measured, best effort | IID and source-held-out manifests pass observed-key crossing checks; all 3,265 groups are singletons because speaker/conversation/voice IDs are absent |
| Interpretable development baseline | Measured, partial-shard only | 10 acoustic statistics: AP 0.7310, AUROC 0.7522, END recall 0.1313 at FPR 0.0181 |
| TinyTCN preview | Measured, partial-shard only | 151,812 parameters: AP 0.6881, AUROC 0.7390, END recall 0.0625 at FPR 0.0181 |
| Paired comparison | Baseline remains winner | TinyTCN minus baseline: AP −0.0429 (95% bootstrap CI −0.1195 to 0.0352); constrained recall −0.0688 (−0.1330 to −0.0122) |
| Domain-shift stress | Measured, confounded | Source-held-out AP 0.5500/0.5368 for TinyTCN/baseline; both near chance in ranking, with wide nine-source bootstrap intervals |
| Runtime/export | Measured | 611 KiB FP32 ONNX; exact PyTorch/ONNX parity; ONNX p95 0.956 ms neural-only and 1.629 ms waveform-to-probability |
| Controller integration | Synthetic only | Metadata-bound 8-checkpoint/3-turn fixture emits 3 response edges with 0 duplicates; not model or product-quality evidence |
| Code quality gate | Passed | Ruff 0.15.16; 136/136 automated tests passed via `unittest` discovery |
| Accuracy/generalization | **Not established** | No full-corpus run, collected Hinglish recordings, real conversation replay, or official-test evaluation |
| Publication tooling | Guarded | Hash-verified HF publisher plus curated Kaggle/GitHub upload builders; verify each remote runtime separately |
The official test set remains deliberately sealed. The preview is real
development evidence, but validation was reused for threshold selection,
checkpoint selection, and bounded follow-up experiments. It is therefore
adaptive development evidence—not an independent estimate. See
[REPORT.md](REPORT.md) for the full reasoning and [MODEL_CARD.md](MODEL_CARD.md)
for the release contract.
The acoustic baseline is the first real-data development result: logistic
regression on ten waveform statistics, trained on all 2,939 train rows and
scored on all 326 validation rows in the single audited shard. At a threshold
selected on that same validation split for `FPR <= 0.02`, it has 3 FP, 21 TP,
163 TN, and 139 FN (`FPR 0.0181`, END recall `0.1313`, AUROC `0.7522`, AP
`0.7310`). The selected TinyTCN reaches 3 FP, 10 TP, 163 TN, and 150 FN at
threshold `0.7410008` on the same split (`FPR 0.0181`, END recall `0.0625`,
AUROC `0.7390`, AP `0.6881`, Brier `0.2133`). Its FPR bootstrap interval is
`[0, 0.0407]`, so this sample does not establish a population 2% interruption
bound. Neither result is independent-test or Hinglish evidence.
Because that baseline is the evidence winner, its 10-weight JSON artifact is
packaged beside the neural model under `reference_models/`; the Gradio Space
continues to demonstrate the ONNX TinyTCN and labels that choice explicitly.
## Why this design
Turn detection is asymmetric. A false `END` interrupts the user and can derail
an entire transaction; a false `HOLD` adds latency. Accuracy at an arbitrary
0.5 threshold therefore is not the primary target. The selection criterion is
**maximum END recall subject to a false-positive (false-interruption) budget**,
with FPR reported at 1%, 2%, and 5% operating points.
The production boundary is also explicit:
```text
16 kHz audio suffix
-> 80-bin log-mel frontend
-> causal depthwise-separable TinyTCN
-> mask-aware attentive mean/std pooling
-> END probability + optional filler auxiliary heads
-> calibrated SPEAKING / HOLD / END controller
```
The selected preview has 151,812 parameters, six 128-channel blocks, and a
four-second suffix. Its convolutions are causal; final attentive pooling makes
this a suffix-level classifier, not a state-cached streaming network. The
repository retains a larger 396,164-parameter/eight-second configuration as a
proposed full-data experiment, not as the packaged model. The controller—not
the neural network—owns minimum silence, threshold relaxation, debounce, and
maximum wait.
No ASR transcript is required at inference. That keeps the path small and
avoids coupling endpoint latency to transcription, while the auxiliary
`midfiller` and `endfiller` objectives encourage the representation to notice
exactly the failure cases the challenge emphasizes. A Whisper-tiny teacher
configuration is included as an experiment, not assumed to be the best serving
model.
## Repository map
| Path | Purpose |
|---|---|
| `src/turn_detection/data/` | Lazy Parquet/HF ingestion, schema normalization, audio audit, transitive grouping, split validation |
| `src/turn_detection/models/` | Canonical log-mel frontend, TinyTCN student, Whisper teacher, attentive pooling |
| `src/turn_detection/training/` | Lazy dataloaders, masked multitask loss, threshold calibration, product metrics, trainer |
| `src/turn_detection/runtime/` | NumPy preprocessing, ONNX inference, turn controller, replay evaluation |
| `configs/` | Reproducible student, teacher, and smoke configurations |
| `scripts/` | Download, audit, split, train, evaluate, export, benchmark, package, publish |
| `data/collection/` | Deterministic 900-recording Hinglish assignment plan; no recordings are included |
| `reports/` | Measured one-shard audit and split reports |
| `tests/` | Unit and integration coverage across data, model, metrics, runtime, and prompts |
| `deployment/kaggle/` | Flat, standalone ONNX inference templates for Kaggle Models |
| `upload-ready/` | Generated Kaggle and GitHub upload folders; never a source input |
## Reproduce the pipeline
Python 3.10–3.12 and [`uv`](https://docs.astral.sh/uv/) are recommended.
These commands target a full source checkout. Raw audio, downloaded Parquet,
processed manifests, and per-example predictions are intentionally not
redistributed; recreate them with the pinned download/audit commands below.
```bash
uv sync --extra all
uv run pytest
```
The curated public repository already includes the selected ONNX artifact, so
the demo resolves it without an environment variable:
```bash
uv sync --extra demo
uv run python app.py
```
Place the Hugging Face token in the ignored `.env` file; never commit it:
```bash
cp .env.example .env
# edit .env and set HF_TOKEN
```
### 1. Download the pinned train snapshot
```bash
bash scripts/download_dataset.sh
```
The script pins revision
`e564e2ac567f774d1880aa1db6ce97afb8c519b7`, resumes safely, and refuses to
declare success until all 83 Parquet shards are present.
### 2. Audit and create manifests
```bash
uv run python scripts/audit_dataset.py \
data/raw/smart-turn-data-v3.2-train \
--output data/processed/manifest.jsonl \
--report artifacts/data_audit.json \
--fail-on-error
uv run python scripts/prepare_splits.py \
--input data/processed/manifest.jsonl \
--output data/processed/splits.jsonl \
--report artifacts/split_report.json \
--split train=0.9 --split validation=0.1 \
--stratify endpoint,language,synthetic,dataset
```
The audit hashes encoded audio and creates privacy-preserving linkage keys for
conversation, speaker, voice, recording, prompt, record, and repeated normalized
text. Union-find turns overlapping keys into transitive components; the split
tool assigns whole components and fails validation if any group, audio hash, or
metadata key crosses splits.
On the available shard, however, upstream rows expose no usable speaker,
conversation, or TTS-voice identity: all 3,265 base linkage groups used by the
IID split contain one row. (The separate stress split deliberately regroups rows
by 12 source-dataset values.)
The measured split is therefore best-effort row-disjoint and duplicate-safe,
**not speaker-disjoint**. Its grouped bootstrap is effectively a row bootstrap;
speaker/voice leakage remains unknown until richer identity metadata or a new
consented speaker-disjoint set exists.
For a harsher domain-shift test, add `--holdout-field dataset`. Interpret that
result carefully: in the currently inspected shard, source is strongly
confounded with language and synthetic status. Leave-one-source-out helpers are
also available in `turn_detection.data.splits`.
### 3. Prove the training path, then run the real experiment
```bash
# Fast generated-feature integration check; never report as model quality.
uv run python scripts/train.py --config configs/smoke.json --smoke-test
# Reproduce the selected one-shard preview (development evidence only).
uv run python scripts/train.py \
--config configs/partial_shard_warmstart_lr3e4_5ep.json
# Proposed primary full-data student experiment, once all shards are audited.
uv run python scripts/train.py --config configs/tiny_tcn.yaml
```
Run the interpretable partial-shard sanity baseline independently:
```bash
uv run python scripts/run_baselines.py audio \
--manifest data/processed/partial-iid-splits.jsonl \
--source-root . \
--output artifacts/partial-baseline \
--epochs 1000 --fpr-budget 0.02
```
The trainer writes the resolved configuration, epoch history, calibrated
threshold, and best checkpoint. Deterministic mode seeds model initialization,
data order, and training. Missing filler labels stay missing and are masked from
their auxiliary BCE losses.
### 4. Evaluate without touching the official test
```bash
uv run python scripts/evaluate.py \
--checkpoint artifacts/partial-shard-warmstart-lr3e4-5ep/best.pt \
--source data/processed/partial-iid-splits.jsonl \
--source-root . \
--split validation \
--output reports/partial_tinytcn_metrics.json
```
Evaluation reports confusion counts, FPR/FNR, AUROC, average precision, Brier
score, ECE/reliability bins, operating points under FPR budgets, and
language/source/synthetic slices. Turn-level interruption rates are emitted only
when genuine turn/conversation IDs exist; this shard supports only a clearly
labelled clip-normalized per-audio-hour proxy. Final comparisons need real
speaker- or conversation-cluster confidence intervals.
The selected checkpoint was chosen by average precision after earlier runs had
already been inspected on this validation set. Its threshold was also selected
on the same 326 examples. Reproduction should yield the stored numbers, but they
must not be treated as a fresh holdout result.
### 5. Freeze, unseal once, and evaluate the official test
Only an experiment trained with exact `run.status: final` can be frozen. The
freeze manifest hashes the checkpoint, selected config, source tree,
preprocessing, controller policy, split manifest, threshold, and pinned test
identity. `configs/final.yaml` and `artifacts/final/*` below are intentionally
future-candidate placeholders; create them only after completing the full-data
experiment. Only then:
```bash
uv run python scripts/freeze_candidate.py \
--checkpoint artifacts/final/best.pt \
--config configs/final.yaml \
--output artifacts/final/frozen_manifest.json
UNSEAL_OFFICIAL_TEST=I_HAVE_FROZEN_MODEL_AND_THRESHOLD \
FROZEN_MANIFEST=artifacts/final/frozen_manifest.json \
bash scripts/download_test_dataset.sh
uv run python scripts/evaluate.py \
--checkpoint artifacts/final/best.pt \
--source data/raw/smart-turn-data-v3.2-test \
--dataset-id pipecat-ai/smart-turn-data-v3.2-test \
--revision 0500378e8ed6d38e37b016e24d261e8e6c6a6859 \
--split test \
--allow-sealed-test \
--frozen-manifest artifacts/final/frozen_manifest.json \
--output artifacts/final/test_metrics.json
```
Do not tune after reading the test result. A new model requires a new untouched
test set or a clearly labeled exploratory result.
### 6. Export and benchmark
```bash
uv run python scripts/export_onnx.py \
--checkpoint artifacts/final/best.pt \
--output artifacts/final/model.onnx
uv run python scripts/benchmark.py \
--model artifacts/final/model.onnx \
--metadata artifacts/final/model_metadata.json \
--threads 1 --batch-size 1 \
--output artifacts/final/cpu_benchmark.json
```
ONNX export checks PyTorch/ONNX numerical parity. Runtime preprocessing,
I/O names, activation semantics, threshold, parameter count, and evidence scope
live in `model_metadata.json`; hashes, shapes, checkpoint/config/split bindings,
parity, and quantization status live in `export_manifest.json`. Static INT8 is
supported with representative calibration features; quantized scores require
threshold recalibration before release.
The packaged preview is
`artifacts/partial-shard-warmstart-lr3e4-5ep/model.onnx`: 625,431 bytes,
151,812 parameters, and FP32 maximum absolute parity error `0.0`. On the
recorded Apple-arm/one-thread runs, ONNX neural-only p50/p95/p99 is
0.919/0.956/0.974 ms; four-second waveform-to-probability p50/p95/p99 is
1.572/1.629/1.651 ms. These are local implementation measurements, not a
deployment SLA.
### 7. Run the demo and exercise the controller
```bash
TURN_MODEL_PATH=artifacts/partial-shard-warmstart-lr3e4-5ep/model.onnx \
uv run python app.py
uv run python scripts/replay_stream.py \
--input data/collection/controller_replay_fixture.jsonl \
--output reports/controller_replay_integration.jsonl \
--metadata artifacts/partial-shard-warmstart-lr3e4-5ep/model_metadata.json \
--evidence-scope synthetic_integration
```
The exported metadata binds the endpoint threshold and the complete controller
policy. `END` is latched until new speech or an explicit reset; only the first
END transition sets `emit_response=true`, preventing duplicate agent replies.
The bundled replay fixture is hand-authored integration data: its probabilities,
targets, and latency values are synthetic. It validates policy binding,
threshold relaxation, timeout, END latching, and edge emission only—it is not
model, conversation, latency, or product-quality evidence. Replace its input
with private, human-annotated VAD checkpoint logs for a real sequence study.
If weights are absent, the demo displays a conspicuous heuristic fallback. That
fallback validates only the UI and must never be cited as a model result. See
[docs/demo.md](docs/demo.md) for the pause-checkpoint contract.
### 8. Build and publish a guarded release
```bash
uv run python scripts/build_release.py \
--model artifacts/final/model.onnx \
--metadata artifacts/final/model_metadata.json \
--metrics artifacts/final/test_metrics.json \
--frozen-manifest artifacts/final/frozen_manifest.json
# Dry validation first.
uv run python scripts/publish_hf.py --username suvradeepp
# Creates/uploads the model repo and Gradio Space only after identity validation.
uv run python scripts/publish_hf.py --username suvradeepp --execute
```
For a personal account without Hugging Face PRO, the publisher requests the
free-account-compatible ZeroGPU tier (`--space-hardware zero-a10g`) only when it
must create a new Space. Hugging Face currently gates new personal Gradio CPU
Spaces behind PRO even when CPU Basic has no hourly charge. ZeroGPU eligibility
still requires a verified account in good standing and an available hosted
Space slot. Paid accounts may override this with `--space-hardware cpu-basic`.
The release builder refuses smoke checkpoints or a release without measured
test metrics unless `--allow-development-artifact` is passed explicitly. That
override must remain visibly development-only. For a reviewable partial-data
preview, pass that flag to the builder and `--allow-development-release` to both
publisher invocations. Development validation metrics are packaged as
`development_metrics.json`, never `test_metrics.json`.
The exact development-preview package command is:
```bash
uv run python scripts/build_release.py \
--model artifacts/partial-shard-warmstart-lr3e4-5ep/model.onnx \
--metadata artifacts/partial-shard-warmstart-lr3e4-5ep/model_metadata.json \
--metrics reports/partial_tinytcn_metrics.json \
--output release \
--allow-development-artifact \
--include-synthetic-controller-replay
uv run python scripts/publish_hf.py \
--username suvradeepp \
--release-dir release \
--allow-development-release
uv run python scripts/build_upload_folders.py \
--release-dir release \
--output upload-ready \
--allow-development-release
```
`upload-ready/kaggle-model/` is a flat drag-and-drop ONNX bundle.
`upload-ready/github-repository/` is a public-repository allowlist containing
source, CI, tests, aggregate evidence, ONNX weights, and the two checkpoints
needed to reproduce the selected warm start. It excludes credentials, caches,
raw/processed data, per-example predictions, stale releases, and publish
receipts. Keep the Kaggle variation private until the derived-weight rights
review described in `NOTICE` and `MODEL_CARD.md` is resolved.
Add `--execute` only after the dry validation succeeds. The publisher verifies
the authenticated owner and every packaged hash before creating or updating the
remote model repository and Space. Its receipt binds exact remote HEAD commits,
requires a visible build/start transition for a new Space upload, and records
the final `RUNNING` stage. Because the installed Hub API does not expose the
commit actually serving behind a Space runtime, the receipt explicitly records
`runtime_commit_bound=false` instead of overstating that guarantee.
The release hash-binds the 2.7 MB split manifest but intentionally does not
redistribute it because it contains upstream text and record identifiers. The
public package carries deterministic preparation code, the pinned revision,
manifest SHA-256, and aggregate reports; per-example rows stay private.
## Hinglish evaluation protocol
The upstream corpus is multilingual, but a `hin` or `eng` tag is not evidence
of within-utterance code-switching. This repository therefore includes a
separate, consent-first Shiprocket-domain protocol:
- 30 speakers × 30 assignments = 900 intended recordings;
- 450 `HOLD` and 450 `END` targets;
- 21/4/5 speaker-disjoint train/dev/test speakers;
- minimal pairs with natural 0.3–2.0 second internal pauses plus roleplay;
- three annotators answer whether responding at the checkpoint feels like an
interruption; uncertain votes remain disagreement, not forced labels.
Only the assignments and protocol exist today—**no participant audio has been
collected**. Do not infer Hinglish performance until that benchmark is recorded
and evaluated. See [docs/data_collection_protocol.md](docs/data_collection_protocol.md)
and [DATA_CARD.md](DATA_CARD.md).
## What “good enough” means
A candidate is releaseable only after all of these gates pass:
1. all 83 train shards audited with zero unresolved hard validation errors;
2. group-safe validation and source/speaker stress tests completed;
3. threshold frozen under a predeclared false-interruption budget;
4. real Hinglish minimal-pair test collected with consent and held out by speaker;
5. official test opened once and reported without subsequent tuning;
6. preprocessing parity, ONNX parity, batch-1 CPU latency, and controller replay measured;
7. failure slices reviewed for language, source, synthetic/human, filler, duration,
device/noise, gender/region where consented, and uncertainty;
8. upstream data/model redistribution terms reviewed before publishing weights.
The current repository passes export/runtime engineering gates and supplies
honest bounded development evidence. It does not pass the full-data, Hinglish,
official-test, sequence-replay, or rights-review gates.
## Documentation
- [Technical report](REPORT.md)
- [Model card](MODEL_CARD.md)
- [Data card](DATA_CARD.md)
- [Experiment registry](docs/experiment_registry.md)
- [Failure-analysis protocol](docs/failure_analysis.md)
- [Hinglish collection protocol](docs/data_collection_protocol.md)
- [Demo/runtime contract](docs/demo.md)
## License and responsible use
Repository-authored code is Apache-2.0; see [LICENSE](LICENSE) and
[NOTICE](NOTICE). The upstream
dataset card did not provide an explicit dataset license at the inspected
revision. This repository does not redistribute its audio. Apache-2.0 on the
code does **not** grant rights to upstream audio, recordings collected under the
local protocol, speaker likeness, customer data, or the development model
weights. The preview weights carry no license grant pending an upstream-rights
review; review data and derived-weight terms before use or redistribution.
This system decides conversational timing; it should not be used for speaker
identification, emotion inference, surveillance, or consequential decisions.