Skip to the lesson
FixTheRobot Explore Robotics / LLM / LM2 Text Data and Tokenization / LM2-T1

LLM · Theory · LM2

LM2-T1 — Text Normalization and Preprocessing

Before a language model sees a single token, someone decided what counts as the same text. Those decisions are permanent.

  • Lesson 1 of 6
  • Module 2: Text Data and Tokenization
  • Theory
  • Difficulty: Beginner
  • About 7 minutes to read
  • Prerequisite: LM1-T5

Raw text is messy. The same word appears with different capitalisation, accents can be stored in two different ways, web pages carry markup, and the same paragraph may appear a thousand times. How you clean, normalise and filter the text decides what the model can learn and what it will never see. This lesson goes through the common preprocessing steps, the choices each involves, and the shift in modern practice towards keeping text nearly as it is and letting a byte-level tokenizer deal with it. It also introduces two statistics, the type-token ratio and Zipf’s law, that explain why vocabularies behave as they do. Lab LM2-L1 lets you feel the effect of these choices.

Learning objectives

After this lesson, you should be able to:

  • List the main preprocessing steps for a text corpus and the information each one removes.
  • Explain Unicode normalisation with a concrete example and apply it in Python.
  • Compute the type-token ratio of a text and describe Zipf’s law.
  • Decide which preprocessing a GPT-style pipeline needs and which it should avoid.

The idea: decide what counts as “the same”

Every preprocessing step answers the question: are these two strings the same thing? Is “The” the same as “the”? Is “café” typed with one character for é the same as with an e and a combining accent? Is a web page the same as itself with an extra cookie banner? If the answer is yes, the model sees one thing and learns from all the occurrences together; if no, it sees two things and each is learned separately, with fewer examples.

Common steps and what they cost

Common preprocessing steps and their side effects
StepWhat it doesWhat it can destroy
Unicode normalisation (NFC or NFKC)writes equivalent characters in one canonical formNFKC also folds distinctions such as ligatures and superscripts
Lowercasingmerges “The”, “the”, “THE”proper nouns, acronyms, emphasis, code identifiers
Whitespace cleaningcollapses repeated spaces and strips endsindentation and layout (fatal for code and tables)
Removing markup and boilerplatestrips HTML tags, menus, footersstructure that may matter (headings, lists)
Splitting punctuationmakes “box.” into “box” and “.”nothing, if the tokenizer can recover spacing
Language and quality filteringdrops text in the wrong language or of low qualityrare dialects, informal text, useful “noisy” data
Deduplicationremoves repeated documents and passageslegitimately repeated text (licences, boilerplate that users also meet)
Word frequencies follow a long-tailed curveA bar chart of word frequency against word rank for a typical text: rank 1 has frequency 1000, rank 2 has 500, rank 3 has 333, rank 4 has 250, rank 5 has 200, falling like one over the rank, followed by a long tail of ranks with very small frequencies.110002500333342505200616771438125911110100 rank of the word, most frequent firstZipf’s law: frequency is roughly proportional to 1 / rank … a verylong tail
Word frequencies are very uneven: a few words are extremely common and most are rare. Any vocabulary has a long tail.

Modern practice: keep it raw, tokenize at the byte level

Early pipelines lowercased, stripped punctuation and removed rare words to keep vocabularies small. Current GPT-style systems mostly keep case, punctuation and whitespace as they are and use a byte-level tokenizer (Lessons LM2-T3 and LM2-T4), so that nothing is ever “unknown” and no information is thrown away before the model can judge its usefulness. The cleaning effort goes instead into data selection: removing duplicates, boilerplate and low-quality text, and checking the licence of the sources.

Why the tail matters

Zipf’s law says that in natural text the r-th most frequent word occurs roughly in proportion to 1/r. A few words (“the”, “of”, “and”) make up a large share of all tokens, and most words occur once or twice. A word-level vocabulary must either include the whole tail (huge, with poorly trained entries) or cut it (many unknowns). That is the reason subword tokenization exists.

The mathematics: type-token ratio and Zipf’s law

Count the number of tokens N (words with repeats) and the number of types V (distinct words). The type-token ratio is

type-token ratio
TTR=VN\mathrm{TTR}=\frac{V}{N}
N
the number of tokens in the text, counting every occurrence.
V
the number of distinct tokens (the vocabulary size of that text).
TTR
between 0 and 1: near 1 means almost every word is new; small means words repeat a lot. It falls as the text gets longer, because common words keep repeating while new words appear more slowly.

Zipf’s law says that the frequency f(r) of the word of rank r (rank 1 is the most frequent) is approximately

Zipf’s law
f(r)≈Crsf(r)\approx \frac{C}{r^{s}}
with s close to 1
r
the rank of a word when words are sorted by frequency.
f(r)
the number of times that word occurs.
C
a constant (about the frequency of the most frequent word).
s
the exponent, close to 1 for natural-language text.

With s = 1 and C = 1000, the ten most frequent words have frequencies 1000, 500, 333, 250, 200, 167, 143, 125, 111, 100. The law is an empirical regularity, not an exact rule, and varies between languages and corpora.

Unicode normalisation. The same visible character can be encoded in more than one way. “é” can be one code point (U+00E9, precomposed) or two (U+0065 “e” followed by U+0301, a combining acute accent). The normal forms NFC (compose) and NFD (decompose) choose one representation; NFKC and NFKD also replace compatibility characters such as the ligature “fi” with plain letters. Without normalisation, two strings that look identical compare unequal and get different tokens.

A worked example: two ways to write “café”, and a type-token ratio

import unicodedata

a = "caf\u00e9"                              # "é" as one code point
b = unicodedata.normalize("NFD", a)         # "e" + combining accent
print(len(a), len(b), a == b)               # 4 5 False  (they look identical)
print(unicodedata.normalize("NFC", b) == a) # True  (normalising both to NFC makes them equal)

The two strings print the same on screen, but one has 4 code points and the other 5, so they are not equal. A byte-level tokenizer would encode them as different byte sequences. Normalising the corpus to one form (NFC is the usual choice) avoids learning two versions of every accented word.

Counting types and tokens. Take the text “the robot picks up the red box the robot picks up the green bin the arm picks up the blue tool”.

from collections import Counter

text = "the robot picks up the red box the robot picks up the green bin the arm picks up the blue tool"
words = text.split()
counts = Counter(words)
print(len(words), len(counts), round(len(counts) / len(words), 3))   # 21 tokens, 11 types, TTR 0.524
print(counts.most_common(3))                                         # [('the', 6), ('picks', 3), ('up', 3)]

There are N = 21 tokens and V = 11 types, so TTR = 11/21 = 0.524. “the” alone is 6 of the 21 tokens (29%), the signature of a Zipf-like distribution; 7 of the 11 types occur only once.

Preprocessing and what it implies for a robot. Suppose a robot’s voice interface logs commands with different spellings: “Pick up the Box”, “pick up the box ”, and “pick up the box.” (with trailing space or full stop). Without normalisation these are three different strings; after lowercasing, trimming whitespace and separating punctuation, they are one. But lowercasing a part number such as “A7” and “a7” would merge two different parts. The right normalisation depends on the data, and the choice should be recorded and tested.

Engineering implications and limitations

Normalise once, at one place. The pipeline that prepares training text and the pipeline that prepares prompts at inference must apply exactly the same steps. A mismatch (for example NFC in training, raw text in production) quietly degrades results.

Prefer reversible steps. If you can reconstruct the original text from the processed one, you keep the option to change your mind. Lowercasing and whitespace collapse are not reversible; a byte-level tokenizer on raw text is.

Treat whitespace as data for code and tables. Collapsing spaces destroys Python indentation and column alignment.

Deduplicate. Repeated passages are over-weighted in training, increase memorisation and can leak test data into training (Lesson LM2-T6). Exact deduplication is easy; near-duplicate detection needs hashing methods and is worth the effort at scale.

Know your licence and provenance. Record where each source came from and under what terms. Do not include data you may not use. This track uses small invented or openly licensed texts for its exercises.

Write down the preprocessing as code with tests. A test with a few ugly strings (accents, tabs, emoji, a stray HTML tag) catches silent changes when the pipeline is modified.

Robots. Voice and chat commands arrive with typos, casing and punctuation noise. Normalise for matching and logging, but keep the raw text to show to a user and for debugging.

Common misconceptions

“Cleaner text is always better.”

Aggressive cleaning removes information (case, punctuation, layout) that a model could use, and may make the training text unlike real inputs. Clean to remove noise, not signal.

“Lowercasing is free.”

It merges names, acronyms and code identifiers that differ only in case, and it makes the model unable to produce correct capitalisation.

“Two strings that look the same are the same.”

Different Unicode code point sequences can render identically. Normalise before comparing or counting.

“Rare words are noise.”

Many important words (names, technical terms) are rare. Dropping them is why unknown tokens appear; subword tokenization keeps them without a huge vocabulary.

Engineering takeaways

  1. Each preprocessing step decides what counts as the same text, and removes some information to do so.
  2. Unicode normalisation (usually NFC) makes equivalent characters identical: “café” has 4 or 5 code points depending on how it is written.
  3. Type-token ratio TTR = V/N falls as a text grows; Zipf’s law f(r) ≈ C/r^s explains the long tail of rare words.
  4. Modern GPT-style pipelines keep case, punctuation and whitespace, use a byte-level tokenizer and spend their effort on data selection and deduplication.
  5. Apply identical preprocessing in training and in production, and keep the raw text.

References and further reading

The core reference is Chapter 2 of the book; the standards below define the normalisation forms used here.

Knowledge check

Three conceptual questions. Write an answer, then reveal the explanation. Your answers stay in your browser.

Question 1

A text has 200 tokens and 80 distinct types. What is its type-token ratio? Would you expect it to rise or fall if the text were ten times longer?

Explanation

TTR = 80/200 = 0.40. It would fall: with more text the common words keep repeating while new words appear more slowly, so V grows much more slowly than N.

Question 2

Under Zipf’s law with s = 1 and C = 600, what are the frequencies of the words of rank 1, 3 and 12?

Explanation

f(1) = 600, f(3) = 600/3 = 200, f(12) = 600/12 = 50.

Question 3

Why can “résumé” typed on two keyboards be counted as two different words, and how do you prevent it?

Explanation

The accents may be stored as precomposed characters on one and as base letter plus combining accent on the other, giving different code point sequences. Normalise the corpus to one Unicode form, usually NFC, before tokenizing or counting.

What’s next

Next: how normalised text is cut into tokens and mapped to integer IDs: characters, words, subwords and the vocabulary.

Practice this lesson

Build a Simple Tokenizer

Write encode and decode for a word-level vocabulary with special tokens and inspect the token IDs.

Next lesson

LM2-T2 — Characters, Words, Subwords and Token IDs

Compare tokenization granularities and build a vocabulary that maps tokens to integer IDs.

Nothing below is required to finish this lesson.