Spaces:
Sleeping
Sleeping
Commit
·
e754c5c
1
Parent(s):
a1a6da3
Upload 8 files
Browse files- .gitattributes +2 -0
- IMDB_Balanced_10000_Rows.csv +3 -0
- app.py +65 -0
- preprocessing with stemming.ipynb +320 -0
- requirements.txt +0 -0
- sentiment_model.pkl +3 -0
- test_data.csv +0 -0
- tfidf_vectorizer.pkl +3 -0
- train_data.csv +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
IMDB_Balanced_10000_Rows.csv filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
train_data.csv filter=lfs diff=lfs merge=lfs -text
|
IMDB_Balanced_10000_Rows.csv
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4c25d2c73fcfd7542e4138150a172bb27d20ccfde564d875f378f7857f94b92f
|
| 3 |
+
size 13205542
|
app.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import re
|
| 4 |
+
import nltk
|
| 5 |
+
import joblib
|
| 6 |
+
from nltk.corpus import stopwords
|
| 7 |
+
from sklearn.feature_extraction.text import TfidfVectorizer
|
| 8 |
+
from sklearn.linear_model import LogisticRegression
|
| 9 |
+
|
| 10 |
+
# Download stopwords if not available
|
| 11 |
+
nltk.download('stopwords')
|
| 12 |
+
|
| 13 |
+
# Paths to datasets
|
| 14 |
+
train_path = r"E:\Projects\Sentiment Analysis Project DEPI\train_data.csv"
|
| 15 |
+
test_path = r"E:\Projects\Sentiment Analysis Project DEPI\test_data.csv"
|
| 16 |
+
|
| 17 |
+
# Load datasets
|
| 18 |
+
train_df = pd.read_csv(train_path)
|
| 19 |
+
|
| 20 |
+
# Text preprocessing function
|
| 21 |
+
def preprocess_text(text):
|
| 22 |
+
text = text.lower() # Convert to lowercase
|
| 23 |
+
text = re.sub(r'\W', ' ', text) # Remove special characters
|
| 24 |
+
text = re.sub(r'\s+', ' ', text).strip() # Remove extra spaces
|
| 25 |
+
stop_words = set(stopwords.words('english')) # Load stopwords
|
| 26 |
+
text = ' '.join(word for word in text.split() if word not in stop_words) # Remove stopwords
|
| 27 |
+
return text
|
| 28 |
+
|
| 29 |
+
# Apply preprocessing
|
| 30 |
+
train_df['cleaned_review'] = train_df['review'].astype(str).apply(preprocess_text)
|
| 31 |
+
|
| 32 |
+
# Train the model
|
| 33 |
+
vectorizer = TfidfVectorizer(max_features=5000, ngram_range=(1,2))
|
| 34 |
+
X_train_tfidf = vectorizer.fit_transform(train_df['cleaned_review'])
|
| 35 |
+
y_train = train_df['sentiment']
|
| 36 |
+
|
| 37 |
+
model = LogisticRegression(max_iter=500)
|
| 38 |
+
model.fit(X_train_tfidf, y_train)
|
| 39 |
+
|
| 40 |
+
# Save the model and vectorizer
|
| 41 |
+
joblib.dump(model, "sentiment_model.pkl")
|
| 42 |
+
joblib.dump(vectorizer, "tfidf_vectorizer.pkl")
|
| 43 |
+
|
| 44 |
+
# Load model and vectorizer for prediction
|
| 45 |
+
model = joblib.load("sentiment_model.pkl")
|
| 46 |
+
vectorizer = joblib.load("tfidf_vectorizer.pkl")
|
| 47 |
+
|
| 48 |
+
# Gradio prediction function
|
| 49 |
+
def predict_sentiment(review):
|
| 50 |
+
processed_review = preprocess_text(review) # Preprocess input
|
| 51 |
+
review_tfidf = vectorizer.transform([processed_review]) # Convert to TF-IDF
|
| 52 |
+
prediction = model.predict(review_tfidf)[0] # Get prediction
|
| 53 |
+
return f"Predicted Sentiment: {prediction}"
|
| 54 |
+
|
| 55 |
+
# Gradio UI
|
| 56 |
+
interface = gr.Interface(
|
| 57 |
+
fn=predict_sentiment,
|
| 58 |
+
inputs=gr.Textbox(label="Enter a Review"),
|
| 59 |
+
outputs=gr.Textbox(label="Sentiment Prediction"),
|
| 60 |
+
title="Sentiment Analysis App",
|
| 61 |
+
description="Enter a review, and the model will predict if it's Positive, Negative, or Neutral."
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
# Launch the app
|
| 65 |
+
interface.launch()
|
preprocessing with stemming.ipynb
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": 8,
|
| 6 |
+
"metadata": {},
|
| 7 |
+
"outputs": [
|
| 8 |
+
{
|
| 9 |
+
"name": "stderr",
|
| 10 |
+
"output_type": "stream",
|
| 11 |
+
"text": [
|
| 12 |
+
"[nltk_data] Downloading package stopwords to\n",
|
| 13 |
+
"[nltk_data] C:\\Users\\HP\\AppData\\Roaming\\nltk_data...\n",
|
| 14 |
+
"[nltk_data] Package stopwords is already up-to-date!\n"
|
| 15 |
+
]
|
| 16 |
+
}
|
| 17 |
+
],
|
| 18 |
+
"source": [
|
| 19 |
+
"import pandas as pd\n",
|
| 20 |
+
"import re\n",
|
| 21 |
+
"from sklearn.model_selection import train_test_split\n",
|
| 22 |
+
"from nltk.stem import PorterStemmer\n",
|
| 23 |
+
"import nltk\n",
|
| 24 |
+
"nltk.download('stopwords')\n",
|
| 25 |
+
"from nltk.corpus import stopwords\n",
|
| 26 |
+
"from nltk.corpus import stopwords\n",
|
| 27 |
+
"from nltk.stem import WordNetLemmatizer\n",
|
| 28 |
+
"from collections import Counter\n",
|
| 29 |
+
"import matplotlib.pyplot as plt\n",
|
| 30 |
+
"lemmatizer = WordNetLemmatizer()\n",
|
| 31 |
+
"stemmer = PorterStemmer()\n"
|
| 32 |
+
]
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"cell_type": "code",
|
| 36 |
+
"execution_count": 5,
|
| 37 |
+
"metadata": {},
|
| 38 |
+
"outputs": [
|
| 39 |
+
{
|
| 40 |
+
"name": "stdout",
|
| 41 |
+
"output_type": "stream",
|
| 42 |
+
"text": [
|
| 43 |
+
"<bound method NDFrame.head of review sentiment\n",
|
| 44 |
+
"0 Starts really well, nice intro and build up fo... negative\n",
|
| 45 |
+
"1 Terrific movie: If you did not watch yet, you ... positive\n",
|
| 46 |
+
"2 I've seen hundreds of silent movies. Some will... positive\n",
|
| 47 |
+
"3 i had been looking for this film for so long b... positive\n",
|
| 48 |
+
"4 Good: Engaging cinematic firefights, great pre... positive\n",
|
| 49 |
+
"... ... ...\n",
|
| 50 |
+
"9995 I almost made a fool of myself when I was goin... negative\n",
|
| 51 |
+
"9996 I feel it is my duty as a lover of horror film... negative\n",
|
| 52 |
+
"9997 Why was this film made? What were the creators... negative\n",
|
| 53 |
+
"9998 If it is true that sadomasochism is a two-side... positive\n",
|
| 54 |
+
"9999 Now I did watch this when it first came out on... negative\n",
|
| 55 |
+
"\n",
|
| 56 |
+
"[10000 rows x 2 columns]>\n"
|
| 57 |
+
]
|
| 58 |
+
}
|
| 59 |
+
],
|
| 60 |
+
"source": [
|
| 61 |
+
"file_path = \"E:\\Projects\\Sentiment Analysis Project DEPI\\IMDB_Balanced_10000_Rows.csv\"\n",
|
| 62 |
+
"df = pd.read_csv(file_path)\n",
|
| 63 |
+
"print(df.head)"
|
| 64 |
+
]
|
| 65 |
+
},
|
| 66 |
+
{
|
| 67 |
+
"cell_type": "code",
|
| 68 |
+
"execution_count": 6,
|
| 69 |
+
"metadata": {},
|
| 70 |
+
"outputs": [
|
| 71 |
+
{
|
| 72 |
+
"ename": "LookupError",
|
| 73 |
+
"evalue": "\n**********************************************************************\n Resource \u001b[93mstopwords\u001b[0m not found.\n Please use the NLTK Downloader to obtain the resource:\n\n \u001b[31m>>> import nltk\n >>> nltk.download('stopwords')\n \u001b[0m\n For more information see: https://www.nltk.org/data.html\n\n Attempted to load \u001b[93mcorpora/stopwords\u001b[0m\n\n Searched in:\n - 'C:\\\\Users\\\\HP/nltk_data'\n - 'c:\\\\Users\\\\HP\\\\anaconda3\\\\nltk_data'\n - 'c:\\\\Users\\\\HP\\\\anaconda3\\\\share\\\\nltk_data'\n - 'c:\\\\Users\\\\HP\\\\anaconda3\\\\lib\\\\nltk_data'\n - 'C:\\\\Users\\\\HP\\\\AppData\\\\Roaming\\\\nltk_data'\n - 'C:\\\\nltk_data'\n - 'D:\\\\nltk_data'\n - 'E:\\\\nltk_data'\n**********************************************************************\n",
|
| 74 |
+
"output_type": "error",
|
| 75 |
+
"traceback": [
|
| 76 |
+
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
| 77 |
+
"\u001b[1;31mLookupError\u001b[0m Traceback (most recent call last)",
|
| 78 |
+
"File \u001b[1;32mc:\\Users\\HP\\anaconda3\\Lib\\site-packages\\nltk\\corpus\\util.py:84\u001b[0m, in \u001b[0;36mLazyCorpusLoader.__load\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 83\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m---> 84\u001b[0m root \u001b[38;5;241m=\u001b[39m nltk\u001b[38;5;241m.\u001b[39mdata\u001b[38;5;241m.\u001b[39mfind(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msubdir\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m/\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mzip_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 85\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mLookupError\u001b[39;00m:\n",
|
| 79 |
+
"File \u001b[1;32mc:\\Users\\HP\\anaconda3\\Lib\\site-packages\\nltk\\data.py:579\u001b[0m, in \u001b[0;36mfind\u001b[1;34m(resource_name, paths)\u001b[0m\n\u001b[0;32m 578\u001b[0m resource_not_found \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00msep\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00msep\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m--> 579\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mLookupError\u001b[39;00m(resource_not_found)\n",
|
| 80 |
+
"\u001b[1;31mLookupError\u001b[0m: \n**********************************************************************\n Resource \u001b[93mstopwords\u001b[0m not found.\n Please use the NLTK Downloader to obtain the resource:\n\n \u001b[31m>>> import nltk\n >>> nltk.download('stopwords')\n \u001b[0m\n For more information see: https://www.nltk.org/data.html\n\n Attempted to load \u001b[93mcorpora/stopwords.zip/stopwords/\u001b[0m\n\n Searched in:\n - 'C:\\\\Users\\\\HP/nltk_data'\n - 'c:\\\\Users\\\\HP\\\\anaconda3\\\\nltk_data'\n - 'c:\\\\Users\\\\HP\\\\anaconda3\\\\share\\\\nltk_data'\n - 'c:\\\\Users\\\\HP\\\\anaconda3\\\\lib\\\\nltk_data'\n - 'C:\\\\Users\\\\HP\\\\AppData\\\\Roaming\\\\nltk_data'\n - 'C:\\\\nltk_data'\n - 'D:\\\\nltk_data'\n - 'E:\\\\nltk_data'\n**********************************************************************\n",
|
| 81 |
+
"\nDuring handling of the above exception, another exception occurred:\n",
|
| 82 |
+
"\u001b[1;31mLookupError\u001b[0m Traceback (most recent call last)",
|
| 83 |
+
"Cell \u001b[1;32mIn[6], line 2\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;66;03m# stop words to be removed later\u001b[39;00m\n\u001b[1;32m----> 2\u001b[0m custom_stopwords \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mset\u001b[39m(stopwords\u001b[38;5;241m.\u001b[39mwords(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124menglish\u001b[39m\u001b[38;5;124m'\u001b[39m)) \n\u001b[0;32m 3\u001b[0m \u001b[38;5;66;03m# Make sure 'review' is a valid column name\u001b[39;00m\n\u001b[0;32m 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(df\u001b[38;5;241m.\u001b[39mcolumns)\n",
|
| 84 |
+
"File \u001b[1;32mc:\\Users\\HP\\anaconda3\\Lib\\site-packages\\nltk\\corpus\\util.py:120\u001b[0m, in \u001b[0;36mLazyCorpusLoader.__getattr__\u001b[1;34m(self, attr)\u001b[0m\n\u001b[0;32m 117\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m attr \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m__bases__\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m 118\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mAttributeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mLazyCorpusLoader object has no attribute \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m__bases__\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m--> 120\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m__load()\n\u001b[0;32m 121\u001b[0m \u001b[38;5;66;03m# This looks circular, but its not, since __load() changes our\u001b[39;00m\n\u001b[0;32m 122\u001b[0m \u001b[38;5;66;03m# __class__ to something new:\u001b[39;00m\n\u001b[0;32m 123\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mgetattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, attr)\n",
|
| 85 |
+
"File \u001b[1;32mc:\\Users\\HP\\anaconda3\\Lib\\site-packages\\nltk\\corpus\\util.py:86\u001b[0m, in \u001b[0;36mLazyCorpusLoader.__load\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 84\u001b[0m root \u001b[38;5;241m=\u001b[39m nltk\u001b[38;5;241m.\u001b[39mdata\u001b[38;5;241m.\u001b[39mfind(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msubdir\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m/\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mzip_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 85\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mLookupError\u001b[39;00m:\n\u001b[1;32m---> 86\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m e\n\u001b[0;32m 88\u001b[0m \u001b[38;5;66;03m# Load the corpus.\u001b[39;00m\n\u001b[0;32m 89\u001b[0m corpus \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m__reader_cls(root, \u001b[38;5;241m*\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m__args, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m__kwargs)\n",
|
| 86 |
+
"File \u001b[1;32mc:\\Users\\HP\\anaconda3\\Lib\\site-packages\\nltk\\corpus\\util.py:81\u001b[0m, in \u001b[0;36mLazyCorpusLoader.__load\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 79\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 80\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m---> 81\u001b[0m root \u001b[38;5;241m=\u001b[39m nltk\u001b[38;5;241m.\u001b[39mdata\u001b[38;5;241m.\u001b[39mfind(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msubdir\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m/\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m__name\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 82\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mLookupError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m 83\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n",
|
| 87 |
+
"File \u001b[1;32mc:\\Users\\HP\\anaconda3\\Lib\\site-packages\\nltk\\data.py:579\u001b[0m, in \u001b[0;36mfind\u001b[1;34m(resource_name, paths)\u001b[0m\n\u001b[0;32m 577\u001b[0m sep \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m*\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m*\u001b[39m \u001b[38;5;241m70\u001b[39m\n\u001b[0;32m 578\u001b[0m resource_not_found \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00msep\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00msep\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m--> 579\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mLookupError\u001b[39;00m(resource_not_found)\n",
|
| 88 |
+
"\u001b[1;31mLookupError\u001b[0m: \n**********************************************************************\n Resource \u001b[93mstopwords\u001b[0m not found.\n Please use the NLTK Downloader to obtain the resource:\n\n \u001b[31m>>> import nltk\n >>> nltk.download('stopwords')\n \u001b[0m\n For more information see: https://www.nltk.org/data.html\n\n Attempted to load \u001b[93mcorpora/stopwords\u001b[0m\n\n Searched in:\n - 'C:\\\\Users\\\\HP/nltk_data'\n - 'c:\\\\Users\\\\HP\\\\anaconda3\\\\nltk_data'\n - 'c:\\\\Users\\\\HP\\\\anaconda3\\\\share\\\\nltk_data'\n - 'c:\\\\Users\\\\HP\\\\anaconda3\\\\lib\\\\nltk_data'\n - 'C:\\\\Users\\\\HP\\\\AppData\\\\Roaming\\\\nltk_data'\n - 'C:\\\\nltk_data'\n - 'D:\\\\nltk_data'\n - 'E:\\\\nltk_data'\n**********************************************************************\n"
|
| 89 |
+
]
|
| 90 |
+
}
|
| 91 |
+
],
|
| 92 |
+
"source": [
|
| 93 |
+
"# stop words to be removed later\n",
|
| 94 |
+
"custom_stopwords = set(stopwords.words('english')) \n",
|
| 95 |
+
"# Make sure 'review' is a valid column name\n",
|
| 96 |
+
"print(df.columns) "
|
| 97 |
+
]
|
| 98 |
+
},
|
| 99 |
+
{
|
| 100 |
+
"cell_type": "code",
|
| 101 |
+
"execution_count": null,
|
| 102 |
+
"metadata": {},
|
| 103 |
+
"outputs": [
|
| 104 |
+
{
|
| 105 |
+
"name": "stdout",
|
| 106 |
+
"output_type": "stream",
|
| 107 |
+
"text": [
|
| 108 |
+
" review \\\n",
|
| 109 |
+
"0 Starts really well, nice intro and build up fo... \n",
|
| 110 |
+
"1 Terrific movie: If you did not watch yet, you ... \n",
|
| 111 |
+
"2 I've seen hundreds of silent movies. Some will... \n",
|
| 112 |
+
"3 i had been looking for this film for so long b... \n",
|
| 113 |
+
"4 Good: Engaging cinematic firefights, great pre... \n",
|
| 114 |
+
"5 I say this. If you want to see art, you go to ... \n",
|
| 115 |
+
"6 Hey, it's only TV. Sure, it's STAR TREK, the m... \n",
|
| 116 |
+
"7 this movie has lot of downsides and thats all ... \n",
|
| 117 |
+
"8 How can you tell that a horror movie is terrib... \n",
|
| 118 |
+
"9 Brian De Palma's undeniable virtuosity can't r... \n",
|
| 119 |
+
"\n",
|
| 120 |
+
" processed_review \n",
|
| 121 |
+
"0 start realli well nice intro build main charac... \n",
|
| 122 |
+
"1 terrif movi watch yet must watch geena davi sa... \n",
|
| 123 |
+
"2 seen hundr silent movi alway classic nosferatu... \n",
|
| 124 |
+
"3 look film long found seen younger love second ... \n",
|
| 125 |
+
"4 good engag cinemat firefight great present veh... \n",
|
| 126 |
+
"5 say want see art go art galleri want see movi ... \n",
|
| 127 |
+
"6 hey tv sure star trek belov bla bla great one ... \n",
|
| 128 |
+
"7 movi lot downsid that could see pain long aw d... \n",
|
| 129 |
+
"8 tell horror movi terribl stop laugh cours plot... \n",
|
| 130 |
+
"9 brian de palma undeni virtuos realli camouflag... \n"
|
| 131 |
+
]
|
| 132 |
+
}
|
| 133 |
+
],
|
| 134 |
+
"source": [
|
| 135 |
+
"def preprocess_text(text):\n",
|
| 136 |
+
" if isinstance(text, str): # Ensure text is a string\n",
|
| 137 |
+
" ## CLEANING\n",
|
| 138 |
+
" # remove special characters\n",
|
| 139 |
+
" text = re.sub(r'\\W+', ' ', text) \n",
|
| 140 |
+
" # remove digits\n",
|
| 141 |
+
" text = re.sub(r'\\d+', '', text)\n",
|
| 142 |
+
" ## LOWERCASING\n",
|
| 143 |
+
" text = text.lower()\n",
|
| 144 |
+
" ## TOKENIZATION\n",
|
| 145 |
+
" words = text.split()\n",
|
| 146 |
+
" ## REMOVE STOPWORDS\n",
|
| 147 |
+
" words = [w for w in words if w not in custom_stopwords] \n",
|
| 148 |
+
" ## APPLY LEMMATIZATION\n",
|
| 149 |
+
" words = [lemmatizer.lemmatize(w) for w in words]\n",
|
| 150 |
+
" ## APPLY STEMMING\n",
|
| 151 |
+
" words = [stemmer.stem(w) for w in words]\n",
|
| 152 |
+
" return ' '.join(words)\n",
|
| 153 |
+
" return \"\"\n",
|
| 154 |
+
"\n",
|
| 155 |
+
"# Apply preprocessing\n",
|
| 156 |
+
"df['processed_review'] = df['review'].apply(preprocess_text)\n",
|
| 157 |
+
"# Show original vs processed text\n",
|
| 158 |
+
"print(df[['review', 'processed_review']].head(10)) "
|
| 159 |
+
]
|
| 160 |
+
},
|
| 161 |
+
{
|
| 162 |
+
"cell_type": "code",
|
| 163 |
+
"execution_count": null,
|
| 164 |
+
"metadata": {},
|
| 165 |
+
"outputs": [
|
| 166 |
+
{
|
| 167 |
+
"data": {
|
| 168 |
+
"image/png": "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31cg9rceHOb3va7Py6+/C/tuCnUF9bkeZHbu3NmqUaOG+SOX9m3//v3zhCH2ce9+r6jp06dbFStWNGWzPWE/9+7x4OnTp62BAwdaF198sQmv99xzjwlaubfj+pyifP+XtjD9j+/GwQAAAAAgsHBOFAAAAADYQIgCAAAAABsIUQAAAABgAyEKAAAAAGwgRAEAAACADYQoAAAAALCBEAUAAAAANhCiAAAAAMAGQhQA4IL9+uuvEhYWJps3bxZ/sX37drn22mulQoUKctVVV/m6OQCAIEKIAoAg8Oijj5oQM3bsWLflixYtMstD0ejRo6VSpUqyY8cOWb58eb7rpaWlSb9+/aR27dpSvnx5qVu3rgwePFgyMjJKtb0AgMBBiAKAIKEjLuPGjZMjR45IsDh37lyxn7t792658cYbTSiKioryuM4vv/wi11xzjezcuVPmzJkju3btkmnTppnQdd1118mff/4p/vTes7OzJScnxyftAQD8f4QoAAgSnTp1kpiYGBkzZky+67z44ot5prZNmTJFLrvsMrdRrbvvvltee+01qVmzplSrVk1efvllOX/+vDz77LNSvXp1qVOnjsycOdPjFLrrr7/eBLpmzZrJN9984/b4Tz/9JLfffrtcdNFFZtsPP/ywpKenOx+/+eabZdCgQTJkyBCJjo6WLl26eHwfGiS0TdqOiIgI856WLl3qfFxH3zZs2GDW0d/1fXuSmJhoRp+++OILad++vcTFxZn2ffXVV/L777/L888/71z37Nmz8txzz0lsbKx5zYYNG8qMGTOcj2/btk26du0qVapUkcqVK0u7du1MkHO8L31PrrSPta8d9DN45ZVX5JFHHjHbeOKJJ2TWrFmm/z/55BOJj483r7tv3z7TlmeeeUYuvfRSM9rWtm1b+frrr53bcjxv2bJlcsUVV5j+vu222+TAgQNubXjvvfekadOmZru1atUyfe9w9OhRefzxx6VGjRqmPbfccov8+OOPzsf19w4dOpj3qo+3atVK1q9f77GfASDYEKIAIEiULVvWBJ8333xTfvvttwva1ooVK2T//v2yatUqmTx5spkapwHh4osvlh9++EGeeuopefLJJ/O8joas4cOHy6ZNm8xIzl133eWcFqcH5XogfvXVV5uDbQ09Bw8elJ49e7ptY/bs2SbYfPfdd2ZUyJM33nhDJk2aJBMnTpQt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",
|
| 169 |
+
"text/plain": [
|
| 170 |
+
"<Figure size 1000x500 with 1 Axes>"
|
| 171 |
+
]
|
| 172 |
+
},
|
| 173 |
+
"metadata": {},
|
| 174 |
+
"output_type": "display_data"
|
| 175 |
+
}
|
| 176 |
+
],
|
| 177 |
+
"source": [
|
| 178 |
+
"# BAG OF WORDS ASSUMPTION\n",
|
| 179 |
+
"word_counts = Counter(\" \".join(df[\"processed_review\"]).split())\n",
|
| 180 |
+
"# Get word frequencies\n",
|
| 181 |
+
"occurrences_values = list(word_counts.values())\n",
|
| 182 |
+
"\n",
|
| 183 |
+
"# Plot histogram to help decide the threshold that will be used to remove rare words later\n",
|
| 184 |
+
"# threshold here is the minimum number of occurrences for one word so that it will not be considered as a rare word\n",
|
| 185 |
+
"plt.figure(figsize=(10, 5))\n",
|
| 186 |
+
"plt.hist(occurrences_values, bins=range(1, 21), edgecolor='black') # Only show words appearing ≤ 20 times \n",
|
| 187 |
+
"plt.yscale('log') # Log scale helps visualize better\n",
|
| 188 |
+
"plt.xlabel(\"Number of Occurrences\")\n",
|
| 189 |
+
"plt.ylabel(\"Number of Words\")\n",
|
| 190 |
+
"plt.title(\"Word Occurrences Distribution\")\n",
|
| 191 |
+
"plt.show()"
|
| 192 |
+
]
|
| 193 |
+
},
|
| 194 |
+
{
|
| 195 |
+
"cell_type": "code",
|
| 196 |
+
"execution_count": null,
|
| 197 |
+
"metadata": {},
|
| 198 |
+
"outputs": [
|
| 199 |
+
{
|
| 200 |
+
"name": "stdout",
|
| 201 |
+
"output_type": "stream",
|
| 202 |
+
"text": [
|
| 203 |
+
" processed_review \\\n",
|
| 204 |
+
"0 start realli well nice intro build main charac... \n",
|
| 205 |
+
"1 terrif movi watch yet must watch geena davi sa... \n",
|
| 206 |
+
"2 seen hundr silent movi alway classic nosferatu... \n",
|
| 207 |
+
"3 look film long found seen younger love second ... \n",
|
| 208 |
+
"4 good engag cinemat firefight great present veh... \n",
|
| 209 |
+
"5 say want see art go art galleri want see movi ... \n",
|
| 210 |
+
"6 hey tv sure star trek belov bla bla great one ... \n",
|
| 211 |
+
"7 movi lot downsid that could see pain long aw d... \n",
|
| 212 |
+
"8 tell horror movi terribl stop laugh cours plot... \n",
|
| 213 |
+
"9 brian de palma undeni virtuos realli camouflag... \n",
|
| 214 |
+
"\n",
|
| 215 |
+
" processed_review2 \n",
|
| 216 |
+
"0 start realli well nice intro build main charac... \n",
|
| 217 |
+
"1 terrif movi watch yet must watch geena davi sa... \n",
|
| 218 |
+
"2 seen hundr silent movi alway classic nosferatu... \n",
|
| 219 |
+
"3 look film long found seen younger love second ... \n",
|
| 220 |
+
"4 good engag cinemat firefight great present veh... \n",
|
| 221 |
+
"5 say want see art go art galleri want see movi ... \n",
|
| 222 |
+
"6 hey tv sure star trek belov bla bla great one ... \n",
|
| 223 |
+
"7 movi lot downsid that could see pain long aw d... \n",
|
| 224 |
+
"8 tell horror movi terribl stop laugh cours plot... \n",
|
| 225 |
+
"9 brian de palma undeni realli camouflag fact pl... \n"
|
| 226 |
+
]
|
| 227 |
+
}
|
| 228 |
+
],
|
| 229 |
+
"source": [
|
| 230 |
+
"## REMOVE RARE WORDS\n",
|
| 231 |
+
"# Most words appear very few times (1-5 occurrences)\n",
|
| 232 |
+
"# The count drops significantly as frequency increases \n",
|
| 233 |
+
"# we will take a threshold of 4 which Keeps moderately rare words that might still carry meaning but Removes very infrequent words that could add noise\n",
|
| 234 |
+
"\n",
|
| 235 |
+
"threshold = 4\n",
|
| 236 |
+
"# Identify rare words\n",
|
| 237 |
+
"rare_words = {w for w, c in word_counts.items() if c <= threshold} \n",
|
| 238 |
+
"\n",
|
| 239 |
+
"def remove_rare_words(text):\n",
|
| 240 |
+
" if isinstance(text, str): # Ensure text is a string\n",
|
| 241 |
+
" words = text.split()\n",
|
| 242 |
+
" return ' '.join([w for w in words if w not in rare_words])\n",
|
| 243 |
+
" return \"\"\n",
|
| 244 |
+
"\n",
|
| 245 |
+
"# Apply to dataset\n",
|
| 246 |
+
"df['processed_review2'] = df['processed_review'].apply(remove_rare_words)\n",
|
| 247 |
+
"\n",
|
| 248 |
+
"# Check results\n",
|
| 249 |
+
"print(df[['processed_review', 'processed_review2']].head(10))\n"
|
| 250 |
+
]
|
| 251 |
+
},
|
| 252 |
+
{
|
| 253 |
+
"cell_type": "code",
|
| 254 |
+
"execution_count": null,
|
| 255 |
+
"metadata": {},
|
| 256 |
+
"outputs": [
|
| 257 |
+
{
|
| 258 |
+
"name": "stdout",
|
| 259 |
+
"output_type": "stream",
|
| 260 |
+
"text": [
|
| 261 |
+
"Training set size: 8000\n",
|
| 262 |
+
"Testing set size: 2000\n",
|
| 263 |
+
"Training Data:\n",
|
| 264 |
+
" review sentiment\n",
|
| 265 |
+
"9254 The makers have chosen the best people for the... positive\n",
|
| 266 |
+
"1561 Obviously, there wasn't a huge budget for this... positive\n",
|
| 267 |
+
"1670 I turned this off within the first five minute... negative\n",
|
| 268 |
+
"6087 I just want to say that I am so glad somebody ... positive\n",
|
| 269 |
+
"6669 What was I thinking when I rented this one? Wh... negative\n",
|
| 270 |
+
"\n",
|
| 271 |
+
"Testing Data:\n",
|
| 272 |
+
" review sentiment\n",
|
| 273 |
+
"6252 Wow - Thank god I was on an airplane and could... negative\n",
|
| 274 |
+
"4684 Not only was the plot of this film contrived w... negative\n",
|
| 275 |
+
"1731 This movie has the most beautiful opening sequ... positive\n",
|
| 276 |
+
"4742 For three quarters of an hour, the story gradu... negative\n",
|
| 277 |
+
"4521 It's been a long time since such an original, ... positive\n"
|
| 278 |
+
]
|
| 279 |
+
}
|
| 280 |
+
],
|
| 281 |
+
"source": [
|
| 282 |
+
"X = df['review'] \n",
|
| 283 |
+
"y = df['sentiment'] \n",
|
| 284 |
+
"\n",
|
| 285 |
+
"X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
|
| 286 |
+
"\n",
|
| 287 |
+
"print(\"Training set size:\", len(X_train))\n",
|
| 288 |
+
"print(\"Testing set size:\", len(X_test))\n",
|
| 289 |
+
"\n",
|
| 290 |
+
"# ////////////////////////////////\n",
|
| 291 |
+
"print(\"Training Data:\")\n",
|
| 292 |
+
"print(pd.DataFrame({'review': X_train, 'sentiment': y_train}).head())\n",
|
| 293 |
+
"\n",
|
| 294 |
+
"print(\"\\nTesting Data:\")\n",
|
| 295 |
+
"print(pd.DataFrame({'review': X_test, 'sentiment': y_test}).head())"
|
| 296 |
+
]
|
| 297 |
+
}
|
| 298 |
+
],
|
| 299 |
+
"metadata": {
|
| 300 |
+
"kernelspec": {
|
| 301 |
+
"display_name": "base",
|
| 302 |
+
"language": "python",
|
| 303 |
+
"name": "python3"
|
| 304 |
+
},
|
| 305 |
+
"language_info": {
|
| 306 |
+
"codemirror_mode": {
|
| 307 |
+
"name": "ipython",
|
| 308 |
+
"version": 3
|
| 309 |
+
},
|
| 310 |
+
"file_extension": ".py",
|
| 311 |
+
"mimetype": "text/x-python",
|
| 312 |
+
"name": "python",
|
| 313 |
+
"nbconvert_exporter": "python",
|
| 314 |
+
"pygments_lexer": "ipython3",
|
| 315 |
+
"version": "3.11.5"
|
| 316 |
+
}
|
| 317 |
+
},
|
| 318 |
+
"nbformat": 4,
|
| 319 |
+
"nbformat_minor": 2
|
| 320 |
+
}
|
requirements.txt
ADDED
|
Binary file (2.35 kB). View file
|
|
|
sentiment_model.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e67e56ce47206bd9d232f6bbc65f135d6629aa0ccfa48a768ee6ca9894becf18
|
| 3 |
+
size 41071
|
test_data.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tfidf_vectorizer.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:af60c3a3696d3e71075bb33e5cdb8c0f028fc548093f3898385131d697b4e51a
|
| 3 |
+
size 185799
|
train_data.csv
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:67adc65add490db75b4839e04a3ce8130bf2dedd24fe79ea2bd64cfd10072e5e
|
| 3 |
+
size 10501309
|