nrl-ai/vn-spell-correction-base
nrl-ai/vn-spell-correction-base — Vietnamese spell correction (ViT5 fine-tune)
Fixes typos, missed accents, and OCR-style char errors in Vietnamese text in one pass: Toi yu Vit Nam → Tôi yêu Việt Nam. Strictly more than diacritic restoration — handles letter-level mistakes, missing / extra characters, and OCR substitutions like o↔0, l↔1, m↔rn.
Fine-tuned from `VietAI/vit5-base` on the `nrl-ai/vn-spell-correction-train` corpus (459K (noisy, clean) Vietnamese pairs synthesized from a register-balanced Wiki+news mix via nom.text.noise).
Adoption gate: ✅ passed — passed (light avg 0.9832, heavy avg 0.9703).
Quick start
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
tok = AutoTokenizer.from_pretrained("nrl-ai/vn-spell-correction-base")
model = AutoModelForSeq2SeqLM.from_pretrained("nrl-ai/vn-spell-correction-base").eval()
text = "Toi yu Vit Nam"
out = model.generate(**tok(text, return_tensors="pt"), max_length=256)
print(tok.decode(out[0], skip_special_tokens=True))
# Tôi yêu Việt NamFor batched inference (recommended for high-throughput pipelines):
from nom.text.diacritic_models import HFDiacriticModel
restorer = HFDiacriticModel(model_id="nrl-ai/vn-spell-correction-base")
fixed = restorer.predict_batch(noisy_sentences, batch_size=16)Evaluation — 8-split spell-correction grid
Evaluation uses `nrl-ai/vn-spell-correction-eval` (2,098 pairs across 4 registers x 2 noise levels). Word accuracy after NFC + punctuation normalization on both sides.
Each split corresponds to a (register, noise level) combination:
- light noise — ~5 % char-level edit distance, models a person typing Vietnamese on a keyboard with a few accent slips and the occasional fat-finger.
- heavy noise — ~15-20 % edit distance, models OCR output of a mid-quality scan with diacritic drops + char confusions.
How we compare
Where this model sits in the public Vietnamese spell-correction landscape — same 8-split grid for every measured row.
The two averaged columns:
- light avg = mean word accuracy across the 4 light-noise splits.
- heavy avg = mean word accuracy across the 4 heavy-noise splits.
Training
- Base: `VietAI/vit5-base` (MIT license)
- Corpus: 545,000 (noisy, clean) pairs from `nrl-ai/vn-spell-correction-train`. Eval-leak guarded against `nrl-ai/vn-spell-correction-eval` and `nrl-ai/vn-diacritic-eval`.
- Validation: 5,000 held-out pairs.
- Epochs: 5
- Effective batch size: 32 (32 per device, grad-accum 1)
- Learning rate: 0.0005 with
cosineschedule, 500 warmup steps - Precision: bf16
- Sequence length: input 256, target 256
- Early stopping: patience=0 on
eval_loss - Training time: 215.0 min on a single NVIDIA RTX 3090 24 GB
- Seed: 42
Intended use
- Recommended: cleaning up noisy Vietnamese text — OCR output, user-generated text from non-VN-IME keyboards, form data with typos, social-media short-form. Strictly harder than diacritic restoration but covers it as a subset.
- Not recommended: text generation, classification, sentiment, NER, or any task the input distribution doesn't match.
Limitations
- In-distribution metric, real-world is harder — measured. Training and eval both use
nom.text.noise. The synthetic 8-split numbers above measure how well we invert our noise generator. We also benchmark on a 200-sentence OOD eval whose noise comes from real Vietnamese error sources rather than our generator (7 slices, bootstrap 95 % CI):
The synthetic grid above measures how well we invert our own noise generator; the aggregate here (80.21 %) is what to plan around. The gap is the cost of a noise model that captures the surface of typos but not real Telex keystroke artefacts (dduwojc for được) or forum-style abbreviations (ko bt for không biết). Real Telex input is the weakest slice by a wide margin and is the primary target of the next training round.
- Heading and letterhead layout is a known blind spot. The training corpus is sentence-segmented, so document furniture (letterheads, all-caps titles, form labels, signature blocks) was filtered out during construction. That is the
furniture_50slice above, and it is the weakest non-Telex register. The model can leave a real-word tone error uncorrected where the same error is fixed in ordinary prose:Độc lập - Tự do - Hạnh phụcis echoed back unchanged, whileTôi rất hạnh phục khi gặp lại bạnis corrected. Two conditions have to coincide, an adverse frequency prior (phụcoutnumbersphúc3,810 to 1,311 in the corpus) and a layout the encoder has not seen corrected. Neither alone reproduces it.
nom.text.heading ships a conservative recovery pass, enabled by default on HFDiacriticModel. When the first pass makes no edit and the input is heading-shaped, it retries on a lowercased copy and keeps only tone-level edits on purely alphabetic tokens. On furniture_50 that moves word accuracy 84.36 % to 87.62 % and sentence-exact 36.00 % to 56.00 %, while every other slice stays bit-identical:
from nom.text.diacritic_models import HFDiacriticModel
speller = HFDiacriticModel(model_id="nrl-ai/vn-spell-correction-base")
speller("Độc lập - Tự do - Hạnh phục")
# 'Độc lập - Tự do - Hạnh phúc'- Heavy-noise corner cases. OCR outputs that drop entire words or add hallucinated text are out-of-scope; the noise generator we trained on caps edits per sentence (max 25 % edit ratio).
- Long sequences truncate at 256 sub-word tokens. Split paragraphs at sentence boundaries before calling.
- No grammar or stylistic correction. This model fixes character / syllable / diacritic errors but doesn't rewrite phrasing.
- Confidence intervals on small splits. business55 (44/55 sents) and formal72 (65/72 sents) have ±3-4 pp 95 % CI; the larger literary_800 split has ±1 pp. Treat single-pp differences with care.
License & attribution
Released under Apache 2.0. Cite both this model and the base:
@misc{nom_vn_spell_correction_2026,
title={Vietnamese Spell Correction — register-balanced fine-tune},
author={Nguyen, Viet-Anh and {Neural Research Lab}},
year={2026},
howpublished={\url{https://huggingface.co/nrl-ai/vn-spell-correction-base}}
}Training data inherits CC-BY-SA-4.0 (Wikipedia portion) + CC-BY-4.0 (news portion). Output text is best treated as CC-BY-SA-4.0 if you want to be safe.
