Every time Gmail moves a message into your spam folder, a news app sorts an article into "Technology" or "Politics," or a support platform routes a ticket to the right team, the same underlying task is running: text classification.

It's one of the most practical and widely deployed NLP capabilities in the world, and it's the natural next step after sentiment analysis. Where sentiment analysis asks how does someone feel?, text classification asks what is this about? or what does this person want?

In this post, we'll build up from the concepts you already understand — TF-IDF, embeddings, and transformers — and show how they apply to classifying text into any category you define.


What Is Text Classification?

Text classification (also called document classification or text categorization) is the task of assigning a predefined label to a piece of text based on its content.

The label depends entirely on what you're trying to do:

  • Topic: Is this article about sports, politics, or technology?
  • Spam: Is this email legitimate or spam?
  • Sentiment: Is this review positive or negative? (covered in the previous post)
  • Intent: Is this customer trying to cancel, upgrade, or get support?
  • Language: Is this text written in English, French, or Spanish?
  • Urgency: Does this support ticket need immediate attention?

In machine learning, text classification is a supervised learning problem. You provide labelled training examples — text paired with its correct category — and the model learns to predict the category for new, unseen text.

The number of categories can vary:

  • Binary classification: two possible labels (spam vs. not spam, positive vs. negative)
  • Multi-class classification: several mutually exclusive labels (sport, politics, tech, health)
  • Multi-label classification: multiple labels can apply simultaneously (an article can be both about technology and business)

text classification document categorization NLP labels diagram Text classification assigns predefined labels to documents based on their content

Approach 1: TF-IDF + Classifier

The simplest and most interpretable approach to text classification is the same one we used for sentiment analysis: convert text to TF-IDF vectors and train a classifier on top.

Here we'll classify news headlines into topics using a logistic regression classifier.

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report

# Example dataset: news headlines with topic labels
texts = [
    "Government announces new tax reform bill",
    "Scientists discover new exoplanet in nearby solar system",
    "Champions League final ends in dramatic penalty shootout",
    "Central bank raises interest rates for third time this year",
    "New study links sleep deprivation to increased health risks",
    "Tech giant releases updated operating system with AI features",
    "Parliament votes on controversial immigration legislation",
    "Astronomers detect unusual radio signals from deep space",
    "National team qualifies for World Cup after tense qualifier",
    "Startup raises $50 million in Series B funding round",
    "Health officials warn of rising flu cases this winter",
    "Research team develops faster battery charging technology"
]

labels = [
    "politics", "science", "sports", "finance",
    "health", "technology", "politics", "science",
    "sports", "finance", "health", "technology"
]

# Split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(
    texts, labels, test_size=0.3, random_state=42
)

# Convert to TF-IDF vectors
vectorizer = TfidfVectorizer()
X_train_vec = vectorizer.fit_transform(X_train)
X_test_vec = vectorizer.transform(X_test)

# Train classifier
model = LogisticRegression()
model.fit(X_train_vec, y_train)

# Evaluate
predictions = model.predict(X_test_vec)
print(classification_report(y_test, predictions))

# Classify new headlines
new_texts = [
    "Prime minister calls snap election amid scandal",
    "Rover sends back first images from Mars surface"
]
new_vecs = vectorizer.transform(new_texts)
print(model.predict(new_vecs))  # ['politics', 'science']

This approach is fast, requires minimal compute, and is highly interpretable — you can inspect which words most strongly predict each category. For many real-world classification problems with clean categories and enough training data, it performs surprisingly well.

Its main limitation is the same as always: it has no understanding of word meaning or context. Topics that share vocabulary can confuse it, and short texts with unusual phrasing may be misclassified.

Approach 2: Sentence Embeddings + Classifier

Replacing TF-IDF vectors with sentence embeddings gives the classifier a richer representation of each document, one that encodes meaning rather than just word frequency.

from sentence_transformers import SentenceTransformer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report

texts = [
    "Government announces new tax reform bill",
    "Scientists discover new exoplanet in nearby solar system",
    "Champions League final ends in dramatic penalty shootout",
    "Central bank raises interest rates for third time this year",
    "New study links sleep deprivation to increased health risks",
    "Tech giant releases updated operating system with AI features",
    "Parliament votes on controversial immigration legislation",
    "Astronomers detect unusual radio signals from deep space",
    "National team qualifies for World Cup after tense qualifier",
    "Startup raises $50 million in Series B funding round",
    "Health officials warn of rising flu cases this winter",
    "Research team develops faster battery charging technology"
]

labels = [
    "politics", "science", "sports", "finance",
    "health", "technology", "politics", "science",
    "sports", "finance", "health", "technology"
]

# Encode with a sentence embedding model
embedder = SentenceTransformer('all-MiniLM-L6-v2')
embeddings = embedder.encode(texts)

# Train and evaluate
X_train, X_test, y_train, y_test = train_test_split(
    embeddings, labels, test_size=0.3, random_state=42
)

classifier = LogisticRegression()
classifier.fit(X_train, y_train)

# Classify new documents
new_texts = [
    "Prime minister calls snap election amid scandal",
    "Rover sends back first images from Mars surface"
]
new_embeddings = embedder.encode(new_texts)
print(classifier.predict(new_embeddings))  # ['politics', 'science']

Because embeddings capture semantic similarity, this approach handles paraphrasing and varied phrasing better than TF-IDF. A headline that says "fiscal policy debate heats up in legislature" can map to politics even if none of those exact words appeared in the training data, because the embedding model understands the underlying meaning.

Approach 3: Zero-Shot Classification (No Training Data Required)

One of the most useful capabilities of modern transformer models is zero-shot classification: classifying text into categories without any labelled training data at all.

Instead of training a classifier on examples, you define your categories as natural language descriptions and let a pretrained model decide which one fits the input best.

from transformers import pipeline

classifier = pipeline("zero-shot-classification",
                      model="facebook/bart-large-mnli")

texts = [
    "Central bank raises interest rates for third time this year",
    "New study links sleep deprivation to increased health risks",
    "Champions League final ends in dramatic penalty shootout"
]

candidate_labels = ["politics", "science", "sports", "finance", "health", "technology"]

for text in texts:
    result = classifier(text, candidate_labels=candidate_labels)
    top_label = result['labels'][0]
    top_score = result['scores'][0]
    print(f"{top_label} ({top_score:.2f}) | {text}")

Output:

finance (0.81) | Central bank raises interest rates for third time this year
health (0.76) | New study links sleep deprivation to increased health risks
sports (0.94) | Champions League final ends in dramatic penalty shootout

Zero-shot classification is particularly valuable when you don't have labelled training data, when your categories change frequently, or when you're prototyping a new classification system and want to test whether your categories make sense before investing in labelling.

Its trade-off is lower accuracy than a fine-tuned model trained on examples from your specific domain. For production systems where precision matters, fine-tuning on labelled data is usually worth the effort.

Approach 4: Fine-Tuned Transformer (State of the Art)

For production classification systems, fine-tuning a pretrained transformer on your labelled data consistently delivers the strongest results. The model starts with deep language understanding from pretraining, and fine-tuning adapts it to your specific categories and domain.

from transformers import pipeline

# A BERT model fine-tuned on news topic classification
classifier = pipeline(
    "text-classification",
    model="fabriceyhc/bert-base-uncased-ag_news"
)

texts = [
    "Prime minister calls snap election amid corruption scandal",
    "New battery technology doubles electric vehicle range",
    "Star striker signs record-breaking deal with European club",
    "Federal Reserve signals further interest rate adjustments"
]

results = classifier(texts)
for text, result in zip(texts, results):
    print(f"{result['label']} ({result['score']:.2f}) | {text}")

Output:

World (0.97) | Prime minister calls snap election amid corruption scandal
Sci/Tech (0.99) | New battery technology doubles electric vehicle range
Sports (1.00) | Star striker signs record-breaking deal with European club
Business (0.96) | Federal Reserve signals further interest rate adjustments

Fine-tuned transformers are the right choice when accuracy is critical, when your text has domain-specific vocabulary, and when you have enough labelled data to train on. For most production applications, this is where you'll end up.

Intent Classification

Intent classification is a specific type of text classification focused on identifying what a user wants to do, rather than what a document is about. It's the engine behind most conversational AI systems, chatbots, and virtual assistants.

When someone types "I need to cancel my subscription" or "What's my account balance?", an intent classifier determines the intent — `cancelsubscription` or `checkbalance` — so the system knows how to respond.

Intents are typically defined by the product team and trained on example phrases that users might say for each one.

from sentence_transformers import SentenceTransformer
from sklearn.linear_model import LogisticRegression

# Example intent training data
intent_examples = [
    ("I want to cancel my account", "cancel_subscription"),
    ("How do I cancel my subscription?", "cancel_subscription"),
    ("Please close my account", "cancel_subscription"),
    ("What's my current balance?", "check_balance"),
    ("Show me how much I owe", "check_balance"),
    ("How much is left on my account?", "check_balance"),
    ("I need help resetting my password", "reset_password"),
    ("I forgot my login details", "reset_password"),
    ("Can't log in, need a new password", "reset_password"),
    ("I'd like to upgrade my plan", "upgrade_plan"),
    ("What's included in the premium tier?", "upgrade_plan"),
    ("How do I get more features?", "upgrade_plan"),
]

texts, labels = zip(*intent_examples)

embedder = SentenceTransformer('all-MiniLM-L6-v2')
embeddings = embedder.encode(texts)

classifier = LogisticRegression()
classifier.fit(embeddings, labels)

# Classify new user messages
new_messages = [
    "I think I want to stop using the service",
    "My password isn't working",
    "I'd like to move to a higher plan"
]
new_embeddings = embedder.encode(new_messages)
predictions = classifier.predict(new_embeddings)
for msg, intent in zip(new_messages, predictions):
    print(f"{intent} | {msg}")

Output:

cancel_subscription | I think I want to stop using the service
reset_password | My password isn't working
upgrade_plan | I'd like to move to a higher plan

Intent classification works well with sentence embeddings because users rarely phrase the same intent the same way twice. Embedding-based classifiers generalise across paraphrases, making them more robust than TF-IDF for conversational text.

In production intent classification systems, it's also common to include a confidence threshold: if the model's confidence in any intent falls below a certain level, the system routes the message to a human agent rather than risking a wrong automated response.

intent classification NLP chatbot conversational AI diagram Intent classification identifies what a user wants to do from their message, routing it to the correct action or response

Multi-Label Classification

Standard classification assumes each document belongs to exactly one category. But real-world content is often more complex.

A news article might be about both technology and business. A support ticket might involve both billing and account access. A research paper might be tagged with machine learning, healthcare, and ethics simultaneously.

Multi-label classification handles this by allowing each document to be assigned multiple labels at once.

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.multiclass import OneVsRestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import MultiLabelBinarizer
import numpy as np

# Training data with multiple labels per document
texts = [
    "AI startup raises funding to develop healthcare diagnostics tool",
    "Tech giant acquires machine learning company for $2 billion",
    "New research applies deep learning to climate change prediction",
    "Government regulates social media platforms over data privacy",
    "Biotech firm uses AI to accelerate drug discovery process",
]

labels = [
    ["technology", "healthcare", "finance"],
    ["technology", "finance"],
    ["technology", "science"],
    ["politics", "technology"],
    ["healthcare", "technology", "science"],
]

# Binarize labels
mlb = MultiLabelBinarizer()
y = mlb.fit_transform(labels)

# Vectorize text
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(texts)

# Train a one-vs-rest classifier
model = OneVsRestClassifier(LogisticRegression())
model.fit(X, y)

# Classify a new document
new_text = ["Pharmaceutical company partners with AI firm to improve diagnostics"]
new_vec = vectorizer.transform(new_text)
predicted = model.predict(new_vec)
print(mlb.inverse_transform(predicted))  # e.g. [('healthcare', 'technology')]

The one-vs-rest strategy trains a separate binary classifier for each label, then combines their outputs. This is the standard baseline for multi-label classification and works well when labels are reasonably independent of each other.

Which Approach Should You Use?

SituationRecommended approach
Quick prototype, no training dataZero-shot classification
Categories change frequentlyZero-shot classification
Have labelled data, need interpretabilityTF-IDF + Logistic Regression
Have labelled data, varied phrasing expectedSentence embeddings + Classifier
Production system, accuracy is criticalFine-tuned transformer
Classifying user intent in a chatbotSentence embeddings + Classifier
Documents can belong to multiple categoriesMulti-label classification (One-vs-Rest)

As with sentiment analysis, start with the simplest approach that could work. TF-IDF classifiers are fast to train and easy to debug. Move to embeddings or fine-tuned transformers when you need better accuracy or when your text involves complex phrasing and paraphrases.

Challenges in Text Classification

Even well-designed classifiers encounter difficult cases:

Ambiguous categories. The boundary between technology and science is blurry. A news article about a new battery may belong equally well in both. When categories overlap, classifiers trained on clean labels can behave inconsistently.

Class imbalance. In most real-world datasets, some categories appear far more often than others. A support ticket classifier might see ten times more billing questions than account deletion requests. Without accounting for this imbalance, the model may perform poorly on rare but important categories.

Short text. Single sentences, product titles, and social media posts give the model very little signal. Short texts are harder to classify reliably because there are fewer words to distinguish one category from another.

Out-of-scope inputs. No real system perfectly covers every possible input. Users will always send messages that don't fit any defined category. Robust classifiers include a confidence threshold or a catch-all category so that low-confidence predictions can be handled gracefully rather than misrouted.

Label quality. Classification models are only as good as the labels they're trained on. Inconsistent or noisy labelling in the training data will propagate errors into the model's predictions.

Where Text Classification Is Used Today

Text classification is one of the most broadly applied NLP tasks. Here's where it shows up:

Email filtering. Spam detection is one of the original large-scale text classification problems. Modern spam filters combine rule-based heuristics with trained classifiers, often processing billions of messages per day.

Content moderation. Platforms classify posts, comments, and images to detect violations automatically. Classifiers flag content for human review or take automated action based on predicted categories such as hate speech, misinformation, or adult content.

Customer support routing. Incoming tickets are classified by topic, urgency, and department so they can be assigned to the right team without manual triage. Intent classification tells the system what the customer wants; topic classification tells it which team should handle it.

News and content organisation. Publishers and aggregators classify articles by topic automatically so they can be surfaced to the right audience or filed in the right section.

Legal and compliance. Law firms and financial institutions classify documents by type, subject matter, and regulatory relevance to speed up review processes that would otherwise require manual reading.

Healthcare. Clinical notes, discharge summaries, and patient feedback are classified to extract structured information from unstructured text at scale.

What Comes Next

Text classification demonstrates how flexible the core NLP toolkit is. The same pipeline — represent text as vectors, train a classifier — can answer almost any categorisation question you define, from topic detection to intent recognition to content moderation.

The next post in this series goes deeper into one of the most important classification problems in production NLP systems: Named Entity Recognition (NER), where the task isn't to classify a whole document but to find and label specific spans of text — names, places, dates, organisations — inside it.

Learn This at Fondra Labs

This post is part of our NLP Foundations series, where we build up practical AI knowledge one concept at a time, from text processing basics all the way to the systems powering modern AI.

At Fondra Labs, we teach AI from production reality, not hype. Every topic in this series is here because it genuinely matters when you sit down to build something real.

If this was useful, explore the rest of the blog. We cover machine learning, deep learning, NLP, and the practical skills that turn understanding into building.

Frequently Asked Questions

What is document classification?
Document classification is the task of automatically assigning a predefined category to a piece of text based on its content. It is a supervised machine learning problem where a model is trained on labelled examples and learns to predict the correct category for new, unseen documents. Common applications include spam detection, news topic classification, support ticket routing, and content moderation.
What is text classification?
Text classification is the broader NLP task of assigning labels to text. It covers a wide range of applications depending on what labels you define: topic categories, sentiment, intent, language, urgency, and more. In practice, text classification and document classification are often used interchangeably. The core approach is the same: represent the text as a vector, train a classifier on labelled examples, and use it to predict labels for new text.
What is text categorization?
Text categorization is another term for text classification or document classification. It refers to the process of organising text into predefined groups based on content. News aggregators use text categorization to sort articles by topic. Support platforms use it to route tickets. The underlying methods are the same as those used for text and document classification.
What is intent classification?
Intent classification is a specific type of text classification that identifies what a user wants to do based on their message. It is the core component of most chatbots and virtual assistants. When a user writes "I need to cancel my subscription," an intent classifier predicts the intent — cancel_subscription — so the system can respond appropriately. Intent classification works well with sentence embeddings because users rarely phrase the same request in exactly the same way twice.
Javier Aguirre

Javier Aguirre

Teaching AI from production reality, not hype.

Build your AI Foundations

Get the free checklist that production engineers use to avoid these common RAG pitfalls.

Get the Free Guide