
OpenThai2.0-OCR-Markdown
Thai document pages to Markdown, with tables as HTML. It is a Thai fine-tune of NVIDIA Nemotron 3.5 Parse (0.63B), by iApp Technology. It keeps Parse's layout boxes, block classes and reading order, and its whole serving and postprocessing stack, so it works anywhere Parse does.
Built on NVIDIA Nemotron. The base model, tokenizer and serving code are NVIDIA's (nvidia/Nemotron-3.5-Parse, OpenMDW-1.1). Their licence, copyright notices, third-party notices and tokenizer attribution are included unchanged in this repository, and NVIDIA's original model card is kept as NVIDIA_NEMOTRON_PARSE_README.md.
Model at a glance
| Parameters | 630.8M: vision encoder 439.0M (C-RADIOv4-SO400M) and text decoder 191.8M (10-layer mBART, width 1,024) |
| Weights | model.safetensors, float32, 2.52 GB; served in bfloat16 |
| Input | one page image, fitted to 1,664 ร 2,048 pixels (width ร height) |
| Output | Markdown with tables as HTML (row and column spans), and a box and a class for every block, in reading order |
| Vocabulary | 72,512 tokens |
| Languages | Thai and English |
| Hardware | one NVIDIA GPU, served with vLLM. Our hosted API runs it in about 8 GB of an H100 (--gpu-memory-utilization 0.09, 32 sequences at once) |
| Speed | 1.6 s per page on one H100, one page at a time (median of 12 table pages); 19,176 pages an hour with 16 pages in flight through our hosted API, on an H100 it shares with another service |
| Base model | nvidia/Nemotron-3.5-Parse |
| Training | full fine-tune, one epoch over 54,596 rows (1,707 steps of 32), on one H100 |
| Licence | OpenMDW-1.1 |
| Version | v1.0.0, November 2026 |
Results
| Model | Size | th-docs-tables v1 (65 pages) | ThaiOCRBench tables (193 images) | Thai text, printed (12 sets) | New Royal Gazette pages (339) | Average | Seconds per page |
|---|---|---|---|---|---|---|---|
| OpenThai2.0-OCR-Markdown | 0.63B | 95.2 | 68.9 | 90.3 | 98.7 | 88.3 | 1.6 |
| Nemotron 3.5 Parse (base) | 0.63B | 91.3 | 68.3 | 89.0 | 91.5 | 85.0 | 1.8 |
| Typhoon OCR V2 2B | 2B | 91.6 | 67.6 | 89.6 | 94.6 | 85.8 | 15.8 |
| Typhoon OCR V2 0.8B | 0.8B | 89.0 | 64.7 | 85.8 | โ | โ | 18.7 |
Bold marks the best score in each column (for seconds per page, the fastest). Average is the mean of the four accuracy columns; Typhoon OCR V2 0.8B was not run on the new Gazette pages, so it has none.
- th-docs-tables v1 is 65 real Thai government and university pages, scored on text and tables together. Against Typhoon OCR V2 2B: +3.6, 95% interval [+1.2, +6.2].
- ThaiOCRBench tables: table TEDS on the 193 table images of ThaiOCRBench. Against Typhoon OCR V2 2B: +1.3 [โ1.7, +4.6], not significant.
- Thai text, printed: character accuracy, 100 ร (1 โ CER), averaged over 12 printed sets.
- The sets are 1,200 pages and 50 court judgments: budget documents, the Royal Gazette, old books, court judgments, ThaiOCRBench full pages and short document crops.
- Against Typhoon OCR V2 2B: +0.76 [โ0.04, +1.51], not significant.
- It reads 8 of the 12 sets better and ties on court judgments. It is behind on OCR104 document photos (by 3.4), OCR104 Thai+English crops (0.7) and one verified Royal Gazette set (0.7).
- A page that fails (cut off at the token limit) counts as empty. None failed.
- New Royal Gazette pages: 339 pages from 293 Gazette documents published 4โ9 November 2026, after every model here was trained; each model read them once.
- Character accuracy against the PDF text layer: +4.1 [+3.7, +4.4] against Typhoon OCR V2 2B.
- With every space removed, so that line-joining conventions do not count: +3.5 [+3.2, +3.9].
- Our training data has Royal Gazette pages from earlier months, and the reference text is made by the same tool as those training labels. So this tests new documents of a familiar kind, written to conventions this model learned.
- Seconds per page: the median of 12 pages sent one at a time to vLLM on one H100. Typhoon ran with
--enforce-eager, which its model needs on our vLLM version; with CUDA graphs it would be faster.
Use
Serve with vLLM exactly as the base model. Then parse pages with NVIDIA's DocumentParser (box-and-class prompt, single pass):
vllm serve iapp/OpenThai2.0-OCR-Markdown --served-model-name nemotron-parse --trust-remote-code
from nemotron_parse.inference import DocumentParser, GenerationError
with DocumentParser(base_url="http://localhost:8000/v1", model="nemotron-parse") as parser:
try:
doc = parser.parse("page.png", mode="single")
print(doc["markdown"]) # Markdown; tables as HTML with rowspan/colspan
except GenerationError:
... # raised if a page is cut off at the token limit (none in our tests; see Limits)
The no-box Markdown prompt also works, but box mode is the one we tested. Use box mode.
A hosted version is the iApp API POST https://api.iapp.co.th/v3/store/ocr/document/markdown (PDF or image in, Markdown and blocks out), which runs this model the same way.
Training
Text labels are exact, never another model's reading. The mix is 54,596 rows:
| Source | Pages | Rows |
|---|---|---|
| Synthetic Thai documents rendered in Chromium: merged-cell tables, forms, memos, minutes, schedules; scan and phone-photo degradation and watermarks. The text is from Thai Wikipedia (CC BY-SA 4.0), set in 24 OFL fonts. | 14,315 | 17,158 |
| Synthetic two- and three-column pages, the same pipeline. | 3,996 | 4,503 |
| Synthetic tables in other styles, the same pipeline: receipts, bank statements, lab and insurance forms, and tables with several row-merged groups. 30% of the pages print every digit as a Thai digit. | 3,974 | 4,660 |
Public table images: FinTabNet and PubTabNet training splits (IBM; annotations CDLA-Permissive), via docling-project/FinTabNet_OTSL and docling-project/PubTabNet_OTSL. Financial-report and scientific-paper tables, labels rebuilt from their cell annotations. PubTabNet's images come from articles in the PubMed Central Open Access subset, which carry their own licences. |
3,982 tables | 4,747 |
Born-digital government pages: Thai parliament budget documents and the Royal Gazette (CC BY 4.0, via open-law-data-thailand/soc-ratchakitcha). Labels come from the PDF text layer and ruled table grids; each page was checked against an independent OCR reading. Each page appears up to six times, in box and no-box form. |
4,553 | 14,626 |
| Dot-matrix crops: text blocks and lines printed in a dot-matrix (impact-printer) look, some as phone photos. The text comes from synthetic pages, verified parliament budget lines and generated fields such as amounts, dates and account numbers. | 2,963 crops | 3,482 |
| Replay: handwriting lines, synthetic handwriting, English and lottery-ticket crops, so that short-crop reading is kept. | 5,000 crops | 5,000 |
| Anti-loop rows: pages on which earlier fine-tunes fell into a repetition loop, with that loop as a negative target. The pages are old-book scans (public-domain Wikisource books), synthetic receipt photos and two parliament pages, none from a benchmark. | 397 | 420 |
Rows and pages: a page can appear in more than one row, in box form and as a no-box Markdown copy.
Layout boxes and classes come from the base model's own reading of each training page, aligned to the exact text. A page is kept only if Parse's own postprocessor turns the target back into the label. Targets that put text inside a Picture block are dropped.
Anti-loop training: an anti-loop row carries an earlier model's own output up to a short span just after its loop starts.
- There is no loss on the text before that span.
- Each token of the span adds an unlikelihood loss, โlog(1 โ p), weighted 3. This pushes down the probability of writing that loop again.
Recipe:
- full fine-tune with AdamW, peak learning rate 1e-5 (vision encoder at half);
- one epoch of 1,707 steps of 32 samples on one NVIDIA H100 provided by Siam AI.
- The released weights are the final checkpoint, not blended with the base. The selection rule also tried weight-space blends with the base (WiSE-FT at 0.5 and 0.7, and encoder/decoder mixes), and they scored lower.
No benchmark data in training: no page from th-docs-tables, ThaiOCRBench, OpenThaiOCR-Bench or the docbench sets was trained on. The leak guard is character 8-gram and perceptual-hash matching.
How it was chosen: the checkpoint, the blend and the decision to replace the previous candidate were made by a rule written before training. Two runs, with unlikelihood weights 1 and 3, were trained, and the rule picked across both and the untrained blends. It uses development sets only:
- a 60-page real-table dev set;
- an 887-item robustness set of old books, forms, text lines, crops, multi-column pages and dot-matrix crops;
- 300 tables from the FinTabNet and PubTabNet validation splits;
- 6,137 unlabelled pages, on which a candidate may be cut off on at most 10. This one is cut off on none.
None of the results above was used to choose it.
Limits
- Repetition loops: none on the 2,270 text-benchmark pages, the 193 ThaiOCRBench tables, the 65 th-docs-tables pages, the 339 new Gazette pages or 6,137 development pages. On other inputs,
DocumentParserraisesGenerationErrorif a page is cut off at the token limit. - Tables unlike Thai government documents are read about as well as by the base model (ThaiOCRBench tables 68.9, base 68.3). Some still come out with rows missing or a row span wrong: on 21 of the 193 ThaiOCRBench tables the output has fewer than 80% of the cells.
- Text set vertically, such as rotated table headers, can be misread.
- Thai digits occasionally come out as Arabic digits. On the 339 new Gazette pages it writes 112 Arabic digits where the pages have 79, against about 22,000 Thai digits, so whole pages are rarely affected; on a few tables it mixes the two within a number.
- Photographed single words and very short crops read worse than full pages: 96.4 on OCR104 document photos, against 99.8 for Typhoon OCR V2 2B.
- Dot-matrix print (bank passbooks, impact-printed forms): 89% character accuracy on our dot-matrix development crops. Those crops are generated the same way as the training crops, from held-out fonts and text. We have no real dot-matrix test set yet, so expect less on real passbooks.
- Handwriting is read only at text-line level; full handwritten pages are not a target.
- Scene text and photos of screens are not targets.
- Charts keep the base model's behaviour; they were not fine-tuned.
- The DFlash2 drafter in this repository is NVIDIA's, trained for the base model. It has not been tested with this fine-tune.
Acknowledgements
This model was trained on NVIDIA H100 GPUs provided by Siam AI. Thanks to NVIDIA for Nemotron 3.5 Parse, and to the people behind the public data above.
Licence
The model is a derivative of NVIDIA Nemotron 3.5 Parse and is distributed under the OpenMDW License Agreement, version 1.1. The tokenizer is MIT-licensed (see TOKENIZER_ATTRIBUTION.md).
- Downloads last month
- 35
Model tree for iapp/OpenThai2.0-OCR-Markdown
Base model
nvidia/Nemotron-3.5-Parse