Sentiment Analysis with Python and Streamlit

Build and deploy your own sentiment classification app using Python and Streamlit

Gourav Bais
Heartbeat

--

Source:Author

Nowadays, working on tabular data is not the only thing in Machine Learning (ML). Data formats like image, video, text, etc., are getting famous with use cases like image classification, object detection, chat-bots, text generation, and more.

One such popular use case is sentiment analysis, the process of determining whether the overall sentiment of a piece of text is positive, negative, or neutral. This is typically done by analyzing words in the text to determine their emotional tone. Sentiment analysis can be used to help businesses understand customer sentiment and make more informed decisions, or to analyze social media posts and other forms of online content.

Sentiment analysis is also important for the customers and consumers because it helps them to better understand the emotional context of a piece of text. By analyzing the words and phrases used in a piece of writing, sentiment analysis algorithms can determine the overall sentiment of the text and provide a more complete understanding of its meaning.

In this article, you will learn about what sentiment analysis is and how you can build and deploy a sentiment analysis system in Python.

Sentiment Analysis Use Cases

There are many potential use cases for sentiment analysis. Some examples include:

  • Social Media Monitoring: Sentiment analysis can be used to track and analyze customer sentiment on social media platforms, such as Reddit or Facebook, and help businesses understand how their customers feel about their products or services.
  • Customer Service: It can be used to analyze customer feedback and help businesses identify areas where they need to improve their products or services.
  • Marketing: It can be used to help businesses understand how their marketing campaigns are being received by their target audience, and make more informed decisions about how to adjust their marketing strategies.
  • Political Analysis: It can also be used to track and analyze public opinion on political issues, candidates, or parties, and help political organizations make more informed decisions.

Sentiment analysis can also be used in a variety of other industries, such as healthcare, finance, and education, to help organizations understand and make better use of text data.

Ways of Implementing the Sentiment Analysis Systems

Broadly, there are two different ways to implement a sentiment analysis system, which are:

Rule-based systems

In a rule-based system, a set of pre-defined rules are used to determine the sentiment of a piece of text. For example, a rule might state that if a piece of text contains certain positive words, such as “happy” or “love,” it is considered to be positive, while if it contains certain negative words, such as “sad” or “angry,” it is considered to be negative. This approach is mostly referred to for small datasets where ML models can not be effective. But this rule-based approach can not handle things like Irony and sarcasm, multiple types of negations, word ambiguity, and multipolarity. For example “I love being ignored” seems to be a positive text but the rule-based approach may tag it as a negative sentiment which is why ML-based sentiment analysis is more popular across the industry.

Machine learning Based Systems

In a machine learning-based system, a model is trained on a large dataset of labeled text data, where the sentiment of each piece of text has been manually annotated. Different ML models use this training data to learn the relationship between the words and phrases in the text and their corresponding sentiment labels, and can then be applied to new, unseen text data to predict the sentiment of that data. Multiple approaches are used for ML-based sentiment analysis, some of the popular ones are LSTM-based Models, Transformer-based Models, Bag of Words Vectorization-based Models, Text Blob based approaches, etc.

In this tutorial, you will see Machine Learning based approach using LSTM to build the sentiment analysis model.

Reddit Threads Sentiment Analysis Model

Political parties and media houses have been using social media platforms like Reddit, Meta, etc., to understand the wave among the people. They use the posts, comments, and threads to analyze the sentiment and recognize the emotion. Then they use these patterns to understand the public’s behavior and predict the election results, thus making more informed political strategies based on population clusters.

In this section, you will see a step-by-step implementation of Reddit political threads sentiment analysis. You will build a web app using Python and Streamlit to recognize the emotion of the user’s reddit threads.

The model-building process involves Natural Language Processing, Deep Learning techniques, and Python libraries. So let’s get the buggy war started! Here we will use Anaconda environment and Jupyter notebook for the model-building process and VS Code for writing the Streamlit app. You can use any IDE or tool to build this app.

Note: Focus of this article is to introduce you to sentiment analysis modeling and the deployment process.

Download Dataset and Create a Virtual Environment

First, you need to the download Reddit Threads dataset from Kaggle. Then you need to open your anaconda prompt and create a virtual environment using the following command:

$ conda create -n sentiment python==3.9

Note: Creating a virtual environment is not mandatory, but it is a good practice as it helps to keep different development environments separate.

Then you need to open the Jupyter notebook in this environment with the jupyter notebook command to start coding the solution.

Load Dependencies and Dataset

Once you have your jupyter-notebook ready, you can start importing the required libraries and dataset as follows:

# Importing required libraries
import pickle
import nltk
import pandas as pd

# preprocessing dependencies
from nltk.corpus import stopwords
from textblob import Word
from sklearn.preprocessing import LabelEncoder
from collections import Counter

# model dependencies
from sklearn.metrics import classification_report,confusion_matrix,accuracy_score
from keras.models import Sequential
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
from keras.layers import Dense, Embedding, LSTM, SpatialDropout1D
from sklearn.model_selection import train_test_split
from keras.metrics import Precision, Recall

# plot dependencies
import matplotlib.pyplot as plt
import wordcloud

Once you have all the dependencies loaded, it is time to load the dataset CSV file for further processing.

# load dataset
reddit = pd.read_csv('Reddit_Data.csv')
reddit.head()
Image captured by Author

Pandas read_csv() function is used to read the dataset while head() function shows the first few rows of the dataset.

Now you need to perform some EDA and cleaning on the data after loading it into the notebook.

EDA and Data Cleaning

First, you will check the frequency of the target variable: Category. This variable denotes the type of emotions represented by the Reddit threads.

There are three different sorts of emotion: Positive, Negative, and Neutral. The dataset contains almost 42.5% positive, 35.28% normal, and 22.22% negative comments.

# check the distribution of values
reddit['category'].value_counts()
Image captured by Author

Following, you need check the missing values in the dataset, and if there is any, you need to drop them to clean the data frame.

# check NaN values
print(reddit.isna().sum())
# drop NaN values
df = reddit.dropna()

Once you have your dataset ready, you can visualize the most common words in your dataset using the word cloud as follows:

# check common words with the word cloud
common_words=''
for i in data_v1.clean_comment:
i = str(i)
tokens = i.split()
common_words += " ".join(tokens)+" "
wordcloud = wordcloud.WordCloud().generate(common_words)
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis("off")
plt.show()
Image captured by Author

You need to simply iterate over your text corpus to create a list of common words and use the wordcloud library to generate the wordcloud. After this, you need to perform some of the text cleaning steps to normalize the text for model training. The following steps make up this cleaning process:

  • Text Lowercase: To make the text uniform, we need to convert all the camel case, title case, etc. text to lowercase.
  • Digits Removal: We only want to focus on the textual data for which we need to remove digits as these digits do not make sense for sentiment analysis.
  • Stopwords Removal: Some regular words come most often in the text but do not contribute much towards analysis. We need to remove these words from our text corpus.
  • Lemmatize Text: Lemmatization is the process of grouping together words with the same root. For example words “cat,” “cats” and “cat’s” would have the root word “cat.” This method normalizes the text to their root word so that each similar word would represent the same meaning.
# clean dataset
def cleaning(df, stop_words):
df['clean_comment'] = df['clean_comment'].apply(lambda x:' '.join(x.lower() for x in x.split()))
# Replacing the digits/numbers
df['clean_comment'] = df['clean_comment'].str.replace('^\d+\s|\s\d+\s|\s\d+$', '')
# Removing stop words
df['clean_comment'] = df['clean_comment'].apply(lambda x:' '.join(x for x in x.split() if x not in stop_words))
# Lemmatization
df['clean_comment'] = df['clean_comment'].apply(lambda x:' '.join([Word(x).lemmatize() for x in x.split()]))
return df

stop_words = stopwords.words('english')
data_v1 = cleaning(data_v1, stop_words)
data_v1.head()
Image captured by Author

In the above function, we are simply iterating over the clean_comment column of the dataset and first converting all the text to lowercase. Then we are using a regular expression to remove digits from the text. You will also notice that we are creating a list of stop_words which stores all the regular words we want to remove from text. After removing stopwords, we are using textblob library to lemmatize the text which would finally give us the required cleaned text.

Comet Artifacts lets you track and reproduce complex multi-experiment scenarios, reuse data points, and easily iterate on datasets. Read this quick overview of Artifacts to explore all that it can do.

Text Preprocessing and Model Building

Now that you have clean text sequences, you need to encode the target variable using LabelEncoder() to normalize it.

# Encoded the target column
lb=LabelEncoder()
data_v1['category'] = lb.fit_transform(data_v1['category'])
data_v1.head()
Image captured by Author

Since you have three different categories, Negative, Neutral, and Positive. The encoded values are 0, 1, and 2 respectively.

One thing that you must be aware of is thatML/DL models cannot be trained on common language, such as text of made up of sentences. You need to first convert the sentences into words and then convert them to numbers, this process is called tokenization. Keras library already provides an implementation for tokenizers where you just need to pass the text and the maximum number of tokens you want your model to generate.

# tokenize input text
tokenizer = Tokenizer(num_words=500, split=' ')
tokenizer.fit_on_texts(data_v1['clean_comment'].values)
X = tokenizer.texts_to_sequences(data_v1['clean_comment'].values)
X = pad_sequences(X)

You might also be aware that different text may have different lengths in the dataset, which is not ideal while working with LSTM models. We need to make inputs of the same length, for which we are using pad_sequence() functions that pads the input with 0's to make them all of the same length.

Whatever preprocessing or data cleaning you apply to text during the training phase needs to be replicated for testing and production as well. For this reason, you need to save this tokenizer object that converts your input text to tokens for model input.

# saving tokenizer
with open('tokenizer_LSTM.pkl', 'wb') as handle:
pickle.dump(tokenizer, handle, protocol=pickle.HIGHEST_PROTOCOL)

Then, you need to split the data into training and testing sets which would be used for model training and testing respectively.

# Splitting the data into training and testing
y = pd.get_dummies(data_v1['category'])
X_train, X_test, y_train, y_test = train_test_split(X,y, test_size = 0.3, random_state = 42)

As discussed above, you will use an LSTM model for predicting the sentiments. We will use LSTM with Keras for building the predictive model. The architecture of the models is as follows:

# create model architecture
model = Sequential()
model.add(Embedding(500, 120, input_length = X.shape[1]))
model.add(SpatialDropout1D(0.4))
model.add(LSTM(176, dropout=0.2, recurrent_dropout=0.2))
model.add(Dense(3,activation='softmax'))
model.compile(loss = 'categorical_crossentropy',
optimizer='adam',
metrics = ['accuracy', Precision(), Recall()])
print(model.summary())
Image captured by Author

As we want to create a cascade model where one layer’s output goes as the input of another one, we are creating an object of the Sequential() pipeline. Then an Embedding layer is used that takes the input of shape 464. We have also added a Dropout with a 40% probability to avoid the model overfitting. Then an LSTM layer is used with 176 neurons, and finally, we have added a flattening layer Dense with 3 output categories indicating our output classes.

Since we have a non-binary target column, the loss function used is categorical_crossentropy. Also adam optimizer is used as a learning strategy. Finally, for evaluation, we are using accuracy, precision, and recall scores.

# run model 
batch_size=32
history = model.fit(X_train, y_train,
validation_data = (X_test, y_test),
epochs = 5, batch_size = batch_size,
verbose = 'auto')
Image captured by Author

Then we train the model on the training set for 5 epochs and validate using the validation set while keeping the batch size as 32.

Model Evaluation

We will begin the model evaluation using the test set. And we will check the evaluation metrics such as accuracy, precision, and recall.

# Analyze the model using the test data.
model.evaluate(X_test,y_test)
Image captured by Author

Then we will plot charts for accuracy and loss using the following function.

def plot_training_hist(history):
'''Plot history for accuracy and loss'''
fig, ax = plt.subplots(1, 2, figsize=(10,4))
# first plot
ax[0].plot(history.history['accuracy'])
ax[0].plot(history.history['val_accuracy'])
ax[0].set_title('Model Accuracy')
ax[0].set_xlabel('epoch')
ax[0].set_ylabel('accuracy')
ax[0].legend(['train', 'validation'], loc='best')
# second plot
ax[1].plot(history.history['loss'])
ax[1].plot(history.history['val_loss'])
ax[1].set_title('Model Loss')
ax[1].set_xlabel('epoch')
ax[1].set_ylabel('loss')
ax[1].legend(['train', 'validation'], loc='best')

plot_training_hist(history)
Image captured by Author

Note: You will see that model is starting to overfit after epoch 4 as validation loss begins to increase.

At last, we also plot the heatmap for the confusion matrix for the results on the test set.

# check the model's performance on testing data
import numpy as np
from sklearn.metrics import confusion_matrix
import seaborn as sns

def plot_confusion_matrix(model, X_test, y_test):
'''Function to plot confusion matrix for the passed model and the data'''

sentiment_classes = ['Negative', 'Neutral', 'Positive']
# use the model to do the prediction
y_pred = model.predict(X_test)
# compute the confusion matrix
cm = confusion_matrix(np.argmax(np.array(y_test),axis=1), np.argmax(y_pred, axis=1))
# plot confusion matrix
plt.figure(figsize=(8,6))
sns.heatmap(cm, cmap=plt.cm.Blues, annot=True, fmt='d',
xticklabels=sentiment_classes,
yticklabels=sentiment_classes)
plt.title('Confusion matrix', fontsize=16)
plt.xlabel('Actual label', fontsize=12)
plt.ylabel('Predicted label', fontsize=12)

plot_confusion_matrix(model, X_test, y_test)
Image captured by Author

As you can observe, the model is doing a great job in classifying the positive and neutral threads while performing average for the negative class. For this, you can go ahead and try the hyperparameter tuning, sampling methods to make the data classes balanced, to improve the performance of the model.

You save the model as an h5 file for our web app to make predictions on the deployed server for the live users.

# save model
model.save('model_LSTM.h5')

Streamlit App

Now that you have built a predictive model, it is time to build an app using the Streamlit Python framework. Let’s start by installing Streamlit in the same sentiment anaconda environment as follows:

$ pip install streamlit

Now you will create an app with the name app.py that would contain all the dependencies, tokenizer object, LSTM model for prediction, and finally a flow for how to take input from the user and make prediction out of it. Let’s start by importing the necessary dependencies:

# load dependencies
import re
import nltk
import pickle
import numpy as np
import pandas as pd
import streamlit as st

from textblob import Word
from keras import backend as K
from nltk.corpus import stopwords
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.sequence import pad_sequences

Then you need to define the path of the model and tokenizer from where you will load them.

# path of the model
MODEL_PATH = r"model_LSTM.h5"
# maximum number of the allowed word in an input
max_words = 500
# shape of input data passed for prediction
max_len = 464
# path of tokenizer file
tokenizer_file = 'tokenizer_LSTM.pkl'

Then you need to load the tokenizer file to tokenize the text as done while training the model.

# load tokenizer
with open(tokenizer_file,'rb') as handle:
tokenizer = pickle.load(handle)

Then you need to create a function that would clean the input text in the same way as it was done during training.

# apply text cleaning to input data
def text_cleaning(line_from_column):
text = line_from_column.lower()
# Replacing the digits/numbers
text = text.replace('d', '')
# remove stopwords
words = [w for w in text if w not in stopwords.words("english")]
# apply stemming
words = [Word(w).lemmatize() for w in words]
# merge words
words = ' '.join(words)
return text

You need to also define the function that loads the classification model as follows.

# load the sentiment analysis model
@st.cache(allow_output_mutation=True)
def Load_model():
model = load_model(MODEL_PATH)
model.summary() # included making it visible when the model is reloaded
session = K.get_session()
return model, session

The Load_model() function will load the model and start the session.

At last, you need to define the execution flow of the app. Here, You need to call your functions to clean and preprocess the text sentence and the model to predict the sentiment of the comment. Then you will show the sentiment output in your app based on the prediction.

if __name__ == '__main__':
st.title('Political Reddit Threads Sentiment Classification app')
st.write('A simple sentiment analysis classification app')
st.subheader('Input the Reddit Thread below')
sentence = st.text_area('Enter your thread here',height=200)
predict_btt = st.button('predict')
model, session = Load_model()
if predict_btt:
clean_text = []
K.set_session(session)
i = text_cleaning(sentence)
clean_text.append(i)
sequences = tokenizer.texts_to_sequences(clean_text)
data = pad_sequences(sequences, maxlen = max_len)
# st.info(data)
prediction = model.predict(data)
prediction_prob_negative = prediction[0][0]
prediction_prob_neutral = prediction[0][1]
prediction_prob_positive= prediction[0][2]
prediction_class = prediction.argmax(axis=-1)[0]
print(prediction.argmax())
st.header('Prediction using LSTM model')
if prediction_class == 0:
st.warning('Thread has negative sentiment')
if prediction_class == 1:
st.success('Thread has neutral sentiment')
if prediction_class==2:
st.success('Thread has positive sentiment')

You can run the app using the following command:

$ streamlit run app.py

After running the app, open the localhost that runs your app and provide the text input into the box and click on the prediction button. It will return the sentiment of your text.

Model Deployment

You have built a predictive model and a web application for sentiment analysis! Since you have tested your app in the local environment, it is time to deploy it on a cloud server. You will use Streamlit cloud to deploy the app and GitHub to store the code and other requirements.

You need to provide all the dependencies into one file to host the app. You can create a requirements.txt file that stores all the dependencies using the following command:

$ pip freeze -r requirements.txt

Note: Make sure you run it into the same Anaconda environment. For us, this environment is sentiment.

After creating the requirements.txt file, you need to create a repository on GitHub and upload all the required files, including app.py, requirements.txt, and model_LSTM.h5, tokenizer_LSTM.pkl, and Jupyter notebook.

Then you need to create an account on Streamlit cloud. Streamlit cloud is very easy to deploy the app on and it is free of cost. Although it allows you to host a single app only, but that is what we need for now.

Image captured by Author

After creating the account, you need to connect our GitHub repo to integrate your app. You also need to provide all the permissions, including access to a private repository (if this repository is hidden.)

Image captured by Author

The next step is to search for your app after clicking on the New App icon. It automatically integrates the repository once you select it. After some time, you will have your app baked out of the oven. You can share the link of your app with whomever you want by providing access or making it public.

Image captured by Author

That’s it, you have built a sentiment classification system that is now live and anyone can use it to make predictions.

Image captured by Author

Conclusion

After reading this article, you now know how you can create a Sentiment Analysis app with Python. You have also seen building an app and deploying it to the Streamlit Cloud. The entire code can be found here.

In case of any questions, you can connect with me on LinkedIn or Twitter.

Editor’s Note: Heartbeat is a contributor-driven online publication and community dedicated to providing premier educational resources for data science, machine learning, and deep learning practitioners. We’re committed to supporting and inspiring developers and engineers from all walks of life.

Editorially independent, Heartbeat is sponsored and published by Comet, an MLOps platform that enables data scientists & ML teams to track, compare, explain, & optimize their experiments. We pay our contributors, and we don’t sell ads.

If you’d like to contribute, head on over to our call for contributors. You can also sign up to receive our weekly newsletter (Deep Learning Weekly), check out the Comet blog, join us on Slack, and follow Comet on Twitter and LinkedIn for resources, events, and much more that will help you build better ML models, faster.

--

--