import streamlit as st import pandas as pd import numpy as np from transformers import pipeline from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer import plotly.express as px from wordcloud import WordCloud import matplotlib.pyplot as plt from collections import Counter import re from datetime import datetime import time # Page Config st.set_page_config(page_title="Sentiment Analytics Pro", page_icon="📊", layout="wide") # Custom Styles st.markdown(""" """, unsafe_allow_html=True) # ------------------------------------------------------------------ # CACHED MODEL LOADING # ------------------------------------------------------------------ @st.cache_resource def load_models(): try: st.info("🔄 Loading AI models... This may take a few minutes on first run.") # English Models (Ensemble) roberta = pipeline( "sentiment-analysis", model="cardiffnlp/twitter-roberta-base-sentiment-latest", tokenizer="cardiffnlp/twitter-roberta-base-sentiment-latest" ) distilbert = pipeline( "sentiment-analysis", model="distilbert-base-uncased-finetuned-sst-2-english" ) vader = SentimentIntensityAnalyzer() # Use a more stable multilingual model multilingual = pipeline( "sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment" ) st.success("✅ All models loaded successfully!") return roberta, distilbert, vader, multilingual except Exception as e: st.error(f"❌ Error loading models: {str(e)}") # Return fallback models try: vader = SentimentIntensityAnalyzer() distilbert = pipeline("sentiment-analysis") return None, distilbert, vader, None except: return None, None, SentimentIntensityAnalyzer(), None # Load models with progress indication with st.spinner("Initializing AI models..."): roberta_model, distilbert_model, vader_model, multi_model = load_models() # Check if essential models loaded if vader_model is None: st.error("❌ Critical error: Failed to load essential models. Please refresh the page.") st.stop() # ------------------------------------------------------------------ # HELPER FUNCTIONS # ------------------------------------------------------------------ def clean_text(text): text = text.lower() text = re.sub(r'http\S+', '', text) # Remove URLs text = re.sub(r'[^\w\s]', '', text) # Remove punctuation return text def get_wordcloud(text): try: wc = WordCloud( width=800, height=400, background_color='white', max_words=100, colormap='viridis' ).generate(text) fig, ax = plt.subplots(figsize=(10, 5)) ax.imshow(wc, interpolation='bilinear') ax.axis('off') return fig except Exception as e: st.error(f"WordCloud error: {e}") return None # ------------------------------------------------------------------ # CORE ANALYSIS LOGIC # ------------------------------------------------------------------ def analyze_english(text): try: # Ensure text is not empty if not text.strip(): return { 'verdict': 'neutral', 'confidence': 'Low (No text)', 'breakdown': {'Error': 'No text provided'}, 'scores': {'Error': 0.0} } results = {} # 1. RoBERTa (if available) if roberta_model is not None: try: rob_out = roberta_model(text[:512])[0] rob_label = rob_out['label'] if rob_label == 'LABEL_0': rob_sent = 'negative' elif rob_label == 'LABEL_1': rob_sent = 'neutral' else: rob_sent = 'positive' results['roberta'] = (rob_sent, rob_out['score']) except Exception as e: st.warning(f"RoBERTa model unavailable: {e}") # 2. VADER (always available) vader_out = vader_model.polarity_scores(text) compound = vader_out['compound'] if compound >= 0.05: vader_sent = 'positive' elif compound <= -0.05: vader_sent = 'negative' else: vader_sent = 'neutral' results['vader'] = (vader_sent, abs(compound)) # 3. DistilBERT (if available) if distilbert_model is not None: try: bert_out = distilbert_model(text[:512])[0] bert_sent = bert_out['label'].lower() results['distilbert'] = (bert_sent, bert_out['score']) except Exception as e: st.warning(f"DistilBERT model unavailable: {e}") # If only VADER is available if len(results) == 1 and 'vader' in results: return { 'verdict': vader_sent, 'confidence': 'Medium (VADER only)', 'breakdown': {'VADER': vader_sent}, 'scores': {'VADER': abs(compound)} } # Consensus Logic (Voting) votes = [sent for sent, score in results.values()] count = Counter(votes) winner, vote_count = count.most_common(1)[0] # Conflict Detection if len(count) == len(results) or vote_count == 1: final_verdict = "ambiguous" confidence = f"Low ({vote_count}/{len(results)} agreement)" else: final_verdict = winner confidence = "High" if vote_count == len(results) else "Medium" return { 'verdict': final_verdict, 'confidence': confidence, 'breakdown': {model: sent for model, (sent, score) in results.items()}, 'scores': {model: score for model, (sent, score) in results.items()} } except Exception as e: st.error(f"Analysis error: {e}") return None def analyze_multilingual(text): try: if not text.strip(): return { 'verdict': 'neutral', 'confidence': 'Low (No text)', 'breakdown': {'Error': 'No text provided'}, 'scores': {'Error': 0.0} } # Use multilingual model if available, otherwise fallback to English analysis if multi_model is not None: result = multi_model(text[:512])[0] label_raw = str(result['label']) score = result['score'] # Map star ratings to sentiment (nlptown model uses 1-5 stars) if '1' in label_raw or '2' in label_raw: sentiment = "negative" elif '3' in label_raw: sentiment = "neutral" else: # 4 or 5 stars sentiment = "positive" return { 'verdict': sentiment, 'confidence': f"{score:.2f}", 'breakdown': {'Multilingual BERT': f"{sentiment.title()} ({score:.2f})"}, 'scores': {'Model Confidence': score} } else: # Fallback to English analysis st.info("🌐 Multilingual model unavailable, using English analysis...") return analyze_english(text) except Exception as e: st.error(f"Multilingual analysis error: {e}") # Fallback to English analysis return analyze_english(text) # ------------------------------------------------------------------ # UI LAYOUT # ------------------------------------------------------------------ # Sidebar st.sidebar.title("⚙️ Configuration") language = st.sidebar.selectbox("Select Language", ["English", "Hindi (हिन्दी)", "Hinglish (Mixed)"]) mode = st.sidebar.selectbox("Analysis Mode", ["Real-time Analysis", "Batch Processing"]) st.sidebar.markdown("---") st.sidebar.info(""" **Model Status:** - ✅ VADER: Available - 🤖 RoBERTa: {'✅' if roberta_model else '❌'} - 🚀 DistilBERT: {'✅' if distilbert_model else '❌'} - 🌐 Multilingual: {'✅' if multi_model else '❌'} """) st.title("🧠 Sentiment Analytics Pro") st.markdown("Advanced AI-powered sentiment analysis across multiple languages") st.markdown("---") if mode == "Real-time Analysis": # Dynamic Input Box if language == "Hindi (हिन्दी)": placeholder_text = "यहाँ अपना टेक्स्ट लिखें (उदा. मुझे यह उत्पाद पसंद आया)" label_text = "Enter Hindi Text:" elif language == "Hinglish (Mixed)": placeholder_text = "Type in Hinglish (e.g., Product bahut achha hai but delivery slow thi)" label_text = "Enter Hinglish Text:" else: placeholder_text = "Type your text here... (e.g., I love this product! Amazing quality.)" label_text = "Enter English Text:" user_input = st.text_area(label_text, height=150, placeholder=placeholder_text) if st.button("🚀 Analyze Sentiment", type="primary", use_container_width=True): if not user_input.strip(): st.warning("⚠️ Please enter some text first.") else: with st.spinner("🔮 Analyzing sentiment with AI models..."): start_time = time.time() # Routing Logic if language == "English": result = analyze_english(user_input) else: result = analyze_multilingual(user_input) if result is None: st.error("❌ Analysis failed. Please try again with different text.") st.stop() latency = time.time() - start_time # 1. Main Verdict Display st.markdown("### 📊 Analysis Results") col1, col2, col3 = st.columns(3) color_map = { 'positive': '#10B981', 'negative': '#EF4444', 'neutral': '#F59E0B', 'ambiguous': '#6B7280' } verdict_color = color_map.get(result['verdict'], '#3B82F6') with col1: st.markdown(f"""
Final Verdict
Confidence Level
Processing Time