Downloads · 30 days
0
0% of all-time downloads
1amBanana/WangchanBERTa-NER-v0
WangchanBERTa-NER-v0 is a token classification model from 1amBanana. Use it when you need labels on individual words, such as names. It is set up for transformers. The card lists the license as cc-by-sa-3.0.
This is a Thai Named Entity Recognition (NER) model fine-tuned from airesearch/wangchanberta-base-att-spm-uncased on the Thai NER v2.2 corpus.
Downloads · 30 days
0
0% of all-time downloads
All-time downloads
20
Public
Repo size
—
Likes
0
Public
Click a slice to open those files.
.md9.5 KB · 86%
From the Hugging Face model README
This is a Thai Named Entity Recognition (NER) model fine-tuned from airesearch/wangchanberta-base-att-spm-uncased on the Thai NER v2.2 corpus.
The base WangchanBERTa checkpoint was used (pretrained MLM only — not the existing fine-tuned variant) and a fresh token-classification head was trained from scratch.
airesearch/wangchanberta-base-att-spm-uncased (RoBERTa-base, ~105M params, SentencePiece tokenizer, vocab 25k)Entity extraction from general-domain Thai text (news, PR content, social media). The model predicts 14 entity types plus O, in IOB scheme (29 labels total).
| Tag | Description |
|---|---|
PERSON | Person name |
ORGANIZATION | Company / organization |
LOCATION | Location |
DATE | Date |
TIME | Time |
MONEY | Monetary amount |
PERCENT | Percentage |
LAW | Legislation / law reference |
LEN | Length measurement |
TEMPERATURE | Temperature |
PHONE | Phone number |
EMAIL | Email address |
URL | URL |
ZIP | Zip code |
pythainlp/thainer-corpus-v2.2 — 7,326 documents (news, PR from KKU, general Thai text), pre-split by the dataset authors:
| Split | Docs |
|---|---|
| Train | 3,938 |
| Validation | 1,313 |
| Test | 1,313 |
Tags in the corpus are stored as integers without a ClassLabel feature; the canonical id2label mapping was imported from pythainlp/thainer-corpus-v2-base-model which was trained on the same corpus.
Hyperparameters follow the WangchanBERTa paper's downstream recipe.
| Setting | Value |
|---|---|
| Epochs | 10 (with early stopping, patience 2) |
| Batch size | 16 |
| Learning rate | 3e-5 |
| LR scheduler | Linear with 10% warmup |
| Optimizer | AdamW (β1=0.9, β2=0.999, wd=0.01) |
| Precision | bf16 |
| Max length | 416 |
| Seed | 42 |
| Best checkpoint criterion | Validation entity-level F1 |
Two WangchanBERTa-specific steps matter for correct results:
<_> before passing to the tokenizer.-100 so they are ignored by the loss.Single premium GPU on Google Colab Pro+ RTX PRO 6000 Blackwell Server Edition.
Entity-level metrics computed with seqeval on the held-out test split (1,313 docs).
| Split | Precision | Recall | F1 |
|---|---|---|---|
| Validation | 0.817274 | 0.872834 | 0.844141 |
| Test | 0.813699 | 0.877883 | 0.844573 |
# Per-entity classification report on the test set
precision recall f1-score support
AGO 0.7292 0.9211 0.8140 38
DATE 0.8122 0.9276 0.8661 373
EMAIL 1.0000 1.0000 1.0000 1
FACILITY 0.4214 0.4558 0.4379 147
LAW 0.5000 0.5909 0.5417 44
LEN 1.0000 0.9412 0.9697 17
LOCATION 0.8159 0.8558 0.8354 839
MONEY 0.7800 0.8797 0.8269 133
ORGANIZATION 0.8453 0.9144 0.8785 1285
PERCENT 0.6800 0.7907 0.7312 43
PERSON 0.8873 0.9395 0.9127 645
PHONE 0.9459 1.0000 0.9722 35
TEMPERATURE 0.0000 0.0000 0.0000 2
TIME 0.7436 0.7838 0.7632 185
URL 0.9583 1.0000 0.9787 23
ZIP 1.0000 1.0000 1.0000 6
micro avg 0.8137 0.8779 0.8446 3816
macro avg 0.7574 0.8125 0.7830 3816
weighted avg 0.8143 0.8779 0.8447 3816
Because of how WangchanBERTa handles spaces, the standard pipeline("ner", ...) gives incorrect results on Thai. Use PyThaiNLP for word-level tokenization and run the model manually:
import torch
from transformers import AutoTokenizer, AutoModelForTokenClassification
from pythainlp.tokenize import word_tokenize
MODEL = "1amBanana/WangchanBERTa-NER-v0"
SPACE = "<_>"
tokenizer = AutoTokenizer.from_pretrained(MODEL)
model = AutoModelForTokenClassification.from_pretrained(MODEL).eval()
id2label = model.config.id2label
def predict_ner(text: str):
words = word_tokenize(text, engine="newmm", keep_whitespace=True)
norm = [SPACE if (w is None or not w.strip()) else w for w in words]
enc = tokenizer(norm, is_split_into_words=True, return_tensors="pt",
truncation=True, max_length=416)
with torch.no_grad():
logits = model(**enc).logits[0]
pred_ids = logits.argmax(-1).tolist()
word_ids = enc.word_ids(batch_index=0)
word_tags = {}
for p, w in zip(pred_ids, word_ids):
if w is None or w in word_tags:
continue
word_tags[w] = id2label[int(p)]
return [(words[i], word_tags.get(i, "O")) for i in range(len(words))]
text = "นายมะลิวาโย บุญรักษา อาศัยอยู่ที่อำเภอนางรอง จังหวัดบุรีรัมย์ อายุ 99 ปี เพิ่งเรียนจบจากมหาวิทยาลัยเชียงใหม่"
result = predict_ner(sample)
pd.DataFrame(result, columns=["word", "tag"])
Here is the result of the block code:
| word | tag | |
|---|---|---|
| 0 | นาย | B-PERSON |
| 1 | มะลิ | I-PERSON |
| 2 | วาโย | I-PERSON |
| 3 | I-PERSON | |
| 4 | บุญ | I-PERSON |
| 5 | รักษา | I-PERSON |
| 6 | O | |
| 7 | อาศัย | O |
| 8 | อยู่ | O |
| 9 | ที่ | O |
| 10 | อำเภอ | B-LOCATION |
| 11 | นางรอง | I-LOCATION |
| 12 | O | |
| 13 | จังหวัด | B-LOCATION |
| 14 | บุรีรัมย์ | I-LOCATION |
| 15 | O | |
| 16 | อายุ | O |
| 17 | O | |
| 18 | 99 | B-AGO |
| 19 | I-AGO | |
| 20 | ปี | I-AGO |
| 21 | O | |
| 22 | เพิ่ง | O |
| 23 | เรียนจบ | O |
| 24 | จาก | O |
| 25 | มหาวิทยาลัยเชียงใหม่ | B-ORGANIZATION |
<_> preprocessing. Required for correct inference. The default transformers NER pipeline does not do this automatically and will produce wrong tags.Base model:
@misc{lowphansirikul2021wangchanberta,
title={WangchanBERTa: Pretraining transformer-based Thai Language Models},
author={Lalita Lowphansirikul and Charin Polpanumas and Nawat Jantrakulchai and Sarana Nutanong},
year={2021},
eprint={2101.09635},
archivePrefix={arXiv},
primaryClass={cs.CL}
}
Dataset:
@dataset{wannaphong_phatthiyaphaibun_2024_10795907,
author = {Wannaphong Phatthiyaphaibun},
title = {Thai NER 2.2},
month = mar,
year = 2024,
publisher = {Zenodo},
version = {2.2},
doi = {10.5281/zenodo.10795907},
url = {https://doi.org/10.5281/zenodo.10795907}
}