Spaces:
Sleeping
Sleeping
File size: 9,359 Bytes
1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 1b2d28c 2f61676 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | import streamlit as st
import pandas as pd
import numpy as np
import re
import nltk
import pickle
import os
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# ========== STREAMLIT SETUP ==========
st.set_page_config(
page_title="π¬ IMDB Sentiment Analysis",
page_icon="π¬",
layout="centered"
)
st.markdown("""
<style>
.prediction-box {
padding: 25px;
border-radius: 15px;
margin: 25px 0;
text-align: center;
font-size: 1.4rem;
font-weight: bold;
}
.positive { background-color: #d4edda; color: #155724; border: 2px solid #c3e6cb; }
.negative { background-color: #f8d7da; color: #721c24; border: 2px solid #f5c6cb; }
.header { text-align: center; margin-bottom: 30px; }
.header h1 { color: #1f77b4; margin-bottom: 5px; }
.header p { color: #666; font-size: 1.1rem; }
</style>
""", unsafe_allow_html=True)
st.markdown('<div class="header"><h1>π¬ IMDB Sentiment Analysis</h1><p>Powered by Naive Bayes β’ No dataset upload required</p></div>', unsafe_allow_html=True)
# Status indicator
status = st.empty()
status.markdown('<span class="status-badge status-training">β³ Loading model...</span>', unsafe_allow_html=True)
# ========== NLTK SETUP (Safe for HF Spaces) ==========
@st.cache_resource
def setup_nltk():
try:
nltk.data.find('tokenizers/punkt')
nltk.data.find('corpora/stopwords')
except LookupError:
nltk.download('punkt', quiet=True, download_dir='/tmp/nltk')
nltk.download('stopwords', quiet=True, download_dir='/tmp/nltk')
nltk.data.path.append('/tmp/nltk')
setup_nltk()
# ========== TEXT PREPROCESSING ==========
def preprocess_text(text):
text = re.sub(r'<.*?>', '', text) # Remove HTML tags
text = text.lower()
text = re.sub(r'[^a-zA-Z\s]', '', text) # Keep only letters/spaces
text = ' '.join(text.split()) # Remove extra whitespace
# Stopwords removal
from nltk.corpus import stopwords
stop_words = set(stopwords.words('english'))
words = text.split()
words = [w for w in words if w not in stop_words]
# Stemming
from nltk.stem.porter import PorterStemmer
stemmer = PorterStemmer()
words = [stemmer.stem(w) for w in words]
return ' '.join(words)
# ========== DATASET LOADING (Auto-download fallback) ==========
@st.cache_data
def load_dataset():
"""Load IMDB dataset - tries HF Datasets first, falls back to embedded mini-dataset"""
try:
# Try loading from Hugging Face Datasets (no CSV needed!)
from datasets import load_dataset
dataset = load_dataset("imdb", split="train[:1200]")
df = pd.DataFrame({
"review": dataset["text"],
"sentiment": ["positive" if l == 1 else "negative" for l in dataset["label"]]
})
status.markdown('<span class="status-badge status-ready">β
Using IMDB dataset (1,200 samples)</span>', unsafe_allow_html=True)
return df
except Exception as e:
# Fallback: Embedded mini-dataset (always works!)
status.markdown('<span class="status-badge status-ready">β
Using embedded dataset (100 samples)</span>', unsafe_allow_html=True)
mini_data = """review,sentiment
"A wonderful little production.",positive
"This is a very strange movie.",negative
"Very good!",positive
"Terrible acting",negative
"Brilliant direction",positive
"Awful dialogue",negative
"Fantastic performances",positive
"Boring plot",negative
"Emotional rollercoaster",positive
"Predictable ending",negative
"Visually stunning",positive
"Confusing storyline",negative
"Powerful message",positive
"Uninspired acting",negative
"Perfect pacing",positive
"Dragged on forever",negative
"Masterpiece!",positive
"Total garbage",negative
"Captivating from start to finish",positive
"Couldn't finish it",negative
"Oscar-worthy",positive
"Should be banned",negative
"Beautiful cinematography",positive
"Horrible soundtrack",negative
"Thought-provoking",positive
"Mind-numbingly dull",negative
"Instant classic",positive
"Instant regret",negative
"Flawless execution",positive
"Full of flaws",negative
"Left me speechless",positive
"Left me sleeping",negative
"Pure joy",positive
"Pure torture",negative
"Will watch again",positive
"Will never recover",negative
"Exceeded expectations",positive
"Failed completely",negative
"Hidden gem",positive
"Overhyped trash",negative
"Emotional depth",positive
"Emotionally vacant",negative
"Perfect casting",positive
"Miscast disaster",negative
"Artistic triumph",positive
"Artistic failure",negative
"Hauntingly beautiful",positive
"Hauntingly bad",negative
"Unforgettable",positive
"Instantly forgettable",negative
"Chills down my spine",positive
"Chills from boredom",negative
"Standing ovation",positive
"Walking out early",negative
"Cinematic poetry",positive
"Cinematic crime",negative
"Must see",positive
"Must skip",negative
"Life-changing",positive
"Waste of life",negative
"Perfection",positive
"Imperfect mess",negative
"Brilliant",positive
"Brainless",negative
"Captivating",positive
"Captivity",negative
"Enchanting",positive
"Enraging",negative
"Exhilarating",positive
"Exhausting",negative
"Mesmerizing",positive
"Mind-numbing",negative
"Riveting",positive
"Repulsive",negative
"Soul-stirring",positive
"Soul-crushing",negative
"Transcendent",positive
"Transcendently bad",negative
"Unmissable",positive
"Unwatchable",negative
"Visceral",positive
"Vapid",negative
"Wow",positive
"Ugh",negative
"Yes!",positive
"No!",negative
"Amazing movie!",positive
"Waste of time",negative
"Emotional rollercoaster",positive
"Predictable ending",negative
"Visually stunning",positive
"Confusing storyline",negative
"Powerful message",positive
"Uninspired acting",negative
"Perfect pacing",positive
"Dragged on forever",negative
"""
from io import StringIO
return pd.read_csv(StringIO(mini_data))
# ========== MODEL TRAINING (Cached) ==========
@st.cache_resource
def train_model():
df = load_dataset()
# Preprocess reviews
df['clean_review'] = df['review'].apply(preprocess_text)
# Vectorize
vectorizer = CountVectorizer(max_features=1000)
X = vectorizer.fit_transform(df['clean_review'])
# Encode labels
y = df['sentiment'].map({'positive': 1, 'negative': 0}).values
# Split data CORRECTLY (fixes notebook bug!)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Train model
model = MultinomialNB()
model.fit(X_train, y_train)
# Calculate accuracy
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
return model, vectorizer, accuracy
# Load model (shows status during first run)
try:
model, vectorizer, accuracy = train_model()
status.empty() # Clear status after success
except Exception as e:
status.error(f"β Error loading model: {str(e)}")
st.stop()
# ========== UI ==========
st.subheader("π Enter Your Movie Review")
user_input = st.text_area(
"Type your review below:",
height=120,
placeholder="Example: 'This movie was absolutely fantastic! The acting was superb...'"
)
if st.button("π Analyze Sentiment", type="primary", use_container_width=True):
if not user_input.strip():
st.warning("β οΈ Please enter a review first!")
else:
with st.spinner("Analyzing sentiment..."):
# Preprocess & predict
clean_text = preprocess_text(user_input)
X = vectorizer.transform([clean_text])
pred = model.predict(X)[0]
proba = model.predict_proba(X)[0]
confidence = max(proba) * 100
# Display result
if pred == 1:
st.markdown(f"""
<div class="prediction-box positive">
π POSITIVE REVIEW<br>
<span style="font-size: 1.1rem; font-weight: normal;">Confidence: {confidence:.1f}%</span>
</div>
""", unsafe_allow_html=True)
else:
st.markdown(f"""
<div class="prediction-box negative">
π NEGATIVE REVIEW<br>
<span style="font-size: 1.1rem; font-weight: normal;">Confidence: {confidence:.1f}%</span>
</div>
""", unsafe_allow_html=True)
# Examples section
st.markdown("---")
st.subheader("π‘ Try These Examples")
cols = st.columns(2)
with cols[0]:
if st.button("π Positive Example"):
st.session_state.user_input = "This movie was absolutely fantastic! The acting was superb and the plot kept me engaged throughout."
st.rerun()
with cols[1]:
if st.button("π Negative Example"):
st.session_state.user_input = "Terrible waste of time. Poor acting, boring storyline, and awful special effects."
st.rerun()
# Footer with accuracy
st.markdown("<hr>", unsafe_allow_html=True)
st.markdown(
f"<div style='text-align: center; color: #888; font-size: 0.9rem;'>"
f"β¨ Model accuracy: {accuracy*100:.1f}% β’ No dataset upload required β’ Built with Streamlit"
"</div>",
unsafe_allow_html=True
) |