CoolFace
Modelpublic

mobilebytesensei/betterflow-indicconformer-ctc

sourceHugging Facemitupdated 1mo agoView on Hugging Face
1likes
Model Card

Betterflow — IndicConformer CTC, ONNX int8 (8 Indic languages)

ONNX int8 exports of AI4Bharat's per-language IndicConformer hybrid CTC/RNNT checkpoints, prepared for on-device offline ASR through sherpa-onnx's OfflineRecognizer.from_nemo_ctc.

These are derived artefacts. We are not the authors of the weights.

Provenance and licence

Upstreamai4bharat/indicconformer_stt_<lang>_hybrid_ctc_rnnt_large
Upstream licenceMIT (verified on each source model card, not inferred)
This repo's licenceMIT, inherited
What changedCTC head exported to ONNX; dynamic int8 quantization; sherpa metadata stamped
What did NOT changethe weights themselves — no fine-tuning, no distillation

Each language directory carries export-meta.json with the sha256 of the source `.nemo`, so provenance is checkable rather than asserted.

Please cite AI4Bharat for the underlying models.

Contents

Eight languages: gu pa bn mr ml te ta kn.

<lang>/model.int8.onnx     140,337,395 B   (identical size across languages, DISTINCT weights)
<lang>/tokens.txt               67,605 B   5,633 entries
<lang>/export-meta.json                    source sha256, checksums, export settings

int8 sha256 — all eight distinct

langsha256 (first 16)langsha256 (first 16)
gu9f8031996be1a4d7ml81c172f6ee3796f5
pa1ad447f477040bbbteb0b3d335f2505ecd
bn8b1bd509bb36ba86ta08b85596996429e1
mraf22de1897679e4akn16d950f36cfdb01e

The eight files are byte-identical in size because they share an architecture. They are different models — the checksums above are the check that matters.

⚠️ Two things that will silently break this

1. `normalize_type` must be `per_feature`. It is stamped on every graph. If it is stripped or set to "", the model loads, runs, and returns EMPTY with no error. Verify by decoding, never by a successful load.

Required ONNX metadata: vocab_size=5633, subsampling_factor=4, normalize_type=per_feature, feature_dim=80.

2. `tokens.txt` opens with Bengali tokens in every language directory — this is correct. The vocabulary is AI4Bharat's shared 22-language tokenizer (5,633 = 22 × 256 + 1) and the CTC head is multi-softmax. It looks like a packaging error and is not. The file is byte-identical across all eight (ee60967630213f31…); it is duplicated per directory so each bundle installs independently.

Unlike the 600m multilingual checkpoint, these per-language models did learn to suppress the other 21 blocks — measured 100% own-script output on all eight, 0/400 wrong-alphabet characters — so no `language_id` masking is required.

Measured quality

Scored on held-out ARTPARK-IISc/Vaani, 50 clips × 50 speakers per language, against the general-purpose multilingual Whisper bundle these replace.

langincumbent pooled WER**this export**script puritycatastrophic
pa106.2%20.2%0% → 100%13 → 0
gu106.6%22.6%0% → 100%16 → 0
bn135.1%30.6%0% → 100%21 → 0
mr100.5%36.6%83% → 100%14 → 0
ml111.9%37.2%0% → 100%16 → 1
te115.5%45.0%6% → 100%8 → 1
ta87.0%52.1%100% → 100%3 → 1
kn100.3%59.5%100% → 100%2 → 1

Script purity is the headline, not WER. A 0% score means the incumbent was not producing the language's alphabet at all. Catastrophic failures fell from 93/400 to 5/400.

te and kn remain insertion-bound and are not recommended without further evaluation.

Runtime cost

⚠️ Peak RSS scales with utterance length — it is not a constant

Conformer self-attention is O(T²) in sequence length, so memory is a function of how long the speaker talks. Measured in-process (Android arm64, sherpa-onnx 1.13.4, int8, 2 threads, one process per utterance, ±1 MB reproducible):

utterancepeak RSSutterancepeak RSS
5 s285 MB45 s868 MB
10 s350 MB60 s986 MB
20 s482 MB90 s1,628 MB
30 s606 MB134 s2,758 MB

Budget accordingly: a typical 3–10 s dictation utterance costs under 350 MB, but a two-minute monologue costs 2.7 GB. If you have a memory ceiling, cap utterance length — that is the cheapest control and it belongs in your app, not in the model.

RTF0.062–0.064 (~16× real time on an M4 laptop)
RTF, 2× ARM Cortex-A55 @1.25 GHz1.33 (~13 s for a 10 s utterance)
RTF, 4× Cortex-A780.255

The int8 build quantizes all 54 `Conv` nodes to ConvInteger. Leaving them in fp32 — as some other re-exports do — makes ONNX Runtime allocate an im2col workspace that scales with clip length, pushing peak RSS to 2,099–2,635 MB from the same nominal precision. If you are getting 4× the memory you expect from an int8 Conformer, check your Conv node dtypes.

Cost of that choice: ConvInteger is ~3.6× slower than fp32 Conv on CPU.

Not evaluated here

Device-side accuracy, languages outside the eight, streaming/partial decoding (these are offline models), and the RNNT head (not exported — it needs numba/CUDA).