|
Download README.md from pythainlp/MaprangSTT: direct link, hf CLI and curl.
- Browser
- Download file 11.2 kB
-
https://huggingface.co/pythainlp/MaprangSTT/resolve/main/README.md
- Command line
-
hf download hf://pythainlp/MaprangSTT/README.md
-
curl -L -o README.md https://huggingface.co/pythainlp/MaprangSTT/resolve/main/README.md
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) |