Loading…
Loading…
A tiny character-level Transformer (3,410,234 parameters), designed and trained completely from scratch to transliterate Roman Urdu into Urdu script — trained only on the openly licensed Dakshina dataset, and evaluated on words it has provably never seen.
Roman Urdu is how millions of people actually type Urdu; proper Urdu script is what publishing, education, and formal communication need. Nuqta-Nano converts the first into the second, one word at a time:
from nuqta_nano import Transliterator
t = Transliterator.from_dir(".") # this repo
t.transliterate("mohabbat") # -> محبت
t.transliterate("yeh kitab meri hai") # -> یہ کتاب میری ہائی (last word wrong — see below)
Measured once on Dakshina's held-out test split (10,327 distinct roman inputs; the split's Urdu word types and lemmata are disjoint from training — verified overlap: 0), under a multi-reference protocol (a prediction is exact if it matches any attested spelling for that input; CER is against the closest attested spelling):
| Metric | Test (held out) | Dev (selection split) |
|---|---|---|
| Corpus CER ↓ | 0.2020 | 0.1970 |
| Exact-match accuracy ↑ | 0.3606 | 0.3756 |
| Mean per-word CER ↓ | 0.2194 | 0.2121 |
Every number was re-derived from the shipped model.safetensors by examples/verify_metrics.py and matched exactly — run it yourself. Context for reading them: the average test word is ~5 letters, so CER 0.20 is roughly one wrong letter per word on words the model has never seen in any form; and the mapping is genuinely many-to-many (9,554 training inputs attest more than one correct spelling).
A secondary, in-context check — transliterating the romanized test sentences word by word — scores token exact-match 0.6352 / token CER 0.1699 over 51,698 tokens. That split is not held out by word type (running text is mostly frequent words), which is why it is reported second, not first.
First 12 test inputs in file order (the test file is alphabetized, so these are all spellings of one word family — آئین "constitution". Disclosed because it makes this block unrepresentative and genuinely hard: vowel-onset ambiguity):
| input | model | attested |
|---|---|---|
| aaieen | آئیں | آئین |
| aaoin | آوئیں | آئین |
| aayean | آئیاں | آئین |
| aayeen | آئیں | آئین |
| aayin | آئیں | آئین |
| aaeeni | آئینی | آئینی |
| aaini | آئنی | آئینی |
| aayinee | آئنی | آئینی |
| aayini | آئنی | آئینی |
| aayiny | آئینی | آئینی |
| ainey | اینے | آئینی |
| aini | اینی | آئینی |
Worst 8 failures in the whole test set (selected by CER, labeled as worst-case):
| input | model | attested |
|---|---|---|
| haa | حا | ھ |
| he | ہے | ھ |
| hey | ہے | ھ |
| monedou | مونیدو | عمر |
| mukeish | مکیش | نل |
| tay | تے | ٹ |
| tey | تے | ٹ |
| amiry | امیری | ہون |
Demo probes (fixed common words, fixed before evaluation): mohabbat→محبت, shukriya→شکریہ, pakistan→پاکستان, zindagi→زندگی, khubsurat→خوبصورت, duniya→دنیا, mushkil→مشکل (unseen in training, correct), waqt→وقت, kitab→کتاب, mohalla→محلہ, taqreeb→تقریب — and the failures: mehman→محمان (should be مہمان), sukhan→سوکھن (should be سخن), and in the sentence demo, hai→ہائی (should be ہے). Full output in example_output.txt.
These come from optimal-alignment analysis of every test prediction (assets/eval_results.json), not from impressions:
t/d do not say which one; the model is right only when context gives it away.scripts/train.py, scripts/resume_train.py, scripts/prepare_data.py.training_log.txt, training_log_resumed.txt); total training compute ≈ 2.1 hours on a shared 2-core VM. The final weights are the resumed run's epoch 12.DATA.md. Nothing was scraped; no other pairs were used.LICENSE). The packaged data splits remain CC BY-SA 4.0 with Dakshina attribution; weights were trained exclusively on that data and carry the attribution note in DATA.md.monedou → مونیدو for عمر in the worst-8).pip install -e ".[dev]"
PYTHONPATH=src python examples/verify_metrics.py # re-derives every headline number
PYTHONPATH=src python -m pytest -q # 8 tests, incl. metric reproduction on a fixed subset
PYTHONPATH=src python examples/transliterate_demo.py
If you use the data, cite Dakshina:
@inproceedings{roark-etal-2020-processing,
title = "Processing {South} {Asian} Languages Written in the {Latin} Script: the {Dakshina} Dataset",
author = "Roark, Brian and Wolf-Sonkin, Lawrence and Kirov, Christo and Mielke, Sabrina J. and Johny, Cibu and Demir{\c{s}}ahin, I{\c{s}}in and Hall, Keith",
booktitle = "Proceedings of The 12th Language Resources and Evaluation Conference (LREC)",
year = "2020",
pages = "2413--2423"
}