An Intro to Natural Language Processing in Python: Framing Text Classification in Familiar Terms

Natural language processing (NLP) is a broad field encompassing many different tasks such as text search, translation, named entity recognition, and topic modeling. On a daily basis, we use NLP whenever we search the internet, ask a voice assistant to tell us the weather forecast, or translate web pages written in another language. Businesses use NLP to understand how their customers talk about their product on social media. NLP may even help you receive better healthcare, as the healthcare industry applies it to electronic health records to better understand patients.

A core concern in NLP is how to best represent text data so that a computer can make use of it. In this post, I take a look at text classification to demonstrate a few common and successful methods of representing text data. While text classification has some unique characteristics — it requires labeled data, unlike other areas of NLP like topic modeling — it shares much of the text processing steps with the rest of the field. This post is aimed at analysts and data scientists familiar with modeling but new to working with text, and all of the code examples are written in Python.

If you would like to follow along with the code, you can download the dataset here . After unzipping, you’ll find a tab-delimited file called “imdb_labelled.txt” which contains a set of IMDB movie reviews and their associated sentiment (1 for positive, 0 for negative). Our task will be to predict whether a movie review was positive or negative using the text of the movie review. You can find the complete code to build a text classifier in the final section, Putting it All Together.

Organizing Your Text: Bag-of-Words

We often call text data “unstructured” because unlike data in a spreadsheet, text data isn’t naturally represented with numbers in a way that can be put into machine learning algorithms. The first step in any text classification process is transforming the text data into a structured form which we can feed into our algorithm of choice.

Bag-of-words is the simplest approach to turning text into a structured form useful for machine learning. This method translates each text document into a row that counts the number of times every word appeared in the text. This output is often called the term-document matrix. Here is what bag-of-words looks like when applied to a few (fake) movie reviews:

The bag-of-words procedure starts by finding the vocabulary, or all of the unique words across all of the text documents. Each unique word is assigned a column number [2]. Here, “loved” is the first column, “movie” is the second column, “great” is the third column and so on. The ellipsis indicates that there are more word columns that aren’t shown but follow the same pattern.

Once we have a column for each word in our vocabulary, we can then start counting the number of times a word appears in each text document. In the first text document, the word “great” appears 3 times, so we put a 3 in the “great” column. In the second document, “awful” appears once, so we put a 1 in the “awful” column. For words that don’t appear in a text document, we put a 0.

You might have noticed that during this transformation, we’ve lost information about word order. Once we transform “I loved this movie! It was great, great, great” into the row of data [1, 1, 3, 0, …], we can longer tell if “movie” came before or after “loved” in the original text. Whenever we use bag-of-words, we make the assumption that word order doesn’t matter. This is a bad assumption since we know a sentence like:

“She put on her shoes and left.”

has a different meaning than:

“She put on her left shoe.”

…despite the fact they use nearly the same words. Yet, in practice we can often throw away word order and still produce very useful models. If we are trying to classify whether a movie review is positive or negative, for example, word order might not matter as much as how many times someone wrote “great” or “awful” in their review.

The scikit-learn library includes a nice utility for bag-of-words called CountVectorizer. We can transform our toy IMDB dataset into a term-document matrix with just a few lines of code in Python:

import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer

filename = ‘imdb_labelled.txt’
names = [‘text’, ‘label’]
df = pd.read_csv(filename, header=None, names=names, sep=’t’, quoting=3)

bow = CountVectorizer()
X = bow.fit_transform(df.text)

Voila! We have a term-document matrix stored in the variable X.

Finding the Words that Matter: tf-idf

A common issue we have with text analysis is that some words are much more frequent than others and aren’t useful for classification. For example, words like “the”, “is”, and “a” are common English words that don’t convey much meaning. These words will differ from task to task depending on the domain of the text documents. If we are working with movie reviews, the word “movie” will be frequent but not useful. If we were working with email data, on the other hand, the word “movie” may not be frequent and would be useful.

The simplest way to account for these overrepresented words is to divide word count by the proportion of text documents each word appeared in. For example, the document:

“I loved this movie! It was great, great, great.”

…contains the word “loved” and “movie” once each. Now, let’s suppose that we look at all the other documents and find that, in total, “loved” appears in 1% of text documents and “movie” appears in 33%. We could now weight our scores as

“loved” = times it appears in text / proportion of texts it appears in = 1 / 1%
“movie” = times it appears in text / proportion of texts it appears in = 1 / 33%

Before applying weights, both “loved” and “movie” had a score of 1 (since each word appeared in the sentence once). After we apply weights, “loved” has a score of 100 and “movie” has a score of 3. The score for “loved” is much higher relative to “movie”, indicating that we care about the word “loved” much more than “movie”.

In fact, our score for “loved” is now 33 times larger than our score for “movie”. While we suspect that “movie” should be less important than “loved” for predicting whether a review is positive or negative, this relative difference might be too big. Very rare words — perhaps, misspelled words — will receive too much relative weight in our current weighting scheme.

We need to strike a balance between downweighting very frequent words without overweighting rare words. This is what term frequency–inverse document frequency (tf-idf) weighting does for us. In the simple weighting scheme, we used the formula:

times a word appears in text * (1 / proportion of texts it appears in)

tf-idf weighting alters this formula slightly by taking the log of the second term:

times a word appears in text * log(1 / proportion of texts it appears in)

By taking the log, we ensure that our weight changes slowly in relation to how frequently a word appears in all our documents. This means that while common words are downweighted, they aren’t downweighted too much. (There’s also a connection to information theory, too).

Let’s apply tf-idf weighting to the bag-of-words matrix we built earlier. The values in the bag-of-words matrix measure the term frequency, which is the left term of the formula above. After counting the total number of documents and how many documents each word appears, we can create a matrix of weights. This is our inverse document frequency, which is the right term of the formula. Multiply these two matrices together elementwise and we create our final tf-idf weighted term-document matrix.

After applying tf-idf weighting, we’re now giving “loved”, “great” and “awful” higher importance relative to “movie”, since “movie” was so common across all text documents. These weights will help our models since they will bias the models to pay attention to what we care about and to ignore less important words.

As bag-of-words is commonly followed by tf-idf weighting, scikit-learn includes a single utility which combines both steps: TfidfVectorizer. We can replace CountVectorizer in our code above with TfidfVectorizer, and we’ll get a tf-idf weighted term-document matrix.

from sklearn.feature_extraction.text import TfidfVectorizer

tfidf = TfidfVectorizer()
X = tfidf.fit_transform(df.text)

Working in High-Dimensional Space

As mentioned above, when using bag-of-words or tf-idf weighting, our term-document matrix has a column for each word in our vocabulary. It’s not unusual to have a vocabulary of tens of thousands of words. This means our term-document matrix will often have tens of thousands of columns or more. Such a wide matrix creates a few problems:

* Wide matrices use a large amount of computer memory.
* Having so many columns can make it difficult to train models that perform well on new, unseen data.

We can partially solve the first issue by being smart about how we store our matrix in memory. Instead of a standard matrix where we store all the values, we use a  sparse matrix.In a sparse matrix, only the non-zero values are stored, and everything else is assumed to be zero. This works well for term-document matrices since each text document only uses a small subset of all the words in the vocabulary. In other words, each row in the term-document matrix will be mostly zeros, and the sparse matrix will use significantly less memory than a standard matrix.

Code that was written for standard matrices will often not handle sparse matrices without modification. Fortunately, many of scikit-learn’s algorithms support sparse matrices, making text data much easier to work with in Python. In fact, CountVectorizer and TfidfVectorizer will return sparse matrices by default, and many classifiers and regressors will handle sparse matrix inputs natively.

To address the second issue with high dimensional data (i.e. generalizability of the model), we have a few options. First of all, we can model the wide matrix directly using an algorithm like scikit-learn’s LogisticRegression. This model uses a technique called regularization to pick the columns that are relevant for classification and filter out those that aren’t. In practice, this often works well and is relatively simple to use and implement. However, it doesn’t take advantage of similarities between words.

We can potentially do better by compressing our term-document matrix before we begin to model. Instead of directly modeling a wide dataset, we first reduce the number of dimensions of our data and build a model on the much narrower dataset. This compression is very much like clustering, where a text document is assigned a score for each cluster to measure how associated it is with the cluster.

The intuition behind this idea is that we expect certain words like “enjoy,” “happy,” and “delight” to have similar meanings. Instead of including a column for each word separately, we compress them into a column that measures the broader idea of enjoyment.

This compression has the benefit of helping our models learn about related words like “enjoy,” “happy,” and “delight” simultaneously. For instance, we may have many training examples that include the word “enjoy” but only a few that include “delight.” Without compression, the model can’t learn very much about “delight” since there are so few rows to learn from. Yet, if we can squeeze “enjoy” together with “delight,” then everything our model learns about “enjoy” can also be applied to “delight.”

There are many methods to reduce the dimensionality of a term-document matrix. A very common method is to apply singular-value decomposition(SVD) to a tf-idf weighted term-document matrix. This procedure is often called latent semantic analysis (LSA). LSA is frequently used when comparing the similarity of two texts. After transforming two texts with LSA, the cosine distance between them is a good measure of their relatedness.

In text classification (and regression) we can also feed the tf-idf weighted matrix directly into a neural net to perform dimensionality reduction. In this approach, we can think of the first hidden layer as a compression step and the later layers as the classification model. The benefit to this approach — relative to LSA — is that the compression takes into account the labels we are trying to predict. In other words, the compression can ignore anything that isn’t helpful for predicting our labels.

Putting It All Together

Let’s combine the methods discussed above with a model to see what a complete text classification workflow looks like. For the model, I’ll be using a multilayer perceptron (MLP) from muffnn, a package open-sourced by Civis. This package wraps the Tensorflow neural network library in a scikit-learn interface, so we can chain our entire text classification process as a single scikit-learn Pipeline. Scikit-learn also has an MLP model, although it doesn’t include as many recent neural network techniques like dropout.

We’ll build our model using a tf-idf weighted term-document matrix as input and an indicator for whether a review was positive or negative (0 or 1) as labels. We’ll perform 5-fold cross-validation and average the out-of-sample accuracies to measure how well our model performs on new, unseen data.

from muffnn import MLPClassifier
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import make_pipeline

# Read Data. Source:
# http://archive.ics.uci.edu/ml/machine-learning-databases/00331/sentiment%20labelled%20sentences.zip

filename = ‘imdb_labelled.txt’
names = [‘text’, ‘label’]
df = pd.read_csv(filename, header=None, names=names, sep=’t’, quoting=3)

# Chain together tf-idf and an MLP with a single hidden layer of size 256
tfidf = TfidfVectorizer()
mlp = MLPClassifier(hidden_units=(256,))
classifier = make_pipeline(tfidf, mlp)

# Get cross-validated accuracy of the model
cv_accuracy = cross_val_score(classifier, df.text, df.label, cv=5)
print(“Mean Accuracy: {}”.format(np.mean(cv_accuracy)))

Great! This model achieves a respectable accuracy of around 78% across the 5 cross-validation folds. From here we can start to build out a more robust framework for modeling, including tuning our model parameters with grid search and following best practices such as holding out some data as a final validation of the model. After tuning our model, we can likely do better than 78% accuracy, although we probably won’t ever achieve 100% due to inconsistencies in what people consider positive and negative.

We can do a lot with text classifiers, from analyzing customer reviews to filtering spam to detecting fraud. Yet since text data doesn’t come in a ready-to-use spreadsheet format, it can often go unused. This blog post shows a few ways to shape text into a form we can plug in our favorite models. With tools like scikit-learn and Tensorflow, it’s easier than ever to get started.

By Keith Ingersoll, Senior Data Scientist @ Civis Analytics https://goo.gl/GsNTek #DataScience #Cloud

AI – From Silo to Ecosystem

ICYDK: I sometimes reflect on how we will reach this stage of AI called AGI (Artificial General Intelligence) which is defined by a state of AI that by some measure equivalents the human condition. Scientists in the field sometimes call this the singularity, or the point where AI will develop much faster and at larger scale than ourselves. This is of course still a hypothetical state and many out there are in the very process of proving it either wrong or right – so I am not going there in this post. Then I am not even touching the subjects of free will, intention, sentience and consciousness… Nevertheless it is interesting to reflect on, independent from utopian or dystopian narratives around which many writings, studies and opinions are centered, how the evolutionary steps of AI look like today.  Usually evolution is considered over very large timeframes, meaning millions or billions of years. In the most narrow sense, AI only had a couple of hundred years. I was pointed to that when reading a recent Economist article referencing the 20 minute success of two robot arms (with some AI behind) assembling an Ikea chair, something with the same initial conditions we could probably do in 5. It may have something to do with this long process of adaptation we as humans have had over countless generations, integrating with a global ecosystem (largely restricted to earth), thus very slowly adapting, but very deeply at the same time. In all fairness, AI never got this kind of exposure. On the other hand, AI was able to master chess and go in less than 100 year and beat the human masters in the game. Of course, a game is a very artificial construct which we humans have also only mastered some hundreds of years (most humans even never did) so it is conceivable that a very focused effort (no need to eat, relax, fill out tax forms or raise kids) of an AI at some point beats us in this particular domain. Impressive, but at no point cataclysmic. Let’s also not forget that AIs today are not forced to survive in any ecosystem, so they lack that key intention that we humans (or life in general) have, namely the will to survive at any cost (even death). AIs are switched off when they are done and resume when deemed necessary, but other than that they can be pretty relaxed. So until now, AI has developed by addressing ad hoc problems through various techniques of supervised and unsupervised learning, with more or less sophisticated algorithms, with more or less compute and with more or less data. Especially the last two, compute and data have developed at a radical pace to the point where simple environments allow a broad community to experiment with AI, achieving impressive results. That is certainly evolution.  What we also start seeing however now is what prompted me to write this article, namely, several aspects of AI coming together in a single environment. It was addressed in the article “5 Ways Artificial Intelligence in Impacting the Automotive Industry”  It mentiones driverless automobiles, Internet of Things, Risk Identification and Emotion Detection, Machine Learning and Assisted Driving and Robotics and Defect Detection as those 5 ways. One could even add (maybe under the IoT umbrella) services like Apple Carplay, which come with their recommender systems, finding the way to the car. The automotive domain is of course a very inviting subject, being a very dense ecosystem where humans interact with the environment in real time, and with already a lot of electronics on board with a diverse set of tasks. It is encouraging however to see that AI starts to meet AI in the same environment. For this really to become an evolutionary step however, these islands of AI will have to come together or else they will remain silos. Development requires connectedness. It will be one of the main challenges to address. We now have different functionalities trained with different data sets using different algorithms. During operation they will use distinct inputs and outputs. They are really different AIs in a box, doing their thing and blissfully ignorant from each other. Even in real-time while they source data from the same reality, it is not the same data. It would be a major breakthrough if all the AIs in the automotive ecosystem would somehow be able to network and communicate. For that to happen many challenges will have to be overcome. First there will be the technical challenge on how to interconnect different AI systems. What is our objective there? Do we want to connect them as independent agents or evolve them into a single agent (one step closer to AGI)? What is then the framework to interconnect them? As independent agents we may expect them at some point to find each other and do their thing, just by letting them observe each other and communicate. As an integrated system we may need to find ways to expose functions and data through APIs to allow them to communicate (archaic?) These are only some technical aspects to the challenge. Then there are economic interests involved. The automotive space is a very lucrative one with many third party providers fighting for a seat at the table. To what extent will they be willing to go in a model of coopetition – or will they shield their intellectual property to the extent that it tempers general innovation? Then there are loads of challenges related to privacy and security and their legal implications. For sure already a very hard topic today at the current state of AI, but imagine how this will shift with interconnected systems – who will take the legal responsability there? The AI itself? A governing agency? The human at the steering wheel? Where there are challenges there are also opportunities. For the consumer, the opportunity is there to become educated at least to the point to asking the right questions when for example buying a car. Make those questions at the ‘consumable’ level. Not technological questions, but questions about responsibility, limitations, expectations. Sensibilisation by the authorities may be a good tool to accomplish this. For business there is a great opportunity to come together do two things. One is to come together on standardization. Another is to work together on common themes such as legal aspects, privacy and security. Always good for progress to have a common challenge. For research there is a great opportunity to deeply share and enable the community to replicate and further develop. There, already great progress has been made and one project worth mentioning there is github. Many top researchers publish their peer reviewed papers online as well as the code and datasets to support the results. Try this only 20 years ago. Now thanks to universal environments based now on Python, Jupyter, Tensorflow, Theano, Keras and others, any researcher can dive in and replicate the results from the paper, and even better, play with the hyperparameters and models to further develop the results. This is simply amazing. This brings me to the final benificiary: the amateur. Amateurs have gone through waves of appreciation and valuation both positive and negative. One one hand they are seen as the passionate enthousiast (example the radio amateur – a disappearing art) – on the other hand as the non-professional (and therefore less relevant). I believe however with AI being largely a software business widely in reach to anyone with a laptop and internet access, the amateur here is to be very highly valuated and will contribute big time to AI. In this particular domain of interconnecting islands, amateurs have the big advantage to be totally independent, free to choose their projects, untied by financial goals or requirements to secure the next research grant. Let this then be a call-out to the larger amateur and DYI community to dive in and start breaking the silos for the better advancement of AI. https://goo.gl/68pNRE #DataScience #Cloud