<jupyter_start><jupyter_text>Sentiment Analysis: Classifying IMDB Movie Reviews**Data:** A series of 50,000 movie reviews from IMDB pairing review text with observed sentiment.**Data Source:** https://www.kaggle.com/lakshmi25npathi/imdb-dataset-of-50k-movie-reviews**Classification Type:** Binary **Classification Categories:** `positive` or `negative`.<jupyter_code># Imports
import os, re
import numpy as np
import pandas as pd

# NLTK
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import sent_tokenize, word_tokenize, RegexpTokenizer

# SpaCy for lemmatization
import spacy

# sk-learn feature extraction
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer

# sk-learn classifiers
from sklearn.naive_bayes import MultinomialNB
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier

from sklearn.model_selection import GridSearchCV

# sk-learn evaluation metrics
from sklearn.metrics import roc_auc_score
from sklearn.metrics import accuracy_score
from sklearn.metrics import roc_curve

# Visualization
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline<jupyter_output><empty_output><jupyter_text>Load Data, Text Pre-Rocessing<jupyter_code>df = pd.read_csv('IMDB Dataset.csv')
df.head()
df['sentiment'].unique()
print('N = {}'.format(len(df)))
print('N_positive = {}'.format(len(df[df['sentiment'] == 'positive'])))
print('N_negative = {}'.format(len(df[df['sentiment'] == 'negative'])))
raw_docs = df['review'].tolist()
%%time

# Use Regex tokenizer to eliminate non-alpha-numeric characters:
tokenizer = RegexpTokenizer(r'\w+')
raw_tokens = [tokenizer.tokenize(s.lower()) for s in raw_docs]
# Preview:
raw_tokens[0][:10]
%%time

# Eliminate stopwords & keep only words with length greater than 2:
stop_words = list(set(stopwords.words('english')))
tokens_nostops = [[w.lower() for w in lst if w.lower() not in stop_words and len(w) > 2] for lst in raw_tokens]
# Preview without stopwords:
tokens_nostops[0][:10]
# SpaCy lemmatizer:
def lemmatize(docs, allowed_postags=['NOUN','ADJ','ADV','VERB']):
    texts = []
    for i, s in enumerate(docs):
        #print('Working on doc #{}...'.format(i))
        # We need to rebuild 'sentences' for the lemmatizer:
        d = nlp(' '.join(s))
        
        # Keep the tokens in the permissable POS tags
        texts.append([token.lemma_ for token in d if token.pos_ in allowed_postags]) # this is a gerneator, so MUST return WITHIN a list.
    return texts
%%time

# This will take some time on this dataset:
nlp = spacy.load('en') # this is the 'nlp' variable from the lemmatize() func
lemma = lemmatize(tokens_nostops)
# Add the lemmatized tokens to the dataframe:
df['tokens'] = lemma
df.head()
%%time

# Transform the lemma into 'sentence' form for vectorization:
df['train'] = df['tokens'].apply(lambda x: ' '.join(x))
df.head()
df = df[['review', 'tokens', 'train', 'sentiment']]
df.head()
# Code the Y variable:
df['coded_sent'] = df['sentiment'].map({'positive': 1, 'negative': 0})
df.head()
# Save the current dataframe for ease of use in future:
df.to_csv('IMDB_tokenized.csv')<jupyter_output><empty_output><jupyter_text>PredictionTry several alogrithms- Multinomial Naive Bayes- Logistic Reression- K Nearest Neighbors  Multinomial Naive Bayes Classifier:<jupyter_code># Split into training and testing data:
X_train, X_test, y_train, y_test = train_test_split(df['train'], df['coded_sent'], test_size=0.33, random_state=0)
print('X_train N = {}'.format(len(X_train)))
print('X_test N = {}'.format(len(X_test)))
# Count vectorize the training data:
vect = CountVectorizer().fit(X_train)
X_train_vect = vect.transform(X_train)

# Fit the naive bayes model:
mdl = MultinomialNB(alpha=0.1).fit(X_train_vect, y_train)

# Predict:
pred = mdl.predict(vect.transform(X_test))
pred_proba = mdl.predict_proba(vect.transform(X_test))[::,1]

# Evaluate:
print('Accuracy: {}'.format(accuracy_score(y_test, pred)))
print('AOC score: {}'.format(roc_auc_score(y_test, pred_proba)))
# Try TF-IDF:
vect = TfidfVectorizer().fit(X_train)
X_train_vect = vect.transform(X_train)
clfr = MultinomialNB(alpha=0.1).fit(X_train_vect, y_train)

# Predict:
pred = mdl.predict(vect.transform(X_test))
pred_proba = mdl.predict_proba(vect.transform(X_test))[::,1]

# Evaluate:
print('Accuracy: {}'.format(accuracy_score(y_test, pred)))
print('AOC score: {}'.format(roc_auc_score(y_test, pred_proba)))
# Seems to be worse performance -- use CountVectorizer() instead:
# Count vectorize the training data:
vect = CountVectorizer().fit(X_train)
X_train_vect = vect.transform(X_train)
%%time

# Grid Search & Cross Validation for Best Model:
grid_params = {'alpha': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], 'fit_prior': [True, False]}

model = MultinomialNB()
mnb = GridSearchCV(estimator=model, param_grid=grid_params, cv=10, n_jobs=-1, scoring='accuracy')
fitted_model = mnb.fit(X_train_vect, y_train)
best_model = fitted_model.best_estimator_
print('Best Parameters: {}'.format(best_model))
# Predict:
pred = best_model.predict(vect.transform(X_test))
pred_proba = best_model.predict_proba(vect.transform(X_test))[::,1]
print('Accuracy: {}'.format(accuracy_score(y_test, pred)))
print('AUC Score: {}'.format(roc_auc_score(y_test, pred_proba)))
# Plot an ROC Curve
fpr, tpr, _ = roc_curve(y_test, pred_proba)
auc = roc_auc_score(y_test, pred_proba)
plt.style.use('seaborn')
plt.figure(figsize=(12,8))
plt.plot(fpr, tpr, label='AUC={}'.format(auc))
plt.legend(loc=4)
plt.title('Predicting IMBD Movie Review Sentiment\n(Multinomial Naive Bayes)')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.margins(0,0)
plt.show()<jupyter_output><empty_output><jupyter_text>Logistic Regression Classifier:<jupyter_code>%%time
# Try logistic regression
lr = LogisticRegression()
grid_params = {'C': np.logspace(-3,3,7), 'penalty': ['l1', 'l2']}
lr_cv = GridSearchCV(lr, grid_params, cv=10, n_jobs=-1, scoring='accuracy')
lr_cv.fit(X_train_vect, y_train)
print('Best parameters: {}'.format(lr_cv.best_params_))
print('Accuracy: {}'.format(lr_cv.best_score_))
# Evaluate Accuracy & AUC:
pred = lr_cv.predict(vect.transform(X_test))
pred_proba = lr_cv.predict_proba(vect.transform(X_test))[::,1]
print('Accuarcy: {}'.format(accuracy_score(y_test, pred)))
print('Area Under Curve: {}'.format(roc_auc_score(y_test, pred_proba)))
# Plot an ROC Curve
fpr, tpr, _ = roc_curve(y_test, pred_proba)
auc = roc_auc_score(y_test, pred_proba)
plt.style.use('seaborn')
plt.figure(figsize=(12,8))
plt.plot(fpr, tpr, label='AUC={}'.format(auc))
plt.legend(loc=4)
plt.title('Predicting IMBD Movie Review Sentiment\n(Logistic Regression Classifier)')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.margins(0,0)
plt.show()<jupyter_output><empty_output><jupyter_text>KNN Classifier:- Extremely slow & computationally expensive.<jupyter_code># X = df['train'].tolist()
# X_vect = TfidfVectorizer().fit_transform(X)
# # Data fits to the model differently; redo train/test split
# X_train, X_test, y_train, y_test = train_test_split(X_vect, df['coded_sent'], test_size=0.33)
# %%time

# # Try a general, untuned, model:
# model = KNeighborsClassifier(n_neighbors=5)
# model.fit(X_train, y_train)
# y_pred = model.predict(X_test)
# y_pred_proba = model.predict_proba(X_test)[::,1]
# print('Accuracy: {}'.format(accuracy_score(y_test, y_pred)))
# print('AUC Score: {}'.format(roc_auc_score(y_test, y_pred_proba)))
# # We can do better:
# params = {'n_neighbors': [10, 20, 30, 40, 50], 'p': [1,2,3], 'leaf_size': [10,30,50]}
# knn = KNeighborsClassifier()
# model = GridSearchCV(knn, params, cv=10, n_jobs=-1, scoring='accuracy')
# %%time
# model.fit(X_train, y_train)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14924232777282567, 'fraction_numerical': 0.012861326881446582, 'mean_word_length': 6.692458374142997, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 22, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '397596', 'license': 'MIT', 'n_tokens_mistral': 3040, 'n_tokens_neox': 2891, 'n_words': 647, 'path': 'IMDB Sentiment.ipynb', 'repo_name': 'doodyology/machine-learning', 'size': '7853'}