File size: 6,391 Bytes
3cf1186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e288c49
3cf1186
7fbae5c
567c5a2
02a7bd2
 
 
 
 
 
3cf1186
 
 
 
 
 
 
 
 
02a7bd2
 
3cf1186
 
 
95eae70
 
 
3cf1186
95eae70
3cf1186
95eae70
 
 
3cf1186
02a7bd2
3cf1186
02a7bd2
3cf1186
02a7bd2
3cf1186
02a7bd2
 
 
 
 
 
3cf1186
02a7bd2
3cf1186
02a7bd2
 
 
 
3cf1186
 
 
 
 
 
02a7bd2
3cf1186
 
02a7bd2
3cf1186
 
 
 
02a7bd2
3cf1186
 
02a7bd2
3cf1186
 
 
 
 
 
 
 
 
 
 
 
 
02a7bd2
3cf1186
 
 
 
 
 
02a7bd2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3cf1186
 
 
02a7bd2
 
 
3cf1186
 
 
02a7bd2
 
3cf1186
 
 
02a7bd2
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
---
language:
- vi
- en
license: cc-by-nc-sa-4.0
library_name: pytorch
pipeline_tag: automatic-speech-recognition
base_model: google/embeddinggemma-2
tags:
- ctc
- vietnamese
- code-switching
- streaming
- embeddinggemma
- asr
metrics:
- wer
---

# VieGem-ASR 🇻🇳💎

<p align="center"><img src="assets/cover.jpg" width="460" alt="VieGem-ASR"></p>

Fast Vietnamese and Vietnamese–English speech recognition: a 359M-parameter streaming CTC model on Google's
**EmbeddingGemma 2** audio encoder. Inspired by Quan Nguyen's **QUAN-Transcribe-1.0**.

- **Fast:** about **3,500× real time** on one H100 (batched, greedy); streams in 0.48 s chunks with no look-ahead.
- **Vietnamese:** mean WER **6.34** over 8 common test sets (**5.29** with the included n-gram LM).
- **Code-switching:** ViMedCSS WER **14.3**; **66%** of English terms written exactly.

## Demo

<video controls width="540" src="https://huggingface.co/5CD-AI/VieGem-ASR/resolve/main/demo/viegem_asr_promo.mp4"></video>

## Results

### Vietnamese test sets

WER %, lower is better, greedy unless marked "+ LM". VieGem-ASR, QUAN-Transcribe-1.0 and Qwen3-ASR-0.6B were run by
us with one normalizer; † = reported in the QUAN-Transcribe-1.0 model card (our scorer matches its numbers within 0.1).

| Test set | What it contains | VieGem-ASR | VieGem-ASR + LM | QUAN-Transcribe-1.0 | Qwen3-ASR-0.6B | Gemini-Flash-3.8 † | ElevenLabs Scribe v2 † | PhoWhisper-large † | Whisper large-v3 † |
|---|---|---|---|---|---|---|---|---|---|
| VIVOS | read speech, quiet room | 6.63 | _4.78_ | 7.52 | 10.22 | 9.36 | 9.14 | **4.52** | 9.18 |
| FLEURS vi | read Wikipedia sentences | 7.33 | _5.85_ | 7.88 | 8.40 | 5.90 | 5.60 | **5.31** | 7.09 |
| Common Voice 17 | read, crowd-sourced microphones | 8.61 | **6.21** | 9.69 | 16.07 | 10.30 | 10.50 | 9.12 | 18.43 |
| PhoAudiobook | audiobooks | 1.39 | 1.04 | 1.39 | 3.23 | 1.60 | 1.45 | **0.87** | 1.85 |
| LSVSC | large mixed-domain corpus | 5.16 | **4.71** | 5.35 | 7.78 | 5.70 | 6.25 | 6.81 | 13.96 |
| Bud500 | mixed domains, tightly cut clips | 3.08 | 2.75 | 1.82 | 6.79 | **1.80** | 3.15 | 9.51 | 64.03 |
| GigaSpeech2 vi | YouTube, spontaneous | 9.50 | **8.52** | 11.60 | 11.37 | 10.70 | 10.50 | 10.14 | 14.19 |
| ViMD | dialects of 63 provinces | 9.05 | **8.46** | 9.84 | 13.98 | 10.00 | 10.05 | 11.38 | 15.56 |
| **Mean of the 8** | | 6.34 | **5.29** | 6.89 | 9.73 | 6.92 | 7.08 | 7.21 | 18.04 |

Bold = best greedy result per row. Text is lowercased, without punctuation, numbers as words.

### Vietnamese–English code-switching (ViMedCSS)

| System | Test WER | Hard WER |
|---|---|---|
| Whisper-large-v3 (paper) | 34.47 | 39.35 |
| PhoWhisper-large (paper) | 31.24 | 37.37 |
| PhoWhisper-small + AG, fine-tuned (paper) | 23.67 | 33.73 |
| QUAN-Transcribe-1.0 | 22.26 | 26.56 |
| **VieGem-ASR** | **14.30** | **21.08** |
| **VieGem-ASR + LM** | **13.00** | **19.84** |

"Paper" = numbers from the ViMedCSS paper (its own normalization, so approximate).

### Streaming and speed

- Streaming (0.48 s chunks): VIVOS 8.73, CV17 10.84, FLEURS 9.02, LSVSC 5.84, ViMD 10.00, ViMedCSS 15.99.
- A 10-minute video is transcribed in **0.49 s** on one H100 (median of 20 runs, ~1,220× real time).

## Usage

```python
import torch, soundfile as sf
from transformers import AutoTokenizer
from asr.model import CTCModel, ModelConfig, greedy_decode     # asr/ folder of this repo
from asr.text import Units

ck = torch.load("viegem_asr.pt", map_location="cpu")
cfg = ModelConfig.from_dict(ck["model_config"])
units = Units(ck["gemma_ids"])
tok = AutoTokenizer.from_pretrained(cfg.encoder_id)               # google/embeddinggemma-2 (accept its terms)
model = CTCModel(cfg, units.gemma_ids)
model.load_state_dict(ck["model"], strict=False)
model = model.cuda().eval()

wav, sr = sf.read("audio.wav", dtype="float32")                  # 16 kHz mono, ≤ 30 s
x = torch.from_numpy(wav)[None].cuda()
with torch.no_grad(), torch.autocast("cuda", dtype=torch.bfloat16):
    logits, lens = model(x, torch.tensor([x.shape[1]], device="cuda"), streaming=False)  # streaming=True: 0.48 s chunks
print(units.decode(tok, greedy_decode(logits, lens)[0]))
```

With the LM (`pip install kenlm`):

```python
from asr.lm_decode import LMDecoder

words = open("lm/viegem_lm.unigrams.txt", encoding="utf-8").read().split()
dec = LMDecoder("lm/viegem_lm.bin", unigrams=words, alpha=0.35, beta=1.5)
pieces = ["", ""] + tok.convert_ids_to_tokens(units.gemma_ids)
lp = torch.log_softmax(logits[0, : lens[0]].float(), -1).cpu().numpy()
print(units.decode(tok, dec.decode(lp, pieces, beam_width=32)))
```

## Model

- **Encoder:** EmbeddingGemma 2 audio tower (12 conformer layers, 25 frames/s); top 4 layers fine-tuned.
- **Reader:** layer mix + 6 transformer blocks (d=768) with chunked attention, so one model serves offline and streaming.
- **Head:** 21,031 Gemma tokens (Vietnamese, English, single characters), initialized from Gemma's text embeddings.
- **Files:** `viegem_asr.pt` (fp16 weights, 311 MB), `asr/` (code), `lm/` (4-gram KenLM from our training transcripts, 71 MB), `demo/`.

## Training data

About **5,800 hours**, every set filtered against all test and dev sentences.

| Type | Sources | Hours |
|---|---|---|
| Audiobooks | PhoAudiobook, InfoRe2 | ~1,900 |
| Social media and YouTube | VietSpeech, viVoice, GigaSpeech2 (subset), VietSuperSpeech | ~2,820 |
| Mixed domains | Bud500, VLSP2020, LSVSC, ViMD, FPT FOSD, InfoRe1, FLEURS, Common Voice 17 | ~780 |
| English | LibriSpeech-100 | ~100 |
| Vietnamese–English (medical) | ViMedCSS | ~23 |

## Limitations

- Output is lowercase, without punctuation; numbers are written as words.
- Clips cut in the middle of a word (as in Bud500) lose the last word more often.
- English is meant for terms mixed into Vietnamese; plain English is weak (LibriSpeech test-clean 10.7).

## License

Non-commercial research use (**CC-BY-NC-SA-4.0**), because the training data includes Bud500, viVoice and PhoAudiobook.
Use must also follow the **Gemma Terms of Use** and Prohibited Use Policy.

## Credits

Google DeepMind (EmbeddingGemma 2); Quan Nguyen (QUAN-Transcribe-1.0, the inspiration for this work); the authors of
VIVOS, Common Voice, FLEURS, LSVSC, ViMD, Bud500, PhoAudiobook, VietSpeech, viVoice, GigaSpeech2, VietSuperSpeech,
VLSP2020, FPT FOSD, InfoRe, ViMedCSS and LibriSpeech. ViMedCSS baselines: Nguyen et al., LREC 2026.