Spaces:
Sleeping
Sleeping
| """ | |
| 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() | |