Duyu-small

A Turkish + English speech recognizer for CPUs and edge devices: 19.3 M active parameters, shipped as ONNX graphs at int8 (56 MB) and 4-bit (34 MB) with a small torch-free engine (numpy + onnxruntime + sentencepiece). It writes whatever is spoken, Turkish, English or both in one sentence, cased and punctuated.

Duyu-small is built for on-device use: live, short, two-language speech, where the model runs next to the microphone. It is not meant for long-form transcription.

Architecture

part what stored params
encoder 9 Conformer blocks (d 256, 4 heads, conv k15) applied in 3 shared refinement passes with learned per-pass gates, 25 Hz output 15.5 M
sparse acoustic memory product-key memory after block 4: 250k Γ— 120 values, 4 heads Γ— 16 reads per frame per pass 30.0 M (23k read per frame)
CTC head 2048 SentencePiece pieces (TR + EN), InterCTC + self-conditioning at block 4 of every pass 0.5 M
decoder 3-layer Transformer (d 256, QK-norm), joint CTC/attention decoding 3.8 M

Active per frame: 19.3 M. Stored: 49.3 M. Compute-equivalent of a 46.6 M single-pass model.

Accuracy

Word error rate (wer_trnum: case-, punctuation- and number-format-blind), PyTorch reference decode (attention beam 5 + CTC rescoring).

test set Duyu-small (49.3 M) whisper-base (74 M) mihuai/turkish-stt (66 M) balbal (45.4 M) Cactus Whistle
voicedata-TR (conversation, 235 chunks) 12.45 29.51 19.89 β€” β€”
FLEURS tr test (743) 7.87 24.19 9.62 7.95 β€”
sierra-tr (call-centre turns, 846, wer_sierra) 16.37 39.08 24.95 17.61 β€”
FLEURS en_us test (500) 22.88 10.81 Turkish only Turkish only 14.37

whisper-base and Cactus Whistle decoded with the language forced to English on FLEURS-en and to Turkish on the Turkish sets; Duyu-small with <en> on FLEURS-en and <tr> elsewhere.

Quantization check on 62 held-out conversational dev clips (13.7 min, the engine on a laptop CPU with 4 threads, benchmark numerics):

engine WER real-time factor
PyTorch reference (GPU, bf16) 20.14 β€”
fp32, attention beam 5 19.56 0.11
int8, attention beam 5 19.56 0.07
4-bit, attention beam 5 19.67 0.13
int8, CTC greedy only 22.19 0.016

The engine's beam search is a port of the reference PyTorch decoder and is verified identical to it on 20/20 dev clips at matching numerics.

Files

path size purpose
int8/, q4/, fp32/ 56 / 34 / 201 MB per set one complete set of the four graphs per precision; DuyuSmall(quant=...) picks the folder
*/encoder_pre.onnx 1.3 MB fbank β†’ subsampler + style vector
*/encoder_pass.onnx 181 / 47.6 / 26.9 MB one refinement pass over the shared blocks, incl. the memory (fp32 / uint8 / packed 4-bit); run 3Γ—
*/encoder_post.onnx 2.1 / 0.5 / 0.3 MB CTC head
*/decoder.onnx 17.4 / 6.4 / 5.0 MB attention decoder (no KV cache)
passes.npz β€” pass embeddings + gates
fbank.npz, spm.model, engine.json, config.json β€” feature tables, tokenizer, special ids, model config
duyu_small/ β€” the engine (Python package)

int8 = onnxruntime dynamic quantization of every MatMul + uint8 memory table with fp16 per-row scales. q4 = 4-bit block-wise (32) MatMul weights + the memory table packed two values per byte, dequantized after the gather with plain ONNX ops (Mod / Div / Reshape), so it runs on stock onnxruntime.

Use

pip install -e .            # or: pip install numpy onnxruntime sentencepiece
duyu-small recording.m4a                    # int8, attention beam 5, writes whatever is spoken
duyu-small a.wav --quant q4 --lang tr       # 4-bit, force Turkish
duyu-small a.wav --mode ctc                 # CTC greedy only (partials / fastest)
from duyu_small import DuyuSmall
m = DuyuSmall()                             # int8 by default; quant="q4" | "fp32"
text = m.transcribe(wav16k)                 # float32 numpy, any length (chunked above 30 s)
text = m.transcribe(wav16k, lang="en", beam=1)

DuyuSmall() finds the model files next to the package (this repository's layout); from elsewhere pass the directory, DuyuSmall("/path/to/Duyu-small"), or set DUYU_SMALL_DIR.

Audio in: 16 kHz mono float32 (duyu_small.audio.load_audio reads anything ffmpeg reads). Languages: tr, en, mix (default), auto.

Limits

  • Full-context encoder: utterances are decoded whole (chunked at 25 s on long input); no streaming partials beyond the CTC head yet.
  • The decoder re-runs the prefix at every step (no KV cache): fine for conversation turns, not tuned for long dictation.
  • English is "understands English in a Turkish conversation" (FLEURS-en 22.9), not Whistle-class English.
  • Read speech with rare proper nouns is where it is weakest.

License

Apache License 2.0: model weights, ONNX graphs and the engine.

Credits

The encoder design (a Conformer applied in 3 shared refinement passes with a sparse acoustic memory of 250k Γ— 120 values, read by 4 heads Γ— 16 records per pass) is taken from balbal, described by its author at erayy.com/balbal-blog. Duyu-small adds the attention decoder and the bilingual Turkish + English vocabulary on top.

Downloads last month
157
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Space using KaanAydinli/Duyu-small 1