Views
No views yet
Fine-tuning Meta's Omnilingual ASR CTC 1B on 50 hours of real-world noisy Bengali audio reduces WER from 65% to 47% and completely eliminates a critical baseline failure mode where the model incorrectly transcribes Bengali speech into Hindi, Tamil, and Telugu scripts.
omniASR_CTC_1B model, despite being trained on 1600+ languages, has a
fundamental limitation for Bengali: CTC models ignore the lang conditioning parameter.
This means the model has no mechanism to know which language it is hearing, and in practice
it frequently outputs the wrong script entirely for Bengali audio.Bengali audio input: "আমি বাংলায় কথা বলছি"
Baseline output (wrong): "मैं हिंदी में बात कर रहा हूं" ← Hindi script
or "நான் தமிழில் பேசுகிறேன்" ← Tamil script
or "میں اردو میں بات کر رہا ہوں" ← Urdu script
Fine-tuned output (correct): "আমি বাংলায় কথা বলছি" ← Bengali ✓| Model | WER ↓ | Improvement |
|---|---|---|
| omniASR_CTC_1B (baseline, no fine-tune) | 65% | — |
| OmniBengali CTC 1B (this model) | 47% | ▼ 18pp |
Training curve image:

WER (Word Error Rate) — out of every 100 words spoken,
how many did the model get wrong?
Baseline: 65 words wrong out of 100
Fine-tuned: 47 words wrong out of 100
Improvement: 18 fewer errors per 100 words ✓
Additionally the baseline was outputting
the WRONG LANGUAGE entirely in most cases —
making the 65% WER misleading since it was
not even attempting Bengali.| Split | Samples | Total_Seconds | Total_Minutes | Total_Hours |
|---|---|---|---|---|
| Train | 21,992 | 44181.28 | 736.35 | 12.27 |
| Validation | 3,350 | 114621.73 | 1910.36 | 31.84 |
| Test | 8,287 | 17469.19 | 291.15 | 4.85 |
| Total | 33,629 | 176,272.2 | 2937.86 | 48.96 |
Input: Raw 16kHz mono audio waveform
↓
CNN Feature Extractor (wav2vec2 frontend)
→ Extracts local acoustic features every 20ms
↓
Wav2Vec2 Transformer Encoder (1B params)
→ 24 transformer layers, hidden dim 1024
→ Models long-range speech dependencies
↓
CTC Linear Projection Head
→ Maps 1024-dim features → 9812 token vocabulary
↓
CTC Decoding (greedy argmax + blank collapse)
→ Outputs Bengali Unicode textpip install omnilingual-asr fairseq2 torchaudio1import torch
2from fairseq2.models.hub import load_model
3from omnilingual_asr.models.inference.pipeline import (
4 ASRInferencePipeline,
5 load_tokenizer,
6)
7from huggingface_hub import hf_hub_download
8
9# ── Load model ────────────────────────────────────────────────────────
10device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
11
12# Download fine-tuned weights from HF Hub
13model_path = hf_hub_download(
14 repo_id="YOUR_USERNAME/omniASR-CTC-1B-bengali",
15 filename="model.pt"
16)
17
18# Load base architecture
19model = load_model(
20 "omniASR_CTC_1B",
21 device=device,
22 dtype=torch.bfloat16
23)
24
25# Inject fine-tuned Bengali weights
26state_dict = torch.load(model_path, map_location=device)
27model.load_state_dict(state_dict, strict=False)
28
29# Load tokenizer
30tokenizer = load_tokenizer("omniASR_CTC_1B")
31
32# Build inference pipeline
33pipeline = ASRInferencePipeline(
34 model_card=None,
35 model=model,
36 tokenizer=tokenizer,
37 device=device,
38 dtype=torch.bfloat16,
39)
40
41# ── Transcribe ────────────────────────────────────────────────────────
42# From file path
43result = pipeline.transcribe(["path/to/bengali_audio.wav"])
44print(result[0])
45
46# From multiple files (batched)
47results = pipeline.transcribe(
48 ["audio1.wav", "audio2.wav", "audio3.wav"],
49 batch_size=4
50)
51for text in results:
52 print(text)1import sounddevice as sd
2import numpy as np
3
4SAMPLE_RATE = 16_000
5DURATION = 5 # seconds to record
6
7print("Recording... speak now")
8audio = sd.rec(
9 int(DURATION * SAMPLE_RATE),
10 samplerate=SAMPLE_RATE,
11 channels=1,
12 dtype=np.float32
13)
14sd.wait()
15audio = audio.squeeze()
16
17# Pass numpy array directly to pipeline
18result = pipeline.transcribe([{"waveform": audio, "sample_rate": SAMPLE_RATE}])
19print(f"Transcription: {result[0]}")1from pathlib import Path
2
3audio_dir = Path("path/to/your/bengali/audio/files")
4wav_files = list(audio_dir.glob("*.wav"))
5
6print(f"Transcribing {len(wav_files)} files...")
7results = pipeline.transcribe(
8 [str(f) for f in wav_files],
9 batch_size=8
10)
11
12for audio_file, text in zip(wav_files, results):
13 print(f"{audio_file.name}: {text}")1import requests
2
3API_URL = "https://api-inference.huggingface.co/models/YOUR_USERNAME/omniASR-CTC-1B-bengali"
4headers = {"Authorization": "Bearer YOUR_HF_TOKEN"}
5
6with open("audio.wav", "rb") as f:
7 data = f.read()
8
9response = requests.post(API_URL, headers=headers, data=data)
10print(response.json())1# app.py — paste this into a new HF Space
2import gradio as gr
3import torch
4from fairseq2.models.hub import load_model
5from omnilingual_asr.models.inference.pipeline import ASRInferencePipeline, load_tokenizer
6from huggingface_hub import hf_hub_download
7
8device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
9model_path = hf_hub_download(repo_id="YOUR_USERNAME/omniASR-CTC-1B-bengali", filename="model.pt")
10model = load_model("omniASR_CTC_1B", device=device, dtype=torch.bfloat16)
11state_dict = torch.load(model_path, map_location=device)
12model.load_state_dict(state_dict, strict=False)
13tokenizer = load_tokenizer("omniASR_CTC_1B")
14pipeline = ASRInferencePipeline(model_card=None, model=model, tokenizer=tokenizer, device=device, dtype=torch.bfloat16)
15
16def transcribe(audio):
17 result = pipeline.transcribe([audio])
18 return result[0]
19
20demo = gr.Interface(
21 fn=transcribe,
22 inputs=gr.Audio(type="filepath", label="Upload Bengali Audio"),
23 outputs=gr.Textbox(label="Bengali Transcription"),
24 title="OmniBengali ASR — 1B CTC",
25 description="Upload a Bengali audio file (.wav) to get Bengali text transcription.",
26)
27demo.launch()1FROM python:3.10-slim
2RUN pip install omnilingual-asr fairseq2 torchaudio fastapi uvicorn huggingface_hub
3COPY serve.py .
4CMD ["uvicorn", "serve:app", "--host", "0.0.0.0", "--port", "8000"]1# serve.py
2from fastapi import FastAPI, UploadFile
3import torch, tempfile, shutil
4from pathlib import Path
5from fairseq2.models.hub import load_model
6from omnilingual_asr.models.inference.pipeline import ASRInferencePipeline, load_tokenizer
7from huggingface_hub import hf_hub_download
8
9app = FastAPI(title="OmniBengali ASR API")
10
11device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
12model_path = hf_hub_download(repo_id="YOUR_USERNAME/omniASR-CTC-1B-bengali", filename="model.pt")
13model = load_model("omniASR_CTC_1B", device=device, dtype=torch.bfloat16)
14state_dict = torch.load(model_path, map_location=device)
15model.load_state_dict(state_dict, strict=False)
16tokenizer = load_tokenizer("omniASR_CTC_1B")
17pipeline = ASRInferencePipeline(model_card=None, model=model, tokenizer=tokenizer, device=device, dtype=torch.bfloat16)
18
19@app.post("/transcribe")
20async def transcribe(file: UploadFile):
21 with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
22 shutil.copyfileobj(file.file, tmp)
23 tmp_path = tmp.name
24 result = pipeline.transcribe([tmp_path])
25 Path(tmp_path).unlink()
26 return {"transcription": result[0]}• Trained on a specific Bengali speech domain
Performance may vary on heavy dialects or accents
not well represented in training data
• CTC decoding has no language model rescoring
Output may contain disfluencies that an LM would clean up
• Optimised for ~10 second clips
Very short (<1s) or very long (>30s) clips may degrade
• Training data is noisy real-world audio
May perform differently on clean studio recordingslang parameter
is documented as ignored for CTC inference. The baseline therefore outputs
whatever script has the highest probability given the acoustic features alone,
which for Bengali audio often means Hindi/Tamil/Telugu due to shared phonetic
space in the pretrained multilingual representation. Fine-tuning on a
single-language corpus biases both the encoder representations and the CTC
projection head toward Bengali-specific phoneme-to-character mappings,
eliminating the cross-lingual confusion without requiring any runtime
language specification.wer_calculator.py where all-blank CTC predictions (common in early
training steps) produced zero-length hypothesis sequences, causing
BatchLayout.of() to raise ValueError: All lengths in seq_lens must be >= 1.
The fix has been submitted as a PR to the upstream
omnilingual-asr repository.1@misc{omnibengali_asr_1b_2025,
2 title = {OmniBengali ASR — Fine-Tuned Omnilingual ASR CTC 1B for Bengali Speech Recognition},
3 author = {YOUR_FULL_NAME and COLLABORATOR_FULL_NAME},
4 year = {2025},
5 url = {https://huggingface.co/YOUR_USERNAME/omniASR-CTC-1B-bengali},
6 note = {Fine-tuned on ~50 hours of Bengali speech data}
7}1@misc{omnilingualasr2025,
2 title = {Omnilingual ASR: Open-Source Multilingual Speech Recognition},
3 author = {Meta AI Research},
4 year = {2025},
5 url = {https://github.com/facebookresearch/omnilingual-asr}
6}