Downloads · 30 days
19
16% of all-time downloads
Aditya109/whisper-small-bhojpuri-lora
whisper-small-bhojpuri-lora is a automatic speech recognition model from Aditya109. Use it when you need speech turned into text. It is set up for peft. The card lists the license as cc-by-4.0.
A LoRA adapter that teaches openai/whisper-small to transcribe Bhojpuri instead of hallucinating standard Hindi at it.
Downloads · 30 days
19
16% of all-time downloads
All-time downloads
116
Public
Repo size
52 MB
Likes
0
Public
Click a slice to open those files.
.safetensors52 MB · 93%
From the Hugging Face model README
A LoRA adapter that teaches openai/whisper-small to transcribe Bhojpuri
instead of hallucinating standard Hindi at it.
Bhojpuri has roughly 50 million speakers and no Whisper language token. Base
Whisper does not fail loudly on it — it hears Bhojpuri and writes fluent,
confident Hindi: wrong verb endings (बा → है), Hindi synonyms substituted
for dialect vocabulary, wrong postpositions. The acoustic front-end already
works; only the decoder's lexical and morphological prior is wrong. That is
exactly what LoRA is good at shifting.
Official Vaani Bhojpuri test split, n = 1,426. All rows scored with the same normalizer (see Normalization).
| Model | Params | WER ↓ | CER ↓ |
|---|---|---|---|
openai/whisper-small (untrained) | 244M | 122.07 | 78.75 |
openai/whisper-large-v3 (untrained) | 1.55B | 65.93 | 37.68 |
ARTPARK-IISc/SraVaani (FastConformer TDT-CTC) | 430M | 34.80 | 20.29 |
| This model (whisper-small + LoRA) | 244M + 13M | 36.41 | 17.05 |
70% relative WER reduction over the base model, and it beats untrained large-v3 by ~30 WER points at roughly one-sixth the size.
Every row was measured by me on the same 1,426 utterances, with the same normalizer and the same metric implementation. Nothing here is quoted from another paper's evaluation setup.
ARTPARK-IISc/SraVaani is trained on this exact dataset and is the obvious
comparison, so it is reported here rather than left for someone else to find.
Both models were scored on the same test split, through the same normalizer,
with the same metric implementation.
It is a split decision, and both halves are statistically significant (paired bootstrap over utterances, 2,000 resamples):
| Metric | This model | SraVaani | Difference | 95% CI |
|---|---|---|---|---|
| WER | 36.41 | 34.80 | SraVaani better by 1.61 | [+0.83, +2.44] |
| CER | 17.05 | 20.29 | This model better by 3.24 | [−3.87, −2.53] |
Per-utterance, this model wins on 519, SraVaani wins on 601, and 306 are tied.
Reading the split: SraVaani gets more whole words exactly right; this model is more character-accurate. Its errors are near-misses — the wrong vowel or inflection — where SraVaani's are further off. For Devanagari that distinction matters, because compound words can legitimately be written joined or split and WER charges two full word errors for a space.
What this model offers instead of a WER win: it is a 50 MB LoRA adapter on
a 244M base, and being a Whisper derivative it runs directly in
faster-whisper, whisper.cpp, and WhisperX. SraVaani is a 430M
FastConformer served as TorchScript via trust_remote_code, and does not drop
into that tooling. If you need Whisper-ecosystem deployment, this is the
trade; if you want the best WER on Bhojpuri and can run their stack, use
SraVaani.
Reproduce with benchmark_sravaani.py in the training repo.
This is legitimate arithmetic, not a bug. WER divides by the reference length and does not cap insertions:
WER = (substitutions + deletions + insertions) / words in reference
Base Whisper-small does not go quiet when confused — it hallucinates. A real example from the measured baseline:
REF: बा हरिहर रंग के (5 words)
HYP: अपने अपने अपने अपने अपने अपने ... (repetition loop)
One 5-word reference, dozens of inserted words. That single utterance scores several hundred percent.
The WER/CER ratio is diagnostic for this language pair:
| WER | CER | ratio | |
|---|---|---|---|
| small, untrained | 122.07 | 78.75 | 1.55 |
| large-v3, untrained | 65.93 | 37.68 | 1.75 |
| SraVaani | 34.80 | 20.29 | 1.72 |
| this model | 36.41 | 17.05 | 2.14 |
A rising ratio means remaining errors are concentrated in fewer words that are nearly spelled right — inflectional near-misses rather than misheard audio. For Devanagari, CER also protects against a scoring artifact: compound words can legitimately be written joined or split, and WER charges two full word errors for a space.
Real outputs from the dev set during training:
REF: एगो इ नदी ह नदी में बाउंड्री boundary कइल बा
HYP: एगो इ नदी ह नदी में बैंज्री boundary कइल बा
REF: पेड़ लगाव गए बाटे
HYP: पेड़ लगावा गई बाटे
The model produces Bhojpuri morphology — बा, बाटे, डाली बिया,
लगावल गइल — rather than the Hindi equivalents the base model defaults to.
Hallucination loops are largely gone: only 10 of 1,426 test utterances (0.7%)
still score above 100% WER.
import torch
from peft import PeftModel
from transformers import WhisperForConditionalGeneration, WhisperProcessor
BASE = "openai/whisper-small"
ADAPTER = "Aditya109/whisper-small-bhojpuri-lora"
processor = WhisperProcessor.from_pretrained(BASE, language="hi", task="transcribe")
model = WhisperForConditionalGeneration.from_pretrained(
BASE, torch_dtype=torch.bfloat16, attn_implementation="sdpa")
model = PeftModel.from_pretrained(model, ADAPTER).merge_and_unload()
model.to("cuda").eval()
model.generation_config.language = "hi"
model.generation_config.task = "transcribe"
model.generation_config.forced_decoder_ids = None
feats = processor.feature_extractor(
audio_16k, sampling_rate=16_000, return_tensors="pt"
).input_features.to("cuda", dtype=torch.bfloat16)
ids = model.generate(feats, max_new_tokens=200, num_beams=1)
print(processor.batch_decode(ids, skip_special_tokens=True)[0])
hi. Whisper has no Bhojpuri token; Hindi is the
nearest acoustic and orthographic proxy. The dialect lives in the adapter.| Base model | openai/whisper-small (244M) |
| Method | LoRA (PEFT), bf16, attn_implementation="sdpa" |
| Rank / alpha / dropout | 32 / 64 / 0.05 |
| Target modules | q_proj, k_proj, v_proj, out_proj, fc1, fc2 |
| Trainable params | ~13M (~5% of total) |
| Learning rate | 1e-3, warmup ratio 0.05 |
| Batch | 16 × 2 grad-accum = 32 effective |
| Epochs | 6 budgeted, ran the full ~2,100 steps |
| Best checkpoint | step 2,000 (dev WER 32.90) |
| Precision | bf16 |
| Hardware | 1 × NVIDIA RTX 5080 (16 GB, Blackwell sm_120) |
| Wall clock | ~1.2 hours |
| Seed | 13 |
Checkpoints were selected on dev WER, not eval_loss. In ASR these
routinely diverge — loss can rise while WER falls, because the model becomes
less confident but more correct. Selection on loss regularly keeps the wrong
checkpoint. Real transcripts were generated at every evaluation
(predict_with_generate=True) and scored with the same normalizer used in
training and final evaluation.
Dev WER curve (400-utterance dev subset):
| step | WER | CER |
|---|---|---|
| 250 | 50.96 | 31.70 |
| 500 | 39.11 | 20.91 |
| 750 | 38.94 | 20.79 |
| 1000 | 36.84 | 18.57 |
| 1250 | 33.24 | 16.05 |
| 1500 | 34.59 | 16.12 |
| 1750 | 32.97 | 15.25 |
| 2000 | 32.90 | 15.17 |
| 2100 | 32.91 | 15.33 |
80% of the improvement arrived by step 1,250. Everything after that oscillates within about ±1 WER, which is the noise floor of a 400-utterance dev subset — the last three checkpoints are statistically indistinguishable.
ARTPARK-IISc/Vaani-transcription-part, config Bhojpuri — ~24 hours of
transcribed spontaneous speech (speakers describing prompt images). CC-BY-4.0,
gated on the Hub.
The dataset's official splits were used unmodified:
| split | n |
|---|---|
| train | 11,191 |
| validation | 1,517 |
| test | 1,426 |
The evaluation scored exactly 1,426 utterances, confirming the test split was never re-partitioned.
Training and evaluation used one shared normalizer, imported by data prep,
training, and evaluation alike — text_norm.py in the training repo. It:
[noise] / (laughs) / <unk> style annotation tags।) and double danda (॥)It deliberately does not touch nukta or matras, since those changes alter actual words.
WER numbers are not comparable across different normalizers. If you benchmark against this model, use the same one or state your own.
Vaani has no speaker ID column. ARTPARK had speaker metadata when they built the official splits; a downstream user does not. Speaker-disjointness is therefore trusted, not verified — by me or by anyone else working from the public dataset. This is a property of the dataset, and any model card claiming verified speaker-disjointness on Vaani is overclaiming.
What was checked, and passed:
The audit script (check_leakage.py) is in the training repo; these numbers
are reproducible.
referenceImage overlap between train and test is expected and is not
speaker leakage — it means speakers were shown some of the same prompt
images, so the splits share topics.
Also note the Vaani transcription convention writes English loanwords twice,
once in Devanagari and once in Latin (टेबल table, क्लास class रूम room).
The model learns this formatting convention, which contributes to the WER
improvement without necessarily reflecting better Bhojpuri understanding.
max_new_tokens=200.openai/whisper-small is MIT licensed.Attribution to the Vaani dataset is required under CC-BY-4.0. This is the citation requested on the dataset page:
@misc{pulikodan2026vaanicapturinglanguagelandscape,
title={VAANI: Capturing the language landscape for an inclusive digital India},
author={Sujith Pulikodan and Abhayjeet Singh and Agneedh Basu and Nihar Desai and Pavan Kumar J and Pranav D Bhat and Raghu Dharmaraju and Ritika Gupta and Sathvik Udupa and Saurabh Kumar and Sumit Sharma and Vaibhav Vishwakarma and Visruth Sanka and Dinesh Tewari and Harsh Dhand and Amrita Kamat and Sukhwinder Singh and Shikhar Vashishth and Partha Talukdar and Raj Acharya and Prasanta Kumar Ghosh},
year={2026},
eprint={2603.28714},
archivePrefix={arXiv},
primaryClass={eess.AS},
url={https://arxiv.org/abs/2603.28714}
}
python prepare_data.py --language Bhojpuri --out data/bhojpuri
python evaluate.py --data data/bhojpuri --split test \
--model openai/whisper-small --dump baseline_small.jsonl
python train_lora.py --data data/bhojpuri --out runs/bhojpuri-small \
--model openai/whisper-small --lr 1e-3 --epochs 6 --batch 16
python evaluate.py --data data/bhojpuri --split test \
--model openai/whisper-small \
--adapter runs/bhojpuri-small/adapter --dump tuned_small.jsonl
python check_leakage.py --data data/bhojpuri --dump tuned_small.jsonl