# app.py import streamlit as st import plotly.graph_objects as go import plotly.express as px import pandas as pd from utils import load_model, clean_text, predict_sentiment # ---- Page Config ---- st.set_page_config( page_title="Sentiment Analysis App", page_icon="๐", layout="wide" ) # ---- CSS ---- st.markdown(""" """, unsafe_allow_html=True) # ---- Load Model ---- tokenizer, model, device = load_model() # ---- Header ---- st.title("๐ Real-Time Sentiment Analysis") st.markdown("*Fine-tuned **DistilBERT** ยท 91% accuracy ยท Built by Nguyen Tin Tin Do*") st.divider() # ---- Tabs ---- tab1, tab2, tab3 = st.tabs(["๐ Single Prediction", "๐ Batch Prediction", "๐ Model Info"]) # ============================== # TAB 1 โ Single Prediction # ============================== with tab1: user_input = st.text_area( "Enter text to analyze:", placeholder="e.g. This movie was absolutely amazing...", height=150 ) if st.button("๐ Analyze Sentiment", use_container_width=True): if user_input.strip(): with st.spinner("Analyzing..."): cleaned = clean_text(user_input) result = predict_sentiment(cleaned, tokenizer, model, device) sentiment = result["sentiment"] confidence = result["confidence"] latency = result["inference_time_ms"] col1, col2, col3 = st.columns(3) col1.metric("Sentiment", f"{'โ ' if sentiment == 'Positive' else 'โ'} {sentiment}") col2.metric("Confidence", f"{confidence*100:.1f}%") col3.metric("Latency", f"{latency}ms") # Gauge chart fig = go.Figure(go.Indicator( mode="gauge+number", value=confidence * 100, title={"text": "Confidence Score (%)"}, gauge={ "axis": {"range": [0, 100]}, "bar": {"color": "#2ecc71" if sentiment == "Positive" else "#e74c3c"}, "steps": [ {"range": [0, 50], "color": "#1a1a2e"}, {"range": [50, 100], "color": "#16213e"} ], "threshold": { "line": {"color": "white", "width": 3}, "value": 50 } } )) fig.update_layout( height=280, paper_bgcolor="rgba(0,0,0,0)", font={"color": "white"} ) st.plotly_chart(fig, use_container_width=True) else: st.warning("โ ๏ธ Please enter some text first.") # ============================== # TAB 2 โ Batch Prediction # ============================== with tab2: batch_input = st.text_area( "Enter multiple texts (one per line):", placeholder="This film was great!\nTerrible acting...\nAbsolutely loved it!", height=180 ) if st.button("๐ Analyze Batch", use_container_width=True): texts = [t.strip() for t in batch_input.split("\n") if t.strip()] if texts: results = [] progress = st.progress(0) for i, text in enumerate(texts): cleaned = clean_text(text) result = predict_sentiment(cleaned, tokenizer, model, device) results.append({ "Text" : text[:60] + "..." if len(text) > 60 else text, "Sentiment" : result["sentiment"], "Confidence": f"{result['confidence']*100:.1f}%", "Latency" : f"{result['inference_time_ms']}ms" }) progress.progress((i + 1) / len(texts)) # Table df = pd.DataFrame(results) st.dataframe(df, use_container_width=True) # Pie chart counts = df["Sentiment"].value_counts() fig = px.pie( values=counts.values, names=counts.index, title=f"Sentiment Distribution ({len(texts)} texts)", color=counts.index, color_discrete_map={"Positive": "#2ecc71", "Negative": "#e74c3c"} ) fig.update_layout(paper_bgcolor="rgba(0,0,0,0)", font={"color": "white"}) st.plotly_chart(fig, use_container_width=True) else: st.warning("โ ๏ธ Please enter at least one text.") # ============================== # TAB 3 โ Model Info # ============================== with tab3: st.markdown(""" ### ๐ค Model Details | | | |---|---| | **Base Model** | DistilBERT (distilbert-base-uncased) | | **Fine-tuned on** | IMDB Movie Reviews (50,000 samples) | | **Training Epochs** | 3 | | **Batch Size** | 16 | | **Learning Rate** | 2e-5 | | **Accuracy** | 91% | | **F1-Score** | 0.89 | ### ๐ Performance vs Baseline | Model | Accuracy | F1-Score | |---|---|---| | TF-IDF + Logistic Regression | 77% | 0.77 | | **DistilBERT (fine-tuned)** | **91%** | **0.89** | ### ๐ ๏ธ Tech Stack `Python` `HuggingFace Transformers` `FastAPI` `Streamlit` `Plotly` `Docker` """) # ---- Footer ---- st.divider() st.markdown( "
", unsafe_allow_html=True )