gemma4-e2b-vaani-asr-s2t-t2t
A full fine-tune of google/gemma-4-E2B-it for three tasks in one model: speech
recognition, speech-to-text translation, and text-to-text translation, across
English and dozens of Indian languages (the long tail of low-resource/tribal
languages comes from Vaani, so ASR coverage goes
well beyond the 19 languages listed in the metadata above — see
Supported languages).
Averaged across all evaluated languages and directions, fine-tuning roughly halves ASR error (35% WER vs. 55% for the base model) and nearly doubles translation quality on both translation tasks (T2T: 47.6 vs. 25.0 chrF++; S2T: 36.4 vs. 18.7 chrF++) — see Results for the full per-language breakdown.
Layout
infer.py inference (task, direction, input -- that's it)
prompt_contract.py the exact prompts and trained language set, no dependencies
README.md
config.json, model.safetensors, tokenizer.json, ... weights, config and tokenizer
Quick start
Same transformers usage as the base model
(google/gemma-4-E2B-it) — just
this repo's id, plus one of the three fixed prompts below.
from transformers import AutoProcessor, AutoModelForMultimodalLM
MODEL_ID = "ARTPARK-IISc/gemma4-e2b-vaani-asr-s2t-t2t"
processor = AutoProcessor.from_pretrained(MODEL_ID)
model = AutoModelForMultimodalLM.from_pretrained(
MODEL_ID,
dtype="auto",
device_map="auto",
attn_implementation="eager", # matches training -- see below
)
ASR — transcribe speech, same language in and out:
messages = [{"role": "user", "content": [
{"type": "audio", "audio": "clip.wav"},
{"type": "text", "text": "Transcribe this audio in Hindi."},
]}]
inputs = processor.apply_chat_template(
[messages], tokenize=True, return_dict=True, return_tensors="pt",
add_generation_prompt=True,
).to(model.device)
input_len = inputs["input_ids"].shape[-1]
outputs = model.generate(**inputs, max_new_tokens=256)
response = processor.tokenizer.decode(outputs[0][input_len:], skip_special_tokens=True)
print(response)
S2T — translate speech into text in another language (same inputs /
generate / decode as above, just a different prompt):
messages = [{"role": "user", "content": [
{"type": "audio", "audio": "clip.wav"},
{"type": "text", "text": "Translate this Hindi audio to English text."},
]}]
T2T — translate text (no audio part at all):
messages = [{"role": "user", "content": [
{"type": "text", "text": "Translate the following text from Hindi to English.\n\nनमस्ते, आप कैसे हैं?"},
]}]
A ready-made infer.py/prompt_contract.py pair is also included, wrapping
the above into an Infer class (load once, call .asr() / .s2t() / .t2t()
repeatedly) plus a CLI:
from infer import Infer
m = Infer()
m.asr("clip.wav", lang="Hindi")
python infer.py --task asr --src-lang Hindi --audio clip.wav
The prompts are part of the model
Hardcoded in prompt_contract.py, verbatim from training — these are not
configurable at training time, and rewording them was not tested, so don't
expect a paraphrase of these to work as well. infer.py always goes through
this file's build_prompt_* functions rather than formatting the instruction
itself:
ASR: Transcribe this audio in {lang}.
S2T: Translate this {src} audio to {tgt} text.
T2T: Translate the following text from {src} to {tgt}.\n\n{text}
Wrapped in Gemma 4's chat template (<|turn>user ... <turn|>, not Gemma 2/3's
<start_of_turn>) — use processor.apply_chat_template, don't hand-write turn
markers.
Supported languages
ASR
English, Assamese, Bengali, Gujarati, Hindi, Kannada, Maithili, Malayalam, Manipuri, Marathi, Nepali, Odia, Punjabi, Sanskrit, Santali, Tamil, Telugu, Urdu, Bhojpuri, Chhattisgarhi, Garo, Magadhi, Mizo — plus ~50 additional lower-resource and tribal languages from Vaani (Angika, Awadhi, Garhwali, Khasi, and many more); accuracy on the long tail varies widely with how much training data each one had — see eval results for the pattern.
T2T
English ↔ Assamese, Bengali, Bodo, Dogri, Gujarati, Hindi, Kannada, Kashmiri, Konkani, Maithili, Malayalam, Manipuri, Marathi, Nepali, Odia, Punjabi, Sanskrit, Santali, Sindhi, Tamil, Telugu, Urdu (22 languages). Both Indic→English and English→Indic are supported; Indic↔Indic is not (see Limitations).
S2T
English ↔ Assamese, Bengali, Gujarati, Hindi, Kannada, Malayalam, Manipuri, Marathi, Nepali, Odia, Punjabi, Sanskrit, Sindhi, Tamil, Telugu, Urdu (16 languages). Both Indic→English and English→Indic are supported, though coverage is thin for Sanskrit and Sindhi (see Results); Indic↔Indic is not (see Limitations).
Results
Scored on held-out test sets built specifically for this evaluation (not seen during training). ASR scored with WER (bracket/punctuation-normalized, applied to both reference and hypothesis, and scored against code-switched references where available — e.g. "mobile" accepted in place of "मोबाइल" — since some sources' native-script references include loanwords the model correctly renders in Latin script, which naive native-script WER penalizes as an error); S2T/T2T scored with chrF++.
T2T — IN22-Gen (chrF++, higher better)
| Language | Indic → English | English → Indic |
|---|---|---|
| Assamese | 54.78 | 41.75 |
| Bengali | 53.18 | 45.38 |
| Bodo | 47.96 | 45.64 |
| Dogri | 57.66 | 53.39 |
| Gujarati | 55.55 | 47.63 |
| Hindi | 57.22 | 52.43 |
| Kannada | 54.32 | 46.48 |
| Kashmiri | 49.10 | 30.25 |
| Konkani | 48.00 | 41.05 |
| Maithili | 54.78 | 47.12 |
| Malayalam | 52.49 | 42.62 |
| Manipuri | 42.30 | 17.98 |
| Marathi | 54.24 | 45.90 |
| Nepali | 57.55 | 45.62 |
| Odia | 54.39 | 42.65 |
| Punjabi | 53.11 | 44.45 |
| Sanskrit | 44.35 | 34.23 |
| Santali | 40.12 | 36.57 |
| Sindhi | 49.64 | 36.25 |
| Tamil | 49.71 | 46.55 |
| Telugu | 54.03 | 47.74 |
| Urdu | 59.94 | 58.10 |
S2T — FLEURS (chrF++, higher better)
| Language | Indic → English | English → Indic |
|---|---|---|
| Assamese | 36.20 | 30.22 |
| Bengali | 44.07 | 37.04 |
| Gujarati | 43.66 | 40.12 |
| Hindi | 43.34 | 43.55 |
| Kannada | 41.30 | 40.07 |
| Malayalam | 43.75 | 39.81 |
| Marathi | 41.38 | 36.30 |
| Nepali | 21.86 | 38.73 |
| Odia | 40.52 | 35.21 |
| Punjabi | 38.21 | 36.62 |
| Sindhi | 26.34 | 0.65 |
| Tamil | 29.54 | 41.09 |
| Telugu | 38.68 | 41.21 |
| Urdu | 38.88 | 36.57 |
ASR — FLEURS + KathBath + Vaani (WER%, lower better)
| Language | Gemma-4-E2B-IT (base) | gemma4-e2b-vaani-asr-s2t-t2t |
|---|---|---|
| Assamese | 51.71 | 23.04 |
| Bengali | 36.88 | 30.6 |
| English | 23.47 | 17.49 |
| Gujarati | 31.23 | 21.69 |
| Hindi | 22.93 | 17.11 |
| Kannada | 51.43 | 41.21 |
| Maithili | 51.69 | 47 |
| Malayalam | 52.83 | 37.25 |
| Manipuri | 106.13 | 46.1 |
| Marathi | 41.26 | 31.4 |
| Nepali | 44.85 | 29.75 |
| Odia | 51.14 | 33.32 |
| Punjabi | 30.67 | 20.69 |
| Sanskrit | 73.59 | 53.43 |
| Santali | 92.89 | 63.34 |
| Tamil | 58.05 | 40.21 |
| Telugu | 49.94 | 39.74 |
| Urdu | 29.89 | 20.82 |
| Bhojpuri | 58.63 | 45.75 |
| Chhattisgarhi | 60.86 | 48.79 |
| Garo | 105.71 | 24.11 |
| Magadhi | 57.82 | 46.54 |
| Mizo | 87.39 | 36.5 |
Limitations
- English-pivot only, same as the text-only translation sibling model. Every S2T/T2T training pair had English on one side; Indic↔Indic translation is untrained. Earlier probing found this model generalizes to unseen Indic↔Indic text pairs reasonably well but not to unseen Indic↔Indic speech pairs — an architectural/training-dynamics asymmetry between the two pathways, not a data-coverage gap (neither was ever trained on direct Indic↔Indic pairs).
- Long low-resource tail is weak. Many Vaani-only languages score 80-150%+ WER — a data-scarcity problem, not something more training fixes.
- Noisy/field-recorded audio is harder than clean studio audio for the same language, by a consistent ~2x WER gap (Vaani vs. FLEURS/KathBath).
- Audio capped at 30 seconds per clip.
- Unused modality. The checkpoint keeps Gemma-4's vision tower, frozen throughout and untouched by fine-tuning — dead weight for these three tasks, kept so the checkpoint loads with the stock model class.
License
Derived from google/gemma-4-E2B-it and therefore governed by the
Gemma Terms of Use; the prohibited-use
policy applies to this model as it does to the base. Training data mixes
BhasaAnuvaad, BPCC, and local Vaani-sourced ASR data from AI4Bharat/IISc —
check each dataset's own licence terms for your use case.
Acknowledgements
Trained with compute awarded by the Gemma Academic Program, supported by google.org. We thank Google for the award.