Instructions to use PorsukR/P220-ThaiOCR-9B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use PorsukR/P220-ThaiOCR-9B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="PorsukR/P220-ThaiOCR-9B") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("PorsukR/P220-ThaiOCR-9B") model = AutoModelForMultimodalLM.from_pretrained("PorsukR/P220-ThaiOCR-9B", device_map="auto") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] inputs = processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use PorsukR/P220-ThaiOCR-9B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "PorsukR/P220-ThaiOCR-9B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "PorsukR/P220-ThaiOCR-9B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/PorsukR/P220-ThaiOCR-9B
- SGLang
How to use PorsukR/P220-ThaiOCR-9B with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "PorsukR/P220-ThaiOCR-9B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "PorsukR/P220-ThaiOCR-9B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "PorsukR/P220-ThaiOCR-9B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "PorsukR/P220-ThaiOCR-9B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use PorsukR/P220-ThaiOCR-9B with Docker Model Runner:
docker model run hf.co/PorsukR/P220-ThaiOCR-9B
P220-ThaiOCR-9B
P220-ThaiOCR-9B is a 9B vision-language model for Thai documents: printed and handwritten text, tables, forms, receipts, government PDFs and scans. It reads pages into plain text, Markdown or HTML tables, and answers questions about a document (key information, classification, charts, diagrams), without per-template rules.
BF16 weights.
Top open model on ThaiOCRBench: P220-ThaiOCR-9B scores higher on ThaiOCRBench than every open model tested, including Qwen3.6-35B-A3B and OpenThai 2.0 27B, and above GPT-6 Luna. Also close to GPT-6.1 Sol on the transcription tasks.
- Developed by: Porsuk Rungarunsrisuk
- Base model: Qwen/Qwen3.5-9B (Apache-2.0), fine-tuned with LoRA and merged.
- Languages: Thai, English.
- Thinking: off. The model was trained and evaluated without thinking, and the included chat template turns thinking off by default.
Variants
| Repo | Format |
|---|---|
| PorsukR/P220-ThaiOCR-9B | BF16 (transformers / vLLM) |
| PorsukR/P220-ThaiOCR-9B-FP8 | FP8 (vLLM) |
| PorsukR/P220-ThaiOCR-9B-W4A16 | INT4 weight-only (vLLM) |
| PorsukR/P220-ThaiOCR-9B-NVFP4 | NVFP4 (vLLM, Blackwell) |
| PorsukR/P220-ThaiOCR-9B-GGUF | GGUF BF16 / Q8_0 / Q4_K_M / Q3_K_M / Q2_K (llama.cpp, Ollama) |
Which file should I use?
| Hardware | Use |
|---|---|
| NVIDIA Blackwell GPU | PorsukR/P220-ThaiOCR-9B-NVFP4 |
| NVIDIA Ada / Hopper GPU | PorsukR/P220-ThaiOCR-9B-FP8 |
| NVIDIA Ampere GPU | PorsukR/P220-ThaiOCR-9B-W4A16 |
| CPU, Mac, or Ollama | PorsukR/P220-ThaiOCR-9B-GGUF Q4_K_M (Q3_K_M / Q2_K for less memory) |
Every quantized P220 file was made from the BF16 weights with Thai OCR quality as the goal: the 4-bit, 3-bit and 2-bit GGUF files use an imatrix built from Thai OCR text, W4A16 / NVFP4 are calibrated on the same text, and FP8 and GGUF Q8_0 need no calibration. Every released file was tested on the full held-out sets (see Quantized variants).
High accuracy for its size: even the smallest file, GGUF Q2_K (3.9 GB), is more accurate (lower CER) than P110-ThaiOCR-4B at BF16 and most other models tested, including GPT-6 Luna.
Usage
Recommended prompt for plain transcription (Thai):
เธญเนเธฒเธเธเนเธญเธเธงเธฒเธกเธเธฑเนเธเธซเธกเธเนเธเธ เธฒเธเธเธตเนเนเธเธเธเธฃเธเธเนเธงเธเนเธฅเธฐเธเธฃเธเธเธฒเธกเธเนเธเธเธเธฑเธ เธฃเธงเธกเธเธถเธเธเธฑเธงเนเธฅเธ เธเธฒเธฃเธฒเธ เธซเธฑเธงเธเนเธญ เนเธฅเธฐเนเธเธทเนเธญเธซเธฒเธเธธเธเธชเนเธงเธ เธซเนเธฒเธกเธชเธฃเธธเธ เธซเนเธฒเธกเธเนเธฒเธกเธชเนเธงเธเนเธ เธซเนเธฒเธกเนเธเธดเนเธกเธเธณเธญเธเธดเธเธฒเธข
vllm serve PorsukR/P220-ThaiOCR-9B --max-model-len 32768
from openai import OpenAI
import base64
client = OpenAI(base_url="http://localhost:8000/v1", api_key="none")
img = base64.b64encode(open("page.png", "rb").read()).decode()
resp = client.chat.completions.create(
model="PorsukR/P220-ThaiOCR-9B",
messages=[{"role": "user", "content": [
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{img}"}},
{"type": "text", "text": "เธญเนเธฒเธเธเนเธญเธเธงเธฒเธกเธเธฑเนเธเธซเธกเธเนเธเธ เธฒเธเธเธตเนเนเธเธเธเธฃเธเธเนเธงเธเนเธฅเธฐเธเธฃเธเธเธฒเธกเธเนเธเธเธเธฑเธ เธฃเธงเธกเธเธถเธเธเธฑเธงเนเธฅเธ เธเธฒเธฃเธฒเธ เธซเธฑเธงเธเนเธญ เนเธฅเธฐเนเธเธทเนเธญเธซเธฒเธเธธเธเธชเนเธงเธ เธซเนเธฒเธกเธชเธฃเธธเธ เธซเนเธฒเธกเธเนเธฒเธกเธชเนเธงเธเนเธ เธซเนเธฒเธกเนเธเธดเนเธกเธเธณเธญเธเธดเธเธฒเธข"},
]}],
temperature=0,
max_tokens=4096, # stops a rare runaway repeat; raise it for very dense pages
extra_body={"chat_template_kwargs": {"enable_thinking": False}},
)
print(resp.choices[0].message.content)
Tested with vLLM 0.29.0 and transformers 5.17.0.
Evaluation
All open models were run on the same hardware with the same scoring code. Qwen3.5-9B is the base model P220 was fine-tuned from. Models with two runs are averaged. Bold marks the best score in each row.
GPT-6.1 Sol and GPT-6 Luna are closed models, called through their API with the same images, prompts and scoring code. GPT-6.1 Sol was the newest top-tier model available for comparison when these evaluations were run (October 2026). GPT-6.1 Sol was run with thinking set to low; every other model, P220 included, was run with thinking off. On ThaiOCRBench, 15 of the 2,808 items were filtered by the Luna API and returned no answer; an item with no answer scores 0.
Prompts: on ThaiOCRBench every model gets the benchmark's own prompt for each task. For CER, the text crops (ocr_th, iapp, openthaigpt) use the plain OCR prompt from the Usage section, and the PDF pages use a page-to-JSON prompt (text blocks, headings, tables) that falls back to the plain OCR prompt if the JSON is invalid. Typhoon OCR 7B is trained for its own fixed prompt, so its CER rows use its official prompt and sampling settings instead.
ThaiOCRBench (13 tasks, higher is better)
ThaiOCRBench, all 2,808 items.
P220 has the highest 13-task average of the open models in this comparison (0.674, up from 0.572 for P110-ThaiOCR-4B; the best other open model is Qwen3.6-35B-A3B at 0.589, and P220's base Qwen3.5-9B scores 0.462). GPT-6.1 Sol is higher overall (0.738). On the 5 transcription tasks, P220 scores above GPT-6.1 Sol on text recognition, table parsing and document parsing, and below it on full-page OCR and handwriting (5-task average 0.635 vs 0.652).
| Task | P220-ThaiOCR-9B | P110-ThaiOCR-4B | GPT-6.1 Sol (API) | GPT-6 Luna (API) | Qwen3.5-9B (base) | Typhoon OCR 7B | Typhoon2 Vision 7B | Qwen-SEA-LION-v4-8B-VL | Gemma-SEA-LION-v4-27B-VL | Qwen3.6-35B-A3B | Qwen3.8-27B | OpenThai 2.0 27B |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Chart parsing | 0.806 | 0.757 | 0.786 | 0.774 | 0.758 | 0.001 | 0.245 | 0.744 | 0.775 | 0.739 | 0.745 | 0.725 |
| Cognition VQA | 0.754 | 0.686 | 0.783 | 0.400 | 0.566 | 0.486 | 0.521 | 0.502 | 0.410 | 0.655 | 0.534 | 0.559 |
| Diagram VQA | 0.591 | 0.476 | 0.749 | 0.417 | 0.538 | 0.358 | 0.277 | 0.403 | 0.280 | 0.590 | 0.548 | 0.504 |
| Document classification | 0.903 | 0.773 | 0.975 | 0.823 | 0.921 | 0.346 | 0.815 | 0.865 | 0.851 | 0.935 | 0.893 | 0.957 |
| Document parsing | 0.508 | 0.409 | 0.502 | 0.472 | 0.451 | 0.054 | 0.230 | 0.486 | 0.376 | 0.422 | 0.530 | 0.260 |
| Fine-grained text recognition | 0.566 | 0.459 | 0.791 | 0.101 | 0.044 | 0.091 | 0.051 | 0.045 | 0.076 | 0.548 | 0.409 | 0.477 |
| Full-page OCR | 0.791 | 0.734 | 0.896 | 0.656 | 0.793 | 0.644 | 0.494 | 0.703 | 0.528 | 0.801 | 0.718 | 0.730 |
| Handwritten content extraction | 0.475 | 0.395 | 0.523 | 0.134 | 0.135 | 0.205 | 0.146 | 0.134 | 0.180 | 0.255 | 0.148 | 0.119 |
| Infographics | 0.825 | 0.755 | 0.809 | 0.391 | 0.645 | 0.496 | 0.564 | 0.558 | 0.451 | 0.699 | 0.592 | 0.603 |
| Key information extraction | 0.443 | 0.217 | 0.575 | 0.425 | 0.314 | 0.000 | 0.039 | 0.369 | 0.417 | 0.367 | 0.369 | 0.422 |
| Key information mapping | 0.704 | 0.464 | 0.860 | 0.674 | 0.521 | 0.000 | 0.004 | 0.700 | 0.608 | 0.561 | 0.478 | 0.663 |
| Table parsing | 0.657 | 0.585 | 0.646 | 0.638 | 0.111 | 0.182 | 0.258 | 0.576 | 0.321 | 0.653 | 0.630 | 0.401 |
| Text recognition | 0.745 | 0.723 | 0.692 | 0.143 | 0.208 | 0.371 | 0.302 | 0.143 | 0.290 | 0.432 | 0.182 | 0.144 |
| Average, all 13 tasks | 0.674 | 0.572 | 0.738 | 0.465 | 0.462 | 0.249 | 0.303 | 0.479 | 0.428 | 0.589 | 0.521 | 0.505 |
| Average, 5 transcription tasks | 0.635 | 0.569 | 0.652 | 0.409 | 0.340 | 0.291 | 0.286 | 0.409 | 0.339 | 0.513 | 0.442 | 0.331 |
Typhoon OCR 7B with its own official prompt scores 0.476 on the 5 transcription tasks (it is not built for the other 8).
Character error rate (CER %, lower is better)
| Test set | P220-ThaiOCR-9B | P110-ThaiOCR-4B | GPT-6.1 Sol (API) | GPT-6 Luna (API) | Qwen3.5-9B (base) | Typhoon OCR 7B | Typhoon2 Vision 7B | Qwen-SEA-LION-v4-8B-VL | Gemma-SEA-LION-v4-27B-VL | Qwen3.6-35B-A3B | Qwen3.8-27B | OpenThai 2.0 27B |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Thai printed text โ mekpro/ocr_th (held-out split, 400) | 1.35 | 1.97 | 0.31 | 5.06 | 3.73 | 8.99 | 35.26 | 7.15 | 9.69 | 3.12 | 10.21 | 1.15 |
| Thai handwriting โ iapp/thai_handwriting_dataset (held-out split, 1,355) | 24.43 | 35.70 | 9.76 | 89.78 | 43.41 | 23.50 | 57.38 | 59.95 | 86.05 | 40.93 | 65.79 | 17.87 |
| openthaigpt/thai-ocr-evaluation (104 crops) | 4.75 | 6.72 | 3.88 | 30.29 | 10.65 | 3.88 | 67.74 | 12.32 | 23.95 | 7.63 | 12.27 | 5.22 |
| Thai government PDFs โ set A (in-house, 20 pages) | 2.74 | 3.49 | 12.23 | 26.58 | 4.78 | 8.41 | 25.68 | 12.95 | 27.18 | 3.71 | 4.85 | 11.85 |
| Thai government PDFs โ set B (in-house, 66 pages) | 5.92 | 7.36 | 7.93 | 33.88 | 7.43 | 9.73 | 24.82 | 12.69 | 29.30 | 7.32 | 9.38 | 21.28 |
| Scanned Thai government documents (in-house, 86 pages) | 5.13 | 6.09 | 6.80 | 13.84 | 11.29 | 9.23 | 47.90 | 13.10 | 27.72 | 5.84 | 8.23 | 13.44 |
GPT-6.1 Sol is the most accurate on the text crops (printed, handwriting, openthaigpt); P220 has the lowest error on the three sets of full government PDF pages. On those PDF sets GPT-6.1 Sol returned no usable answer for 2 pages in each set, and OpenThai 2.0 27B for 1 page of set B; a page with no answer scores 100%.
How CER is measured
CER is the character edit distance (Levenshtein) between the model output and the reference text, divided by the
length of the reference, averaged over items. Each item is capped at 100%, so one runaway output cannot dominate the
average. Before comparing, both texts go through the same normalization for every model: Unicode NFC, a detached sara
am (เน + เธฒ) joined into เธณ, curly quotes made straight, markdown table rules and |*# markup removed, dotted or
underlined fill-in lines (โฆโฆ, ___) removed, and all whitespace removed (Thai does not separate words with spaces, and
models differ in how they space and break lines).
The held-out splits of mekpro/ocr_th and iapp/thai_handwriting_dataset are 10% of each subset, chosen with a fixed seed (42) before training and never trained on. The training data was also checked for overlap with ThaiOCRBench. The three PDF sets are in-house pages of Thai government documents with verified reference text.
Quantized variants
| Variant | Format | For | CER % handwriting / printed |
|---|---|---|---|
| BF16 | safetensors | vLLM, transformers | 24.59 / 1.39 |
| FP8 | compressed-tensors FP8 dynamic | vLLM, Ada / Hopper and newer | 24.56 / 1.59 |
| W4A16 | compressed-tensors INT4 (GPTQ) | vLLM, Ampere and newer | 25.60 / 1.37 |
| NVFP4 | compressed-tensors NVFP4 (MLP) + FP8 (attention) | vLLM, Blackwell | 25.41 / 1.28 |
| Q8_0 | GGUF | llama.cpp, Ollama | 25.14 / 1.34 |
| Q4_K_M | GGUF | llama.cpp, Ollama | 24.81 / 1.29 |
| Q3_K_M | GGUF | llama.cpp, Ollama, low memory | 27.39 / 1.46 |
| Q2_K | GGUF | llama.cpp, Ollama, lowest memory | 29.63 / 1.56 |
The full held-out sets from the CER table (iapp handwriting, 1,355 items; ocr_th printed text, 400 items), run on each released file, lower is better. The BF16 row is the released merged weights; the CER table above comes from the evaluation run before merging, so its numbers differ slightly. GGUF files were run in llama.cpp with --image-min-tokens 64 and the F16 vision encoder.
Training data (summary)
An expanded and improved version of the P110-ThaiOCR-4B training data, built with newer and more varied synthetic document techniques, together with pages from publicly available Thai documents with verified text and public Thai OCR datasets (mekpro/ocr_th, iapp/thai_handwriting_dataset, training splits only). No benchmark test data was used for training.
Limitations
- Upside-down or heavily rotated pages are not detected; rotate pages upright first.
- Very long or dense pages can occasionally loop; set a
max_tokenslimit. - Rare stray non-Thai glyphs inside Thai words can appear.
License and attribution
Developed by Porsuk Rungarunsrisuk. Released under Apache-2.0 (see LICENSE and NOTICE). Built on Qwen3.5-9B by the Qwen team
(Alibaba Cloud); this project is not affiliated with or endorsed by the Qwen team.
Citation
@misc{rungarunsrisuk2026p220thaiocr,
title = {P220-ThaiOCR-9B: a 9B vision-language model for Thai document OCR},
author = {Rungarunsrisuk, Porsuk},
year = {2026},
url = {https://huggingface.co/PorsukR/P220-ThaiOCR-9B}
}
- Downloads last month
- -


