| import os, re, json, time, warnings, subprocess, signal |
| warnings.filterwarnings("ignore") |
|
|
| import numpy as np |
| import gradio as gr |
| import matplotlib |
| matplotlib.use("Agg") |
| import matplotlib.pyplot as plt |
| import torch |
| import torch.nn.functional as F |
| from transformers import AutoTokenizer, AutoModelForSequenceClassification |
| from huggingface_hub import hf_hub_download |
|
|
| print("APP STARTED") |
|
|
| |
|
|
| MODEL_PATH = "" |
| HF_MODEL_REPO = "Jaykumardas/Multilingual_News_Model" |
|
|
| |
| def load_model_and_labels(): |
| model_source = HF_MODEL_REPO if HF_MODEL_REPO else MODEL_PATH |
| print(f"[INFO] Loading from: {model_source}") |
|
|
| try: |
| tokenizer = AutoTokenizer.from_pretrained(model_source) |
| print("[INFO] Tokenizer loaded OK") |
| except Exception as e: |
| raise RuntimeError(f"Tokenizer load failed: {e}") |
|
|
| id2label = None |
| lmap = os.path.join(model_source, "label_map.json") |
| try: |
| lmap_path = hf_hub_download( |
| repo_id=model_source, |
| filename="label_map.json" |
| ) |
| with open(lmap_path, encoding="utf-8") as f: |
| lm = json.load(f) |
| id2label = {int(k): v for k, v in lm["id2label"].items()} |
| print(f"[INFO] id2label loaded from HF: {id2label}") |
|
|
| except Exception as e: |
| print(f"[WARN] label_map.json not found in HF repo: {e}") |
|
|
| if id2label is None: |
| cfg_path = os.path.join(model_source, "config.json") |
| if os.path.isfile(cfg_path): |
| with open(cfg_path, encoding="utf-8") as f: |
| cfg = json.load(f) |
| if cfg.get("id2label"): |
| id2label = {int(k): v for k, v in cfg["id2label"].items()} |
| print(f"[INFO] id2label from config.json: {id2label}") |
|
|
| if id2label is None: |
| raise RuntimeError("label_map.json not found. Re-run your save cell in Kaggle.") |
|
|
| try: |
| model = AutoModelForSequenceClassification.from_pretrained( |
| model_source, num_labels=len(id2label), ignore_mismatched_sizes=True) |
| device = "cuda" if torch.cuda.is_available() else "cpu" |
| model.to(device).eval() |
| print(f"[INFO] Model OK — {len(id2label)} classes — {device.upper()}") |
| except Exception as e: |
| raise RuntimeError(f"Model load failed: {e}") |
|
|
| return model, tokenizer, id2label, device |
|
|
| try: |
| MODEL, TOKENIZER, ID2LABEL, DEVICE = load_model_and_labels() |
| CLASS_NAMES = [ID2LABEL[i] for i in sorted(ID2LABEL)] |
| NUM_CLASSES = len(CLASS_NAMES) |
| MODEL_LOADED = True |
| print(f"[INFO] Classes: {CLASS_NAMES}") |
| except Exception as e: |
| print(f"[ERROR] {e}") |
| MODEL_LOADED = False |
| CLASS_NAMES = ["Model not loaded"] |
| NUM_CLASSES = 1 |
| ID2LABEL = {0: "Model not loaded"} |
| DEVICE = "cpu" |
|
|
| |
| ICONS = { |
| "entertainment":"🎬","sports":"🏏","state":"🗺️","national":"🇮🇳", |
| "international":"🌏","business":"📈","technology":"💻","science":"🔬", |
| "health":"🏥","politics":"🏛️", |
| } |
| ICONS.update({k.title(): v for k, v in list(ICONS.items())}) |
|
|
| def get_icon(label): return ICONS.get(label, "📰") |
|
|
| REAL_METRICS = { |
| "TF-IDF + LR": {"test_acc":83.84,"test_f1":77.85,"color":"#3b82f6","train_time":"< 2 min"}, |
| "BiLSTM": {"test_acc":79.36,"test_f1":67.16,"color":"#8b5cf6","train_time":"~14 min"}, |
| "XLM-RoBERTa": {"test_acc":86.12,"test_f1":78.75,"color":"#10b981","train_time":"~45 min"}, |
| } |
|
|
| SAMPLES = { |
| "Telugu": "హైదరాబాద్లో క్రికెట్ టోర్నమెంట్ ప్రారంభమైంది; జిల్లా స్థాయి జట్లు పాల్గొంటున్నాయి.", |
| "Malayalam":"కേരളത്തിൽ ഇന്ന് കനത്ത മഴ; ഒൻപത് ജില്ലകളിൽ യെല്ലോ അലർട്ട് പ്രഖ്യാപിച്ചു.", |
| "Marathi": "मुंबई शेअर बाजारात आज मोठी तेजी; सेन्सेक्स ५०० अंकांनी वधारला.", |
| "Tamil": "தமிழ்நாட்டில் புதிய தொழில்நுட்ப பூங்கா திறப்பு; ஆயிரக்கணக்கான வேலை வாய்ப்புகள்.", |
| "Gujarati": "ગુજરાત ટીમ સ્ટેટ ક્રિકેટ ચેમ્પિયનશિપ જીતી; ખેલાડીઓ ઉત્સાહિત.", |
| } |
|
|
| |
| def clean_text(text): |
| if not isinstance(text, str): return "" |
| text = re.sub(r"https?://\S+|www\.\S+", " ", text) |
| text = re.sub(r"<[^>]+>", " ", text) |
| text = re.sub(r"[\u200b\u200c\u200d\ufeff\u00ad]", "", text) |
| text = re.sub( |
| r"[^\w\s\u0900-\u097F\u0C00-\u0C7F\u0D00-\u0D7F\u0B80-\u0BFF\u0A80-\u0AFF]", |
| " ", text) |
| return re.sub(r"\s+", " ", text).strip() |
|
|
| |
| def predict_text(text): |
| if not MODEL_LOADED: |
| return {c: 0.0 for c in CLASS_NAMES}, "Model not loaded", 0.0, 0 |
| t_clean = clean_text(text) |
| if not t_clean: |
| return {c: 0.0 for c in CLASS_NAMES}, "Empty input", 0.0, 0 |
| enc = TOKENIZER(t_clean, max_length=128, padding="max_length", |
| truncation=True, return_tensors="pt") |
| enc = {k: v.to(DEVICE) for k, v in enc.items()} |
| t0 = time.time() |
| with torch.no_grad(): |
| logits = MODEL(**enc).logits |
| ms = int((time.time() - t0) * 1000) |
| probs = F.softmax(logits, dim=-1).squeeze().cpu().numpy() |
| idx = int(np.argmax(probs)) |
| label = ID2LABEL.get(idx, f"class_{idx}") |
| return ({ID2LABEL.get(i, f"class_{i}"): float(probs[i]) for i in range(len(probs))}, |
| label, float(probs[idx]), ms) |
|
|
| |
| def conf_chart(probs_dict, pred_label): |
| paired = sorted(zip(probs_dict.values(), probs_dict.keys()), reverse=True) |
| vals = [p[0]*100 for p in paired] |
| labs = [p[1] for p in paired] |
| colors = ["#10b981" if l == pred_label else "#6366f1" if v > 10 else "#334155" |
| for l, v in zip(labs, vals)] |
| fig, ax = plt.subplots(figsize=(9, max(4, len(labs)*0.5+1))) |
| fig.patch.set_facecolor("#0f172a"); ax.set_facecolor("#0f172a") |
| bars = ax.barh(labs[::-1], vals[::-1], color=colors[::-1], height=0.55, edgecolor="none") |
| for bar, v in zip(bars, vals[::-1]): |
| ax.text(bar.get_width()+0.5, bar.get_y()+bar.get_height()/2, |
| f"{v:.1f}%", va="center", ha="left", color="#e2e8f0", fontsize=10, fontweight="bold") |
| ax.set_xlim(0, 115) |
| ax.set_xlabel("Confidence (%)", color="#94a3b8", fontsize=11) |
| ax.set_title("Prediction Confidence", color="#f1f5f9", fontsize=13, fontweight="bold", pad=12) |
| ax.tick_params(colors="#94a3b8", labelsize=10) |
| for s in ax.spines.values(): s.set_visible(False) |
| ax.grid(axis="x", color="#1e293b", linewidth=0.8) |
| plt.tight_layout(pad=1.5) |
| return fig |
|
|
| def metrics_chart(): |
| models = list(REAL_METRICS.keys()) |
| accs = [REAL_METRICS[m]["test_acc"] for m in models] |
| f1s = [REAL_METRICS[m]["test_f1"] for m in models] |
| cols = [REAL_METRICS[m]["color"] for m in models] |
| x, w = np.arange(len(models)), 0.32 |
| fig, ax = plt.subplots(figsize=(10, 5)) |
| fig.patch.set_facecolor("#0f172a"); ax.set_facecolor("#0f172a") |
| b1 = ax.bar(x-w/2, accs, w, label="Test Accuracy (%)", color=[c+"cc" for c in cols], edgecolor="none") |
| b2 = ax.bar(x+w/2, f1s, w, label="Test F1 Macro (%)", color=cols, edgecolor="none", alpha=0.75) |
| for bars in [b1, b2]: |
| for bar in bars: |
| h = bar.get_height() |
| ax.text(bar.get_x()+bar.get_width()/2, h+0.5, f"{h:.1f}", |
| ha="center", va="bottom", color="#e2e8f0", fontsize=10, fontweight="bold") |
| ax.set_xticks(x); ax.set_xticklabels(models, color="#94a3b8", fontsize=11) |
| ax.set_ylim(0, 105); ax.set_ylabel("Score (%)", color="#94a3b8", fontsize=11) |
| ax.set_title("Model Comparison — Test Results", color="#f1f5f9", fontsize=13, fontweight="bold", pad=14) |
| ax.tick_params(colors="#94a3b8") |
| ax.legend(facecolor="#1e293b", edgecolor="none", labelcolor="#e2e8f0") |
| for s in ax.spines.values(): s.set_visible(False) |
| ax.grid(axis="y", color="#1e293b", linewidth=0.8) |
| plt.tight_layout(pad=1.5) |
| return fig |
|
|
| _METRICS_FIG = metrics_chart() |
|
|
| |
| def classify_single(text): |
| if not text or not text.strip(): |
| return '<p style="color:#f87171;padding:20px;">Please enter a headline.</p>', None, None |
|
|
| pd, label, conf, ms = predict_text(text) |
| icon = get_icon(label) |
| pct = conf * 100 |
| cc = "#10b981" if pct >= 70 else "#f59e0b" if pct >= 40 else "#ef4444" |
|
|
| html = f""" |
| <div style="background:linear-gradient(135deg,#1e293b,#0f172a);border:1px solid #334155; |
| border-radius:16px;padding:28px 32px;font-family:sans-serif; |
| box-shadow:0 8px 32px rgba(0,0,0,0.4);"> |
| <div style="display:flex;align-items:center;gap:12px;margin-bottom:18px;"> |
| <span style="font-size:44px;">{icon}</span> |
| <div> |
| <div style="font-size:11px;text-transform:uppercase;letter-spacing:2px;color:#64748b;font-weight:600;"> |
| Predicted Category</div> |
| <div style="font-size:30px;font-weight:800;color:#f1f5f9;line-height:1.15;">{label.title()}</div> |
| </div> |
| </div> |
| <div style="display:flex;gap:32px;flex-wrap:wrap;"> |
| <div> |
| <div style="font-size:11px;text-transform:uppercase;letter-spacing:1.5px;color:#64748b;margin-bottom:4px;">Confidence</div> |
| <div style="font-size:38px;font-weight:900;color:{cc};">{pct:.1f}%</div> |
| </div> |
| <div> |
| <div style="font-size:11px;text-transform:uppercase;letter-spacing:1.5px;color:#64748b;margin-bottom:4px;">Model</div> |
| <div style="font-size:16px;font-weight:600;color:#94a3b8;">XLM-RoBERTa</div> |
| </div> |
| <div> |
| <div style="font-size:11px;text-transform:uppercase;letter-spacing:1.5px;color:#64748b;margin-bottom:4px;">Inference</div> |
| <div style="font-size:16px;font-weight:600;color:#94a3b8;">{ms} ms</div> |
| </div> |
| </div> |
| <hr style="border:none;border-top:1px solid #1e293b;margin:18px 0 10px;"> |
| <div style="font-size:12px;color:#475569;"> |
| IndicGLUE · 5 languages · {NUM_CLASSES} categories · Test acc: 86.12% |
| </div> |
| </div>""" |
| return html, conf_chart(pd, label), pd |
|
|
| def classify_batch(batch_text): |
| if not batch_text or not batch_text.strip(): |
| return '<p style="color:#f87171;padding:20px;">Enter at least one headline.</p>', None |
| lines = [l.strip() for l in batch_text.strip().split("\n") if l.strip()][:50] |
| rows = "" |
| labels_list = [] |
| for i, line in enumerate(lines, 1): |
| pd, label, conf, _ = predict_text(line) |
| icon = get_icon(label); pct = conf*100 |
| cc = "#10b981" if pct >= 70 else "#f59e0b" if pct >= 40 else "#ef4444" |
| prev = (line[:80]+"…") if len(line) > 80 else line |
| labels_list.append(label) |
| rows += f"""<tr style="border-bottom:1px solid #1e293b;"> |
| <td style="padding:10px 8px;color:#64748b;font-size:13px;">{i}</td> |
| <td style="padding:10px 8px;color:#cbd5e1;font-size:13px;max-width:340px;word-break:break-word;">{prev}</td> |
| <td style="padding:10px 8px;font-size:14px;color:#e2e8f0;">{icon} {label.title()}</td> |
| <td style="padding:10px 8px;font-weight:700;color:{cc};font-size:14px;">{pct:.1f}%</td> |
| </tr>""" |
| from collections import Counter |
| counts = Counter(labels_list) |
| summary = " · ".join(f"{get_icon(k)} {k.title()}: {v}" for k,v in counts.most_common(5)) |
| table = f""" |
| <div style="background:#0f172a;border-radius:14px;padding:20px; |
| font-family:sans-serif;border:1px solid #1e293b;"> |
| <div style="font-size:12px;color:#64748b;margin-bottom:14px;text-transform:uppercase;letter-spacing:1.5px;"> |
| {len(lines)} headlines — {summary}</div> |
| <div style="overflow-x:auto;"> |
| <table style="width:100%;border-collapse:collapse;"> |
| <thead><tr style="border-bottom:2px solid #334155;"> |
| <th style="padding:8px;color:#475569;font-size:11px;text-align:left;text-transform:uppercase;">#</th> |
| <th style="padding:8px;color:#475569;font-size:11px;text-align:left;text-transform:uppercase;">Headline</th> |
| <th style="padding:8px;color:#475569;font-size:11px;text-align:left;text-transform:uppercase;">Category</th> |
| <th style="padding:8px;color:#475569;font-size:11px;text-align:left;text-transform:uppercase;">Conf.</th> |
| </tr></thead> |
| <tbody style="color:#e2e8f0;">{rows}</tbody> |
| </table></div> |
| </div>""" |
| |
| fig, ax = plt.subplots(figsize=(7, 5)) |
| fig.patch.set_facecolor("#0f172a"); ax.set_facecolor("#0f172a") |
| pal = ["#10b981","#6366f1","#f59e0b","#ef4444","#3b82f6","#8b5cf6","#ec4899","#14b8a6","#f97316","#84cc16"] |
| cd = dict(counts) |
| wedges, texts, ats = ax.pie(cd.values(), labels=[k.title() for k in cd], |
| autopct="%1.0f%%", colors=pal[:len(cd)], startangle=140, |
| wedgeprops={"edgecolor":"#0f172a","linewidth":2}) |
| for t in texts: t.set_color("#94a3b8"); t.set_fontsize(10) |
| for at in ats: at.set_color("#0f172a"); at.set_fontweight("bold"); at.set_fontsize(9) |
| ax.set_title("Category Distribution", color="#f1f5f9", fontsize=13, fontweight="bold", pad=14) |
| plt.tight_layout() |
| return table, fig |
|
|
| |
| |
| |
| CSS = """ |
| * { box-sizing: border-box; } |
| .gradio-container { |
| max-width: 1100px !important; |
| margin: 0 auto !important; |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important; |
| } |
| .app-header { |
| background: linear-gradient(135deg, #0f172a, #1e1b4b 50%, #0f172a); |
| border: 1px solid #1e293b; border-radius: 14px; |
| padding: 32px 40px 24px; text-align: center; margin-bottom: 8px; |
| } |
| .header-badge { |
| display: inline-block; background: linear-gradient(90deg, #6366f1, #8b5cf6); |
| color: white; font-size: 10px; font-weight: 700; letter-spacing: 2.5px; |
| text-transform: uppercase; padding: 4px 14px; border-radius: 20px; margin-bottom: 14px; |
| } |
| .header-title { font-size: 38px; font-weight: 800; color: #f1f5f9; line-height: 1.1; margin: 0 0 8px; } |
| .header-title span { |
| background: linear-gradient(90deg, #6366f1, #10b981); |
| -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; |
| } |
| .header-sub { font-size: 14px; color: #64748b; margin: 0; } |
| .header-stats { display: flex; justify-content: center; gap: 16px; margin-top: 20px; flex-wrap: wrap; } |
| .stat-pill { |
| background: #1e293b; border: 1px solid #334155; border-radius: 8px; |
| padding: 7px 16px; font-size: 12px; color: #94a3b8; |
| } |
| .stat-pill strong { color: #e2e8f0; } |
| .tab-nav { background: #0f172a !important; border-bottom: 1px solid #1e293b !important; } |
| .tab-nav button { |
| color: #64748b !important; font-weight: 600 !important; font-size: 13px !important; |
| padding: 12px 20px !important; border: none !important; |
| border-bottom: 2px solid transparent !important; background: transparent !important; |
| } |
| .tab-nav button.selected { color: #6366f1 !important; border-bottom-color: #6366f1 !important; } |
| textarea, input[type=text] { |
| background: #1e293b !important; border: 1px solid #334155 !important; |
| color: #e2e8f0 !important; border-radius: 10px !important; font-size: 14px !important; |
| } |
| label { color: #94a3b8 !important; font-size: 12px !important; text-transform: uppercase !important; } |
| button.primary { |
| background: linear-gradient(135deg, #6366f1, #8b5cf6) !important; |
| color: white !important; font-weight: 700 !important; |
| border: none !important; border-radius: 10px !important; |
| } |
| button.secondary { |
| background: #1e293b !important; color: #94a3b8 !important; |
| border: 1px solid #334155 !important; border-radius: 8px !important; |
| } |
| .app-footer { |
| background: #0f172a; border: 1px solid #1e293b; border-radius: 14px; |
| padding: 24px 40px; text-align: center; margin-top: 24px; |
| } |
| .footer-team { display: flex; justify-content: center; gap: 32px; flex-wrap: wrap; margin-bottom: 14px; } |
| .footer-member { display: flex; align-items: center; gap: 10px; } |
| .footer-avatar { |
| width: 32px; height: 32px; border-radius: 50%; |
| display: flex; align-items: center; justify-content: center; |
| font-weight: 800; font-size: 13px; color: white; |
| } |
| .footer-name { font-size: 13px; color: #94a3b8; } |
| .footer-roll { font-size: 11px; color: #475569; } |
| .footer-copy { font-size: 12px; color: #64748b; margin-top: 10px; } |
| footer { display: none !important; } |
| """ |
|
|
| HEADER = """ |
| <div class="app-header"> |
| <div class="header-badge">Generative AI Assignment · CBIT · 2025-26</div> |
| <h1 class="header-title">Multilingual News<br><span>Classification</span></h1> |
| <p class="header-sub">Chaitanya Bharathi Institute of Technology · Dept. of AI & ML</p> |
| <div class="header-stats"> |
| <div class="stat-pill">Model <strong>XLM-RoBERTa</strong></div> |
| <div class="stat-pill">Languages <strong>5 Indic</strong></div> |
| <div class="stat-pill">Dataset <strong>IndicGLUE</strong></div> |
| <div class="stat-pill">Test Acc <strong>86.12%</strong></div> |
| </div> |
| </div> |
| """ |
|
|
| FOOTER = """ |
| <div class="app-footer"> |
| <div class="footer-team"> |
| <div class="footer-member"> |
| <div class="footer-avatar" style="background:linear-gradient(135deg,#6366f1,#8b5cf6);">J</div> |
| <div><div class="footer-name">Jay Kumar Das</div><div class="footer-roll">160123748035</div></div> |
| </div> |
| <div class="footer-member"> |
| <div class="footer-avatar" style="background:linear-gradient(135deg,#10b981,#059669);">S</div> |
| <div><div class="footer-name">Siddhartha Dontula</div><div class="footer-roll">160123748036</div></div> |
| </div> |
| <div class="footer-member"> |
| <div class="footer-avatar" style="background:linear-gradient(135deg,#f59e0b,#d97706);">P</div> |
| <div><div class="footer-name">Praneeth Reddy Ganta</div><div class="footer-roll">160123748037</div></div> |
| </div> |
| </div> |
| <div class="footer-copy"> |
| © 2025-26 · Dept. of AI & ML · CBIT Hyderabad · |
| Guided by <strong style="color:#64748b;">Mr. Panigrahi Srikanth</strong> |
| </div> |
| </div> |
| """ |
|
|
| PROJECT_HTML = """ |
| <div style="font-family:sans-serif;padding:8px 0;"> |
| <div style="background:#1e293b;border:1px solid #334155;border-radius:12px;padding:20px 24px;margin-bottom:16px;"> |
| <h3 style="color:#e2e8f0;margin:0 0 8px;">Problem Statement</h3> |
| <p style="color:#94a3b8;font-size:13px;line-height:1.6;margin:0;"> |
| A single unified model that reads Telugu, Malayalam, Marathi, Tamil, and Gujarati natively, |
| classifying news headlines into up to 10 categories — no translation required. |
| </p> |
| </div> |
| <div style="background:#1e293b;border:1px solid #334155;border-radius:12px;padding:20px 24px;margin-bottom:16px;"> |
| <h3 style="color:#e2e8f0;margin:0 0 8px;">Dataset — IndicGLUE (ai4bharat/indic_glue)</h3> |
| <p style="color:#94a3b8;font-size:13px;line-height:1.6;margin:0;"> |
| iNLTK Headlines subsets — 37,069 labeled headlines across 5 languages.<br> |
| <strong style="color:#e2e8f0;">Split:</strong> Train 25,945 · Val 3,707 · Test 7,414 |
| </p> |
| </div> |
| <div style="background:#1e293b;border:1px solid #334155;border-radius:12px;padding:20px 24px;"> |
| <h3 style="color:#e2e8f0;margin:0 0 8px;">Results (Test Set)</h3> |
| <p style="color:#94a3b8;font-size:13px;line-height:1.6;margin:0;"> |
| <strong style="color:#3b82f6;">TF-IDF + LR:</strong> 83.84% · F1 77.85%<br> |
| <strong style="color:#8b5cf6;">BiLSTM:</strong> 79.36% · F1 67.16%<br> |
| <strong style="color:#10b981;">XLM-RoBERTa:</strong> 86.% · F1 78.75% |
| </p> |
| </div> |
| </div> |
| """ |
|
|
| TEAM_HTML = """ |
| <div style="font-family:sans-serif;padding:8px 0;"> |
| <div style="text-align:center;margin-bottom:24px;"> |
| <div style="font-size:22px;font-weight:800;color:#f1f5f9;">Meet the Team</div> |
| <div style="font-size:13px;color:#64748b;margin-top:4px;"> |
| Dept. of AI & ML · CBIT · Guided by <strong style="color:#94a3b8;">Mr. Panigrahi Srikanth</strong> |
| </div> |
| </div> |
| <div style="background:linear-gradient(135deg,#1e293b,#0f172a);border:1px solid #334155;border-top:3px solid #6366f1;border-radius:14px;padding:22px 26px;margin-bottom:14px;"> |
| <div style="display:flex;align-items:center;gap:14px;margin-bottom:12px;"> |
| <div style="width:48px;height:48px;border-radius:50%;background:linear-gradient(135deg,#6366f1,#8b5cf6);display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:800;color:white;">J</div> |
| <div> |
| <div style="font-size:17px;font-weight:700;color:#f1f5f9;">Jay Kumar Das</div> |
| <div style="font-size:11px;color:#6366f1;">160123748035 · Phase 1 Lead</div> |
| </div> |
| </div> |
| <p style="color:#94a3b8;font-size:13px;line-height:1.6;margin:0;"> |
| IndicGLUE data loading, Unicode-safe preprocessing, TF-IDF baseline (84.95%), EDA. |
| </p> |
| </div> |
| <div style="background:linear-gradient(135deg,#1e293b,#0f172a);border:1px solid #334155;border-top:3px solid #10b981;border-radius:14px;padding:22px 26px;margin-bottom:14px;"> |
| <div style="display:flex;align-items:center;gap:14px;margin-bottom:12px;"> |
| <div style="width:48px;height:48px;border-radius:50%;background:linear-gradient(135deg,#10b981,#059669);display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:800;color:white;">S</div> |
| <div> |
| <div style="font-size:17px;font-weight:700;color:#f1f5f9;">Siddhartha Dontula</div> |
| <div style="font-size:11px;color:#10b981;">160123748036 · Phase 2 Lead</div> |
| </div> |
| </div> |
| <p style="color:#94a3b8;font-size:13px;line-height:1.6;margin:0;"> |
| BiLSTM design (60k vocab, GlobalMaxPool), training curves, per-class evaluation (79.36%). |
| </p> |
| </div> |
| <div style="background:linear-gradient(135deg,#1e293b,#0f172a);border:1px solid #334155;border-top:3px solid #f59e0b;border-radius:14px;padding:22px 26px;"> |
| <div style="display:flex;align-items:center;gap:14px;margin-bottom:12px;"> |
| <div style="width:48px;height:48px;border-radius:50%;background:linear-gradient(135deg,#f59e0b,#d97706);display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:800;color:white;">P</div> |
| <div> |
| <div style="font-size:17px;font-weight:700;color:#f1f5f9;">Praneeth Reddy Ganta</div> |
| <div style="font-size:11px;color:#f59e0b;">160123748037 · Phase 3 Lead</div> |
| </div> |
| </div> |
| <p style="color:#94a3b8;font-size:13px;line-height:1.6;margin:0;"> |
| XLM-RoBERTa fine-tuning, full evaluation, Gradio UI deployment (86.12%). |
| </p> |
| </div> |
| </div> |
| """ |
|
|
| |
| |
| |
|
|
| with gr.Blocks(css=CSS, title="Multilingual News Classification") as demo: |
|
|
| gr.HTML(HEADER) |
|
|
| with gr.Tabs(): |
|
|
| with gr.Tab("Classify News"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| txt_in = gr.Textbox( |
| placeholder="Paste a news headline in any of the 5 supported languages...", |
| lines=4, label="News Headline") |
| gr.HTML('<div style="font-size:11px;color:#475569;margin:8px 0 4px;text-transform:uppercase;letter-spacing:1px;">Load Sample</div>') |
| with gr.Row(): |
| for lang in ["Telugu", "Malayalam", "Marathi"]: |
| b = gr.Button(lang, size="sm") |
| b.click(fn=lambda l=lang: SAMPLES.get(l,""), outputs=txt_in) |
| with gr.Row(): |
| for lang in ["Tamil", "Gujarati"]: |
| b = gr.Button(lang, size="sm") |
| b.click(fn=lambda l=lang: SAMPLES.get(l,""), outputs=txt_in) |
| go_btn = gr.Button("Classify", variant="primary", size="lg") |
| with gr.Column(scale=1): |
| res_html = gr.HTML() |
| res_chart = gr.Plot() |
| res_json = gr.JSON(visible=False) |
| go_btn.click(fn=classify_single, |
| inputs=txt_in, |
| outputs=[res_html, res_chart, res_json]) |
|
|
| with gr.Tab("Batch Classify"): |
| gr.HTML('<div style="background:#1e293b;border:1px solid #334155;border-radius:10px;padding:14px 18px;margin-bottom:12px;font-family:sans-serif;font-size:13px;color:#64748b;"><strong style="color:#e2e8f0;">Batch mode</strong> — one headline per line, max 50.</div>') |
| with gr.Row(): |
| with gr.Column(scale=1): |
| batch_in = gr.Textbox(placeholder="One headline per line...", |
| lines=12, label="Headlines") |
| batch_btn = gr.Button("Classify All", variant="primary") |
| with gr.Column(scale=1): |
| batch_tbl = gr.HTML() |
| batch_chart = gr.Plot() |
| batch_btn.click(fn=classify_batch, |
| inputs=batch_in, |
| outputs=[batch_tbl, batch_chart]) |
|
|
| with gr.Tab("Model Comparison"): |
| gr.Plot(value=_METRICS_FIG) |
| with gr.Row(): |
| for mname, md in REAL_METRICS.items(): |
| with gr.Column(): |
| gr.HTML(f""" |
| <div style="background:#1e293b;border:1px solid {md['color']}40;border-top:3px solid {md['color']};border-radius:12px;padding:18px 20px;font-family:sans-serif;"> |
| <div style="font-size:14px;font-weight:700;color:#f1f5f9;margin-bottom:12px;">{mname}</div> |
| <div style="font-size:22px;font-weight:800;color:{md['color']};">{md['test_acc']}%</div> |
| <div style="font-size:11px;color:#475569;text-transform:uppercase;">Test Accuracy</div> |
| <div style="font-size:22px;font-weight:800;color:{md['color']};margin-top:8px;">{md['test_f1']}%</div> |
| <div style="font-size:11px;color:#475569;text-transform:uppercase;">F1 Macro</div> |
| <div style="font-size:13px;color:#64748b;margin-top:10px;">{md['train_time']}</div> |
| </div>""") |
|
|
| with gr.Tab("Project Details"): |
| gr.HTML(PROJECT_HTML) |
|
|
| with gr.Tab("Team"): |
| gr.HTML(TEAM_HTML) |
|
|
| gr.HTML(FOOTER) |
|
|
| |
| |
| def _free_ports(): |
| for port in range(7860, 7871): |
| try: |
| r = subprocess.run(["lsof", "-ti", f"tcp:{port}"], |
| capture_output=True, text=True) |
| for pid in r.stdout.strip().split("\n"): |
| if pid: |
| os.kill(int(pid), signal.SIGKILL) |
| print(f"[INFO] Freed port {port} (killed PID {pid})") |
| except Exception: |
| pass |
|
|
| _free_ports() |
| try: |
| demo.close() |
| except Exception: |
| pass |
|
|
| import time as _t; _t.sleep(1) |
|
|
| demo.launch( |
| share=True, |
| server_port=7860, |
| server_name="0.0.0.0", |
| show_error=True, |
| quiet=False, |
| ) |
|
|