AI Hints
Home›NLP Tutorial›Classifying Text
NLP · Lesson 11 of 106

Text Classification

Sorting the inbox.

⏱ 2 min read▶ Video at 19:25🎓 Free
Watch this lesson in the NLP courseWatch on YouTube ↗Full NLP course

It's time for Scribe's first real job. Each morning, two thousand messages need to be sorted into three piles. Delivery problems, refund requests, and questions. Done by hand, that's four hours. Let's see if we can do it in four milliseconds.

Text Classification
From the video: Text Classification
  • Three piles of messages
  • Four hours by hand
  • Four milliseconds by machine

A ticket router

Here's a tiny set of labelled tickets. Our recipe is simple. TF-IDF, with word pairs, followed by logistic regression, all in one pipeline. Just eighteen examples. Now we test it on messages that it hasn't seen. Run it. The first three are sorted correctly, even with new wording. The last one, the book is damaged, isn't quite like anything it's seen, and see how the confidence is lower. With a few thousand real tickets, this simple recipe would be remarkably strong. And it runs in less than a millisecond.

router.pypython
tickets = [
 ("my parcel never arrived", "delivery"), ("where is my order", "delivery"),
 ("tracking says delivered but nothing came", "delivery"),
 ("courier left it at the wrong house", "delivery"),
 ("still waiting after two weeks", "delivery"), ("delivery is very late", "delivery"),
 ("i want my money back", "refund"), ("please refund this order", "refund"),
 ("the charge was wrong, return my payment", "refund"),
 ("how do i get a refund", "refund"), ("cancel and refund me", "refund"),
 ("you charged me twice", "refund"),
 ("do you have this in hardback", "question"), ("what time do you open", "question"),
 ("is the sequel available", "question"), ("do you sell gift cards", "question"),
 ("can you recommend a thriller", "question"), ("are you open on sunday", "question"),
]

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline

texts, labels = zip(*tickets)
model = make_pipeline(TfidfVectorizer(ngram_range=(1, 2)), LogisticRegression())
model.fit(texts, labels)

for m in ["the courier lost my parcel", "give me my money back",
          "do you stock audiobooks", "the book is damaged"]:
    print(f"{model.predict([m])[0]:<9} {model.predict_proba([m]).max():.2f}  {m}")
Output
delivery  0.45  the courier lost my parcel
refund    0.51  give me my money back
question  0.54  do you stock audiobooks
delivery  0.39  the book is damaged

Why did it decide that?

With a linear model, we can look inside. Each word has a weight for each class. We print the top words for every class. Run it. Look at these lists. For refunds, it's money, refund, and charged. For delivery, words like delivered, and parcel. This is a huge advantage of simple models. You can read what they learned, you can spot problems, and you can explain it to the shop owner.

why.pypython
tickets = [
 ("my parcel never arrived", "delivery"), ("where is my order", "delivery"),
 ("tracking says delivered but nothing came", "delivery"),
 ("courier left it at the wrong house", "delivery"),
 ("still waiting after two weeks", "delivery"), ("delivery is very late", "delivery"),
 ("i want my money back", "refund"), ("please refund this order", "refund"),
 ("the charge was wrong, return my payment", "refund"),
 ("how do i get a refund", "refund"), ("cancel and refund me", "refund"),
 ("you charged me twice", "refund"),
 ("do you have this in hardback", "question"), ("what time do you open", "question"),
 ("is the sequel available", "question"), ("do you sell gift cards", "question"),
 ("can you recommend a thriller", "question"), ("are you open on sunday", "question"),
]

import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression

texts, labels = zip(*tickets)
vec = TfidfVectorizer()
clf = LogisticRegression().fit(vec.fit_transform(texts), labels)
words = vec.get_feature_names_out()

for i, name in enumerate(clf.classes_):
    top = np.argsort(clf.coef_[i])[-5:][::-1]
    print(f"{name:<9}", ", ".join(words[j] for j in top))
Output
delivery  where, is, arrived, never, parcel
question  you, open, do, available, sequel
refund    refund, me, charged, twice, please

A real comparison

Let's try this at scale, with almost four thousand real posts. We compare naive Bayes, the classic, with logistic regression on TF-IDF. Run it. Both reach high accuracy, in seconds, and on a laptop. Remember these scores. Later, we'll compare them with transformers. Never skip this step. In many business problems, this baseline is hard to beat, and it costs almost nothing to run.

compare.pypython
from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import make_pipeline

cats = ["rec.autos", "sci.med", "sci.space", "comp.graphics"]
data = fetch_20newsgroups(subset="all", categories=cats,
                          remove=("headers", "footers", "quotes"))
tr_x, te_x, tr_y, te_y = train_test_split(data.data, data.target,
                                          stratify=data.target, random_state=0)
for name, m in [("Naive Bayes", make_pipeline(CountVectorizer(stop_words="english"), MultinomialNB())),
                ("Logistic", make_pipeline(TfidfVectorizer(stop_words="english", sublinear_tf=True),
                                           LogisticRegression(max_iter=1000)))]:
    print(f"{name:<12} {m.fit(tr_x, tr_y).score(te_x, te_y):.3f}")
Output
Naive Bayes  0.893
Logistic     0.908

Summary

TF-IDF and logistic regression are fast, cheap, and readable. That's your baseline for every text task. Scribe can now sort the inbox. But how do customers feel? Next, reading emotions.

Key takeaways
  • TF-IDF + logistic is a strong baseline
  • You can read what it learned
  • Always start here
Next up: Sentiment Analysis — how do customers feel?