""" Complete Gradio App for Emotion Detection Deploy to Hugging Face Spaces """ import os # --- Runtime repair to fix Hugging Face dependency override --- os.system( "pip install -U gradio==4.44.0 huggingface_hub==0.20.3 transformers==4.39.3 --no-cache-dir > /dev/null" ) # --------------------------------------------------------------------- import gradio as gr import torch import torch.nn as nn from transformers import RobertaTokenizer, RobertaModel from huggingface_hub import hf_hub_download import json import numpy as np # ==================== Model Architecture ==================== class RobertaForMultiLabelClassification(nn.Module): def __init__(self, model_name, num_labels, dropout_rate=0.3, use_mean_pooling=True): super().__init__() self.roberta = RobertaModel.from_pretrained(model_name) self.use_mean_pooling = use_mean_pooling hidden_size = self.roberta.config.hidden_size self.dropout1 = nn.Dropout(dropout_rate) self.fc1 = nn.Linear(hidden_size, hidden_size // 2) self.relu = nn.ReLU() self.dropout2 = nn.Dropout(dropout_rate) self.fc2 = nn.Linear(hidden_size // 2, num_labels) def mean_pooling(self, token_embeddings, attention_mask): input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float() sum_embeddings = torch.sum(token_embeddings * input_mask_expanded, 1) sum_mask = torch.clamp(input_mask_expanded.sum(1), min=1e-9) return sum_embeddings / sum_mask def forward(self, input_ids, attention_mask): outputs = self.roberta(input_ids, attention_mask=attention_mask) if self.use_mean_pooling: pooled_output = self.mean_pooling(outputs.last_hidden_state, attention_mask) else: pooled_output = outputs.pooler_output x = self.dropout1(pooled_output) x = self.fc1(x) x = self.relu(x) x = self.dropout2(x) logits = self.fc2(x) return logits # ==================== Emotion Predictor ==================== class EmotionPredictor: def __init__(self, model_name="Lakssssshya/roberta-large-goemotions"): print(f"🔄 Loading model: {model_name}") self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') print(f"📍 Using device: {self.device}") self.tokenizer = RobertaTokenizer.from_pretrained(model_name) # Load config config_path = hf_hub_download(repo_id=model_name, filename="config.json") with open(config_path, 'r') as f: config = json.load(f) self.num_labels = config['num_labels'] self.model = RobertaForMultiLabelClassification( model_name='roberta-large', num_labels=self.num_labels, dropout_rate=config.get('dropout_rate', 0.3), use_mean_pooling=config.get('use_mean_pooling', True) ) # Load weights weights_path = hf_hub_download(repo_id=model_name, filename="pytorch_model.bin") state_dict = torch.load(weights_path, map_location=self.device) self.model.load_state_dict(state_dict) self.model.to(self.device) self.model.eval() # Load thresholds try: thresholds_path = hf_hub_download(repo_id=model_name, filename="optimal_thresholds.json") with open(thresholds_path, 'r') as f: self.thresholds = np.array(json.load(f)) except: self.thresholds = np.ones(self.num_labels) * 0.5 self.emotion_labels = [ 'admiration', 'amusement', 'anger', 'annoyance', 'approval', 'caring', 'confusion', 'curiosity', 'desire', 'disappointment', 'disapproval', 'disgust', 'embarrassment', 'excitement', 'fear', 'gratitude', 'grief', 'joy', 'love', 'nervousness', 'optimism', 'pride', 'realization', 'relief', 'remorse', 'sadness', 'surprise', 'neutral' ] print("✅ Model loaded successfully!") def predict(self, text, top_k=5): inputs = self.tokenizer(text, return_tensors='pt', truncation=True, max_length=128, padding=True) inputs = {k: v.to(self.device) for k, v in inputs.items()} with torch.no_grad(): logits = self.model(input_ids=inputs['input_ids'], attention_mask=inputs['attention_mask']) probs = torch.sigmoid(logits).cpu().numpy()[0] predictions = (probs > self.thresholds).astype(int) predicted_emotions = [self.emotion_labels[i] for i in range(len(predictions)) if predictions[i] == 1] top_indices = np.argsort(probs)[::-1][:top_k] top_emotions = [ { 'emotion': self.emotion_labels[idx], 'score': float(probs[idx]), 'predicted': bool(predictions[idx]) } for idx in top_indices ] return { 'text': text, 'emotions': predicted_emotions, 'top_emotions': top_emotions } # ==================== Initialize Predictor ==================== print("🚀 Initializing Emotion Predictor...") predictor = EmotionPredictor() # ==================== Gradio Interface Functions ==================== def predict_emotions(text): """Main prediction function for Gradio""" if not text.strip(): return "⚠️ Please enter some text to analyze!" result = predictor.predict(text, top_k=5) # Format output as markdown output = "## 🎯 Detected Emotions\n\n" if result['emotions']: output += ", ".join([f"**{e}**" for e in result['emotions']]) else: output += "**neutral**" output += "\n\n---\n\n## 📊 Top 5 Emotions by Confidence\n\n" for emotion_data in result['top_emotions']: bar_length = int(emotion_data['score'] * 20) bar = "█" * bar_length + "░" * (20 - bar_length) check = "✓" if emotion_data['predicted'] else " " score_pct = emotion_data['score'] * 100 output += f"{check} **{emotion_data['emotion']}** `{bar}` {score_pct:.1f}%\n\n" return output # ==================== Gradio Interface ==================== with gr.Blocks(theme=gr.themes.Soft()) as demo: gr.Markdown(""" # 🎭 Emotion Detector ### Multi-label emotion classification using RoBERTa-Large GoEmotions This model detects 28 different emotions from your text using state-of-the-art NLP. """) with gr.Row(): with gr.Column(): text_input = gr.Textbox( label="📝 Enter your text", placeholder="Type something here... (e.g., 'I'm so proud and excited about this achievement!')", lines=5 ) analyze_btn = gr.Button("🔍 Analyze Emotions", variant="primary", size="lg") gr.Markdown("### 💡 Try these examples:") with gr.Row(): example1 = gr.Button("🏆 Achievement", size="sm") example2 = gr.Button("😔 Regret", size="sm") example3 = gr.Button("😲 Surprise", size="sm") with gr.Row(): example4 = gr.Button("🙏 Gratitude", size="sm") example5 = gr.Button("🤢 Disgust", size="sm") example6 = gr.Button("❤️ Love", size="sm") with gr.Column(): output = gr.Markdown(label="Results") # Button actions analyze_btn.click(fn=predict_emotions, inputs=text_input, outputs=output) # Example buttons example1.click( lambda: "I'm so proud and excited about this achievement!", outputs=text_input ) example2.click( lambda: "I really regret saying that earlier.", outputs=text_input ) example3.click( lambda: "I can't believe this happened!", outputs=text_input ) example4.click( lambda: "Thank you so much for all your help and support!", outputs=text_input ) example5.click( lambda: "This is absolutely disgusting and infuriating!", outputs=text_input ) example6.click( lambda: "I love spending time with you!", outputs=text_input ) gr.Markdown(""" --- ### 📊 About This Model - **Model**: [Lakssssshya/roberta-large-goemotions](https://huggingface.co/Lakssssshya/roberta-large-goemotions) - **Architecture**: RoBERTa-Large with optimized thresholds - **Training**: Focal loss + per-label threshold optimization - **Emotions**: 28 labels from GoEmotions dataset Built with ❤️ using Hugging Face Transformers """) # ==================== Launch ==================== if __name__ == "__main__": demo.launch()