Spaces:
Sleeping
Sleeping
| """Kalenjin ASR — Gradio demo for Tonykip/whisper-kalenjin-v3-turbo. | |
| Uses the CHUNKED long-form path (chunk_length_s=30, stride 5) + anti-loop decoding | |
| (num_beams=5, no_repeat_ngram_size=3, repetition_penalty=1.15). The chunked path does NOT | |
| invoke Whisper's sequential generate_with_fallback, so it sidesteps a transformers 5.x bug | |
| in `_need_fallback` (IndexError on segment scores) while still preventing the long-form | |
| "…ab usnatet…" looping. ZeroGPU-aware with a CPU fallback. | |
| (The slightly-higher-accuracy "B3 robust-sequential" config — CER ~0.193 — is reserved for the | |
| production endpoint, which needs word timestamps and uses return_timestamps='word', the code path | |
| that avoids the same bug.) | |
| """ | |
| import os | |
| import gradio as gr | |
| import torch | |
| from transformers import pipeline | |
| if os.environ.get("SPACES_ZERO_GPU") is not None: | |
| import spaces | |
| gpu = spaces.GPU | |
| else: | |
| def gpu(func=None, **_kw): | |
| return func if func is not None else (lambda f: f) | |
| MODEL = "Tonykip/whisper-kalenjin-v3-turbo" | |
| GEN = {"language": "sw", "task": "transcribe", "num_beams": 5, | |
| "no_repeat_ngram_size": 3, "repetition_penalty": 1.15} | |
| _PIPE = None | |
| def _pipe(): | |
| global _PIPE | |
| if _PIPE is None: | |
| cuda = torch.cuda.is_available() | |
| dt = torch.float16 if cuda else torch.float32 | |
| _PIPE = pipeline("automatic-speech-recognition", model=MODEL, | |
| device=0 if cuda else -1, torch_dtype=dt, | |
| chunk_length_s=30, stride_length_s=5) # chunked → no sequential fallback | |
| return _PIPE | |
| def transcribe(path): | |
| if not path: | |
| return "🎙️ Record or upload Kalenjin audio, then press Transcribe." | |
| try: | |
| txt = _pipe()(path, generate_kwargs=GEN)["text"].strip() | |
| except Exception: | |
| txt = _pipe()(path)["text"].strip() | |
| return txt or "(no speech detected — try a clearer or longer clip)" | |
| DESCRIPTION = """ | |
| # 🎙️ Kalenjin Speech Recognition | |
| Transcribe spoken **Kalenjin** — covering **Kipsigis** and **Nandi**. Record a phrase or upload a | |
| clip (any length) and press **Transcribe**. | |
| *Powered by [`Tonykip/whisper-kalenjin-v3-turbo`](https://huggingface.co/Tonykip/whisper-kalenjin-v3-turbo), | |
| a fine-tune of OpenAI Whisper-large-v3-turbo, with chunked long-form decoding + anti-repetition | |
| controls so it stays stable on longer clips (no looping).* | |
| """ | |
| ABOUT = """ | |
| **Model:** [`Tonykip/whisper-kalenjin-v3-turbo`](https://huggingface.co/Tonykip/whisper-kalenjin-v3-turbo) | |
| — fine-tune of Whisper-large-v3-turbo. The current best open Kalenjin ASR (~CER 0.21 on held-out | |
| KaleBench-ASR). | |
| **Decoding:** chunked long-form (30s windows) + beam-5 + `no_repeat_ngram_size=3` + | |
| `repetition_penalty` — removes the long-form looping older configs showed. | |
| **Tips & limits:** works best on clear Kipsigis/Nandi speech; English/Swahili code-switches are | |
| usually kept; very noisy or overlapping audio is harder. On free CPU hardware transcription takes a | |
| few seconds — switch the Space to a GPU/ZeroGPU runtime for near-instant results. | |
| A separate experimental Parakeet fine-tune exists but is **not recommended for use**. | |
| Built by **Tony Kipkemboi**, a native Kalenjin speaker. | |
| """ | |
| theme = gr.themes.Soft(primary_hue="green", secondary_hue="yellow") | |
| with gr.Blocks(theme=theme, title="Kalenjin ASR") as demo: | |
| gr.Markdown(DESCRIPTION) | |
| with gr.Row(): | |
| with gr.Column(): | |
| audio_in = gr.Audio(sources=["microphone", "upload"], type="filepath", | |
| label="Kalenjin audio") | |
| go = gr.Button("Transcribe", variant="primary", size="lg") | |
| with gr.Column(): | |
| out = gr.Textbox(label="Transcription", lines=6, | |
| placeholder="The transcription will appear here…") | |
| go.click(transcribe, inputs=audio_in, outputs=out) | |
| audio_in.stop_recording(transcribe, inputs=audio_in, outputs=out) | |
| with gr.Accordion("About this model", open=False): | |
| gr.Markdown(ABOUT) | |
| if __name__ == "__main__": | |
| demo.queue(max_size=20).launch() | |