sea-asr-id β fast Indonesian speech recognition on EmbeddingGemma 2 (CTC)
Pixel ML builds video intelligence for Southeast Asian media. These are the speech models we run for Southeast Asian media: captions, search, clips. Open weights, open data, runs on your GPU β or on a CPU.
Google's EmbeddingGemma 2 audio encoder with a CTC head, trained by Pixel ML on openly licensed Indonesian
speech (YODAS, FLEURS, Common Voice). An optional n-gram language model (folder lm/, 0.27 GB) built from
Indonesian Wikipedia comes with the weights.
Speed (one H100 80GB, side by side with Whisper on the same GPU type and container size; sea_asr v0.2.1):
greedy decoding is about 18x faster than batched Whisper large-v3 and 9x faster than large-v3-turbo on a 79-minute
recording (long-form), and 30x / 20x faster on FLEURS test (short clips). With the n-gram LM or rescoring, the beam
search on CPU sets the speed: on short clips both modes are still faster than Whisper, but on the long-form recording
- n-gram LM is slower than turbo and + rescoring is slower than large-v3 (table in "Max accuracy" below). It also runs on a CPU. One pass, no autoregressive decode, streaming in 0.48 s chunks. Word timestamps and SRT/VTT output included.
Trained on CC-BY YouTube audio (via YODAS) and read speech. No purchased data.
Results
Same H100 for every open model, one normaliser for all (quanphid/text.py: lower case, no punctuation, numbers as
words), WER % (lower is better). Avg = mean of FLEURS and CV27. Speed = audio seconds / wall seconds on FLEURS id
test (short clips), one H100 80GB, batched, model load and warm-up excluded; ours sea_asr v0.2.1, Whisper on the
same GPU type and container size. Params = Hugging Face safetensors total (ours: counted from this repo).
| Model | Params | Avg | Speed (Γ RT) | FLEURS id test (684) | CV27 id test (3,711) | GigaSpeech 2 id test (16,218) |
|---|---|---|---|---|---|---|
| Qwen3-ASR-1.7B (our teacher) | 2,349M | 5.1 | β | 4.9 | 5.3 | 14.9 |
| sea-asr-id + n-gram LM + Sailor2-1B rescoring (opt-in) | 364M + 0.27 GB LM + 1B LM | 5.9 | 229 | 5.5 [5.0, 6.0] | 6.3 [5.9, 6.7] | 16.6 [16.3, 16.9] |
| Whisper large-v3 | 1,543M | 5.8 | 114 | 5.2 | 6.3 | 17.3 |
| Whisper large-v3-turbo | 809M | 6.5 | 171 | 5.8 | 7.2 | 18.0 |
| sea-asr-id + n-gram LM | 364M + 0.27 GB LM | 6.6 | 389 | 6.1 [5.5, 6.7] | 7.0 [6.6, 7.4] | 17.4 [17.1, 17.7] |
| Qwen3-ASR-0.6B | 938M | 8.1 | β | 7.9 | 8.4 | 17.6 |
| sea-asr-id, greedy | 364M | 9.0 | 3,427 | 9.0 [8.3, 9.7] | 9.1 [8.6, 9.6] | 18.7 [18.4, 19.0] |
| Whisper medium | 764M | 10.1 | β | 9.1 | 11.2 | 20.7 |
| Whisper small | 242M | 16.4 | β | 15.2 | 17.6 | 25.7 |
| omniASR CTC-1B β | ~1B | β | β | 15.0 | 18.9 | 28.0 |
| omniASR CTC-300M β | ~0.3B | β | β | 24.7 | 30.6 | 38.0 |
| Gemini 3.5 Transcribe (closed API) | β | β | β | 2.3 | β | β |
Size class. Under 1B: sea-asr-id with its n-gram LM ties Whisper large-v3-turbo on read speech, lower WER on conversational Indonesian (GigaSpeech 2) than every other open model we tested. Paired 95 % CI of the WER difference (sea-asr-id + LM β rival): vs Qwen3-ASR-0.6B (2.6Γ larger) FLEURS β1.8 [β2.3, β1.3], CV27 β1.4 [β1.8, β1.0], GigaSpeech 2 β0.3 [β0.4, β0.1]; vs Whisper large-v3-turbo FLEURS +0.4 [β0.1, +0.8], CV27 β0.2 [β0.7, +0.2] (tie), GigaSpeech 2 β0.6 (CI below 0); vs Whisper large-v3 FLEURS +0.9 [+0.4, +1.3], CV27 +0.7 [+0.3, +1.1]. Whisper large-v3 and Qwen3-ASR-1.7B are more accurate. Greedy, without the LM: ties Whisper medium on FLEURS (β0.1 [β0.7, +0.5]) and is better on CV27 (β2.1 [β2.7, β1.6]).
- GS2: test video 45 excluded for all systems; it overlaps our YODAS training audio.
- β = not measured in this container. Qwen3-ASR, Whisper medium / small and omniASR were not re-run here. In an earlier H100 run (different container, vLLM max batch 128), Qwen3-ASR-1.7B ran at 1,650Γ real time on FLEURS id test and Qwen3-ASR-0.6B at 810β1,597Γ across our test sets: faster than sea-asr-id + n-gram LM (389Γ), slower than greedy.
- Speed with the LM: beam 128 (the shipped default), one beam-search worker (the v0.2.1 default; 4 workers change it by less than 1 %). The beam search runs on CPU. Long-form speed: see "Max accuracy (opt-in)" below.
- LM settings (alpha 0.4, beta 1.5, beam 128) and the LM itself were chosen on FLEURS id validation only. The LM text has every sentence removed that equals or shares an 8-gram with a FLEURS, CV27 or GigaSpeech 2 test or dev reference; an LM built without that filter gives CV27 6.99 vs 7.00.
- CV27 test clips and sentences were held out of all our training data. β omniASR was trained on FLEURS and Common Voice (Meta, arXiv 2511.09690): its FLEURS and CV27 rows are not held-out scores.
Max accuracy (opt-in): 1B rescoring LM
With a 1B rescoring LM, on par with Whisper large-v3 (1.55B) on FLEURS and Common Voice, and better on GigaSpeech 2.
A second pass rescores the n-gram beam search's 32 best hypotheses with Sailor2-1B (Sea AI Lab, Apache-2.0). Weights of the scores were chosen on FLEURS id validation only. System: 364M acoustic model + 0.27 GB n-gram LM + 1B rescoring LM. Paired 95 % CI of the WER difference (ours + rescoring β Whisper large-v3): FLEURS +0.28 [β0.20, +0.74], CV27 β0.03 [β0.41, +0.36] (both: CI includes 0, on par), GigaSpeech 2 (video 45 excluded) β0.62 [β0.79, β0.45] (better).
WER %, and speed in Γ real time on one H100 80GB (sea_asr v0.2.1; Whisper on the same GPU type and container size,
32 vCPU):
| Mode | FLEURS | CV27 | GigaSpeech 2 | Speed, FLEURS test (short clips) | Speed, CV27 test (short clips) | Speed, 79-min recording (long-form) |
|---|---|---|---|---|---|---|
| greedy | 9.0 | 9.1 | 18.7 | 3,427 | 3,512 | 3,293 (1.4 s) |
+ n-gram LM (default with lm=) |
6.1 | 7.0 | 17.4 | 389 | 520 | 272 (17.5 s) |
| + n-gram LM + Sailor2-1B rescoring | 5.5 [5.0, 6.0] | 6.3 [5.9, 6.7] | 16.6 [16.3, 16.9] | 229 | 261 | 149 (31.9 s) |
| Whisper large-v3 | 5.2 | 6.3 | 17.3 | 114 | 63 | 181 (26.3 s) |
| Whisper large-v3-turbo | 5.8 | 7.2 | 18.0 | 171 | 113 | 351 (13.6 s) |
Speed: whole transcribe() wall time, load and warm-up excluded; FLEURS test = 684 clips, 2.34 h; CV27 test = 3,711
clips, 4.27 h; long-form = one 79 min 10 s Indonesian parliament plenary recording, whole file per call. The beam
search runs on CPU (one worker, the v0.2.1 default) and is most of the cost with the LM. On long-form audio + n-gram LM
is slower than Whisper large-v3-turbo and + rescoring is slower than Whisper large-v3. Rescoring needs about 2 GB of
extra GPU memory (bf16).
Error tail: CTC cannot loop (no autoregressive decoder); utterances with WER > 100 %: sea-asr-id 0 % on FLEURS, 0.08 % on CV27; Whisper large-v3 0.22 % on CV27.
Same-GPU setup: Whisper via faster-whisper 1.2.1 fp16, beam 5, Silero VAD, batch 16 (clips, or the batched long-form pipeline); ours 600 s of padded audio per forward pass. CAUTION: Whisper here spends part of its wall time on per-clip CPU work (Silero VAD + log-mel), and batch 16 is conservative for an H100. A Whisper setup tuned for throughput would narrow the gap.
Gemini is a closed benchmark row only; its outputs were not used for training (Gemini API terms).
Use
pip install "sea-asr[lm] @ git+https://github.com/PixelML/sea-asr@v0.2.1" # drop [lm] for greedy only
pip install "sea-asr[rescore] @ git+https://github.com/PixelML/sea-asr@v0.2.1" # opt-in max accuracy
from sea_asr import SeaASR, to_srt
asr = SeaASR.from_pretrained("PixelML/sea-asr-id") # greedy; device="cuda" or "cpu"
asr = SeaASR.from_pretrained("PixelML/sea-asr-id", lm="PixelML/sea-asr-id") # + n-gram LM from lm/
asr = SeaASR.from_pretrained("PixelML/sea-asr-id", lm="PixelML/sea-asr-id",
rescore=True) # + Sailor2-1B rescoring (opt-in)
print(asr.transcribe("clip.wav"))
out = asr.transcribe("newscast.wav", return_words=True) # {"text", "words": [{"word", "start", "end"}]}
open("newscast.srt", "w").write(to_srt(out["words"]))
1
00:00:00,880 --> 00:00:04,120
tim tim virtual memiliki standar
keunggulan yang sama dengan tim
2
00:00:04,320 --> 00:00:10,320
konvensional tetapi ada sedikit
perbedaan segera setelah
Cues: at most 2 lines Γ 32 characters, at most 6 s. Input: 16 kHz mono (other rates are resampled). Inputs longer
than 30 s are cut at quiet points; timestamps stay on the original timeline. CLI: python -m sea_asr --model PixelML/sea-asr-id --lm PixelML/sea-asr-id clip.wav --srt.
Limits
- Lower case, no punctuation; caption-ready output needs a punctuation + casing restorer (next, on the EG2 text backbone).
- The n-gram LM is built from Wikipedia: it helps most on read, formal speech. On casual speech the gain is smaller.
- Machine labels from Qwen3-ASR-1.7B and Whisper large-v3: the model inherits their shared errors and does not beat them on read speech.
- Code-switching to English and regional languages (Javanese, Sundanese) is not evaluated. Speaker, accent and gender balance are not measured.
- Do not use for speaker identification or surveillance.
Training
YODAS id000 + id100, scored with an earlier model (CER β€ 0.6), relabelled with Qwen3-ASR + Whisper consensus (keep
where CER(qwen, whisper) β€ 0.2, label = Qwen): 1,901 h of segments plus 386 h of 1β3 s clips, + Common Voice 27.0
train Γ5 (41 h) + FLEURS train Γ5 (45 h) = 2,373 h. Fine-tuned from the previous round on one NVIDIA DGX Spark
(GB10); best checkpoint at step 2,000 on FLEURS validation. Labels: PixelML/sea-asr-id-data. Code and recipe:
PixelML/sea-asr.
The round-1 data filter used a pilot model that saw CC BY-NC (WorldSpeech) audio. That model only scored data; no NC audio or labels entered training. WorldSpeech is used as a test set only.
The n-gram LM: KenLM 4-gram on Indonesian Wikipedia (dump 20231101, 10.3 M sentences, 156 M words after the test
filter), pruned to 0.27 GB; build details in lm/manifest.json.
Roadmap: punctuation + casing head; Malay, Vietnamese, Thai.
Credits
- Base encoder: Google DeepMind, EmbeddingGemma 2 (914f7f89)
- Data: YODAS (ESPnet/CMU, 52c5a1b9); FLEURS (Google); CC-BY YouTube creators listed in ATTRIBUTION
- Data (pins added for this release): FLEURS train (google/fleurs,
70bb2e84, CC-BY-4.0); Common Voice 27.0 Indonesian train (Mozilla, CC0, via Mozilla Data Collective); the creator list is ATTRIBUTION.tsv - Teachers: Qwen3-ASR-1.7B (Alibaba Qwen, 7278e1e7); Whisper large-v3 (OpenAI) via faster-whisper (SYSTRAN)
- Language model text: Indonesian Wikipedia, Wikipedia contributors / Wikimedia Foundation (
wikimedia/wikipedia20231101.id, b04c8d1c, CC-BY-SA-3.0 + GFDL) - Language model tools: KenLM by Kenneth Heafield (kpu/kenlm
4cb443e6); pyctcdecode 0.5.0 (Kensho Technologies, kensho-technologies/pyctcdecode) - Rescoring LM (opt-in): Sailor2-1B, Sea AI Lab (sail/Sailor2-1B
75c2856f, Apache-2.0) - Test sets: GigaSpeechBench (SpeechColab); Common Voice 27.0 Indonesian test (Mozilla, CC0); WorldSpeech (disco-eth)
- Test sets (this release): GigaSpeech 2 Indonesian test (SpeechColab, non-commercial terms, test only); FLEURS Indonesian test (same revision as above)
- Recipe: Quan Nguyen (@qnguyen3), QUAN-Transcribe β https://github.com/qnguyen3/QUAN-Transcribe (4f45e02)
Licence
Model weights and code: Apache-2.0. Base encoder EmbeddingGemma 2 is Apache-2.0; deployments must adhere to the Gemma Prohibited Use Policy.
The n-gram LM in lm/ is derived from Wikipedia text and is licensed CC BY-SA 3.0 (see lm/LICENSE.md). If you
ship or modify lm/, keep the attribution and share it under the same licence. Greedy decoding does not use lm/.
- Downloads last month
- 43
Model tree for PixelML/sea-asr-id
Base model
google/embeddinggemma-2