--- license: mit language: - bo base_model: KoichiYasuoka/bert-base-tibetan tags: - token-classification - tibetan - word-segmentation - bert - onnx library_name: onnxruntime --- # Tibetan Word Segmentation BUDA Fine-tuned `KoichiYasuoka/bert-base-tibetan` for syllable-level Tibetan word segmentation. Predicts **B** (begin-word) or **I** (inside-word) for each syllable. Used as the segmentation engine in [TradutorBUDA](https://tashirabten.github.io/BUDA_APPs_Port/). **99.95% accuracy** — B F1: 99.92% | I F1: 99.97% — on 20,903 validation sequences. ## Model Files | File | Size | Description | |------|------|-------------| | `model_quantized.onnx` | 104.5 MB | INT8 quantized ONNX (deployed) | | `tokenizer.json` | 0.8 MB | HuggingFace tokenizer | | `vocab.txt` | 0.4 MB | Vocabulary | | `config.json` | — | Model configuration | | `segmentation_config.json` | — | Label mappings (`B`/`I`) | | `dictionary_words.json` | 16.6 MB | 353K Tibetan words for constraint decoding | > The FP32 ONNX (413 MB) is not included — use the INT8 model for CPU deployment. ## Architecture Base: `KoichiYasuoka/bert-base-tibetan` (BERT-base, 110M parameters, pre-trained on 4.63 GB Tibetan text) Task head: token classification → 2 labels (B / I) ## Results | Metric | Score | |--------|-------| | Accuracy | 99.95% | | B precision | 99.97% | | B recall | 99.88% | | **B F1** | **99.92%** | | I precision | 99.95% | | I recall | 99.99% | | I F1 | 99.97% | ## Dataset - **Train:** 208,077 sequences — **Val:** 20,903 sequences - 200K synthetic sequences generated from 353K Tibetan dictionary terms (B/I labels derived from word boundaries) - 16K silver-standard sequences from [Botok](https://github.com/Esukhia/botok) segmentation output - Input format: space-separated Tibetan syllables; each syllable ends with a tsheg (་) Example: ``` Syllables: བྱང་ ཆུབ་ སེམས་ དཔའི་ སྤྱོད་ པ Labels: B I B I B I Words: བྱང་ཆུབ་ / སེམས་དཔའི་ / སྤྱོད་པ ``` ## Training Configuration - **Base model:** `KoichiYasuoka/bert-base-tibetan` - **Optimizer:** AdamW (lr=3e-5, weight_decay=0.01) - **LR schedule:** 10% linear warmup → constant - **Epochs:** 5 (early stopping patience=3; did not trigger) - **Batch size:** 64 - **Max sequence length:** 256 syllable-tokens - **Mixed precision:** FP16 on NVIDIA A100 (40 GB) - **Best checkpoint metric:** B F1 ## Usage (ONNX / Java — TradutorBUDA) The model is loaded by `TibetanSegmentationService.java` using ONNX Runtime: ```java OrtSession session = env.createSession("models/segmentation_onnx/model_quantized.onnx", opts); ``` Inputs: `input_ids`, `attention_mask`, `token_type_ids` (shape: `[batch, 256]`) Output: `logits` (shape: `[batch, 256, 2]`) — argmax gives B=0 / I=1 per token. ## Usage (Python) ```python import onnxruntime as ort from transformers import AutoTokenizer import numpy as np import json, re tokenizer = AutoTokenizer.from_pretrained("trabten/tibetan_segmentation_BUDA") session = ort.InferenceSession("model_quantized.onnx", providers=["CPUExecutionProvider"]) MAX_LENGTH = 256 ID2LABEL = {0: "B", 1: "I"} def segment(text): syllables = [s for s in re.split(r'(?<=་)', text.strip()) if s.strip()] token_ids, word_ids = [], [] for i, syl in enumerate(syllables): ids = tokenizer.encode(syl, add_special_tokens=False) token_ids.extend(ids) word_ids.extend([i] * len(ids)) cls, sep = tokenizer.cls_token_id, tokenizer.sep_token_id input_ids = [cls] + token_ids[:MAX_LENGTH-2] + [sep] word_ids = [-1] + word_ids[:MAX_LENGTH-2] + [-1] attn_mask = [1] * len(input_ids) tok_types = [0] * len(input_ids) pad = MAX_LENGTH - len(input_ids) input_ids += [tokenizer.pad_token_id] * pad attn_mask += [0] * pad tok_types += [0] * pad feeds = { "input_ids": np.array([input_ids], dtype=np.int64), "attention_mask": np.array([attn_mask], dtype=np.int64), "token_type_ids": np.array([tok_types], dtype=np.int64), } logits = session.run(None, feeds)[0][0] preds = logits.argmax(axis=-1) syl_labels, prev = [], None for wid, pred in zip(word_ids, preds): if wid == -1: continue if wid != prev: syl_labels.append(ID2LABEL[pred]) prev = wid words, current = [], [] for syl, lbl in zip(syllables, syl_labels): if lbl == "B" and current: words.append("".join(current)) current = [syl] else: current.append(syl) if current: words.append("".join(current)) return words print(segment("བྱང་ཆུབ་སེམས་དཔའི་སྤྱོད་པ")) # ['བྱང་ཆུབ་', 'སེམས་དཔའི་', 'སྤྱོད་པ'] ``` ## Background Tibetan has no spaces between words — segmentation is required before dictionary lookup, POS tagging, or machine translation. Traditional rule-based segmenters (e.g. Botok) rely on hand-curated lexicons and miss out-of-vocabulary terms. This model learns segmentation patterns from context, achieving near-perfect accuracy on Buddhist canonical vocabulary. ## References - Yasuoka, K. "Universal Dependencies for Classical Tibetan." (base model) - Esukhia. [Botok — Tibetan NLP toolkit](https://github.com/Esukhia/botok) (silver-standard data source) - Devlin et al. "BERT: Pre-training of Deep Bidirectional Transformers." (ICLR 2019)