--- language: [fa] license: cc-by-nc-4.0 library_name: onnx pipeline_tag: automatic-speech-recognition tags: [automatic-speech-recognition, speech, persian, farsi, fastconformer, ctc, streaming, cache-aware-streaming, on-device, tract, rust, shenava, shenava-1, visualears, edge] base_model: [Reza2kn/Shenava-Rizeh-Pizeh-v1.0] --- # Shenava — Rizeh-Pizeh v1.0 (6.9M) · cache-aware streaming · **native-Rust (tract)** Cache-aware **streaming** CTC export of [`Shenava-Rizeh-Pizeh-v1.0`](https://huggingface.co/Reza2kn/Shenava-Rizeh-Pizeh-v1.0) that runs in the **pure-Rust [tract](https://github.com/sonos/tract) engine** — no C++, no ONNX Runtime. Part of [VisualEars / Shenava](https://shenava.app): offline, on-device, streaming Persian ASR for the Deaf/Hard-of-Hearing. Quality: **intelligible** (24.55% golden-6669 WER); real-time on a 2015 Cortex-A7. **RTF ≈ 0.018** (20.3 ms/chunk on x86 CPU; chunk = 1.12 s audio). ## ⚠️ Requires patched tract (until upstreamed) Stock tract rejects NeMo cache-aware streaming graphs in two inference-layer spots. Fix = a **23-line, 2-file patch** (`shenava_tract_streaming.patch`, included) — PR open at **[sonos/tract#2441](https://github.com/sonos/tract/pull/2441)**. Build tract with the patch, then load `model.onnx` normally. The graph itself is valid (identical decode to ONNX Runtime). ## Streaming contract Per-step inputs / outputs (fixed shapes, greedy CTC): - `audio_signal` `[1,80,121]` — un-normalized log-mel chunk (NeMo featurizer, `normalize=NA`) - `length` `[1]` i64 — true valid frames in the chunk - `cache_last_channel` `[1,12,70,144]`, `cache_last_time` `[1,12,144,8]`, `cache_last_channel_len` `[1]` i64 — start zeros / 0 - → `logprobs` `[1,T',1025]` + next caches **Chunking:** feed 121-mel-frame chunks, shift 112 (9-frame pre-encode overlap). First chunk is 105 → pad to 121; pad the tail too; pass the true `length`. Thread the `*_next` caches back each step (cast `cache_last_channel_len_next` to i64). **Greedy CTC: carry the previous token across chunk boundaries** when collapsing repeats; blank id = 1024; map via `tokens.txt`; `▁`→space. ## Numbers are spoken-form → ITN The model spells numbers (هشت not ۸). Apply `persian_itn.py` at display for spoken→Persian-digit (cardinals + هزار/میلیون/میلیارد + «و» + compounds). ## Shenava-1 family (all native-Rust streaming) - [Koochik 114M](https://huggingface.co/Reza2kn/Shenava-Koochik-v1.0-tract-streaming) — flagship - [Rizeh 32M](https://huggingface.co/Reza2kn/Shenava-Rizeh-v1.0-tract-streaming) — mid - [Rizeh-Pizeh 6.9M](https://huggingface.co/Reza2kn/Shenava-Rizeh-Pizeh-v1.0-tract-streaming) — tiniest ## Quantized variants — int4 / int8 (NEW) Our streaming support is **merged into tract main** ([sonos/tract#2441](https://github.com/sonos/tract/pull/2441)), which also ships int4 (`MatMulNBits` -> Q4_0) and int8 GEMM kernels. So tract main runs quantized versions of this streaming model: | file | precision | size | notes | |---|---|---|---| | `model.onnx` | fp32 | 33MB | reference | | `model.int4.onnx` | **int4** (MatMulNBits / Q4_0, weight-only) | **14MB** | ⭐ recommended — 2.4x smaller, ~fp32 speed, **byte-identical** decode | | `model.int8.onnx` | int8 (matmul-only, MatMulInteger) | 17MB | byte-identical; slower on small-batch streaming (per-matmul `DynamicQuantizeLinear`) — best for large-batch / offline, or CPUs where it wins | Both quants decode **byte-identically** to fp32. For edge/on-device streaming, use **`model.int4.onnx`** (weight-only, no per-matmul activation quant). Needs **tract main** — the streaming fixes are upstream now, so the bundled `.patch` is no longer required.