|
Download README.md from yothinS/Aaron: direct link, hf CLI and curl.
- Browser
- Download file 8.3 kB
-
https://huggingface.co/yothinS/Aaron/resolve/main/README.md
- Command line
-
hf download hf://yothinS/Aaron/README.md
-
curl -L -o README.md https://huggingface.co/yothinS/Aaron/resolve/main/README.md
8.3 kB
| language: | |
| - th | |
| license: mit | |
| base_model: jhu-clsp/mmBERT-base | |
| tags: | |
| - system-1 | |
| - system one | |
| - jev | |
| - typed-decisions | |
| - modernbert | |
| - calibration | |
| - customer-support | |
| - classification | |
| - business | |
| datasets: | |
| - yothinS/thai-customer-support | |
| pipeline_tag: text-classification | |
| # Thai Jev: System 1 Decision Model (mmBERT) | |
| An implementation of the **Jev System 1** decision architecture built on top of [`jhu-clsp/mmBERT-base`](https://huggingface.co/jhu-clsp/mmBERT-base) and fine-tuned on [`yothinS/thai-customer-support`](https://huggingface.co/datasets/yothinS/thai-customer-support). | |
| Unlike standard classifiers tied to fixed, rigid output heads, this model evaluates **dynamic runtime schemas** in a single non-autoregressive forward pass. It supports three typed decision primitives: | |
| * **`Choice`**: Categorical routing over arbitrary dynamic candidate strings with calibrated softmax distributions. | |
| * **`Noul`**: Calibrated binary boolean verification ([0.0, 1.0]) for arbitrary hypotheses. | |
| * **`Score`**: Continuous scalar evaluation ([0.0, 1.0]) guided by custom natural-language rubrics. | |
| --- | |
| ## Model Architecture & Training | |
| The architecture couples a shared multilingual ModernBERT transformer backbone with three specialized projection heads operating on mean-pooled token representations: | |
| ```text | |
| [State + Candidate / Rubric Prompt] | |
| │ | |
| ┌────────▼────────┐ | |
| │ mmBERT-base │ (ModernBERT encoder) | |
| └────────┬────────┘ | |
| │ Mean Pooling | |
| ┌────────▼────────┐ | |
| │ d_model (768) │ | |
| └────────┬────────┘ | |
| ┌─────────┼─────────┐ | |
| ▼ ▼ ▼ | |
| Choice Noul Score | |
| Head Head Head | |
| (GELU+MLP)(GELU+MLP)(GELU+Sigmoid) | |
| ``` | |
| ### Calibrated Multi-Task Loss | |
| To prevent overconfidence and provide well-calibrated probabilities for downstream routing agents, training combines classification losses with a **Brier score penalty**: | |
| * **Brier Weight:** `0.5` | |
| * **Tail Reservation Strategy:** A fixed reserve buffer (`TAIL_RESERVE = 72`) ensures ticket contexts are clipped *before* prompts and candidate options are appended, preventing candidate truncation. | |
| --- | |
| ## Training Hyperparameters | |
| | Parameter | Setting | | |
| | :--- | :--- | | |
| | **Base Model** | `jhu-clsp/mmBERT-base` | | |
| | **Dataset** | `yothinS/thai-customer-support` | | |
| | **Max Sequence Length** | 192 tokens | | |
| | **Epochs** | 3 | | |
| | **Optimizer** | AdamW ($\text{lr} = 2\times 10^{-5}$, $\text{weight decay} = 0.01$) | | |
| | **Learning Rate Schedule** | Linear warmup with decay (Warmup ratio: `0.06`) | | |
| | **Gradient Accumulation** | 8 steps | | |
| | **Precision** | Mixed Precision (AMP `torch.autocast`) | | |
| | **Gradient Clipping** | Max norm 1.0 | | |
| --- | |
| ## Input Formats | |
| Each decision type formats the input string prior to tokenization: | |
| * **Choice candidate format:** | |
| `สถานะ: {state} | คำถาม: {question} | ตัวเลือก: {option}` | |
| * **Noul hypothesis format:** | |
| `สถานะ: {state} | สมมติฐาน: {hypothesis}` | |
| * **Score rubric format:** | |
| `สถานะ: {state} | เกณฑ์ประเมิน: {rubric}` | |
| --- | |
| ## Quickstart & Inference | |
| ```python | |
| import os | |
| import torch | |
| import torch.nn as nn | |
| from transformers import AutoModel, AutoTokenizer | |
| from safetensors.torch import load_file | |
| from huggingface_hub import hf_hub_download | |
| # 1. Define Model Class | |
| class aaronSystemOne(nn.Module): | |
| def __init__(self, backbone_id="jhu-clsp/mmBERT-base"): | |
| super().__init__() | |
| self.encoder = AutoModel.from_pretrained(backbone_id) | |
| d_model = self.encoder.config.hidden_size | |
| def make_head(sigmoid=False): | |
| layers = [ | |
| nn.Linear(d_model, d_model // 2), | |
| nn.GELU(), | |
| nn.Dropout(0.1), | |
| nn.Linear(d_model // 2, 1) | |
| ] | |
| if sigmoid: | |
| layers.append(nn.Sigmoid()) | |
| return nn.Sequential(*layers) | |
| self.choice_head = make_head() | |
| self.noul_head = make_head() | |
| self.score_head = make_head(sigmoid=True) | |
| def extract_embedding(self, input_ids, attention_mask): | |
| out = self.encoder(input_ids=input_ids, attention_mask=attention_mask) | |
| mask = attention_mask.unsqueeze(-1).float() | |
| return (out.last_hidden_state * mask).sum(dim=1) / mask.sum(dim=1).clamp(min=1e-9) | |
| def score_choices(self, input_ids, attention_mask): | |
| return self.choice_head(self.extract_embedding(input_ids, attention_mask)).squeeze(-1) | |
| def score_noul(self, input_ids, attention_mask): | |
| return self.noul_head(self.extract_embedding(input_ids, attention_mask)).squeeze(-1) | |
| def score_metric(self, input_ids, attention_mask): | |
| return self.score_head(self.extract_embedding(input_ids, attention_mask)).squeeze(-1) | |
| # 2. Load Pipeline and Engine | |
| # Note: Ensure modeling_jev.py is in your working directory | |
| from modeling_jev import aaronEngine, Choice, Noul, Score | |
| MODEL_ID = "yothinS/Aaron" | |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) | |
| model = aaronSystemOne(backbone_id=MODEL_ID) | |
| # Support both local folder and remote Hugging Face repository | |
| weights_path = f"{MODEL_ID}/model.safetensors" | |
| if not os.path.exists(weights_path): | |
| weights_path = hf_hub_download(repo_id=MODEL_ID, filename="model.safetensors") | |
| model.load_state_dict(load_file(weights_path)) | |
| model.to(device) | |
| model.eval() | |
| client = aaronEngine(model, tokenizer, device=device, max_length=192) | |
| client.warmup() | |
| # 3. Evaluate an Unseen Ticket | |
| state = "ลูกค้าโวยวายหนักมาก บอกว่าบัตรเครดิตถูกชาร์จซ้ำซ้อน 3 รอบและขู่จะฟ้อง สคบ. ถ้าไม่ได้เงินคืนภายในวันนี้" | |
| decision = client.judge(state, { | |
| "route_department": Choice( | |
| question="คำร้องนี้ควรส่งต่อไปยังทีมงานใด?", | |
| options=["ทีมกฎหมายและข้อพิพาท", "ทีมการเงินและบิลลิ่ง", "ทีมวิศวกรรมไอที"] | |
| ), | |
| "legal_threat": Noul("ลูกค้ามีการข่มขู่ว่าจะดำเนินการทางกฎหมายใช่หรือไม่"), | |
| "churn_risk": Score("ประเมินระดับความเสี่ยงที่ลูกค้าจะเลิกใช้บริการ (Churn Risk)") | |
| }) | |
| print(decision) | |
| ``` | |
| ### Example Output | |
| ```json | |
| { | |
| "route_department": { | |
| "type": "Choice", | |
| "value": "ทีมการเงินและบิลลิ่ง", | |
| "probability": 0.8842, | |
| "confidence": 0.7931, | |
| "distribution": { | |
| "ทีมการเงินและบิลลิ่ง": 0.8842, | |
| "ทีมกฎหมายและข้อพิพาท": 0.0911, | |
| "ทีมวิศวกรรมไอที": 0.0247 | |
| } | |
| }, | |
| "legal_threat": { | |
| "type": "Noul", | |
| "value": true, | |
| "probability": 0.9412 | |
| }, | |
| "churn_risk": { | |
| "type": "Score", | |
| "value": 0.8625 | |
| } | |
| } | |
| ``` | |
| --- | |
| ## Intended Uses & Limitations | |
| ### Best Use Cases | |
| * **Triage & Intent Routing:** Fast dispatching of incoming Thai customer tickets to target departments. | |
| * **Guardrails & Escalation Checks:** Instant binary verification of high-risk conditions (e.g., fraud claims, legal actions, VIP accounts). | |
| * **System 1 Filtering:** Operating as a high-throughput, low-latency filter in front of slower autoregressive LLMs (System 2). | |
| ### Limitations | |
| * **Maximum Context Length:** The default sequence length is capped at 192 tokens. Extremely long conversations will have older turns truncated. | |
| * **Domain Specificity:** The fine-tuning target represents customer support operations. Performance on generic open-domain tasks may be limited without further fine-tuning. | |
| * **Scoring Subjectivity:** Score predictions depend directly on how clearly the rubric text reflects patterns represented during training. |