Tasbeeh keyword-spotting heads (Arabic dhikr counting)

Eight tiny (~860 KB each) ONNX binary classifiers that fire on spoken dhikr phrases over continuous open-mic audio, in the openWakeWord architecture: frozen Google speech_embedding features (96 dims, 0.125 s/frame) -> per-phrase DNN head. Five short heads use the standard 2 s window (16 frames); three longer Azkar use wide windows (12/56/32 frames) sized from measured clip durations. Designed as an on-device tasbeeh counter: VAD/energy gate -> sliding windows -> per-phrase score >= threshold -> counter with a refractory period.

Phrases and thresholds

phrase Arabic input frames hold-out recall@0.5 FP/hr best threshold
subhanallah ุณุจุญุงู† ุงู„ู„ู‡ 16 (2 s) 0.93 0.00 0.3
alhamdulillah ุงู„ุญู…ุฏ ู„ู„ู‡ 16 (2 s) 0.86 0.00 0.3
allahuakbar ุงู„ู„ู‡ ุฃูƒุจุฑ 16 (2 s) 0.93 0.00 0.3
la_ilaha_illa_allah ู„ุง ุฅู„ู‡ ุฅู„ุง ุงู„ู„ู‡ 16 (2 s) 0.93 0.00 0.3
astaghfirullah ุฃุณุชุบูุฑ ุงู„ู„ู‡ 16 (2 s) 0.93 0.00 0.3
la_hawla_wala_quwwata ู„ุง ุญูˆู„ ูˆู„ุง ู‚ูˆุฉ ุฅู„ุง ุจุงู„ู„ู‡ 12 (~1.5 s) 0.87 0.00 0.5
la_ilaha_illa_allah_wahdahu ู„ุง ุฅู„ู‡ ุฅู„ุง ุงู„ู„ู‡ ูˆุญุฏู‡ ู„ุง ุดุฑูŠูƒ ู„ู‡ ู„ู‡ ุงู„ู…ู„ูƒ ูˆู„ู‡ ุงู„ุญู…ุฏ ูˆู‡ูˆ ุนู„ู‰ ูƒู„ ุดูŠุก ู‚ุฏูŠุฑ 56 (~7 s) 0.56 0.00 0.5
la_ilaha_illa_anta ู„ุง ุฅู„ู‡ ุฅู„ุง ุฃู†ุช ุณุจุญุงู†ูƒ ุฅู†ูŠ ูƒู†ุช ู…ู† ุงู„ุธุงู„ู…ูŠู† 32 (~4 s) 0.54 0.00 0.5

Rapid-tasbeeh detection on 2 noisy simulated streams x 25 utterances each (short heads): subhanallah 34/50, alhamdulillah 20/50, allahuakbar 24/50, la_ilaha_illa_allah 40/50, astaghfirullah 36/50. The wide-window heads were evaluated on hold-out only (no rapid sim).

Overlap note: the long dhikr ู„ุง ุฅู„ู‡ ุฅู„ุง ุงู„ู„ู‡ ูˆุญุฏู‡ ู„ุง ุดุฑูŠูƒ ู„ู‡โ€ฆ contains the short phrase ู„ุง ุฅู„ู‡ ุฅู„ุง ุงู„ู„ู‡ as its opening. When the 56-frame head fires, the app should count that dhikr and suppress the 16-frame head for that burst.

Usage

import onnxruntime as ort
import numpy as np

sess = ort.InferenceSession("tasbeeh_subhanallah.onnx", providers=["CPUExecutionProvider"])
# feats: (N, 16, 96) windows from openwakeword's AudioFeatures().embed_clips
scores = sess.run(None, {"input": feats.astype(np.float32)})[0].reshape(-1)

Wide-window heads take the same 96-dim features with more frames per window: (N, 12, 96), (N, 56, 96), (N, 32, 96) respectively โ€” slide a window of that many frames over the stream (stream hop is 0.08 s/frame; window span = frames x 0.125 s).

For counting in an app: score windows every 80 ms over a VAD-gated stream, fire on score >= threshold (0.3 short heads, 0.5 wide heads), and use a refractory of ~1.2 s (the rapid-tasbeeh simulation shows the 2 s analysis window fires 2-3 times per single utterance with a 0.45 s refractory โ€” a ~1.2 s refractory collapses those to one count).

Training data and recipe

  • Positives: short heads โ€” 449 synthetic clips (5 phrases x 3 text variants x 7 Arabic dialects x 4 speeds); wide heads โ€” 252 regenerated clips (3 phrases x 7 dialects x 4 speeds x 3 seeds). All synthesized with SWivid/Habibi-TTS (F5-TTS), then RIR-reverberated and noise-augmented (8 rounds). Dataset: mZahran001/tasbeeh-kws-data. (Wide heads were first trained on 21 clips/phrase and collapsed to 0 recall โ€” the 84-clip-per-phrase volume restored recall.)
  • Adversarial negatives: similar-but-not-dhikr clips from the same TTS pipeline.
  • General negatives: ~2.7k 2 s segments of Common Voice Arabic (validation split, dhikr-containing sentences filtered out) + openWakeWord's FP validation feature set (481k windows) for false-positive training.
  • Training: openWakeWord Model.auto_train, 25k steps per phrase, binary cross-entropy, balanced validation mix, and a post-train collapse guard that retries with gentler FP pressure if a head stops firing on its own training positives.

Caveats (read before shipping)

  • All evaluation is synthetic. Recall numbers come from TTS hold-out clips and simulated streams; real-device accuracy will differ. Before production, record 50-100 real utterances per phrase on target phones and re-tune the thresholds.
  • The two wide-window heads (~0.55 recall) are the weakest โ€” long duas on 84 synthetic clips are the hardest case; expect them to improve most from real-recorded data.
  • Trained heads assume the exact speech_embedding feature pipeline used here (openwakeword >= 0.6 from GitHub main).

Files

  • tasbeeh_<phrase>.onnx โ€” 5 short heads (input: (N, 16, 96) float32)
  • dhikr_<phrase>_<frames>.onnx โ€” 3 wide-window heads (input: (N, 12|56|32, 96) float32)
  • eval_report.json โ€” short heads: hold-out recall, adversarial FP rate, threshold sweep, rapid-tasbeeh simulation results
  • eval_report_new.json โ€” wide heads: hold-out recall, FP/hr, threshold
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support