""" Taana-Tracker v1.0 ➜ (Hindi + English) Sarcasm Detector Model: XLM-RoBERTa (fine-tuned) | Framework: PyTorch | UI: Streamlit """ import streamlit as st import torch import pandas as pd from transformers import AutoTokenizer, AutoModelForSequenceClassification from pathlib import Path # ─────────────────────── PAGE CONFIG ───────────────────────────────────────── st.set_page_config( page_title="Taana-Tracker: AI Sarcasm Intelligence", page_icon="🔥", layout="wide", initial_sidebar_state="collapsed", ) # ─────────────────────── CSS ────────────────────────────────────────────────── st.markdown(""" """, unsafe_allow_html=True) # ─────────────────────── CONSTANTS ─────────────────────────────────────────── FINETUNED_REPO = "PrachiSandipkumar/Taana-Tracker-Sarcasm" BASE_MODEL = "xlm-roberta-base" MAX_LEN = 256 DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") # ─────────────────────── MODEL LOADING ─────────────────────────────────────── @st.cache_resource(show_spinner=False) def load_model(): """ Loads tokenizer from xlm-roberta-base and fine-tuned weights from the dedicated Hugging Face model repository. """ load_log = [] source = "base-NOT-fine-tuned" # 1. Tokenizer ➔ Always pull from xlm-roberta-base as per Cell 22 try: tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL) load_log.append("Tokenizer: Successfully loaded from xlm-roberta-base") except Exception as e: st.error(f"Failed to load base tokenizer: {e}") raise e # 2. Model ➔ Pull configuration and weights from your fine-tuned repo try: # Load model with weights from your fine-tuned repository model = AutoModelForSequenceClassification.from_pretrained( FINETUNED_REPO, num_labels=2 ) # Check and handle vocab size alignment safely tokenizer_vocab_size = len(tokenizer) model_vocab_size = model.get_input_embeddings().weight.shape[0] if tokenizer_vocab_size != model_vocab_size: load_log.append(f"Resizing embeddings: {model_vocab_size} → {tokenizer_vocab_size}") model.resize_token_embeddings(tokenizer_vocab_size) source = "fine-tuned" load_log.append(f"Model: Fine-tuned weights successfully loaded from HF Hub ({FINETUNED_REPO})") except Exception as e: # Fallback safeguard back to base if repo files fail to initialize load_log.append(f"Model: Fine-tuned load FAILED ➔ {e}") load_log.append("Model: WARNING ➔ Falling back to base xlm-roberta-base.") model = AutoModelForSequenceClassification.from_pretrained(BASE_MODEL, num_labels=2) source = "base-NOT-fine-tuned" model.to(DEVICE) model.eval() return model, tokenizer, source, load_log # ─────────────────────── NOTEBOOK FUNCTIONS ────────────────────────────────── def clean_text(text: str) -> str: """From notebook Cell 9.""" return text.strip() def predict_sarcasm(text: str, model, tokenizer): """ Exact replica of predict_sarcasm() from notebook Cell 42. Patched for stable single-sentence inference on Hugging Face. Returns: (predicted_label, confidence, sarcasm_pct, not_sarcasm_pct) """ text = clean_text(text) # FIX: Changed padding to 'max_length' to match your training pipeline shape inputs = tokenizer( text, padding="max_length", truncation=True, max_length=MAX_LEN, return_tensors="pt" ) input_ids = inputs["input_ids"].to(DEVICE) attention_mask = inputs["attention_mask"].to(DEVICE) model.eval() with torch.no_grad(): outputs = model(input_ids=input_ids, attention_mask=attention_mask) logits = outputs.logits probabilities = torch.softmax(logits, dim=1) predicted_class = torch.argmax(probabilities, dim=1).item() confidence = probabilities[0][predicted_class].item() label_map = {0: "Not Sarcastic", 1: "Sarcastic"} predicted_label = label_map[predicted_class] sarcasm_pct = probabilities[0][1].item() * 100 not_sarcasm_pct = probabilities[0][0].item() * 100 return predicted_label, confidence, sarcasm_pct, not_sarcasm_pct # ─────────────────────── HELPERS ───────────────────────────────────────────── def confidence_label(conf: float) -> str: if conf >= 0.90: return "High Confidence" if conf >= 0.70: return "Moderate" return "Low / Uncertain" def render_result(label, confidence, sarcasm_pct, not_sarcasm_pct): """Shared result block used in Tab 1 and Tab 2.""" conf_lbl = confidence_label(confidence) css_cls = "verdict-sarcastic" if label == "Sarcastic" else "verdict-neutral" icon = "😏" if label == "Sarcastic" else "🙂" st.markdown(f"""
{icon} {label}
Confidence: {confidence*100:.1f}%  ·  {conf_lbl}
""", unsafe_allow_html=True) # Probability bars (plain HTML ➜ no extra lib needed) st.markdown(f"""
Sarcastic   {sarcasm_pct:.1f}%
Not Sarcastic   {not_sarcasm_pct:.1f}%
""", unsafe_allow_html=True) # Model awareness warning (from requirements) if confidence < 0.90: st.markdown("""
⚠️ Model Awareness: This prediction may be less reliable for contextual or implicit (Hindi + English) sarcasm.
""", unsafe_allow_html=True) # ─────────────────────── MAIN ──────────────────────────────────────────────── def main(): with st.spinner("Loading model…"): model, tokenizer, source, load_log = load_model() if model is None: st.error("❌ Model failed to load.") st.stop() # ── Hero ────────────────────────────────────────────────────────────── st.markdown(f"""

🔥 Taana-Tracker

An AI that understands sarcasm ➜ and knows when it fails

XLM-RoBERTa PyTorch (Hindi + English) 94% Accuracy
""", unsafe_allow_html=True) # Sidebar ➜ model info with st.sidebar: st.markdown("## 🔥 Taana-Tracker") st.markdown("---") if source == "fine-tuned": st.success("✅ Fine-tuned model loaded") else: st.error("❌ Base model only ➜ predictions will be wrong!") with st.expander("🔍 Load Details", expanded=(source != "fine-tuned")): for line in load_log: st.caption(line) if source != "fine-tuned": st.warning(""" **Fix:** Your fine-tuned weights were not found. Add this to your notebook after Cell 45: ```python tokenizer.save_pretrained("/content/fine_tuned_roberta_model") ``` Then copy the full `fine_tuned_roberta_model/` folder next to `app.py`. """) st.markdown(f""" **Model:** XLM-RoBERTa-base **Task:** Binary Classification **Languages:** (Hindi + English) **Max Length:** {MAX_LEN} tokens **Test Accuracy:** 94% """) st.markdown("---") st.markdown("#### 🔗 Quick Links") col_g, col_h = st.columns(2) with col_g: if st.button("GitHub", use_container_width=True): st.markdown("[Repository link](https://github.com/10Prachi2006/Sarcasm-Intelligence-System.git)") with col_h: if st.button("HuggingFace", use_container_width=True): st.markdown("[HuggingFace model](https://huggingface.co/xlm-roberta-base)") st.markdown("") col_d, col_p = st.columns(2) with col_d: if st.button("Dataset", use_container_width=True): st.markdown("[Dataset file](https://github.com/10Prachi2006/Sarcasm-Intelligence-System.git)") with col_p: if st.button("Paper", use_container_width=True): st.markdown("[XLM-R paper](https://arxiv.org/abs/1911.02116)") st.caption("Taana-Tracker v1.0 · PyTorch + Streamlit") # ── Tabs ────────────────────────────────────────────────────────────── tab1, tab2, tab3, tab4, tab5 = st.tabs([ "🔍 Analyze", "⚡ Live Demo", "📊 Model Insights", "🧪 Stress Test", "📌 About", ]) # ══════════════════════════════════════════════════════════════════════ # TAB 1 ➜ ANALYZE # ══════════════════════════════════════════════════════════════════════ with tab1: st.markdown("### Drop your sentence… let's measure the taana level 🌶️") user_text = st.text_area( label="", placeholder='e.g. "Wah beta fail hoke bhi proud moment"', height=120, max_chars=256, key="tab1_input", ) char_count = len(user_text) st.caption(f"{char_count}/256 characters") if st.button("🎯 Detect Sarcasm", type="primary", use_container_width=True, key="tab1_btn"): if not user_text.strip(): st.warning("Please enter some text first.") else: with st.spinner("Analyzing…"): label, confidence, sarcasm_pct, not_sarcasm_pct = predict_sarcasm( user_text, model, tokenizer ) render_result(label, confidence, sarcasm_pct, not_sarcasm_pct) # Deterministic, no-model reasoning (only for exact matches) reasoning_examples = { "Wah beta fail hoke bhi proud moment": "Possible cue: Contradiction between failure ('fail hoke') and celebration ('proud moment'), a common sarcasm pattern.", "Bahut badiya, sab barbaad kar diya": "Possible cue: Positive praise followed by negative outcome ('barbaad kar diya').", "Oh, you're leaving at 6 PM? Half-day le liya kya aaj": "Possible cue: Rhetorical question used to mock normal workplace behavior.", "Aaj mausam bahut accha hai.": "Possible cue: Straightforward statement without strong sarcasm indicators.", "Thank you for your support!": "Possible cue: Direct appreciation with no obvious sarcastic indicators.", } if user_text in reasoning_examples: st.info(f"💡 {reasoning_examples[user_text]}") c1, c2, c3 = st.columns(3) with c1: st.metric("🎯 Verdict", label) with c2: st.metric("🧠 Confidence", f"{confidence*100:.1f}%") with c3: st.metric("📊 Conf. Level", confidence_label(confidence)) # ══════════════════════════════════════════════════════════════════════ # TAB 2 ➜ LIVE DEMO # ══════════════════════════════════════════════════════════════════════ with tab2: st.markdown("### ⚡ Live Demo ➜ Click any example to analyze") demo_examples = { "💼 Workplace Sarcasm": [ "Oh you came on time today, miracle hai kya?", "Oh, you're leaving at 6 PM? Half-day le liya kya aaj", "It's okay, you're only 2 hours late, no wories....", ], "🤖 Tech Sarcasm": [ "Bhai tera ML model itna fast hai ki output agle janam mein aayega!", "Wow, you used GenAI for a simple 'Hello World' code? Einstein ho kya", "Nice logic! Iska patent karwa le, dimaag kharch hone se bach jayega.", "Bhai kya speed hai... 10 min mein 1 epoch", "Model itna fast hai, result agle janam mein", ], "😈 Savage Hindi + English": [ "Wah beta fail hoke bhi proud moment", "Bahut badiya, sab barbaad kar diya", "Wah kya baat hai, fail ho gaya", "Kya baat hai, itni achi salary pe bhi khush nahi ho?", ], "🙂 Genuine (Not Sarcastic)": [ "Aaj mausam bahut accha hai.", "Mujhe tumhari madad chahiye thi.", "Thank you for your support!", "I really appreciate your help.", ], } selected_cat = st.selectbox("Pick a category:", list(demo_examples.keys())) for idx, ex in enumerate(demo_examples[selected_cat]): if st.button(f"📝 {ex}", key=f"demo_{selected_cat}_{idx}", use_container_width=True): with st.spinner("Analyzing…"): label, confidence, sarcasm_pct, not_sarcasm_pct = predict_sarcasm( ex, model, tokenizer ) st.markdown(f"**Analyzing:** `{ex}`") render_result(label, confidence, sarcasm_pct, not_sarcasm_pct) # ══════════════════════════════════════════════════════════════════════ # TAB 3 ➜ MODEL INSIGHTS # ══════════════════════════════════════════════════════════════════════ with tab3: st.markdown("### 📊 Model Insights & Performance") # Metrics from notebook classification_report (Cell 37) m1, m2, m3, m4 = st.columns(4) with m1: st.metric("✅ Accuracy", "94%") with m2: st.metric("🎯 Precision", "94%") with m3: st.metric("📡 Recall", "94%") with m4: st.metric("⚖️ F1-Score", "0.94") st.divider() # Per-class table from notebook classification_report st.markdown("#### Per-Class Performance (Test Set ➜ 1,439 samples)") st.dataframe(pd.DataFrame({ "Class": ["Not Sarcastic (0)", "Sarcastic (1)"], "Precision": [0.96, 0.93], "Recall": [0.91, 0.97], "F1-Score": [0.93, 0.95], "Support": [633, 806], }), use_container_width=True, hide_index=True) st.divider() # Confusion matrix from notebook (Cell 36) st.markdown("#### Confusion Matrix (Test Set)") st.markdown(""" | | Predicted: Not Sarcastic | Predicted: Sarcastic | |---------------------|:------------------------:|:--------------------:| | **Actual: Not Sarcastic** | 576 ✅ (TN) | 57 ❌ (FP) | | **Actual: Sarcastic** | 24 ❌ (FN) | 782 ✅(TP) | """) st.divider() # Dataset analysis from notebook st.markdown("#### Dataset Analysis") col_a, col_b = st.columns(2) with col_a: st.markdown("**Class Distribution**") st.dataframe(pd.DataFrame({ "Class": ["Sarcastic (1)", "Not Sarcastic (0)", "Total"], "Samples": [5544, 4049, 9593], "Share": ["57.8%", "42.2%", "100%"], }), use_container_width=True, hide_index=True) with col_b: st.markdown("**Data Splits (from notebook)**") st.dataframe(pd.DataFrame({ "Split": ["Train (70%)", "Validation (15%)", "Test (15%)"], "Samples": [6714, 1439, 1440], }), use_container_width=True, hide_index=True) st.divider() st.markdown("#### 🔑 Key Observations") st.markdown("""
""", unsafe_allow_html=True) # ══════════════════════════════════════════════════════════════════════ # TAB 4 ➜ STRESS TEST # ══════════════════════════════════════════════════════════════════════ with tab4: st.markdown("### 🧪 Stress Test ➜ Where the Model Fails") st.markdown("""
🔬 Transparency: A model that hides its failures is a dangerous model. These are cases from the notebook (Cell 43) marked as #--> FAILED.
""", unsafe_allow_html=True) # Failure cases from notebook Cell 43 ➜ marked #--> FAILED failure_cases = [ { "text": "Bhai tera ML model itna fast hai ki output agle janam mein aayega!", "expected": "Sarcastic", "reason": "Indirect metaphor ('next birth = never'). Model misses the cultural exaggeration.", }, { "text": "Please, thoda aur slow chalao car. Cycle wale bhi humein overtake karke jaa raha hai", "expected": "Sarcastic", "reason": "Context-dependent irony ➜ meaning flips only when you know slow car + cycles overtaking = embarrassing.", }, { "text": "Oh, you're leaving at 6 PM? Half-day le liya kya aaj", "expected": "Sarcastic", "reason": "Workplace irony needing domain context. Question form lowers model confidence.", }, { "text": "Meri MLOps knowledge aur mera bank balance—dono hi zero hain", "expected": "Sarcastic", "reason": "Self-deprecating humor stated plainly ➜ model may read it as factual.", }, ] if st.button("🧪 Run All Failure Cases", type="primary", use_container_width=True): correct = 0 for i, case in enumerate(failure_cases, 1): with st.spinner(f"Running case {i}/{len(failure_cases)}…"): label, confidence, _, _ = predict_sarcasm(case["text"], model, tokenizer) got_it_right = label == case["expected"] if got_it_right: correct += 1 icon = "✅" if got_it_right else "❌" conf_lbl = confidence_label(confidence) st.markdown(f"""
{icon} Case {i}: "{case['text']}"

Expected: {case['expected']}  |  Got: {label}  |  Confidence: {confidence*100:.1f}% ({conf_lbl})

🔬 Why it's hard: {case['reason']}
""", unsafe_allow_html=True) st.divider() st.metric("Score on Hard Cases", f"{correct}/{len(failure_cases)}") # ══════════════════════════════════════════════════════════════════════ # TAB 5 ➜ ABOUT # ══════════════════════════════════════════════════════════════════════ with tab5: st.markdown("### 📌 About Taana-Tracker") col1, col2 = st.columns(2) with col1: st.markdown("#### System Description") st.markdown(""" **Taana-Tracker** is a fine-tuned XLM-RoBERTa model for sarcasm detection in (Hindi + English) text. Trained on ~9,593 real-world Hindi + English samples from social media and news comments. """) with col2: st.markdown("#### Tech Stack") st.dataframe(pd.DataFrame({ "Component": ["Model", "Framework", "UI", "Language"], "Technology": ["XLM-RoBERTa-base (fine-tuned)", "PyTorch + HuggingFace Transformers", "Streamlit", "Python 3.10+"], }), use_container_width=True, hide_index=True) st.divider() st.markdown("#### ⚠️ Limitations") st.markdown("""
""", unsafe_allow_html=True) st.divider() st.markdown("#### 🔗 References") st.markdown(""" - [XLM-RoBERTa Paper ➜ Conneau et al. 2019](https://arxiv.org/abs/1911.02116) - [HuggingFace `xlm-roberta-base`](https://huggingface.co/xlm-roberta-base) - [HuggingFace Transformers Docs](https://huggingface.co/docs/transformers) """) if __name__ == "__main__": main()