--- language: - ckb pipeline_tag: automatic-speech-recognition library_name: pytorch tags: - icefall - zipformer2 - streaming - rnnt - sorani - speech-recognition --- # Sorani Zipformer2 Streaming — Run B A causal streaming speech-to-text model for **Central Kurdish (Sorani, `ckb`)**, trained independently from scratch on a 188.696-hour mixture of human, pseudo-labeled, and synthetic speech. This repository contains the final development-selected Run B model, exported as **PyTorch TorchScript**. - **Test WER: 23.69%; test CER: 4.54%**, streaming greedy decoding. - 66,135,543 parameters in the training model; shared 512-piece Sorani BPE. - Export fixed to 320 ms encoder chunks and 2.56 seconds of left context. - Selected running-model average spanning **epochs 17–24**, using Icefall's accumulator endpoints at epochs 16 (excluded) and 24. - Output is Kurdish text without punctuation restoration or capitalization. - Runs locally on CPU with the included script; no Icefall/k2 installation or rented GPU is needed for this inference example. ## Quick start: transcribe an audio file Use Python 3.12 in a fresh environment. The core versions in `requirements.txt` match the verified Mac CPU evaluation runtime. Other platforms/versions have not been verified for this release. TorchScript may print a deprecation warning in recent PyTorch versions; this artifact still requires `torch.jit.load`. ```bash python3.12 -m venv .venv source .venv/bin/activate python -m pip install --upgrade pip huggingface_hub python - <<'PY' from huggingface_hub import snapshot_download snapshot_download("Amin-khattab/sorani-zipformer2-streaming", local_dir="sorani-model") PY python -m pip install -r sorani-model/requirements.txt python sorani-model/transcribe.py recording.wav ``` To display changing partial text as audio is processed: ```bash python sorani-model/transcribe.py recording.wav --partials ``` The file example mixes channels to mono and resamples to 16 kHz when needed. It reads a file as fast as the computer permits; it does not capture a microphone or pace playback in real time. WAV and FLAC are suitable inputs. Convert other formats to WAV if your libsndfile installation cannot read them. ## Feed microphone PCM incrementally Run this Python code from inside the downloaded `sorani-model` directory: ```python import numpy as np from transcribe import Recognizer recognizer = Recognizer(".") # load model once stream = recognizer.create_stream() # In your microphone callback/consumer, supply mono FLOAT32 PCM at 16 kHz. # A normal packet contains 5120 samples (320 ms). Replace this silence with # actual microphone samples, typically scaled to [-1, 1]. pcm = np.zeros(5120, dtype=np.float32) partial_text = stream.accept(pcm) print(partial_text) # After the last audio packet, flush once: final_text = stream.accept(np.empty(0, dtype=np.float32), final=True) print(final_text) # For the next independent utterance: stream = recognizer.create_stream() ``` Encoder caches and decoder history persist within a stream. Partial hypotheses can change as additional pieces form words. The final flush appends 500 ms of silence to expose the last speech frames through the encoder's lookahead. **320 ms chunk size is not a measured first-partial or end-to-end latency.** Do not pass int16 PCM directly: first convert to float32 and divide by 32768. Resample microphone audio before calling `accept` if it is not already 16 kHz. Serialize calls on a stream. There is no VAD, speaker diarization, automatic endpointing, translation, word timestamps, or punctuation-restoration module. This is not a Transformers checkpoint: `pipeline("automatic-speech-recognition")` and `AutoModel.from_pretrained` are not supported by this repository. A hosted inference endpoint or interactive Space is not included. ## Evaluation The held-out test set has **2,014 utterances / 3.014328 hours**. Checkpoints were selected on development data before test evaluation. The reference transcript and audio SHA-256 audits found no identical audio hashes or utterance IDs shared between test and the training/development splits. Speaker metadata was unavailable; **speaker-disjointness is not established**. | Model | Training data | Test WER | Test CER | |---|---|---:|---:| | Run A (comparison baseline) | Common Voice only | 41.24% | 12.14% | | **Run B (this release)** | Complete natural mixture | **23.69%** | **4.54%** | Run B reduces test WER by 17.55 absolute percentage points / 42.55% relative. Both rows use genuine cached streaming inference with incremental PCM input, 320 ms chunks, 2.56-second left context, and **greedy decoding with at most one emitted symbol per encoder frame**. Corpus totals are 20,064 reference words and 100,959 reference characters; Run B has 4,753 word edits and 4,583 character edits. WER uses whitespace-separated words. CER uses Unicode codepoints after removing inter-word whitespace. No Unicode or spelling normalization is applied. Percentages are corpus edit totals divided by corpus reference lengths, not averages of utterance-level rates. The CPU test runtime used PyTorch 2.14.0, one PyTorch thread, and kaldi-native-fbank 1.22.3. Source audio was 16 kHz mono; test evaluation did not use the example script's resampling path. Feature settings: 80-bin log-power Mel filterbanks, 25 ms frames, 10 ms shift, Povey window, preemphasis 0.97, dither 0, DC removal, power-of-two FFT padding, `snip_edges=false`, 20–7600 Hz. Training feature extraction used CUDA Kaldifeat; the portable test runtime is recorded separately to avoid implying they were the same installation. **Development results use a different decoder:** the selected Run B average obtained 21.40% WER / 4.13% CER with state-cached streaming modified beam search and four active paths. Those are development scores, not test scores. The included inference script implements greedy search only. Formal RTF, first-partial latency, finalization latency, full-context test results, and beam-search test results have not been measured/reported for this release. ## Training and model selection Icefall source commit: [`3f848bb6d0acc970c9b294a30ca0a04a7c9c78d1`](https://github.com/k2-fsa/icefall/tree/3f848bb6d0acc970c9b294a30ca0a04a7c9c78d1). | Source | Cuts | Hours | |---|---:|---:| | Common Voice training | 110,242 | 119.987560 | | Hard pseudo-labels | 7,701 | 14.737583 | | Easy/anchor pseudo-labels | 7,106 | 21.745444 | | Synthetic speech | 22,995 | 32.225867 | | Total | 148,044 | 188.696454 | Natural sampling uses each selected training cut once per epoch, without weighted source resampling. The 512-piece tokenizer was trained only on Common Voice training transcripts and shared with Run A. Development: 9,947 cuts / 11.761278 h. The source categories reflect the experiment manifests; this repository does not redistribute dataset recordings or transcripts or identify every upstream dataset release. Pseudo-label errors and synthetic-speech biases are possible. Encoder layers: `2,2,3,4,3,2`; dimensions: `192,256,384,512,384,256`; feed-forward dimensions: `512,768,1024,1536,1024,768`; encoder stage downsampling: `1,2,4,8,4,2`. Total feature-to-output subsampling is fourfold. Stateless predictor context 2, embedding 512; tanh joiner dimension 512. Objective: pruned RNN-T, prune range 5, LM scale 0.25, AM scale 0, simple-loss final scale 0.5; no CTC. Training: 30 epochs from scratch, 20,938 optimizer updates, FP16, ScaledAdam, base LR 0.045, Eden schedule, 500-update LR warmup, clipping scale 2.0, no weight decay, batch max-duration 120 seconds and gradient accumulation 9. SpecAugment enabled; MUSAN/noise mixing and speed perturbation disabled. Training used an RTX 3060 with 12 GB VRAM. Training plus validation-loss passes took 32 h 38 m 59 s; epoch cycles including development decoding took 38 h 14 m 02 s; the controller through final selection took 40 h 12 m 41 s. Individual epochs 1–30 and running-average windows of eight epochs ending at 20–30 were compared on development WER, with CER as the tie-breaker. The selected 17–24 average beat the best individual checkpoint, epoch 29 (24.51% dev WER / 5.32% CER). The released TorchScript model is the selected average, not epoch 30. ## Contents and limitations - `model.pt`: selected streaming TorchScript model, fixed chunk/context export. - `bpe.model`, `tokens.txt`: matching tokenizer and token table; do not substitute another tokenizer or rename token IDs. - `transcribe.py`, `requirements.txt`: portable CPU greedy inference example. - `feature_config.json`: training feature configuration. - `evaluation.json`: aggregate test scores and decoding protocol. - `SHA256SUMS`: integrity hashes for the release files. This is an inference release; optimizer states, full training checkpoints, private logs, raw audio, and per-utterance transcripts are not bundled. Accuracy outside Central Kurdish or on noisy speech, uncommon names, code-switching, and unseen recording conditions has not been established. Verify important transcriptions manually. Aggregate held-out scores do not guarantee accuracy for every speaker or dialect. ## Licensing and attribution A distribution/reuse license for these trained weights has not yet been specified by the publisher. Public availability does not itself grant unrestricted reuse or commercial rights. Contact the publisher for licensing. Icefall upstream code is Apache-2.0 licensed; that code license is not a declaration of a license for the trained weights or the constituent datasets. This model uses the Zipformer2 architecture from [k2-fsa/icefall](https://github.com/k2-fsa/icefall). No paper citation or DOI has been assigned here; cite the repository URL and the specific Hugging Face commit revision when using this artifact.