Views
No views yet
xlm-roberta-base to classify emotions in Khmer text, using the
phonsobon/Text_Emotional_kh
dataset (4.19M rows). The fine-tuned model is published on the Hugging Face
Hub at phonsobon/xlmr-khmer-emotion.| File | Description |
|---|---|
train_khmer_emotion_colab.ipynb | End-to-end training notebook — designed to run in Google Colab |
train_khmer_emotion.py | Same pipeline as a standalone script (for local/server GPU use) |
id, text, labelkhmer-nltk —
Khmer script has no spaces between words, so word-level segmentation is inserted
as explicit boundaries before subword tokenization. This gives XLM-R's
SentencePiece tokenizer cleaner splits to work with.xlm-roberta-base tokenizer.Trainer (accuracy, macro F1, weighted F1 tracked;
early stopping on macro F1).pip install -U transformers torch khmer-nltkdatasets, accelerate, evaluate, scikit-learn are only needed for
training, not for testing/inference below.)train_khmer_emotion_colab.ipynb in Colab, set a GPU runtime
(Runtime > Change runtime type > GPU), and run all cells. See in-notebook
comments for config options (SUBSAMPLE, EPOCHS, PUSH_TO_HUB, etc.).1python train_khmer_emotion.py --subsample 100000 # quick test run
2python train_khmer_emotion.py # full datasetPUSH_TO_HUB = True (notebook) or --push_to_hub (script) to publish the
result to the Hub, e.g. as phonsobon/xlmr-khmer-emotion.xlm-roberta-base)| Metric | Value |
|---|---|
| eval_loss | 0.0017 |
| eval_accuracy | 0.9997 |
| eval_f1_macro | 0.9997 |
| eval_f1_weighted | 0.9997 |
| eval_runtime | 734.7229s |
| eval_samples_per_second | 569.615 |
| eval_steps_per_second | 4.451 |
| epoch | 0.1957 |
| step | 11515 |
2e-056412842AdamW (torch fused), betas=(0.9, 0.999), epsilon=1e-08linear0.0635.13.12.11.0+cu1285.0.00.22.2⚠️ Worth double-checking: 99.97% accuracy after only ~0.2 epochs (step 11515) is unusually high this early in training. That pattern often points to the eval set being too easy relative to train — commonly caused by near-duplicate or templated rows leaking across the train/val split, rather than the model being genuinely this good at 3 epochs in. Before trusting this number, it's worth spot-checking for duplicate/near-duplicatetextrows in the dataset, and testing the model on genuinely unseen, out-of-distribution Khmer text (e.g. text you write yourself) to see if performance holds up — which is exactly what the test script below is for.
khmer-nltk segmentation described above)transformers will download the
model straight from the Hub the first time you run this, and cache it locally
after that. Important: since the model was trained on khmer-nltk-segmented
text, apply the same segmentation at inference time — skipping it will give
worse/inconsistent predictions.1from transformers import pipeline
2from khmernltk import word_tokenize as khmer_word_tokenize
3
4HF_MODEL_ID = "phonsobon/xlmr-khmer-emotion" # change if your repo name differs
5
6# If the repo is private, log in first (uncomment):
7# from huggingface_hub import login
8# login() # will prompt for a token, or set the HF_TOKEN env var
9
10clf = pipeline("text-classification", model=HF_MODEL_ID, top_k=None)
11
12def segment(text: str) -> str:
13 """Apply the same khmer-nltk word segmentation used during training."""
14 tokens = khmer_word_tokenize(text, return_tokens=True)
15 tokens = [t for t in tokens if t.strip() != ""]
16 return " ".join(tokens)
17
18def predict(text: str):
19 segmented = segment(text)
20 results = clf(segmented)[0] # list of {label, score} for every class
21 results.sort(key=lambda r: r["score"], reverse=True)
22 top = results[0]
23 return top["label"], top["score"], results
24
25if __name__ == "__main__":
26 test_texts = [
27 "ខ្ញុំសប្បាយចិត្តណាស់ថ្ងៃនេះបានជួបជុំគ្រួសារ។",
28 "ខ្ញុំមិនអាចគេងលក់បានព្រោះខ្វល់ខ្វាយអំពីបញ្ហាលុយកាក់។",
29 "ខ្ញុំខឹងណាស់ព្រោះគេបានកុហកខ្ញុំ។",
30 ]
31
32 for text in test_texts:
33 label, score, all_scores = predict(text)
34 print(f"Text: {text}")
35 print(f"Predicted: {label} (confidence: {score:.3f})")
36 print(f"All scores: {all_scores}")
37 print()id2label in phonsobon/xlmr-khmer-emotion's
config.json on the Hub):Text: ខ្ញុំសប្បាយចិត្តណាស់ថ្ងៃនេះបានជួបជុំគ្រួសារ។
Predicted: happy (confidence: 0.94)
All scores: [{'label': 'happy', 'score': 0.94}, {'label': 'neutral', 'score': 0.03}, ...]1from sklearn.metrics import classification_report
2from datasets import load_dataset
3
4# sample rows from the dataset that weren't necessarily in your exact train split -
5# for a truly clean check, prefer text you write yourself over dataset rows
6test_ds = load_dataset("phonsobon/Text_Emotional_kh", split="train").shuffle(seed=123).select(range(1000))
7
8y_true, y_pred = [], []
9for row in test_ds:
10 label, _, _ = predict(row["text"])
11 y_true.append(row["label"])
12 y_pred.append(label)
13
14print(classification_report(y_true, y_pred))khmer-nltk takes a while even
with multiprocessing — the notebook caches the segmented dataset to Google
Drive after the first run so you don't pay that cost twice.xlm-roberta-large instead of xlm-roberta-base for higher accuracy if
you have the GPU memory/time budget for it.pipeline(..., model=HF_MODEL_ID) downloads and caches
the model locally (usually to ~/.cache/huggingface); subsequent runs are
fast since they use the cache.