""" models.py — Hugging Face Hub edition ======================================= This version loads your three fine-tuned BanglaBERT models from the Hugging Face Hub (instead of Google Drive), which is what makes permanent hosting on Hugging Face Spaces possible. app.py and the frontend are unchanged. You only need to edit TWO things below: 1. MODEL_IDS -> your three model repositories on the Hub 2. LABELS -> the class names IN THE EXACT ORDER your model outputs them """ from __future__ import annotations import os import torch import torch.nn.functional as F from transformers import AutoTokenizer, AutoModelForSequenceClassification # BanglaBERT's authors recommend normalizing Bangla text before inference. try: from normalizer import normalize except Exception: def normalize(text): return text # Only needed if your model repositories are PRIVATE. On Hugging Face Spaces # you set this by adding a secret named HF_TOKEN in the Space settings. # For PUBLIC model repositories you can ignore this entirely. HF_TOKEN = os.environ.get("HF_TOKEN") # Fallback tokenizer if a repo somehow lacks its own tokenizer files. BASE_BANGLABERT = "csebuetnlp/banglabert" # =========================================================================== # 1) YOUR THREE MODELS ON THE HUGGING FACE HUB. # Replace YOUR_USERNAME with your Hugging Face username. These must match # the repository names you created when uploading (see the guide, Part 2). # =========================================================================== MODEL_IDS = { "ai_detection": "Istihad/banglabert-ai-detection", "hate_speech": "Istihad/banglabert-hate-speech", "sentiment": "Istihad/banglabert-emotion", } # =========================================================================== # 2) LABELS for each task, IN THE EXACT ORDER the model outputs them # (the class your model calls 0 goes first, then 1, and so on). # If a prediction ever looks wrong/flipped, fix the order here. # =========================================================================== LABELS = { "ai_detection": ["Human-written", "AI-generated"], "hate_speech": ["Not hateful", "Hate / Offensive"], "sentiment": ["Anger", "Disgust", "Fear", "Joy", "Sadness", "Surprise"], } # Human-readable names — also used to build the dropdown on the website. TASKS = { "hate_speech": "Detect Bangla hate speech", "sentiment": "Detect emotion / mood", "ai_detection": "Detect AI-generated Bangla text", } DEVICE = "cuda" if torch.cuda.is_available() else "cpu" class HubModel: """Loads one fine-tuned BanglaBERT model from the Hub and runs predictions.""" def __init__(self, task_key: str): self.task_key = task_key self.repo = MODEL_IDS[task_key] self.labels = LABELS[task_key] self.tokenizer = None self.model = None self._loaded = False def ensure_loaded(self): if self._loaded: return try: self.tokenizer = AutoTokenizer.from_pretrained(self.repo, token=HF_TOKEN) except Exception: self.tokenizer = AutoTokenizer.from_pretrained(BASE_BANGLABERT, token=HF_TOKEN) self.model = AutoModelForSequenceClassification.from_pretrained( self.repo, token=HF_TOKEN ) self.model.to(DEVICE) self.model.eval() self._loaded = True def predict(self, text: str) -> dict: self.ensure_loaded() clean = normalize(text) inputs = self.tokenizer( clean, return_tensors="pt", truncation=True, max_length=512, padding=True ).to(DEVICE) with torch.no_grad(): logits = self.model(**inputs).logits probs = F.softmax(logits, dim=-1)[0].cpu().tolist() best_idx = int(max(range(len(probs)), key=lambda i: probs[i])) details = {} for i, p in enumerate(probs): name = self.labels[i] if i < len(self.labels) else f"class_{i}" details[name] = round(p, 4) best_label = ( self.labels[best_idx] if best_idx < len(self.labels) else f"class_{best_idx}" ) return {"label": best_label, "score": float(probs[best_idx]), "details": details} # One model object per task (weights download from the Hub on first use). _REGISTRY = {key: HubModel(key) for key in MODEL_IDS} def run_task(task_key: str, text: str) -> dict: """Entry point used by app.py.""" if task_key not in _REGISTRY: raise ValueError(f"Unknown task: {task_key!r}") return _REGISTRY[task_key].predict(text)