Text Preprocessing (Tokenization, Stemming, Lemmatization)
A network takes numbers. Text is not numbers, and every decision on the way from one to the other — where a token starts, what counts as the same word, which words to keep — is made before the model sees anything and cannot be undone by training.
This page counts those decisions on 5,000 IMDB reviews decoded back into words: 1,219,106 tokens, 43,783 distinct ones. It opens with an uncomfortable observation about that corpus, because it is the most common way this material misleads people.
The decisions were already made for you
Section titled “The decisions were already made for you”keras.datasets.imdb ships integer ids, not text. Somebody already tokenized,
lowercased and stripped punctuation. Apply those steps yourself and watch nothing
happen:
| Step | Distinct words | Tokens |
|---|---|---|
| raw (as shipped) | 43,783 | 1,219,106 |
| lowercased | 43,783 | 1,219,106 |
| punctuation stripped | 43,618 | 1,218,706 |
| 27 stopwords removed | 43,591 | 820,241 |
| suffixes stripped | 35,215 | 820,241 |
Lowercasing changes the vocabulary by zero words. Stripping punctuation removes 165. If you benchmark preprocessing on this dataset you will conclude that preprocessing does not matter, and you will be measuring the previous engineer’s work rather than your own.
The evidence that the cleanup was imperfect sits at rank 7 of the frequency table:
| Rank | Token | Uses | Share of all tokens |
|---|---|---|---|
| 1 | the | 68,738 | 0.0564 |
| 2 | and | 33,462 | 0.0274 |
| 3 | a | 33,325 | 0.0273 |
| … | |||
| 7 | br | 20,864 | 0.0171 |
br is the remains of <br /> tags. It is the seventh most common “word” in the
corpus — ahead of in, it and i — and it carries no meaning at all. Every model in
this phase spends capacity on it.
What you’ll learn
Section titled “What you’ll learn”- Why the vocabulary never stops growing: Heaps’ exponent 0.573 on this corpus.
- Zipf’s law, measured: slope −1.0731 over the first 2,000 ranks.
- What a vocabulary cap costs: 10,000 words cover 0.9450 of tokens, so 5.5% of the
text becomes
<oov>. - Why 42.9% of the vocabulary is words seen exactly once, and they are 1.54% of the text.
- What each normalisation step buys and what it destroys.
- Where classic preprocessing ends and subword tokenization begins.
The two laws that shape every vocabulary
Section titled “The two laws that shape every vocabulary”Heaps’ law says the vocabulary grows without bound. Zipf’s law says the extra words are nearly worthless: frequency falls as roughly , so a few thousand words are almost all of the text.
| Vocabulary | Token coverage | OOV rate |
|---|---|---|
| 1,000 | 0.7624 | 0.2376 |
| 5,000 | 0.9013 | 0.0987 |
| 10,000 | 0.9450 | 0.0550 |
| 20,000 | 0.9764 | 0.0236 |
Every OOV rate there is text your model never sees. Doubling the vocabulary from 10,000 to 20,000 halves the loss — and doubles the embedding matrix. The next page gets the OOV rate to exactly zero without either cost.
Normalisation: what it buys, what it destroys
Section titled “Normalisation: what it buys, what it destroys”| Technique | What it does | Cost |
|---|---|---|
| lowercasing | The → the | loses US vs us, Apple vs apple |
| punctuation stripping | don't → dont | splits or merges contractions inconsistently |
| stopword removal | drops the, a, of… | 32.7% of tokens gone; destroys negation and phrasing |
| stemming | affecting → affect | rule-based; merges words that differ |
| lemmatization | better → good | dictionary-based, slower, needs a POS tagger |
Two of these deserve a warning rather than a description:
- Stopword removal deleted 32.7% of the corpus. For a bag-of-words model that is
usually harmless. For anything that reads word order it is destructive — “not good”
and “good” become identical once
notis dropped, and plenty of published stopword lists containnot. - Stemming merges words a reader would not. The measured collisions include
car,cared,cares,caring→car. It buys a 19.6% smaller vocabulary; whether that is worth it depends on whether your model has enough data to learn the inflections separately.
Modern practice is to do less of this, not more. Subword tokenization removes the vocabulary pressure that made aggressive normalisation attractive, and a model with enough data learns that “affects” and “affecting” are related without being told.
flowchart TB A["raw text"] --> B["tokenize
where does a token start?"] B --> C{"normalise?"} C -->|"lowercase"| D["0 words here
already lowercase"] C -->|"strip punctuation"| E["-165 words"] C -->|"drop stopwords"| F["-32.7% of tokens"] C -->|"stem"| G["-19.6% of vocabulary
plus collisions"] D --> H["cap the vocabulary"] E --> H F --> H G --> H H --> I["10,000 words
= 5.5% OOV"] H --> J["subword pieces
= 0% OOV"]
Pitfalls
Section titled “Pitfalls”- Benchmarking preprocessing on a pre-processed corpus. Lowercasing IMDB changes the vocabulary by 0 words; you are measuring somebody else’s pipeline.
- Trusting the cleanup.
bris the 7th most frequent token here, at 20,864 uses. - Removing stopwords before a sequence model. It deleted 32.7% of tokens, and many
stopword lists include
not. - Calling a rule-based suffix stripper a lemmatizer.
caring→caris a measured collision, not a hypothetical one. - Choosing a vocabulary cap without measuring coverage. 1,000 words sounds reasonable and discards 23.8% of the text.
- Assuming a bigger vocabulary is free. It is a bigger embedding matrix, and 42.9% of the words it adds appear exactly once.
- Fitting the vocabulary on train and test. The cap is learned from training data only, exactly like a scaler.
- 5,000 IMDB reviews decode to 1,219,106 tokens and 43,783 distinct words; the shipped index has 88,584 entries.
- Heaps’ law with exponent 0.573 — the vocabulary never stops growing.
- Zipf’s law with slope −1.0731 — 10,000 words cover 0.9450 of tokens, 1,000 cover 0.7624.
- 18,762 words (42.9% of the vocabulary) appear exactly once and make up 1.54% of the text.
- Lowercasing this corpus changed nothing; stopword removal deleted 32.7% of tokens;
suffix stripping cut the vocabulary 19.6% and merged
carwithcaring. br, an HTML remnant, is the 7th most common token.
Classic preprocessing manages the vocabulary. Subword tokenization removes the problem instead, and it is what every modern model actually uses: Subword Tokenization (BPE and WordPiece).
-
Lowercasing the decoded IMDB corpus changed the vocabulary from 43,783 words to 43,783 words. What does that tell you?
keras.datasets.imdb ships integer ids. Tokenization, lowercasing and punctuation stripping all happened before you loaded it — imperfectly, since 'br' survived as the 7th most frequent token.
pch.quizShowAnswer
B — This corpus was already lowercased before it shipped — benchmarking preprocessing on it measures the previous engineer's pipeline, not yours — keras.datasets.imdb ships integer ids. Tokenization, lowercasing and punctuation stripping all happened before you loaded it — imperfectly, since 'br' survived as the 7th most frequent token.
-
A vocabulary of 10,000 words covers 0.9450 of tokens on this corpus. What happens to the rest?
This is exactly the failure subword tokenization removes: any word can be built from a small set of pieces, so the OOV rate is zero.
pch.quizShowAnswer
B — They collapse to a single <oov> id — 5.5% of every review becomes one meaningless token, and no amount of training recovers it — This is exactly the failure subword tokenization removes: any word can be built from a small set of pieces, so the OOV rate is zero.
-
42.9% of the vocabulary appears exactly once, but those words are only 1.54% of the tokens. What follows for the embedding matrix?
Heaps' law guarantees it gets worse with scale: the vocabulary grows as tokens^0.573, so more text brings proportionally more once-seen words.
pch.quizShowAnswer
B — Their embedding rows receive one gradient update each in the entire corpus — they cost memory and learn essentially nothing — Heaps' law guarantees it gets worse with scale: the vocabulary grows as tokens^0.573, so more text brings proportionally more once-seen words.
-
Removing 27 stopwords deleted 32.7% of all tokens but only 27 distinct words. When is that a bad trade?
For bag-of-words it is usually harmless. For a sequence model it destroys exactly the structure the model exists to read.
pch.quizShowAnswer
B — Whenever word order matters — 'not good' becomes 'good' if 'not' is on the list, and many published lists include it — For bag-of-words it is usually harmless. For a sequence model it destroys exactly the structure the model exists to read.
-
A crude suffix stripper merged 'car', 'cared', 'cares' and 'caring' into 'car'. What is the honest summary of stemming?
Modern practice is less normalisation, not more: subword tokenization removes the vocabulary pressure that made stemming attractive.
pch.quizShowAnswer
B — It trades vocabulary size for precision — 19.6% fewer words here, at the cost of collisions a dictionary-based lemmatizer would not make — Modern practice is less normalisation, not more: subword tokenization removes the vocabulary pressure that made stemming attractive.
🧪 Try It Yourself
Section titled “🧪 Try It Yourself”pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading