Instructions to use phonsobon/khmer-tag-recommendation_xlmr with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use phonsobon/khmer-tag-recommendation_xlmr with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="phonsobon/khmer-tag-recommendation_xlmr")# Load model directly from transformers import AutoTokenizer, AutoModelForSequenceClassification tokenizer = AutoTokenizer.from_pretrained("phonsobon/khmer-tag-recommendation_xlmr") model = AutoModelForSequenceClassification.from_pretrained("phonsobon/khmer-tag-recommendation_xlmr") - Notebooks
- Google Colab
- Kaggle
khmer-tag-recommendation_xlmr
This model is a fine-tuned version of xlm-roberta-base for automatic tag recommendation on Khmer-language documents. Given the text of a Khmer document, it predicts a set of relevant tags from a fixed vocabulary of 500 tags, using multi-label classification.
It achieves the following results on the evaluation set:
- eval_loss: 0.0074
- eval_f1_micro: 0.9404
- eval_f1_macro: 0.9518
- eval_precision_micro: 0.8877
- eval_recall_micro: 0.9998
- eval_hamming_loss: 0.0018
- eval_runtime: 400.6343
- eval_samples_per_second: 96.337
- eval_steps_per_second: 1.508
- epoch: 1.4509
- step: 14000
Model description
khmer-tag-recommendation_xlmr is a multi-label sequence classification model built on top of xlm-roberta-base, with a classification head sized to a 500-tag vocabulary (problem_type="multi_label_classification"). Given raw Khmer document text, the model outputs a probability for each of the 500 tags, indicating how likely that tag applies to the document.
The tag vocabulary was built by taking the 500 most frequent tags (each appearing at least 5 times) from the training dataset, since the raw tag space in the source dataset is effectively unbounded (many tags are near-unique per document).
Because the tags follow a long-tail distribution, the model was trained with a weighted binary cross-entropy loss (pos_weight capped at 20x) to avoid ignoring rarer tags, and per-tag decision thresholds were tuned on a held-out validation set (see thresholds.npy in this repo) rather than using a flat 0.5 cutoff for every tag.
Intended uses & limitations
Intended use: recommending tags for Khmer-language documents β e.g. government letters, certificates, mission reports, articles, and similar administrative or informational text β to assist with document organization, search, and categorization.
Limitations:
- The tag vocabulary is fixed to the 500 most frequent tags seen during training. The model cannot predict tags outside this vocabulary.
- Training data leans toward Khmer government/administrative document styles (certificates, mission letters, official reports). Performance on stylistically different content (news articles, blog posts, product descriptions, etc.) has not been rigorously validated and may vary.
- Per-tag thresholds were tuned using an unconstrained per-tag F1 search; tags with very low positive support in the validation set may have less reliable tuned thresholds. Use
thresholds.npytogether with judgment, and considermin_tags/max_tagsbounds at inference time rather than trusting the threshold in isolation for every tag. - This is a general-purpose classifier, not tuned or evaluated for high-stakes or safety-critical decisions.
Training and evaluation data
- Dataset: phonsobon/khmer_tag_recommendations β Khmer-language documents, each with 5β12 associated tags.
- Tag vocabulary: top 500 most frequent tags (minimum count of 5), extracted from the training split.
- Split: 80% train / 10% validation / 10% test, random split (seed 42).
- Labels: multi-label binary vectors over the 500-tag vocabulary, built with
sklearn.preprocessing.MultiLabelBinarizer.
Training procedure
Training hyperparameters
The following hyperparameters were used during training:
- learning_rate: 2e-05
- train_batch_size: 32
- eval_batch_size: 64
- seed: 42
- optimizer: Use OptimizerNames.ADAMW_TORCH_FUSED with betas=(0.9,0.999) and epsilon=1e-08 and optimizer_args=No additional optimizer arguments
- lr_scheduler_type: linear
- lr_scheduler_warmup_steps: 0.06
- num_epochs: 5
- mixed_precision_training: Native AMP
Framework versions
- Transformers 5.13.1
- Pytorch 2.10.0+cu128
- Datasets 5.0.0
- Tokenizers 0.22.2
How to use
This repo includes the fine-tuned model, tokenizer, the 500-tag vocabulary (tag_vocab.json), and the per-tag tuned decision thresholds (thresholds.npy). The example below loads all of these from the Hub and runs adaptive tag recommendation (always returns between MIN_TAGS and MAX_TAGS tags, ranked by confidence).
import json
import numpy as np
import torch
from huggingface_hub import hf_hub_download
from transformers import AutoTokenizer, AutoModelForSequenceClassification
# ---- Config ----
HUB_MODEL_ID = "phonsobon/khmer-tag-recommendation_xlmr"
MAX_LENGTH = 256
MIN_TAGS = 4
MAX_TAGS = 12
device = "cuda" if torch.cuda.is_available() else "cpu"
# ---- Load model + tokenizer from the Hub ----
tokenizer = AutoTokenizer.from_pretrained(HUB_MODEL_ID)
model = AutoModelForSequenceClassification.from_pretrained(HUB_MODEL_ID).to(device)
model.eval()
# ---- Load tag vocab + tuned thresholds pushed alongside the model ----
tag_vocab_path = hf_hub_download(repo_id=HUB_MODEL_ID, filename="tag_vocab.json")
thresholds_path = hf_hub_download(repo_id=HUB_MODEL_ID, filename="thresholds.npy")
with open(tag_vocab_path, "r", encoding="utf-8") as f:
tag_vocab = json.load(f)
thresholds = np.load(thresholds_path)
print(f"Loaded model with {len(tag_vocab)} tags.")
# ---- Inference function (adaptive tag count) ----
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def recommend_tags_adaptive(text, min_tags=MIN_TAGS, max_tags=MAX_TAGS, use_tuned_thresholds=True):
enc = tokenizer(
text, truncation=True, padding="max_length", max_length=MAX_LENGTH, return_tensors="pt"
).to(device)
with torch.no_grad():
logits = model(**enc).logits.cpu().numpy()[0]
probs = sigmoid(logits)
thr = thresholds if use_tuned_thresholds else 0.5
above_threshold = np.where(probs >= thr)[0]
# rank everything above threshold by confidence
ranked = sorted(above_threshold.tolist(), key=lambda i: probs[i], reverse=True)
if len(ranked) < min_tags:
# not enough passed the threshold -> fill up to min_tags with next-best overall
all_ranked = np.argsort(probs)[::-1]
for i in all_ranked:
i = int(i)
if i not in ranked:
ranked.append(i)
if len(ranked) >= min_tags:
break
elif len(ranked) > max_tags:
# too many passed the threshold -> cut to the top max_tags
ranked = ranked[:max_tags]
return [(tag_vocab[i], float(probs[i])) for i in ranked]
# ---- Just paste your Khmer content here and run ----
content = """
ααααααΆααΆααΆα
αααααααα»ααΆ
ααΆαα· ααΆαααΆ αααααα αΆαααααα
ααααα½ααααααααΈαα αα·αααΌαααααΆαααα
αααα»αααΆαααΆαααα·ααααααααααΈααΈαα
αααα α α‘α¨α’α€ αα/αα
ααΆαααΆααΈααααααα ααααααΈα’α₯ ααααΈα αΆ ααααΆαα’α α’α§
# αααΆαααΆααααααααααα
**ααααααααα»α** αααΆαααΆαααααααΈααΈααΆααααααααααααααα
αΌααα½αααααα·ααΈαα’ααααααΆαα·ααααΈααΈααΆαααααΆααααααΌαααα·ααααααααααΈααΈαα αα·αααΆαααααααααααα·ααααααααΆααΆααα αα
ααΆααΆαααααααα·ααα αα»ααΈ
**αααα**
* αα·αα·αα’αα»ααααΆαααααααααααααααααα α α‘α§α₯α¦ αα/α’α α
α»αααααααΈα α₯ ααααΈα αΆ ααααΆαα’α α’α§
* αα·αα·αα’ααααΎαααΈααααααααΆαα·ααΆααααα
αααααα·ααΈα Smart Government Summit 2027
* αααααΆαα’αα·ααααααα·ααααααααααΈααΈαα ααααΆαα’α α’α§βα’α α£α‘
**ααΌα**
α―αα§αααα αααααΆααααα»αααΆαααΆαααα·ααααααααααΈααΈαα
## α‘. ααα
ααααΈααααΎα
α’αα»ααααααΆααα·αα·αα’αα»ααααΆααααααααααααααααΆαααΎ αααα»αααΆα **αα»α αα»αα»ααα** αααααΆααααα»αααΆαααΆααααααΆαα·ααααα·αααα·α αααααα»αααΆαααΆαααα·ααααααααααΈααΈαα ααΆαα
αΌααα½αααααα·ααΈαα’ααααααΆαα·ααααΈααΈ **ααΆαααααΆααααααΌαααα·ααααααααααΈααΈαα αα·αααΆαααααααααααα·ααααααααΆααΆααα** αααααΆααααα
αα‘αΎααα
αααααααα·ααα αα»ααΈ αααααΆαααΆαα
αΌααα½αααΈαααααααΈαααα α’αααααααΆαααααΆα αα·αα’αααααααΆαααααααα
αα
αααα·ααααΆααααΈαααααΆααααααααΆα
αααΎαα
## α’. ααααααΆαα’αααΈαααααααα
* ααΈααααααα αα·ααα αα»ααΈ
* ααΆαααα·α
ααααα ααααααΈα‘α₯ αααααααααΈα’α ααααΈα αΆ ααααΆαα’α α’α§
* ααααααα α¦αααα
* α’αααα
αΌααα½αα ααααΆαααααα½ααααααααΈαα αα·αααΌαααααΆαααα
## α£. αααααααΆααααααΆαα’αα»αααα
αααα»αα’αα‘α»αααααααααααα ααΆαα
αΌααα½ααααααααΆαααΌα
ααΆααααααα
* α
αΌααα½αααααα·ααΈαααααΈααΈαα»αααααΆααααααααααΆαα·ααΆαααΈααΈααα
* αα·ααααΆααΆαααααααααααα·ααααααααΆαα· αα·α Data Governanceα
* αα·ααααΆααΆαααααΎααααΆαααααααΆαα·ααααα·αααα·ααααα»αααΆααααααααααΆααΆααΆαααα
* α
αΌααα½ααα·ααααΆααΆααΆα’αααΈ Cybersecurity αα·αααΆαααΆαααΆααα·ααααααα
* ααααααα·α
αα
αα
ααααααααα Smart Nation αα·α Government Technology Agencyα
* ααααΆααααααΌααααα·αααααααΆαα½αα’αααααααΆα αα·αααααΆααααααΆαα·ααΆαααΈααααααααααααα
## α€. αααααααααααα½αααΆα
ααΆααααααααααααααα ααα½αααΆαααααααααααΆααααα½αααΆαα
* αααααΉαα’αααΈααααααααΆα Smart Governmentα
* αα·ααααΆααααΌααΆαααααααααααα·ααααααααΆααΆααααααααααααα»ααααα·ααΆαα
* ααα½αααΆαα
ααααααΉαα’αααΈααΆαααααΎααααΆαα AI αααα»αααΆααααααα
α
α·αααααα’ααααΎαα·ααααααα
* ααααααααααΈααΆααααα
α Digital Identity αα·α Digital Public Infrastructureα
* αααααΎααααααΆαααααΆααααααααΆαα½αα’αααααααΆα αα·αααααΆαααα’ααααααΆαα·α
## α₯. αααα αΆααααα
* ααΆααα»αααααΆαααααααααααααα· αα·ααααααααΆααα·αααααααααΆαααααααα
* αααααΌαα±ααααΆαααααΆαααα»αααααααΆααααααααααα»ααα·ααα AI αα·α Data Engineeringα
* αααααΌαα±ααααΆαααΆααα·αα·αααααΎα αααααΆαα
ααΆαααααααα Cloud αα·α GPUα
## α¦. α’αα»ααΆααα
ααΎααααΈα’αα»ααααα
ααααααΉααααααα½αααΆα ααΌαααααΎα’αα»ααΆαααααΌα
ααΆααααααα
* αααααΎαααααααααΆααααα AI αααααΆααααΆαααΆαααααααΆαα
* αααααΉαααΆαααααααααααα·αααααα αα·α Data Governanceα
* αααα
αααααααααα»ααααααΆαααΌααααααααΈααααΆαα
* αααααΈααα·α
αα
αα ααααα·ααααα·ααΆαααΆαα½αααααΆαααα’ααααααΆαα·α
* αααα»αααΆαα’αα·ααααααααΆααΆααΆαααααΈααΈααα
## α§. ααα
ααααΈααααα·ααααΆα
ααΆαα½α αααααααααααααΆααααααα
ααΆαααααααααααααΆαααααα αα·αααΆααααααααΌαα
ααααααΉα αααα·ααααα αα·ααααα·αααααΈα αααα’αΆα
ααααα’αα»αααααααα»αααΆαα’αα·αααααααααααααα
αα
αααα·ααααΆααΈααΈαα ααΆαααααααααααα·αααααα αα·αααΆαααΎααααααααα»αααΆαααααΆααΆααΆαααααααααααα½ααααααααΈαα αα·αααΌαααααΆααααα
ααΌαα―αα§αααα ααααααΆααα½αααααΆαα
ααΆαααΆααΈααααααα
ααααααΈα’α₯ ααααΈα αΆ ααααΆαα’α α’α§
α αααααααΆ
**αα»α αα»αα»ααα**
αααααΆααααα»αααΆαααΆααααααΆαα·ααααα·αααα·α
αααα»αααΆαααΆαααα·ααααααααααΈααΈαα
α
ααααααΌαα
* ααΆααααααΆααα»ααααα·α αααααΆααααααΆα
* α―αααΆα
"""
predicted_tags = recommend_tags_adaptive(content, min_tags=MIN_TAGS, max_tags=MAX_TAGS)
print(f"\nPredicted tags ({len(predicted_tags)}, adaptive between {MIN_TAGS}-{MAX_TAGS}):")
for tag, score in predicted_tags:
crossed = "β" if score >= thresholds[tag_vocab.index(tag)] else " "
print(f" [{crossed}] {tag} ({score:.3f})")
Citation
If you use this model, please cite the base model and the source dataset:
Base model: xlm-roberta-base (Conneau et al., 2020)
Fine-tuning dataset: phonsobon/khmer_tag_recommendations
- Downloads last month
- 56
Model tree for phonsobon/khmer-tag-recommendation_xlmr
Base model
FacebookAI/xlm-roberta-base