Mon Tokenizer
Mon mixes with Burmese constantly and English routinely, so all three are trained on and measured separately. Anything else on the page (Thai, emoji, IPA, CJK) round-trips through byte fallback rather than being lost.
pip install mon-tokenizer
from mon_tokenizer import MonTokenizer
tokenizer = MonTokenizer()
result = tokenizer.encode("ဂွံအခေါင်အရာမွဲသ္ဂောံဒုင်စသိုင်ကၠာကၠာရ။")
result["pieces"] # ['▁ဂွံ', 'အခေါင်အရာ', 'မွဲ', 'သ္ဂောံ', 'ဒုင်စသိုင်', 'ကၠာ', 'ကၠာရ။']
result["ids"] # token ids
result["text"] # the normalized string that was encoded
assert tokenizer.decode_ids(result["ids"]) == result["text"]
Also on the Hub, producing identical ids:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("janakhpon/mon_tokenizer")
Measured
Vocabulary 64,256 · trained on the train split of 893,936 lines / 85.8M characters · scored on the whole validation split.
| stratum | chars/token | syllable violations | round-trip | byte fallback |
|---|---|---|---|---|
| Mon | 4.686 | 1.07% (n=492,469) | 100% | 0.00% |
| Burmese | 4.117 | 0.93% (n=25,546) | 100% | 0.00% |
| English | 4.112 | — (n=0) | 100% | 0.02% |
| mixed script | 3.804 | 0.81% (n=28,133) | 100% | 0.19% |
98.74% of the distinct characters in the Mon validation split are single tokens — 392 of 397, over all 29,600 lines. The remaining five (a combining diaeresis below, a Greek capital pi, two Sinhala letters and one emoji; seven occurrences in 2.28M characters) round-trip through byte fallback at one to four tokens each.
Compression describes a corpus, not a tokenizer — quote it with the corpus. Violations count token boundaries inside a Myanmar syllable; the denominator is given because English legitimately has none. Round-trip compares after normalization, which is a deliberate transform.
model_card() returns these machine-readably, and a test fails if they drift
from what the artifact does.
Round-trip, and its two known limits
for text in ["🙏 emoji", "ภาษาไทย", "漢字", "Ωπ√∫", "ကျော် page 42 — “quoted” ၏"]:
r = tokenizer.encode(text)
assert tokenizer.decode_ids(r["ids"]) == r["text"]
Without byte fallback, characters outside the vocabulary aren't flagged — they're deleted, leaving fluent-looking output with content missing. That matters if you feed OCR output back into a corpus.
Two inputs are known not to round-trip. The loss happens at encode, so no
decoder can recover it:
tokenizer.encode(" abc")["ids"] == tokenizer.encode("abc")["ids"] # True
tokenizer.encode("a b")["ids"] == tokenizer.encode("a▁b")["ids"] # True
A single leading space is dropped, and a literal U+2581 is read as a space. The table above still holds: the corpus it was measured on is stripped per line and contains no U+2581, so it excludes both cases by construction rather than disproving them. Both are recorded as xfail tests.
Fixing this needs prepend_scheme="never", which changes every token id and so
requires a retrain and a new Hub artifact. Deferred on purpose, because id
stability is worth more than these two cases. Interior and trailing whitespace,
tabs, newlines, NUL and control characters all round-trip.
Normalization
Applied automatically, and stored inside the artifact so it cannot drift from
the model: invisible characters stripped, Unicode space separators folded to
U+0020, then NFC. Runs of spaces are preserved.
API
encode(text) |
{"pieces": [...], "ids": [...], "text": normalized} |
encode_ids(text) / encode_batch(texts) |
list[int] / list[list[int]] |
decode(pieces) / decode_ids(ids) |
str |
normalize(text) |
the artifact's own normalizer |
get_vocab_size() / get_vocab() |
int / dict[str, int] |
id_to_piece(id) / piece_to_id(piece) |
str / int |
unk_id bos_id eos_id pad_id |
0 1 2 3 |
model_card() |
artifact_version, corpus digest, config, measured metrics |
The artifact is cached by path, so constructing many instances is cheap.
CLI
pip install 'mon-tokenizer[cli]'
mon-tokenizer "ဂွံအခေါင်အရာမွဲ" # tokenize
mon-tokenizer -v "ဂွံအခေါင်အရာမွဲ" # per-token table
mon-tokenizer -d --ids "316,12644,294" # decode ids
Exit codes: 0 ok · 1 usage · 2 artifact failed to load · 130 interrupted.
Upgrading from 0.2.x
Every token id has changed. Rebuild any embedding matrix built against 0.2.x,
or pin mon-tokenizer<1.0 — it stays on PyPI and works.
Requirements
Python 3.11+. Two direct dependencies: tokenizers and regex. The install is
17 packages / ~30MB, because tokenizers requires huggingface-hub.
Development
uv sync --all-extras
uv run ruff format --check . && uv run ruff check . && uv run mypy && uv run pytest
Retraining, releasing and the house rules for tests: docs/how_to_contribute.md.
Design
docs/architecture.md — every decision with the measurement behind it and the condition that would reverse it: algorithm, vocabulary size, why grapheme clusters are the wrong unit for Myanmar, and what was rejected.
Links
- Hugging Face
- MonCorpusCollection — the corpus
- Awesome Mon NLP — the ecosystem
MIT.
Metadata
Release files for mon-tokenizer 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mon_tokenizer-1.0.1.tar.gz | 1.1 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mon_tokenizer-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 2.2 MB
Release files / mon_tokenizer-1.0.1.tar.gz
| Download URL | mon_tokenizer-1.0.1.tar.gz |
|---|---|
| Size | 1.1 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
10c2b7e75edb624713c285dec681e0e9755b30bc59b92962b8d2630eb95de2de
|
|
BLAKE2b-256 checksum How to use checksums |
3c1547333dfd8f0284c3367dba697c39aa1edf8f1965c45968944af85e1b1ce9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / mon_tokenizer-1.0.1-py3-none-any.whl
| Download URL | mon_tokenizer-1.0.1-py3-none-any.whl |
|---|---|
| Size | 1.1 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6e8d2d78662478571991f5de44a68ae946e2038bdc7f8e010cbcfdbff78d9a45
|
|
BLAKE2b-256 checksum How to use checksums |
975b9a3e69b23bc9866b9f410bfd42a3a7f353530e6ed7d4cae3f8c77201d812
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|