zainab1234awan commited on
Commit
1b2d28c
Β·
verified Β·
1 Parent(s): daf8cee

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +298 -0
app.py ADDED
@@ -0,0 +1,298 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import re
5
+ import nltk
6
+ from nltk.corpus import stopwords
7
+ from nltk.stem.porter import PorterStemmer
8
+ from nltk.tokenize import word_tokenize
9
+ from sklearn.feature_extraction.text import CountVectorizer
10
+ from sklearn.preprocessing import LabelEncoder
11
+ from sklearn.naive_bayes import MultinomialNB, BernoulliNB
12
+ from sklearn.metrics import accuracy_score
13
+ import pickle
14
+ import os
15
+
16
+ # Download NLTK data
17
+ @st.cache_resource
18
+ def download_nltk_data():
19
+ nltk.download('stopwords', quiet=True)
20
+ nltk.download('punkt', quiet=True)
21
+
22
+ download_nltk_data()
23
+
24
+ # Set page config
25
+ st.set_page_config(
26
+ page_title="IMDB Sentiment Analysis",
27
+ page_icon="🎬",
28
+ layout="wide",
29
+ initial_sidebar_state="expanded"
30
+ )
31
+
32
+ # Custom CSS
33
+ st.markdown("""
34
+ <style>
35
+ .main-header {
36
+ font-size: 2.5rem;
37
+ font-weight: bold;
38
+ color: #1f77b4;
39
+ text-align: center;
40
+ margin-bottom: 1rem;
41
+ }
42
+ .sub-header {
43
+ font-size: 1.2rem;
44
+ color: #666;
45
+ text-align: center;
46
+ margin-bottom: 2rem;
47
+ }
48
+ .prediction-box {
49
+ padding: 20px;
50
+ border-radius: 10px;
51
+ margin: 20px 0;
52
+ text-align: center;
53
+ }
54
+ .positive {
55
+ background-color: #d4edda;
56
+ color: #155724;
57
+ border: 1px solid #c3e6cb;
58
+ }
59
+ .negative {
60
+ background-color: #f8d7da;
61
+ color: #721c24;
62
+ border: 1px solid #f5c6cb;
63
+ }
64
+ </style>
65
+ """, unsafe_allow_html=True)
66
+
67
+ # Title and description
68
+ st.markdown('<p class="main-header">🎬 IMDB Movie Review Sentiment Analysis</p>', unsafe_allow_html=True)
69
+ st.markdown('<p class="sub-header">Powered by Naive Bayes | Built with Streamlit</p>', unsafe_allow_html=True)
70
+
71
+ # Sidebar
72
+ with st.sidebar:
73
+ st.header("βš™οΈ Settings")
74
+ model_choice = st.selectbox(
75
+ "Select Model",
76
+ ["Multinomial Naive Bayes", "Bernoulli Naive Bayes"],
77
+ help="Choose which Naive Bayes algorithm to use"
78
+ )
79
+
80
+ st.markdown("---")
81
+ st.header("πŸ“Š Model Info")
82
+ st.info("""
83
+ - **Algorithm**: Naive Bayes
84
+ - **Features**: Bag of Words
85
+ - **Preprocessing**:
86
+ - HTML tag removal
87
+ - Lowercasing
88
+ - Special character removal
89
+ - Stopword removal
90
+ - Stemming
91
+ """)
92
+
93
+ # Preprocessing functions
94
+ def remove_html_tags(review):
95
+ clean_text = re.sub(r'<.*?>', '', review)
96
+ return clean_text
97
+
98
+ def remove_special(review):
99
+ cleaned = re.sub(r'[^a-zA-Z0-9\s]', '', review)
100
+ return cleaned
101
+
102
+ def remove_stopwords(review):
103
+ stop_words = set(stopwords.words('english'))
104
+ words = review.split()
105
+ filtered_words = [word for word in words if word not in stop_words]
106
+ return " ".join(filtered_words)
107
+
108
+ def apply_stemming(review):
109
+ stemmer = PorterStemmer()
110
+ words = word_tokenize(review)
111
+ stemmed_words = [stemmer.stem(word) for word in words]
112
+ return " ".join(stemmed_words)
113
+
114
+ def preprocess_text(text):
115
+ text = remove_html_tags(text)
116
+ text = text.lower()
117
+ text = remove_special(text)
118
+ text = remove_stopwords(text)
119
+ text = apply_stemming(text)
120
+ return text
121
+
122
+ # Load or train model
123
+ @st.cache_resource
124
+ def load_or_train_model():
125
+ model_file = 'sentiment_model.pkl'
126
+ vectorizer_file = 'vectorizer.pkl'
127
+ encoder_file = 'label_encoder.pkl'
128
+
129
+ if os.path.exists(model_file) and os.path.exists(vectorizer_file) and os.path.exists(encoder_file):
130
+ # Load saved model
131
+ with open(model_file, 'rb') as f:
132
+ model_data = pickle.load(f)
133
+ with open(vectorizer_file, 'rb') as f:
134
+ cv = pickle.load(f)
135
+ with open(encoder_file, 'rb') as f:
136
+ le = pickle.load(f)
137
+ return model_data['model'], cv, le, model_data['accuracy']
138
+ else:
139
+ # Train new model
140
+ try:
141
+ df = pd.read_csv("IMDB Dataset.csv")
142
+ df = df.sample(1200, random_state=42)
143
+
144
+ # Preprocess data
145
+ df['review'] = df['review'].apply(remove_html_tags)
146
+ df['review'] = df['review'].str.lower()
147
+ df['review'] = df['review'].apply(remove_special)
148
+ df['review'] = df['review'].apply(remove_stopwords)
149
+ df['review'] = df['review'].apply(apply_stemming)
150
+
151
+ # Vectorize
152
+ cv = CountVectorizer()
153
+ X = cv.fit_transform(df['review'])
154
+
155
+ # Encode labels
156
+ le = LabelEncoder()
157
+ y = le.fit_transform(df['sentiment'])
158
+
159
+ # Train model
160
+ from sklearn.model_selection import train_test_split
161
+ X_train, X_test, y_train, y_test = train_test_split(
162
+ X, y, test_size=0.2, random_state=42
163
+ )
164
+
165
+ model = MultinomialNB()
166
+ model.fit(X_train, y_train)
167
+ y_pred = model.predict(X_test)
168
+ accuracy = accuracy_score(y_test, y_pred)
169
+
170
+ # Save model
171
+ with open(model_file, 'wb') as f:
172
+ pickle.dump({'model': model, 'accuracy': accuracy}, f)
173
+ with open(vectorizer_file, 'wb') as f:
174
+ pickle.dump(cv, f)
175
+ with open(encoder_file, 'wb') as f:
176
+ pickle.dump(le, f)
177
+
178
+ return model, cv, le, accuracy
179
+ except:
180
+ st.error("Could not load dataset. Please ensure 'IMDB Dataset.csv' is in the same directory.")
181
+ return None, None, None, None
182
+
183
+ # Load model
184
+ model, vectorizer, label_encoder, model_accuracy = load_or_train_model()
185
+
186
+ # Main content
187
+ col1, col2 = st.columns([2, 1])
188
+
189
+ with col1:
190
+ st.header("πŸ“ Enter Your Movie Review")
191
+
192
+ # Text input
193
+ user_input = st.text_area(
194
+ "Type or paste your movie review here:",
195
+ height=150,
196
+ placeholder="E.g., 'This movie was absolutely fantastic! The acting was superb and the plot kept me engaged throughout...'"
197
+ )
198
+
199
+ # Predict button
200
+ if st.button("πŸ” Analyze Sentiment", type="primary", use_container_width=True):
201
+ if user_input.strip() == "":
202
+ st.warning("⚠️ Please enter a movie review first!")
203
+ elif model is None:
204
+ st.error("❌ Model not available. Please check the dataset.")
205
+ else:
206
+ with st.spinner("Analyzing sentiment..."):
207
+ # Preprocess input
208
+ processed_text = preprocess_text(user_input)
209
+
210
+ # Vectorize
211
+ text_vector = vectorizer.transform([processed_text])
212
+
213
+ # Predict
214
+ prediction = model.predict(text_vector)[0]
215
+ sentiment = label_encoder.inverse_transform([prediction])[0]
216
+
217
+ # Get prediction probabilities
218
+ probabilities = model.predict_proba(text_vector)[0]
219
+ confidence = max(probabilities) * 100
220
+
221
+ # Display result
222
+ st.markdown("---")
223
+ st.subheader("πŸ“Š Prediction Result")
224
+
225
+ if sentiment == "positive":
226
+ st.markdown(f"""
227
+ <div class="prediction-box positive">
228
+ <h2>😊 POSITIVE REVIEW</h2>
229
+ <p style="font-size: 1.2rem; margin-top: 10px;">
230
+ This review expresses a <strong>positive</strong> sentiment about the movie.
231
+ </p>
232
+ <p style="font-size: 1rem; margin-top: 15px;">
233
+ Confidence: <strong>{confidence:.2f}%</strong>
234
+ </p>
235
+ </div>
236
+ """, unsafe_allow_html=True)
237
+ else:
238
+ st.markdown(f"""
239
+ <div class="prediction-box negative">
240
+ <h2>😞 NEGATIVE REVIEW</h2>
241
+ <p style="font-size: 1.2rem; margin-top: 10px;">
242
+ This review expresses a <strong>negative</strong> sentiment about the movie.
243
+ </p>
244
+ <p style="font-size: 1rem; margin-top: 15px;">
245
+ Confidence: <strong>{confidence:.2f}%</strong>
246
+ </p>
247
+ </div>
248
+ """, unsafe_allow_html=True)
249
+
250
+ # Show probabilities
251
+ with st.expander("πŸ“ˆ View Prediction Probabilities"):
252
+ prob_df = pd.DataFrame({
253
+ 'Sentiment': label_encoder.classes_,
254
+ 'Probability': probabilities
255
+ })
256
+ prob_df['Probability'] = prob_df['Probability'] * 100
257
+ prob_df = prob_df.sort_values('Probability', ascending=False)
258
+ st.bar_chart(prob_df.set_index('Sentiment'))
259
+
260
+ with col2:
261
+ st.header("ℹ️ About")
262
+
263
+ st.info("""
264
+ This app analyzes movie reviews and predicts whether the sentiment is **positive** or **negative**.
265
+
266
+ ### How it works:
267
+ 1. Your review is cleaned and preprocessed
268
+ 2. Text is converted to numerical features
269
+ 3. Naive Bayes model makes the prediction
270
+ 4. Results are displayed with confidence score
271
+ """)
272
+
273
+ if model_accuracy is not None:
274
+ st.success(f"**Model Accuracy**: {model_accuracy*100:.2f}%")
275
+
276
+ st.markdown("---")
277
+
278
+ st.header("πŸ§ͺ Try Examples")
279
+
280
+ example_reviews = [
281
+ "This movie was absolutely fantastic! The acting was superb and the plot kept me engaged throughout.",
282
+ "Terrible waste of time. Poor acting, boring storyline, and awful special effects.",
283
+ "A masterpiece of cinema. Brilliant direction and outstanding performances from the entire cast.",
284
+ "I fell asleep halfway through. The pacing was painfully slow and nothing happened."
285
+ ]
286
+
287
+ for i, example in enumerate(example_reviews):
288
+ if st.button(f"Example {i+1}", key=f"ex_{i}"):
289
+ st.session_state.user_input = example
290
+
291
+ # Footer
292
+ st.markdown("---")
293
+ st.markdown("""
294
+ <div style="text-align: center; color: #666; font-size: 0.9rem;">
295
+ <p>Built with ❀️ using Streamlit | Naive Bayes Sentiment Analysis</p>
296
+ <p>Dataset: IMDB Movie Reviews</p>
297
+ </div>
298
+ """, unsafe_allow_html=True)