Automatic Speech Recognition
cactus
wannaphong commited on
Commit
5c60154
·
verified ·
1 Parent(s): d8c28a3

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +179 -0
README.md CHANGED
@@ -1,3 +1,182 @@
1
  ---
 
 
 
 
 
 
 
 
 
2
  license: apache-2.0
 
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ language:
3
+ - th
4
+ - en
5
+ - de
6
+ - fr
7
+ - es
8
+ - it
9
+ - nl
10
+ - pl
11
  license: apache-2.0
12
+ library_name: cactus
13
+ datasets:
14
+ - google/fleurs
15
+ - n-order/Thai-dialect-corpus
16
+ - huangjianbyte2024/thai_Elderly_Speech_dataset
17
+ - typhoon-ai/thai-dialect-isan-dataset
18
+ base_model:
19
+ - Cactus-Compute/whistle
20
+ pipeline_tag: automatic-speech-recognition
21
  ---
22
+
23
+ # MaprangSTT
24
+
25
+ MaprangSTT is a Thai speech-to-Text model built on [Cactus-Compute/whistle](https://huggingface.co/Cactus-Compute/whistle) for **Standard Thai and Regional Thai Dialects** (Central, Isan / Northeastern, Northern / Khummuang, Korat, Southern / Pattani, and Elderly speech). It was trained on open-data datasets.
26
+
27
+ Trained on **1,022.78 hours (594,231 clips)** of curated Thai speech using quantization-aware LoRA (`CQ W4 STE + A8`) and an expanded Thai SentencePiece vocabulary, the final model compiles into a standalone **32.49 MB** `.cact` archive ready for fast on-device CPU/GPU/mobile inference.
28
+
29
+ ## Model Summary
30
+
31
+ | Property | Value |
32
+ | :--- | :--- |
33
+ | **Base Architecture** | Cactus Whistle (8 encoder/decoder layers) |
34
+ | **Fine-Tuning Method** | Quantization-Aware LoRA (`rank=16`, `alpha=32`, 15 weight groups) + Trainable New Embedding Rows |
35
+ | **Precision / Quantization** | `W4A8` (4-bit weights with straight-through estimator, 8-bit activations & KV cache) |
36
+ | **Supported Languages** | `th` (Thai), plus base `en`, `de`, `fr`, `es`, `it`, `nl`, `pl` |
37
+ | **Vocabulary Expansion** | `+2,067` Thai tokens (`+10,958` SentencePiece pieces + `<\|th\|>` language tag) |
38
+ | **Standalone `.cact` Size** | **32.49 MB** (`whistle-thai-combined.cact`, 681 tensors) |
39
+ | **LoRA Adapter Size** | **29.0 MB** (`adapter.safetensors`) |
40
+
41
+ ### Dataset Summary
42
+
43
+ | Dataset Source | Split / Text Column | Valid Clips | Duration (Hours) |
44
+ | :--- | :--- | ---: | ---: |
45
+ | **Mozilla Common Voice v27 (`th`)** | `validated` − (`dev` + `test`) / `sentence` | 126,690 | 149.41 h |
46
+ | **Google FLEURS (`th_th`)** | `train` / `transcription` | 2,174 | 6.84 h |
47
+ | **`n-order/Thai-dialect-corpus`** | `*_train` (all 4 subsets) / `sentence` | 437,563 | 816.74 h |
48
+ | **`huangjianbyte2024/thai_Elderly_Speech_dataset`** | `train` / `transcription` | 19,198 | 26.56 h |
49
+ | **`typhoon-ai/thai-dialect-isan-dataset`** | `train` / **`isan_spelling`** | **8,606** | **23.22 h** |
50
+ | **TOTAL** | **All 5 Datasets Combined** | **594,231** | **1,022.78 h** |
51
+
52
+ *(1,381 clips from `typhoon-ai/thai-dialect-isan-dataset` were filtered out because they were $>30\text{s}$ or contained non-Thai characters after number-to-Thai-words conversion.)*
53
+
54
+ ## Preprocessing & Tokenizer Expansion
55
+
56
+ 1. **Thai Text Normalization & Cleaning**:
57
+ - Normalized Thai character order and vowel sequences via `pythainlp.util.normalize` (including `ํา` $\rightarrow$ `ำ`).
58
+ - Applied community-verified transcript fixes from [`thai_commonvoice_dataset`](https://github.com/wannaphong/thai_commonvoice_dataset) (`db-lastest.db` and `list_file.tsv`).
59
+ 2. **Number-to-Thai-Words Conversion**:
60
+ - Converted both Thai digits (`๐-๙`) and Arabic digits (`0-9`), comma-formatted numbers, and decimals into spoken Thai words using `pythainlp.util.num_to_thaiword` and `pythainlp.util.digit_to_text`.
61
+ - Filtered out any remaining clips containing non-Thai characters or exceeding 30 seconds.
62
+ 3. **Word Tokenization**:
63
+ - Segmented all reference transcripts into space-delimited words using `pythainlp.tokenize.word_tokenize(..., engine="newmm")`.
64
+ 4. **Thai National Corpus (TNC) Vocabulary Expansion (`tokens.txt`)**:
65
+ - Whistle's base SentencePiece model only covers Latin/European scripts (`en, de, fr, es, it, nl, pl`).
66
+ - We expanded the vocabulary with **2,067 tokens** consisting of all **67 base Thai characters** (`ก-ฮ`, vowels, and tone marks) plus the **top 2,000 most frequent Thai words** from the Thai National Corpus (`pythainlp.corpus.tnc`), adding `10,958` prefix-closed SentencePiece pieces.
67
+ ## Training Progression & Evaluation
68
+
69
+ ### Hyperparameters
70
+ - **Epochs**: 3 (`111,420` total steps, `37,140` steps/epoch)
71
+ - **Batch Size**: 16
72
+ - **Optimizer**: AdamW (`peak_lr=1e-3`, warmup `5,571` steps, cosine decay to `0.0`, global grad norm clip `1.0`)
73
+ - **Hardware**: NVIDIA GeForce RTX 4090 (24 GB VRAM)
74
+
75
+ ### Epoch-by-Epoch Loss
76
+
77
+ | Epoch | Step | Train Loss | Validation Loss (`Common Voice dev`) |
78
+ | :---: | :---: | :---: | :---: |
79
+ | **Epoch 1 / 3** | `37,140 / 111,420` | `1.3467` | `0.5841` |
80
+ | **Epoch 2 / 3** | `74,280 / 111,420` | `0.2417` | `0.4239` |
81
+ | **Epoch 3 / 3** | `111,420 / 111,420` | **`0.1803`** | **`0.3867`** |
82
+ ## Evaluation Results
83
+
84
+ All benchmarks below were evaluated on the exported `W4A8` `.cact` archive using `cactus.transcribe(..., language="th")`.
85
+ - **Character Error Rate (CER)**: Computed over Thai characters (whitespace removed).
86
+ - **Word Error Rate (WER)**: Computed over space-delimited tokens segmented via `pythainlp` (`newmm`).
87
+
88
+ ### Google FLEURS (`th_th`) Test Set — Comparison with Popular Thai ASR Models (`1,021` Clips)
89
+
90
+ Evaluated on all **1,021 clips** of the [`google/fleurs`](https://huggingface.co/datasets/google/fleurs) (`th_th`) test set alongside popular open-source Thai ASR models via [`pythaiasr`](https://github.com/PyThaiNLP/pythaiasr) ([compare_fleurs.py](file:///workspace/whistle-thai/compare_fleurs.py)). All outputs were normalized with the same PyThaiNLP text normalizer, number-to-Thai-words converter, and `newmm` word tokenizer:
91
+
92
+ | Model | Architecture / Precision | Model Size | Google FLEURS (`th_th`) CER | Google FLEURS (`th_th`) WER | Size Ratio vs Ours |
93
+ | :---------------------------------------------------------------------------------------------------- | :------------------------------------ | ----------: | :-------------------------: | :-------------------------: | :----------------: |
94
+ | **`MaprangSTT` (Ours)** | **Cactus Whistle (`W4A8` `.cact`)** | **32.5 MB** | **`13.58%`** | **`31.05%`** | **1.0×** |
95
+ | [`openai/whisper-small`](https://huggingface.co/openai/whisper-small) | Whisper Small (`244M`, FP32) | 967.0 MB | `24.21%` | `75.74%` | 29.8× larger |
96
+ | [`airesearch/wav2vec2-large-xlsr-53-th`](https://huggingface.co/airesearch/wav2vec2-large-xlsr-53-th) | Wav2Vec2-Large-XLSR-53 (`315M`, FP32) | 1,260.0 MB | `9.56%` | `35.98%` | 38.8× larger |
97
+ | `typhoon_nemotron_asr` | Nemotron-3.5-ASR (`0.6B`, INT4 ONNX) | ~450.0 MB | `12.28%` | `27.52%` | 13.8× larger |
98
+ | [`biodatlab/whisper-small-th-combined`](https://huggingface.co/biodatlab/whisper-small-th-combined) | Whisper Small Thai (`244M`, FP16) | 563.0 MB | `9.32%` | `24.30%` | 17.3× larger |
99
+ | `typhoon_asr` | FastConformer RNN-T (ONNX) | 435.0 MB | `9.02%` | `20.11%` | 13.4× larger |
100
+
101
+ > **Key Takeaways**:
102
+ > - At only **32.5 MB**, **`MaprangSTT`** outperforms [`openai/whisper-small`](https://huggingface.co/openai/whisper-small) (`967 MB`) by **10.63% absolute CER** (`13.58%` vs `24.21%`) and **44.69% absolute WER** (`31.05%` vs `75.74%`).
103
+ > - It achieves a **4.93% lower WER** (`31.05%` vs `35.98%`) than [`airesearch/wav2vec2-large-xlsr-53-th`](https://huggingface.co/airesearch/wav2vec2-large-xlsr-53-th) while being **38.8× smaller** (`32.5 MB` vs `1.26 GB`).
104
+ > - It comes within **1.30% CER** of the `0.6B`-parameter `typhoon_nemotron_asr` (`13.58%` vs `12.28%`) at **1/14th the model size**.
105
+ ## Usage
106
+
107
+ ### 1. Install Cactus
108
+
109
+ ```bash
110
+ pip install cactus-compute huggingface_hub
111
+ ```
112
+
113
+ ### 2. Transcribe Audio in Python
114
+
115
+ `cactus.transcribe()` expects a 16 kHz mono PCM `.wav` file:
116
+
117
+ ```python
118
+ import os
119
+ os.environ.setdefault("NEEDLE_TELEMETRY", "0") # needle reports usage unless these are set
120
+ os.environ.setdefault("DO_NOT_TRACK", "1")
121
+
122
+ from huggingface_hub import hf_hub_download
123
+ from needle import Whistle
124
+
125
+ model = Whistle(hf_hub_download("pythainlp/MaprangSTT", "maprang.cact"))
126
+ print(model.transcribe("speech.wav", language="th")["text"])
127
+ ```
128
+
129
+ If your input audio is `.mp3` or a different sample rate, convert it to 16 kHz mono `.wav` first (e.g., with `ffmpeg` or `soundfile` + `soxr`):
130
+
131
+ ```python
132
+ import subprocess
133
+ import tempfile
134
+ import cactus
135
+
136
+ def transcribe_any_audio(audio_path: str, weights_path: str = "maprang.cact") -> str:
137
+ with tempfile.NamedTemporaryFile(suffix=".wav") as tmp_wav:
138
+ subprocess.run(
139
+ ["ffmpeg", "-y", "-i", audio_path, "-ar", "16000", "-ac", "1", tmp_wav.name],
140
+ stdout=subprocess.DEVNULL,
141
+ stderr=subprocess.DEVNULL,
142
+ check=True,
143
+ )
144
+ res = cactus.transcribe(tmp_wav.name, weights=weights_path, language="th")
145
+ return res["text"].strip()
146
+ ```
147
+
148
+ ### 3. Rebuild `.cact` Archive from the LoRA Adapter
149
+
150
+ If you want to build a platform-specific runnable bundle or slice decoder layers (`--layers 4` or `--layers 6`) from `adapter.safetensors`:
151
+
152
+ ```bash
153
+ pip install "cactus-compute[train]"
154
+
155
+ # Build full 8-layer standalone .cact archive
156
+ cactus whistle build \
157
+ --lora adapter.safetensors \
158
+ --out model.cact
159
+
160
+ # Or build a smaller/faster 4-layer decoder archive
161
+ cactus whistle build \
162
+ --lora adapter.safetensors \
163
+ --layers 4 \
164
+ --out model-l.cact
165
+ ```
166
+
167
+ ## Repository Files
168
+
169
+ - `maprang.cact` (`32.49 MB`): Standalone quantized (`W4A8`) Whistle model archive ready for `cactus.transcribe()`.
170
+ - `adapter.safetensors` (`29.0 MB`): Trained LoRA weights (`rank=16`, `alpha=32`) and expanded Thai vocabulary embedding rows.
171
+ - `tokens.txt` (`2,067` lines): Base Thai characters and top 2,000 TNC words used to expand the SentencePiece tokenizer.
172
+
173
+ ## Citation & Acknowledgements
174
+
175
+ - **Base Model & Framework**: [Cactus Compute — Finetuning Whistle](https://cactuscompute.com/blog/finetuning-whistle)
176
+ - **NLP Preprocessing & Corpus**: [PyThaiNLP](https://github.com/PyThaiNLP/pythainlp) & [thai_commonvoice_dataset](https://github.com/wannaphong/thai_commonvoice_dataset)
177
+ - **Speech Datasets**:
178
+ - [Mozilla Common Voice](https://commonvoice.mozilla.org/)
179
+ - [Google FLEURS (`google/fleurs`)](https://huggingface.co/datasets/google/fleurs)
180
+ - [`n-order/Thai-dialect-corpus`](https://huggingface.co/datasets/n-order/Thai-dialect-corpus)
181
+ - [`huangjianbyte2024/thai_Elderly_Speech_dataset`](https://huggingface.co/datasets/huangjianbyte2024/thai_Elderly_Speech_dataset)
182
+ - [`typhoon-ai/thai-dialect-isan-dataset`](https://huggingface.co/datasets/typhoon-ai/thai-dialect-isan-dataset)