Skip to content
datarekha

Text classification: CNN & RNN

Assigning a label to a whole text — sentiment, spam, topic — is the workhorse NLP task. Classical bag-of-words classifiers ignore word order; the neural approaches fix that. A 1D CNN learns n-gram detectors and max-pools them; an RNN reads the sequence in order. Both beat bag-of-words by combining embeddings with structure.

Written and reviewed by Last reviewed

8 min read Intermediate NLP & Transformers Lesson 8 of 44

What you'll learn

  • The text-classification setup — embeddings, an encoder, a classifier head
  • CNN for text — 1D convolution as an n-gram detector plus max-pooling
  • RNN/LSTM for text — reading the sequence in order for long-range cues
  • The CNN-vs-RNN trade-off, and where transformers took over

Before you start

The most common NLP task is text classification: one label for a whole text — positive vs negative review, spam vs not, which topic. Classical models (Naive Bayes, SVM on TF-IDF) do this well but treat the text as a bag of words: “not good” and “good not” look identical. The neural approaches keep word order by feeding word embeddings through an architecture that respects sequence. Two classic ones: the CNN and the RNN.

CNN for text: learnable n-gram detectors

A 1D convolution slides a small filter across the sequence of word embeddings. A filter spanning 2–3 words is an n-gram detector — it learns to fire on a phrase like “not good” or “highly recommend.” Run many filters and you detect many phrases; then max-pool each filter’s activations over the whole sentence, which answers “did this pattern appear anywhere?” — a position-invariant feature.

import numpy as np

emb = {"the":[0,0], "movie":[0,0], "was":[0,0], "i":[0,0], "not":[1,0], "good":[0,1], "loved":[0,1]}
filt = np.array([[1.5, 0.0], [0.0, 1.5]])      # window-2 filter: negation-then-positive detector

def conv_maxpool(sent):
    v = np.array([emb[w] for w in sent], float)
    acts = [float((v[i:i+2] * filt).sum()) for i in range(len(v) - 1)]   # slide the filter
    return max(acts), [round(a, 1) for a in acts]                         # max-pool

for sent in [["the","movie","was","not","good"], ["i","loved","the","movie"]]:
    mp, acts = conv_maxpool(sent)
    print(f"{sent} -> maxpool={mp:.1f}  per-position acts={acts}")
['the', 'movie', 'was', 'not', 'good'] -> maxpool=3.0  per-position acts=[0.0, 0.0, 0.0, 3.0]
['i', 'loved', 'the', 'movie'] -> maxpool=1.5  per-position acts=[1.5, 0.0, 0.0]

The filter lights up strongest (3.0) exactly at the “not good” window and stays quiet elsewhere; max-pool grabs that 3.0 no matter where in the sentence it occurred. That’s the CNN’s strength: fast, parallel detection of local cue phrases — and for sentiment/topic, the presence of a few key phrases is often most of the signal.

CNN for text: embed → convolve → max-pool → classifyembeddingswords × dimsconvsliding n-gramfiltersfeature mapper positionmax-poolpattern anywhere?dense+ softmaxlabel
CNN-for-text: filters detect cue phrases, max-pool makes them position-invariant, a dense head classifies.

RNN for text: read it in order

A recurrent network takes the opposite approach: read the words one at a time, left to right, updating a hidden state, and use the final state (a summary of the whole sentence) for the label. Because it carries state forward, an RNN/LSTM captures order and long-range dependencies a CNN’s fixed window can miss — negation scope (“I did not think the movie, despite the hype, was good”), or agreement across a long clause. A bidirectional LSTM reads both directions so each position sees its full context. The cost is that it’s sequential — it can’t parallelize across the sentence the way a CNN can.

Choosing, and what came next

The trade-off is clean: CNN is fast, parallel, and excellent at local cue phrases; RNN/LSTM is slower but models order and long-range structure. Both beat bag-of-words by using embeddings plus architecture instead of raw counts — and for many classification tasks the CNN’s speed makes it the pragmatic pick, since a few key phrases carry most of the signal.

In one breath

  • Text classification assigns one label to a whole text (sentiment, spam, topic); classical bag-of-words models work but ignore word order.
  • CNN for text: a 1D convolution slides filters over word embeddings as n-gram detectors, then max-pools to detect a cue phrase anywhere (the demo: “not good” fires 3.0, max-pooled position-invariantly) — fast and parallel.
  • RNN/LSTM for text: reads the sequence in order, carrying a hidden state, capturing order and long-range dependencies (negation scope) a fixed CNN window misses; BiLSTM reads both ways; cost is being sequential.
  • Trade-off: CNN = fast/local, RNN = order-aware/long-range; both beat bag-of-words via embeddings + structure.
  • The modern default is fine-tuning a transformer (BERT) — but CNN/RNN stay useful for fast, cheap classification.

Quick check

Quick check

0/4
Q1Why do neural text classifiers (CNN/RNN) improve on bag-of-words models?
Q2What does a 1D convolution plus max-pooling do for text?
Q3What does an RNN/LSTM capture that a CNN's fixed window can struggle with?
Q4What is the main trade-off between CNN and RNN for text classification?

Next

Text classification labels a whole document; its per-token sibling is sequence labeling. The building blocks are CNNs and RNNs/LSTMs applied to word embeddings; the modern successor is BERT.

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Practice this in an interview

All questions
What are the main approaches for converting raw text into features for a machine learning model?

Bag-of-words (CountVectorizer) and TF-IDF represent text as sparse word-count vectors and are fast baselines for linear models. Word embeddings (Word2Vec, GloVe) produce dense, semantically meaningful vectors by averaging word representations. Contextual embeddings from transformer encoders (BERT, sentence-transformers) capture full sentence semantics and outperform bag-of-words on most tasks at higher compute cost.

What are n-grams and when should you use them in NLP?

An n-gram is a contiguous sequence of n tokens, such as a unigram, bigram, or trigram. Use n-grams when short-range word order is useful and you want a fast, interpretable feature representation, especially for classical text classification, search, spelling, or low-resource language modelling.

Why is Naive Bayes called 'naive,' and why does it still work so well for text classification?

Naive Bayes is called naive because it assumes features are conditionally independent given the class, an assumption that is rarely literally true for words. It remains effective for text because the factorized model is fast and data-efficient for high-dimensional sparse inputs, and its approximate scores often still rank the correct class highest even when its probabilities are overconfident.

Why do CNNs outperform fully-connected networks on image data?

CNNs exploit three structural properties of images — local correlation, translation invariance, and compositional hierarchy — through parameter sharing and local receptive fields. A dense network treats every pixel as independent, ignoring spatial structure and requiring orders of magnitude more parameters.

Related lessons

Explore further