Spaces:
Running on Zero
Running on Zero
| """ | |
| models/roberta.py | |
| ================== | |
| RoBERTa via AutoModelForMultipleChoice (05_RoBERTa.ipynb). | |
| Unlike DeBERTa (5 independent binary passes), RoBERTa sees all 5 options | |
| together and produces one joint softmax over the 5 choices — the | |
| multiple-choice head is built into `AutoModelForMultipleChoice`, no | |
| custom classification head needed. | |
| """ | |
| from pathlib import Path | |
| from typing import List | |
| import numpy as np | |
| import torch | |
| from transformers import AutoTokenizer, AutoModelForMultipleChoice | |
| from ..config import ROBERTA_CFG | |
| from ..preprocessing import build_roberta_inputs_single | |
| from ..utils import get_device | |
| class RoBERTaModel: | |
| """Convenience wrapper: loads tokenizer + checkpoint, predicts option probs.""" | |
| def __init__(self, checkpoint_path: Path = None, device=None): | |
| self.checkpoint_path = Path(checkpoint_path or ROBERTA_CFG["checkpoint_path"]) | |
| self.device = device or get_device() | |
| self.tokenizer = None | |
| self.model: AutoModelForMultipleChoice = None | |
| def load(self) -> "RoBERTaModel": | |
| self.tokenizer = AutoTokenizer.from_pretrained(ROBERTA_CFG["model_name"]) | |
| self.model = AutoModelForMultipleChoice.from_pretrained( | |
| ROBERTA_CFG["model_name"] | |
| ).to(self.device) | |
| self.model.load_state_dict(torch.load(self.checkpoint_path, map_location=self.device)) | |
| self.model.eval() | |
| return self | |
| def predict_proba_single(self, prompt: str, options: List[str]) -> np.ndarray: | |
| """Returns a (5,) joint-softmax probability array in A-E order.""" | |
| if self.model is None: | |
| self.load() | |
| prompts_x5, options_x5 = build_roberta_inputs_single(prompt, options) | |
| enc = self.tokenizer( | |
| prompts_x5, options_x5, | |
| max_length=ROBERTA_CFG["max_len"], | |
| truncation=True, | |
| padding="max_length", | |
| return_tensors="pt", | |
| ) | |
| # Reshape to (batch=1, num_choices=5, seq_len) as AutoModelForMultipleChoice expects | |
| input_ids = enc["input_ids"].unsqueeze(0).to(self.device) | |
| attention_mask = enc["attention_mask"].unsqueeze(0).to(self.device) | |
| out = self.model(input_ids=input_ids, attention_mask=attention_mask) | |
| proba = torch.softmax(out.logits, dim=1).cpu().numpy()[0] | |
| return proba | |
| def predict_top3_single(self, prompt: str, options: List[str]) -> List[str]: | |
| from ..config import REVERSE_MAP | |
| proba = self.predict_proba_single(prompt, options) | |
| top3_idx = np.argsort(proba)[-3:][::-1] | |
| return [REVERSE_MAP[i] for i in top3_idx] |