--- language: - th pipeline_tag: text-classification tags: - sentiment-analysis - thai - wangchanberta - bilstm - cnn - lstm license: apache-2.0 library_name: transformers datasets: - wisesight_sentiment --- # Thai Sentiment (WangchanBERTa + LSTM Heads) โมเดลสำหรับวิเคราะห์อารมณ์ (2 คลาส: NEG/POS) ภาษาไทย โดยใช้ **WangchanBERTa** เป็น backbone และเพิ่มหัว (heads) แบบ LSTM/CNN-LSTM หลายสถาปัตยกรรมสำหรับเปรียบเทียบและใช้งานตามบริบท รีโปนี้บรรจุโมเดล 4 ตัว (เก็บเป็นโฟลเดอร์ย่อย): - `WCB/` — WangchanBERTa (ใช้ [CLS]) - `WCB_BiLSTM/` — WangchanBERTa → BiLSTM → Pooling - `WCB_CNN_BiLSTM/` — WangchanBERTa → CNN → BiLSTM → Pooling - `WCB_4Layer_BiLSTM/` — WangchanBERTa (ถ่วงน้ำหนัก 4 เลเยอร์สุดท้าย) → BiLSTM → Pooling แต่ละโฟลเดอร์มี `model.safetensors` และ `config.json` (เมตาดาต้า: `id2label/label2id`, `max_length`, `pooling_after_lstm`, `base_model`) --- ## สรุปผลการประเมิน (5-fold CV) | Model | Accuracy (%) | F1-Score (%) | AUC (%) | |---|---:|---:|---:| | WCB | **90.33 ± 0.32** | **89.92 ± 0.33** | **95.72 ± 0.22** | | WCB_BiLSTM | **90.93 ± 0.37** | **90.54 ± 0.39** | **95.57 ± 1.22** | | WCB_CNN_BiLSTM | **90.14 ± 0.66** | **89.73 ± 0.68** | **95.83 ± 0.42** | | WCB_4Layer_BiLSTM | **90.52 ± 0.65** | **90.13 ± 0.68** | **95.43 ± 0.36** | **ข้อสังเกตย่อ** - **แม่นยำสูงสุด**: `WCB_BiLSTM` (Acc/F1 สูงสุด) แต่ AUC แปรปรวนกว่าตัวอื่นเล็กน้อย (±1.22%). - **AUC สูงสุด/เสถียรดี**: `WCB_CNN_BiLSTM` (AUC 95.83% ±0.42) เหมาะหากให้ความสำคัญกับการแยกคลาสจากสกอร์ความเชื่อมั่น แต่ Acc/F1 ต่ำกว่าเล็กน้อย. - **เร็ว/เสถียร**: `WCB` เร็วที่สุดและเสถียรสุด เหมาะงานทรัพยากรจำกัด. ### เวลาเทรน (โดยเฉลี่ย) | Model | วินาที/รอบ | เวลารวม (ชม.) | |---|---:|---:| | WCB | 54.67 | 4.58 | | WCB_BiLSTM | 67.84 | 5.68 | | WCB_CNN_BiLSTM | 68.72 | 5.76 | | WCB_4Layer_BiLSTM | 72.91 | 6.11 | --- ## โครงสร้างรีโป ``` . ├─ WCB/ │ ├─ model.safetensors │ └─ config.json ├─ WCB_BiLSTM/ │ ├─ model.safetensors │ └─ config.json ├─ WCB_CNN_BiLSTM/ │ ├─ model.safetensors │ └─ config.json ├─ WCB_4Layer_BiLSTM/ │ ├─ model.safetensors │ └─ config.json ├─ common/ │ ├─ models.py │ └─ __init__.py ├─ requirements.txt ├─ LICENSE └─ README.md ``` --- ## วิธีใช้งาน ### 🔧 ติดตั้ง Dependencies ```bash pip install torch transformers huggingface-hub safetensors ``` ### 📦 วิธีที่ 1: โหลดโมเดลจาก Hugging Face Hub (แนะนำ) ```python import torch import torch.nn.functional as F from transformers import AutoTokenizer from huggingface_hub import hf_hub_download from safetensors.torch import load_file import json import importlib.util # ===== ตั้งค่า ===== REPO_ID = "Dusit-P/thai-sentiment" # เปลี่ยนเป็น repo ของคุณ MODEL_NAME = "WCB_BiLSTM" # เลือก: WCB, WCB_BiLSTM, WCB_CNN_BiLSTM, WCB_4Layer_BiLSTM # ===== 1. ดาวน์โหลดไฟล์จำเป็น ===== config_path = hf_hub_download(REPO_ID, filename=f"{MODEL_NAME}/config.json") weights_path = hf_hub_download(REPO_ID, filename=f"{MODEL_NAME}/model.safetensors") models_py = hf_hub_download(REPO_ID, filename="common/models.py") # ===== 2. โหลด config ===== with open(config_path, "r", encoding="utf-8") as f: config = json.load(f) # ===== 3. โหลด tokenizer ===== base_model = config.get("base_model", "airesearch/wangchanberta-base-att-spm-uncased") tokenizer = AutoTokenizer.from_pretrained(base_model) # ===== 4. โหลดโมเดล ===== # Import models.py spec = importlib.util.spec_from_file_location("models", models_py) models = importlib.util.module_from_spec(spec) spec.loader.exec_module(models) # สร้างโมเดล architecture = config.get("architecture", MODEL_NAME) num_labels = config.get("num_labels", 2) pooling = config.get("pooling_after_lstm", "masked_mean") model = models._build(architecture, base_model, num_labels, pooling) # โหลด weights state_dict = load_file(weights_path) model.load_state_dict(state_dict, strict=False) model.eval() # ===== 5. ทำนาย ===== text = "มือถือรุ่นนี้ดีมาก ราคาคุ้มค่า แนะนำเลย!" # Tokenize inputs = tokenizer( text, truncation=True, padding=True, max_length=config.get("max_length", 128), return_tensors="pt" ) # Predict with torch.no_grad(): logits = model(inputs["input_ids"], inputs["attention_mask"]) probs = F.softmax(logits, dim=1)[0] pred_id = torch.argmax(logits, dim=1).item() # แสดงผล id2label = {int(k): v for k, v in config["id2label"].items()} print(f"Text: {text}") print(f"Prediction: {id2label[pred_id]}") print(f"Probabilities: NEG={probs[0]:.4f}, POS={probs[1]:.4f}") ``` **Output ตัวอย่าง:** ``` Text: มือถือรุ่นนี้ดีมาก ราคาคุ้มค่า แนะนำเลย! Prediction: positive Probabilities: NEG=0.0234, POS=0.9766 ``` --- ### 📦 วิธีที่ 2: Clone Repo แล้วใช้งาน ```bash git clone https://huggingface.co/Dusit-P/thai-sentiment cd thai-sentiment pip install -r requirements.txt ``` ```python import torch import torch.nn.functional as F from transformers import AutoTokenizer from safetensors.torch import load_file from common.models import _build import json # ===== เลือกโมเดล ===== MODEL_DIR = "WCB_BiLSTM" # ===== โหลด config ===== with open(f"{MODEL_DIR}/config.json", "r") as f: config = json.load(f) # ===== โหลด tokenizer ===== base_model = config.get("base_model", "airesearch/wangchanberta-base-att-spm-uncased") tokenizer = AutoTokenizer.from_pretrained(base_model) # ===== โหลดโมเดล ===== model = _build( config.get("architecture", MODEL_DIR), base_model, config.get("num_labels", 2), config.get("pooling_after_lstm", "masked_mean") ) state_dict = load_file(f"{MODEL_DIR}/model.safetensors") model.load_state_dict(state_dict, strict=False) model.eval() # ===== ทำนาย ===== text = "ของแพงไป คุณภาพไม่คุ้มราคา" inputs = tokenizer( text, truncation=True, padding=True, max_length=config.get("max_length", 128), return_tensors="pt" ) with torch.no_grad(): logits = model(inputs["input_ids"], inputs["attention_mask"]) probs = F.softmax(logits, dim=1)[0] pred_id = torch.argmax(logits, dim=1).item() id2label = {int(k): v for k, v in config["id2label"].items()} print(f"Prediction: {id2label[pred_id]}") print(f"Probabilities: {probs}") ``` --- ### 📦 วิธีที่ 3: ทำนายหลายข้อความพร้อมกัน (Batch Prediction) ```python import torch import torch.nn.functional as F from transformers import AutoTokenizer from huggingface_hub import hf_hub_download from safetensors.torch import load_file import json import importlib.util # ===== Setup ===== REPO_ID = "Dusit-P/thai-sentiment" MODEL_NAME = "WCB_BiLSTM" # ===== โหลดโมเดล (ตามวิธีที่ 1) ===== config_path = hf_hub_download(REPO_ID, filename=f"{MODEL_NAME}/config.json") weights_path = hf_hub_download(REPO_ID, filename=f"{MODEL_NAME}/model.safetensors") models_py = hf_hub_download(REPO_ID, filename="common/models.py") with open(config_path, "r") as f: config = json.load(f) base_model = config.get("base_model", "airesearch/wangchanberta-base-att-spm-uncased") tokenizer = AutoTokenizer.from_pretrained(base_model) spec = importlib.util.spec_from_file_location("models", models_py) models = importlib.util.module_from_spec(spec) spec.loader.exec_module(models) model = models._build( config.get("architecture", MODEL_NAME), base_model, config.get("num_labels", 2), config.get("pooling_after_lstm", "masked_mean") ) state_dict = load_file(weights_path) model.load_state_dict(state_dict, strict=False) model.eval() # ===== ทำนายหลายข้อความ ===== texts = [ "อาหารอร่อยมาก บริการดีมาก", "ของแพงไป รสชาติก็ธรรมดา", "บรรยากาศดี แต่รอนานไป", "คุ้มค่ามาก แนะนำเลย" ] # Tokenize batch inputs = tokenizer( texts, truncation=True, padding=True, max_length=config.get("max_length", 128), return_tensors="pt" ) # Predict batch with torch.no_grad(): logits = model(inputs["input_ids"], inputs["attention_mask"]) probs = F.softmax(logits, dim=1) pred_ids = torch.argmax(logits, dim=1) # แสดงผล id2label = {int(k): v for k, v in config["id2label"].items()} print("=" * 70) for i, text in enumerate(texts): label = id2label[pred_ids[i].item()] neg_prob = probs[i][0].item() pos_prob = probs[i][1].item() print(f"Text: {text}") print(f" → Prediction: {label}") print(f" → Confidence: NEG={neg_prob:.4f}, POS={pos_prob:.4f}") print("-" * 70) ``` **Output ตัวอย่าง:** ``` ====================================================================== Text: อาหารอร่อยมาก บริการดีมาก → Prediction: positive → Confidence: NEG=0.0156, POS=0.9844 ---------------------------------------------------------------------- Text: ของแพงไป รสชาติก็ธรรมดา → Prediction: negative → Confidence: NEG=0.8923, POS=0.1077 ---------------------------------------------------------------------- Text: บรรยากาศดี แต่รอนานไป → Prediction: positive → Confidence: NEG=0.3421, POS=0.6579 ---------------------------------------------------------------------- Text: คุ้มค่ามาก แนะนำเลย → Prediction: positive → Confidence: NEG=0.0089, POS=0.9911 ---------------------------------------------------------------------- ``` --- ## 🎯 เลือกโมเดลให้เหมาะกับงาน - **ต้องการความแม่นยำสูงสุด** → `WCB_BiLSTM` Acc/F1 สูงสุด (90.93% / 90.54%) - **ทรัพยากรจำกัด / ต้องการความเร็ว** → `WCB` เร็วที่สุด (54.67 วิ/รอบ) และเสถียรสุด - **โฟกัส AUC / การจัดอันดับความเสี่ยง** → `WCB_CNN_BiLSTM` AUC สูงสุด (95.83%) และเสถียร - **สมดุลโดยรวม** → `WCB_4Layer_BiLSTM` ประสิทธิภาพดี --- ## 🚀 Demo Application ลองใช้งานโมเดลผ่าน Gradio Demo: https://huggingface.co/spaces/Dusit-P/Thai-Sentiment-GUI --- ## 📄 License Apache-2.0 --- ## 🙏 Acknowledgments - **WangchanBERTa**: airesearch/wangchanberta-base-att-spm-uncased - **Dataset**: wisesight_sentiment --- ## 📧 Contact หากมีคำถามหรือข้อเสนอแนะ กรุณาติดต่อผ่าน GitHub Issues