Automatic Speech Recognition
cactus
MaprangSTT / README.md
wannaphong's picture
Update README.md
e071365 verified
|
Raw History Blame Contribute Delete
11.2 kB
---
language:
- th
- en
- de
- fr
- es
- it
- nl
- pl
license: apache-2.0
library_name: cactus
datasets:
- google/fleurs
- n-order/Thai-dialect-corpus
- huangjianbyte2024/thai_Elderly_Speech_dataset
- typhoon-ai/thai-dialect-isan-dataset
base_model:
- Cactus-Compute/whistle
pipeline_tag: automatic-speech-recognition
---
# MaprangSTT
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.
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.
- **GitHub**: [https://github.com/wannaphong/MaprangSTT](https://github.com/wannaphong/MaprangSTT)
- **In-Browser Web Demo (`cactus-needle`)**: [https://wannaphong.github.io/MaprangSTT/demo](https://wannaphong.github.io/MaprangSTT/demo)
## Model Summary
| Property | Value |
| :--- | :--- |
| **Base Architecture** | Cactus Whistle (8 encoder/decoder layers) |
| **Fine-Tuning Method** | Quantization-Aware LoRA (`rank=16`, `alpha=32`, 15 weight groups) + Trainable New Embedding Rows |
| **Precision / Quantization** | `W4A8` (4-bit weights with straight-through estimator, 8-bit activations & KV cache) |
| **Supported Languages** | `th` (Thai), plus base `en`, `de`, `fr`, `es`, `it`, `nl`, `pl` |
| **Vocabulary Expansion** | `+2,067` Thai tokens (`+10,958` SentencePiece pieces + `<\|th\|>` language tag) |
| **Standalone `.cact` Size** | **32.49 MB** (`maprang.cact`, 681 tensors) |
| **LoRA Adapter Size** | **29.0 MB** (`adapter.safetensors`) |
### Dataset Summary
| Dataset Source | Split / Text Column | Valid Clips | Duration (Hours) |
| :--- | :--- | ---: | ---: |
| **Mozilla Common Voice v27 (`th`)** | `validated` − (`dev` + `test`) / `sentence` | 126,690 | 149.41 h |
| **Google FLEURS (`th_th`)** | `train` / `transcription` | 2,174 | 6.84 h |
| **`n-order/Thai-dialect-corpus`** | `*_train` (all 4 subsets) / `sentence` | 437,563 | 816.74 h |
| **`huangjianbyte2024/thai_Elderly_Speech_dataset`** | `train` / `transcription` | 19,198 | 26.56 h |
| **`typhoon-ai/thai-dialect-isan-dataset`** | `train` / **`isan_spelling`** | **8,606** | **23.22 h** |
| **TOTAL** | **All 5 Datasets Combined** | **594,231** | **1,022.78 h** |
*(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.)*
## Preprocessing & Tokenizer Expansion
1. **Thai Text Normalization & Cleaning**:
- Normalized Thai character order and vowel sequences via `pythainlp.util.normalize` (including `ํา` $\rightarrow$ `ำ`).
- Applied community-verified transcript fixes from [`thai_commonvoice_dataset`](https://github.com/wannaphong/thai_commonvoice_dataset) (`db-lastest.db` and `list_file.tsv`).
2. **Number-to-Thai-Words Conversion**:
- 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`.
- Filtered out any remaining clips containing non-Thai characters or exceeding 30 seconds.
3. **Word Tokenization**:
- Segmented all reference transcripts into space-delimited words using `pythainlp.tokenize.word_tokenize(..., engine="newmm")`.
4. **Thai National Corpus (TNC) Vocabulary Expansion (`tokens.txt`)**:
- Whistle's base SentencePiece model only covers Latin/European scripts (`en, de, fr, es, it, nl, pl`).
- 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.
## Training Progression & Evaluation
### Hyperparameters
- **Epochs**: 3 (`111,420` total steps, `37,140` steps/epoch)
- **Batch Size**: 16
- **Optimizer**: AdamW (`peak_lr=1e-3`, warmup `5,571` steps, cosine decay to `0.0`, global grad norm clip `1.0`)
- **Hardware**: NVIDIA GeForce RTX 4090 (24 GB VRAM)
### Epoch-by-Epoch Loss
| Epoch | Step | Train Loss | Validation Loss (`Common Voice dev`) |
| :---: | :---: | :---: | :---: |
| **Epoch 1 / 3** | `37,140 / 111,420` | `1.3467` | `0.5841` |
| **Epoch 2 / 3** | `74,280 / 111,420` | `0.2417` | `0.4239` |
| **Epoch 3 / 3** | `111,420 / 111,420` | **`0.1803`** | **`0.3867`** |
## Evaluation Results
All benchmarks below were evaluated on the exported `W4A8` `.cact` archive using `cactus.transcribe(..., language="th")`.
- **Character Error Rate (CER)**: Computed over Thai characters (whitespace removed).
- **Word Error Rate (WER)**: Computed over space-delimited tokens segmented via `pythainlp` (`newmm`).
### Google FLEURS (`th_th`) Test Set — Comparison with Popular Thai ASR Models (`1,021` Clips)
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:
| Model | Architecture / Precision | Model Size | Google FLEURS (`th_th`) CER | Google FLEURS (`th_th`) WER | Size Ratio vs Ours |
| :---------------------------------------------------------------------------------------------------- | :------------------------------------ | ----------: | :-------------------------: | :-------------------------: | :----------------: |
| **`MaprangSTT` (Ours)** | **Cactus Whistle (`W4A8` `.cact`)** | **32.5 MB** | **`13.58%`** | **`31.05%`** | **1.0×** |
| [`openai/whisper-small`](https://huggingface.co/openai/whisper-small) | Whisper Small (`244M`, FP32) | 967.0 MB | `24.21%` | `75.74%` | 29.8× larger |
| [`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 |
| `typhoon_nemotron_asr` | Nemotron-3.5-ASR (`0.6B`, INT4 ONNX) | ~450.0 MB | `12.28%` | `27.52%` | 13.8× larger |
| [`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 |
| `typhoon_asr` | FastConformer RNN-T (ONNX) | 435.0 MB | `9.02%` | `20.11%` | 13.4× larger |
> **Key Takeaways**:
> - 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%`).
> - 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`).
> - 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**.
## Usage
### 1. Install Cactus
```bash
pip install cactus-compute huggingface_hub cactus-needle
```
### 2. Transcribe Audio in Python
`cactus.transcribe()` expects a 16 kHz mono PCM `.wav` file:
```python
import os
os.environ.setdefault("NEEDLE_TELEMETRY", "0") # needle reports usage unless these are set
os.environ.setdefault("DO_NOT_TRACK", "1")
from huggingface_hub import hf_hub_download
from needle import Whistle
model = Whistle(hf_hub_download("pythainlp/MaprangSTT", "maprang.cact"))
print(model.transcribe("speech.wav", language="th")["text"])
```
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`):
```python
import subprocess
import tempfile
import cactus
def transcribe_any_audio(audio_path: str, weights_path: str = "maprang.cact") -> str:
with tempfile.NamedTemporaryFile(suffix=".wav") as tmp_wav:
subprocess.run(
["ffmpeg", "-y", "-i", audio_path, "-ar", "16000", "-ac", "1", tmp_wav.name],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=True,
)
res = cactus.transcribe(tmp_wav.name, weights=weights_path, language="th")
return res["text"].strip()
```
### 3. Rebuild `.cact` Archive from the LoRA Adapter
If you want to build a platform-specific runnable bundle or slice decoder layers (`--layers 4` or `--layers 6`) from `adapter.safetensors`:
```bash
pip install "cactus-compute[train]"
# Build full 8-layer standalone .cact archive
cactus whistle build \
--lora adapter.safetensors \
--out model.cact
# Or build a smaller/faster 4-layer decoder archive
cactus whistle build \
--lora adapter.safetensors \
--layers 4 \
--out model-l.cact
```
## Repository Files
- `maprang.cact` (`32.49 MB`): Standalone quantized (`W4A8`) Whistle model archive ready for `cactus.transcribe()`.
- `adapter.safetensors` (`29.0 MB`): Trained LoRA weights (`rank=16`, `alpha=32`) and expanded Thai vocabulary embedding rows.
- `tokens.txt` (`2,067` lines): Base Thai characters and top 2,000 TNC words used to expand the SentencePiece tokenizer.
## Citation & Acknowledgements
- **Base Model & Framework**: [Cactus Compute — Finetuning Whistle](https://cactuscompute.com/blog/finetuning-whistle)
- **NLP Preprocessing & Corpus**: [PyThaiNLP](https://github.com/PyThaiNLP/pythainlp) & [thai_commonvoice_dataset](https://github.com/wannaphong/thai_commonvoice_dataset)
- **Speech Datasets**:
- [Mozilla Common Voice](https://commonvoice.mozilla.org/)
- [Google FLEURS (`google/fleurs`)](https://huggingface.co/datasets/google/fleurs)
- [`n-order/Thai-dialect-corpus`](https://huggingface.co/datasets/n-order/Thai-dialect-corpus)
- [`huangjianbyte2024/thai_Elderly_Speech_dataset`](https://huggingface.co/datasets/huangjianbyte2024/thai_Elderly_Speech_dataset)
- [`typhoon-ai/thai-dialect-isan-dataset`](https://huggingface.co/datasets/typhoon-ai/thai-dialect-isan-dataset)