Text classification is the most common task in all of NLP: take a piece of text and assign it a label. Is this email spam or not? Is this review positive or negative? Which department should this support ticket go to? Enormous amounts of practical value come from this one idea. We will use sentiment analysis, deciding whether text expresses a positive or negative opinion, as the running example, because it is intuitive and it exposes exactly where the classical methods shine and where they break.
Every text classification task has the same structure. You have documents and a fixed set of possible labels, and you want a model that reads a document and predicts the right label. Spam detection has two labels. Sentiment usually has two or three (positive, negative, and sometimes neutral). Topic classification might have dozens. The pipeline is the one we have been building toward: take the text, turn it into numeric features using something like the TF-IDF from the representation chapter, and feed those features to a classifier.
For a huge range of problems, you do not need deep learning at all. TF-IDF features fed into a simple classifier give strong results quickly, and these are the models to reach for first. Three classifiers come up constantly. Naive Bayes is fast and a remarkably good baseline for text, based on simple word-probability counting. Logistic regression is usually the best classical default, accurate and easy to interpret, and you will recognise it from the Machine Learning series. Linear support vector machines also perform very well on the high-dimensional sparse vectors that text produces. None of these is complicated, and together with TF-IDF they form the bread and butter of practical text classification.
Here is a full, working sentiment classifier in a handful of lines. It vectorises the text with TF-IDF, trains logistic regression, and evaluates it. This is genuinely the template most real classification projects start from.
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
texts = [
"I loved this movie, it was fantastic",
"Absolutely wonderful, a brilliant film",
"Terrible film, I hated every minute",
"Awful, a complete waste of time",
"Great acting and a gripping story",
"Boring and far too long, very poor",
]
labels = ["pos", "pos", "neg", "neg", "pos", "neg"]
X_train, X_test, y_train, y_test = train_test_split(
texts, labels, test_size=0.33, random_state=1)
clf = Pipeline([
("tfidf", TfidfVectorizer()),
("model", LogisticRegression()),
])
clf.fit(X_train, y_train)
print(clf.predict(["what a brilliant, gripping film"])) # ['pos']
print(classification_report(y_test, clf.predict(X_test)))
The Pipeline matters: it bundles the vectoriser and the model together so the exact same TF-IDF transformation is applied during training and prediction. Forgetting to do that, and vectorising train and test separately, is one of the most common bugs in text classification.
Sometimes you do not even need to train. Lexicon-based tools come with a dictionary of words pre-scored for sentiment and combine those scores, handling things like negation and intensifiers with rules. VADER is the popular choice for social media and short text, and it works out of the box with no training data. It is less accurate than a trained model on your own domain, but it is an excellent quick start.
Accuracy, the fraction of predictions that are correct, is the obvious metric, but it can mislead. If 95 percent of emails are not spam, a model that labels everything "not spam" scores 95 percent accuracy while being useless. That is why you also look at precision (of the items the model flagged positive, how many really were) and recall (of the truly positive items, how many it caught), and the F1 score that balances the two. A confusion matrix, which lays out correct and incorrect predictions per class, shows you exactly where the model is making its mistakes. These are the same metrics we met for NER, and they recur throughout NLP.
The cracks show up exactly where word order and context carry the meaning. Because TF-IDF is a bag of words, "this was not good at all" and "this was good, not bad at all" look almost the same to it. Sarcasm like "oh great, another delay" defeats it entirely, since the words are positive but the meaning is not. Negation, long-range context, and tone are precisely the things a model that ignores word order cannot capture. You can patch some of this with n-grams, but the real fix is a model that understands sequence and context, which is deep learning. That is the next chapter.
To handle the word order and context that defeat bag-of-words models, we turn to deep learning for text. The next chapter builds text classifiers with embeddings and neural networks, connecting directly to the architectures from the Neural Networks series.
Sign in to join the discussion and post comments.
Sign in