Text to numbers: tokenization
How language becomes a sequence of integers — and what gets lost
Every language model — from a 2013 word2vec model to a 2026 frontier LLM — starts by chopping text into tokens and mapping them to integers. The algorithm that does this (byte-pair encoding and its cousins) decides the vocabulary size that sizes the embedding table (7.4 Inside the block: residuals, LayerNorm, FFN and embeddings), how expensive each language is to process (9.9 Production: latency, cost, caching, guardrails, security and privacy), how many tokens fit in a context window (9.6 Generation side: context assembly, citations, structured outputs and tool calling) and a whole catalogue of strange LLM failures that have nothing to do with intelligence and everything to do with tokenization.
You'll be able to
- Compare character, word and subword tokenization and state the trade-offs each makes
- Run the BPE training algorithm by hand on a tiny corpus and tokenize a new word with the learned merges
- Distinguish byte-level BPE, WordPiece and Unigram/SentencePiece and name which models use which
- Predict tokenization-driven failure modes (arithmetic, spelling, multilingual cost) and budget text in tokens
Key ideas
- Tokenization maps text to integer ids from a fixed vocabulary; subwords balance vocabulary size against sequence length and eliminate unknown words.
- BPE training: start from characters/bytes, repeatedly merge the most frequent adjacent pair, record merge rules; the number of merges sets the vocabulary size.
- Tokenizing new text applies the learned merges in order, so unseen words decompose into known pieces ("low" + "est").
Show 4 more key ideas
- Byte-level BPE (GPT-2/4, Llama 3) uses 256 bytes as the base and a regex pre-tokenizer; WordPiece merges by likelihood gain; Unigram/SentencePiece prunes a large vocabulary by likelihood.
- Vocabulary sizes grew from 50k to 100k–250k: fewer tokens per text, larger embedding and output layers.
- Many LLM weaknesses are tokenization artifacts: arithmetic on chunked digits, letter counting, 2–5× token cost for non-English text, whitespace handling, glitch tokens.
- Budget in tokens: ≈ 0.75 English words or 4 characters per token.
Suggested path through this lesson
Models can't read
A neural network consumes vectors of numbers. To feed it text we need a vocabulary of symbols, a rule that splits a string into a sequence of those symbols (tokens), and an integer id for each. The id then selects a row of an embedding matrix (6.3 Word embeddings, 7.4 Inside the block: residuals, LayerNorm, FFN and embeddings) and the model never sees the characters again. Three design choices sit on a triangle:
| unit | vocabulary size | sequence length | problems |
|---|---|---|---|
| characters / bytes | tiny (≈100–256) | very long | the model must learn spelling → meaning from scratch; attention cost grows with length |
| words | huge (10⁵–10⁶) | short | out-of-vocabulary words, morphology ("run", "running", "runner" unrelated), no sharing across languages |
| subwords | 30k–250k | medium | the compromise every modern model makes |
Subword tokenizers keep frequent words whole ("the", "machine") and split rare ones into meaningful pieces ("tokeniz" + "ation"), so there are no unknown words and the vocabulary stays manageable.
Byte-pair encoding, step by step
BPE (Sennrich et al., 2016, adapting a 1994 compression algorithm) learns a vocabulary from data:
- Start with a base vocabulary of characters (or bytes) and represent every word as a sequence of them, with an end-of-word marker.
- Count all adjacent symbol pairs across the corpus (weighted by word frequency).
- Merge the most frequent pair into a new symbol; add it to the vocabulary and record the merge rule.
- Repeat until the vocabulary reaches the target size. The number of merges is the only hyperparameter.
Worked example. Corpus word counts: low ×5, lower ×2, newest ×6, widest ×3. Write each as characters plus the marker ⟨/w⟩: l o w ⟨/w⟩, l o w e r ⟨/w⟩, n e w e s t ⟨/w⟩, w i d e s t ⟨/w⟩. Pair counts: (e,s) = 6+3 = 9, (s,t) = 9, (t,⟨/w⟩) = 9, (w,e) = 2+6 = 8, (l,o) = 7, (o,w) = 7, (n,e) = 6, (e,w) = 6, (w,⟨/w⟩) = 5, and so on.
- Merge 1: (e, s) →
es[9]. Now newest =n e w es t ⟨/w⟩. - Merge 2: (es, t) →
est[9]. - Merge 3: (est, ⟨/w⟩) →
est⟨/w⟩[9]. - Merge 4: (l, o) →
lo[7]. Merge 5: (lo, w) →low[7]. - Merge 6: (n, e) →
ne; Merge 7: (ne, w) →new; Merge 8: (new, est⟨/w⟩) →newest⟨/w⟩[6] …
To tokenize an unseen word, apply the merge rules in the order they were learned: lowest → l o w e s t ⟨/w⟩ → (merge 1) l o w es t → (2) l o w est → (3) l o w est⟨/w⟩ → (4) lo w est⟨/w⟩ → (5) low est⟨/w⟩. Result: ["low", "est⟨/w⟩"] — a word never seen in training, represented by two meaningful pieces. The compression is real: 4 words × ~6 characters became a handful of tokens.
Intuition. BPE is a greedy compression algorithm. Frequent strings get short codes (one token); rare strings get spelled out. That is why common English words are one token and a rare Finnish word may be eight.
The modern variants
Byte-level BPE (GPT-2 and every OpenAI model since, Llama 3): the base vocabulary is the 256 byte values, so anything — emoji, code, any script — can be tokenized with no unknown token. A regular-expression pre-tokenizer first splits text into chunks (words, numbers, punctuation, leading spaces) so merges never cross a space or mix letters with digits; GPT-4's pattern caps digit runs at three, and Llama 3 copies that choice. Vocabulary sizes grew from GPT-2's 50,257 to ~100k (GPT-4's cl100k_base), ~128k (Llama 3) and ~200k (o200k_base): bigger vocabularies mean fewer tokens per text (cheaper, longer effective context) at the cost of a larger embedding and output layer (7.4 Inside the block: residuals, LayerNorm, FFN and embeddings).
WordPiece (BERT): same merge loop, but it merges the pair that maximizes the likelihood gain rather than raw frequency, and marks word-internal pieces with ## ("playing" → play, ##ing).
Unigram / SentencePiece (T5, Llama 1–2, many multilingual models): start from a large candidate vocabulary and prune it, removing the tokens whose deletion hurts the corpus likelihood least under a unigram model . Segmentation is then the most probable split (Viterbi), which also allows sampling alternative segmentations as regularization. SentencePiece treats the input as a raw stream and encodes spaces as ▁, so it is language-agnostic.
All tokenizers add special tokens: ⟨|endoftext|⟩, BERT's [CLS]/[SEP]/[MASK], and the chat-template tokens that mark system/user/assistant turns (8.2 Fine-tuning and PEFT).
Why tokenization explains so many LLM failures
- Arithmetic. "1234567" may be split as
1234567; the model must learn place value across arbitrary chunk boundaries. Digit-grouping rules and tool use (10.2 Tool use and function calling) are the fixes. - Spelling and counting letters. The model sees
strawberry, not letters, so "how many r's?" is genuinely hard; reversing a word is harder still. - Multilingual cost and fairness. English-centric vocabularies spend 2–5× more tokens on Hindi, Burmese or Amharic text than on English, so the same prompt costs more and fits less in the context window. Newer vocabularies (o200k, Gemma's 256k) narrow the gap.
- Code and whitespace. GPT-2 spent one token per space; indentation ate the context. GPT-4-era tokenizers merge runs of spaces.
- Glitch tokens. Rare tokens that appeared in the tokenizer's training text but almost never in the model's training data (the famous "SolidGoldMagikarp") have untrained embeddings and provoke bizarre behaviour.
- Trailing spaces and capitalization. "Hello" and " Hello" and "hello" are different tokens; a prompt ending in a space can push the model into an unlikely region.
Rule of thumb: one token ≈ ¾ of an English word ≈ 4 characters; 100k tokens of context is roughly 75k words. You will budget everything — prompts, retrieved chunks, memory — in tokens (9.4 Chunking and ingestion, 9.6 Generation side: context assembly, citations, structured outputs and tool calling).
Where this shows up later
- The token ids index the embedding table — 6.3 Word embeddings, 7.4 Inside the block: residuals, LayerNorm, FFN and embeddings; vocabulary size enters every parameter count — 7.4 Inside the block: residuals, LayerNorm, FFN and embeddings.
- Byte-level BPE is what you build in Karpathy's tokenizer video and what nanoGPT uses — 7.8 Build GPT from scratch (nanoGPT walkthrough).
- Context windows and costs are measured in tokens — 9.4 Chunking and ingestion, 9.6 Generation side: context assembly, citations, structured outputs and tool calling, 9.9 Production: latency, cost, caching, guardrails, security and privacy; tokenizer-free and byte-level architectures are an active research direction — 11.6 Frontier 2026: where the field is heading.
Common pitfalls (4)
- Applying merges in frequency order at inference time instead of in the order they were learned — you get different tokens than the model was trained on.
- Counting characters or words when the context limit and the bill are in tokens.
- Assuming a model that fails at spelling or digit tasks 'cannot reason' — check whether the tokenizer is the culprit first.
- Mixing tokenizers: a model's embeddings are meaningless under any tokenizer other than its own.
Recap: all 7 key points
- Tokenization maps text to integer ids from a fixed vocabulary; subwords balance vocabulary size against sequence length and eliminate unknown words.
- BPE training: start from characters/bytes, repeatedly merge the most frequent adjacent pair, record merge rules; the number of merges sets the vocabulary size.
- Tokenizing new text applies the learned merges in order, so unseen words decompose into known pieces ("low" + "est").
- Byte-level BPE (GPT-2/4, Llama 3) uses 256 bytes as the base and a regex pre-tokenizer; WordPiece merges by likelihood gain; Unigram/SentencePiece prunes a large vocabulary by likelihood.
- Vocabulary sizes grew from 50k to 100k–250k: fewer tokens per text, larger embedding and output layers.
- Many LLM weaknesses are tokenization artifacts: arithmetic on chunked digits, letter counting, 2–5× token cost for non-English text, whitespace handling, glitch tokens.
- Budget in tokens: ≈ 0.75 English words or 4 characters per token.
Watch
Builds byte-level BPE from scratch (minbpe), then tours GPT-2/GPT-4 regex patterns, SentencePiece and the catalogue of tokenization weirdness. The definitive lecture.
Read
Try it live
Interactive demo
runs in your browserExercises
- BPE by hand
Using the corpus hug ×10, pug ×5, pun ×12, bun ×4, hugs ×5 (characters plus an end marker), compute the pair counts and perform the first four merges, breaking ties alphabetically. Then tokenize bugs. Expected: the first merge is (u, g) with count 20; bugs ends up as
b+ug+s⟨/w⟩or similar depending on tie-breaks — explain why. - Implement BPE in 40 lines
Write
train_bpe(text, num_merges)that returns an ordered list of merges, andencode(text, merges). Train on a paragraph of your choice with 50 merges and inspect the vocabulary. Expected: the first merges are common bigrams such as "th", "e⟨/w⟩", "in"; encoding the training text uses far fewer tokens than characters. - Tokenization archaeology
Using the Tiktokenizer site or the
tiktokenlibrary, compare token counts for the same sentence in English, Hindi and Chinese under GPT-2's and o200k's vocabularies; tokenize "1234567890", a Python snippet with 8-space indentation, and "strawberry". Expected: non-English text costs several times more tokens under the older vocabulary; digits are grouped in threes under the newer one.
Quiz
Flashcards
These cards are now in your spaced-repetition queue. Review all due cards →