IndicConformer 120M — Per-Language ONNX Models for Indian Language ASR
CTC-only ONNX exports of AI4Bharat's IndicConformer hybrid CTC/RNN-T large models (~120M parameters each). One model per language for 12 Indian languages, optimized for batch inference with ONNX Runtime (GPU via CUDA/ROCm, or CPU).
1import json, numpy as np, librosa, onnxruntime as ort
2from huggingface_hub import hf_hub_download
34REPO ="sulabhkatiyar/indicconformer-120m-onnx"5LANG ="hi"# Change to any supported language code67# 1. Download model and vocabulary8model_path = hf_hub_download(REPO,f"{LANG}/model.onnx")9vocab_path = hf_hub_download(REPO,f"{LANG}/vocab.json")1011# 2. Load ONNX session (auto-selects best available provider)12providers =["CUDAExecutionProvider","ROCMExecutionProvider","CPUExecutionProvider"]13session = ort.InferenceSession(model_path, providers=providers)1415# 3. Load vocabulary16withopen(vocab_path,"r", encoding="utf-8")as f:17 vocab = json.load(f)18blank_id =len(vocab)1920# 4. Load and preprocess audio21audio, sr = librosa.load("audio.wav", sr=16000)2223# Preemphasis24audio_pe = np.concatenate([audio[:1], audio[1:]-0.97* audio[:-1]])2526# Mel spectrogram27mel = librosa.feature.melspectrogram(28 y=audio_pe, sr=16000, n_fft=512, hop_length=160, win_length=400,29 n_mels=80, fmin=0, fmax=8000, norm="slaney", power=2.0,30)31log_mel = np.log(mel +2**-24).astype(np.float32)32mean = log_mel.mean(axis=1, keepdims=True)33std = log_mel.std(axis=1, ddof=1, keepdims=True)+1e-534log_mel =(log_mel - mean)/ std
3536# Batch dimensions: [1, 80, T]37mel_batch = log_mel[np.newaxis,:,:].astype(np.float32)38mel_length = np.array([mel_batch.shape[2]], dtype=np.int64)3940# 5. Run inference41logits = session.run(None,{"audio_signal": mel_batch,"length": mel_length})[0]4243# 6. CTC greedy decode44log_probs = logits - logits.max(axis=-1, keepdims=True)45log_probs = log_probs - np.log(np.exp(log_probs).sum(axis=-1, keepdims=True))46ids = np.argmax(log_probs[0], axis=-1)4748# Collapse consecutive duplicates, remove blanks49prev, tokens =-1,[]50for t in ids:51if t != prev:52if t != blank_id and t <len(vocab):53 tokens.append(vocab[t])54 prev = t
5556transcript ="".join(tokens).replace("\u2581"," ").strip()57print(transcript)
Batch Inference
For throughput, pad mel spectrograms to the same length within a batch and set the length input to each utterance's actual frame count. The model handles variable-length inputs natively — invalid frames beyond the given length are ignored.
python
1# Assuming mels is a list of [80, T_i] arrays2mel_lengths = np.array([m.shape[1]for m in mels], dtype=np.int64)3max_len =int(mel_lengths.max())4mel_batch = np.zeros((len(mels),80, max_len), dtype=np.float32)5for i, m inenumerate(mels):6 mel_batch[i,:,:m.shape[1]]= m
78logits = session.run(None,{"audio_signal": mel_batch,"length": mel_lengths})[0]9# Decode each utterance using its actual output length (~T_mel / 4)
Export Process
These ONNX models were exported from AI4Bharat's NeMo .nemo checkpoints:
Load the hybrid CTC/RNN-T model: EncDecHybridRNNTCTCBPEModel.restore_from(nemo_path)
Switch to CTC decoder: model.cur_decoder = "ctc"
Export: model.export("model.onnx")
Extract vocabulary: list(model.ctc_decoder.vocabulary) saved as vocab.json