Anuvaad-Small
Anuvaad ("translation" in Sanskrit/Hindi) is a family of English↔Hindi translation models built via GRPO reinforcement learning fine-tuning. Anuvaad-Small is fine-tuned from google/gemma-4-E2B-it.
Why GRPO instead of supervised fine-tuning
Earlier attempts to improve Hindi translation quality via standard supervised fine-tuning (multiple rounds, capped and full-dataset variants) consistently caused catastrophic forgetting — Hindi quality regressed relative to the untouched base model in every SFT variant tried. GRPO's KL-divergence penalty against the reference (base) model directly constrains how far the policy can drift while still optimizing for reward, which SFT has no equivalent of. This is the first approach in this project's history that improves both translation directions simultaneously, with zero regression relative to base.
Training details
- Base model:
google/gemma-4-E2B-it - Method: GRPO (via TRL's
GRPOTrainer), LoRA adapters (r=16, alpha=16, scoped to the text-decoder attention/MLP layers only) - Reward function: chrF++ (sacrebleu,
word_order=2) between each sampled completion and the reference translation - Dataset: approx. 88.9M sentence pairs (44.4M English→Hindi + 44.4M Hindi→English), derived from BPCC, sampled via reservoir sampling from the full uncapped corpus
- Hyperparameters:
learning_rate=1e-5,beta=0.04(KL penalty coefficient),temperature=0.8,top_p=0.95,num_generations=16,max_completion_length=256 - Hardware: 4x H200 GPUs (SkyPilot-managed), DDP with
ddp_find_unused_parameters=True(the base model's vision/audio towers are unused for this text-only task) - Training extent: this checkpoint represents approx. 26,500 real cumulative optimizer steps into GRPO training. This model's benchmark performance plateaued around this point — the vast majority of its total improvement over base happened in the first approx. 2,500 steps, after which scores oscillated in a narrow band with no further net gain. See Anuvaad-Large (fine-tuned from the larger
google/gemma-4-E4B-it) for a variant that kept improving well past this point.
Prompt format
Uses the standard Gemma-4 chat template with a system + user turn:
System: You are a professional machine translation system. Your sole purpose is to translate
text accurately from one language to another.
CRITICAL INSTRUCTIONS:
1. Output ONLY the translation of the provided text.
2. Do NOT include any explanations, notes, alternatives, or commentary.
3. Do NOT include phrases like "Here is the translation:", "Translation:", or similar prefixes.
4. Do NOT include any meta-commentary about the translation quality or choices.
5. Do NOT wrap the translation in quotes or any formatting.
6. Preserve the original formatting, punctuation style, and structure.
7. If the source text is a single sentence, output only a single translated sentence.
8. Begin your response directly with the translated text.
User: Translate the following text to {Hindi|English}:
{source_text}
Benchmark results (chrF++)
Evaluated on FLORES-200 devtest (approx. 1,012 sentence pairs) and IN22-Gen (approx. 1,024 sentence pairs), both directions, greedy decoding.
English → Hindi
| Rank | Model | FLORES | IN22-Gen |
|---|---|---|---|
| 1 | Bodhan-ai/indic-translate | 57.63 | 55.29 |
| 2 | Sarvam-Translate | 57.21 | 56.43 |
| 3 | Anuvaad-Large | 56.76 | 54.13 |
| 4 | Anuvaad-Small (this model) | 56.12 | 53.45 |
| 5 | Base google/gemma-4-E4B-it |
55.48 | 53.80 |
| 6 | Base google/gemma-4-E2B-it |
54.89 | 50.39 |
Hindi → English
| Rank | Model | FLORES | IN22-Gen |
|---|---|---|---|
| 1 | Bodhan-ai/indic-translate | 65.50 | 64.01 |
| 2 | Anuvaad-Large | 64.68 | 63.55 |
| 3 | Base google/gemma-4-E4B-it |
63.75 | 62.83 |
| 4 | Anuvaad-Small (this model) | 62.97 | 61.57 |
| 5 | Base google/gemma-4-E2B-it |
61.78 | 60.24 |
| 6 | Sarvam-Translate | 55.45 | 56.03 |
Summary: this model beats its own base (google/gemma-4-E2B-it) on every single benchmark/direction combination (8/8), averaging 96.6% of the Bodhan-ai/indic-translate reference score — a dedicated, purpose-built translation model — across both benchmarks and directions.
Limitations
- This checkpoint reflects a training plateau for this model size specifically — its KL divergence from the reference model climbed substantially (0.6-0.9) without corresponding further benchmark gains, suggesting it exhausted the easy wins available under this reward signal. See Anuvaad-Large for a larger variant trained with the same recipe that continued improving well past this point.
- Only Hindi↔English translation is supported; other Indic languages are not covered.
- Trained and evaluated on formal/written register text (news, government documents); performance on informal or code-mixed text is untested.
Usage
from transformers import AutoTokenizer, Gemma4ForConditionalGeneration
model_id = "PVKREDDY0808/Anuvaad-Small"
tok = AutoTokenizer.from_pretrained(model_id)
model = Gemma4ForConditionalGeneration.from_pretrained(model_id, dtype="bfloat16", device_map="cuda")
messages = [
{"role": "system", "content": "You are a professional machine translation system. Your sole purpose is to translate text accurately from one language to another.\n\nCRITICAL INSTRUCTIONS:\n1. Output ONLY the translation of the provided text.\n2. Do NOT include any explanations, notes, alternatives, or commentary.\n3. Do NOT include phrases like \"Here is the translation:\", \"Translation:\", or similar prefixes.\n4. Do NOT include any meta-commentary about the translation quality or choices.\n5. Do NOT wrap the translation in quotes or any formatting.\n6. Preserve the original formatting, punctuation style, and structure.\n7. If the source text is a single sentence, output only a single translated sentence.\n8. Begin your response directly with the translated text."},
{"role": "user", "content": "Translate the following text to Hindi:\n\nThe economy grew steadily over the past year."},
]
prompt = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tok(prompt, return_tensors="pt").to(model.device)
out = model.generate(**inputs, max_new_tokens=200, do_sample=False)
print(tok.decode(out[0, inputs.input_ids.shape[1]:], skip_special_tokens=True))
- Downloads last month
- 12