Module 09 · Part III · Building an LLM
Tokenization
Words, Characters and Subwords, Byte-Pair Encoding, Other Tokenizers, Vocabulary Trade-Offs, and the Quirks Tokens Cause
By the end you can run BPE by hand and predict how the tokenizer changes cost and behaviour.
Check yourself ▸
Before you start, can you already…
- ☐choose the right family for a task and explain why decoder-only won for generation. (Module 8)
If any is shaky, the linked module teaches it.
A model reads integers, not text. The tokenizer decides how text becomes integers, once, before training, and the choice is frozen into everything the model learns. It sets sequence length, embedding-table size, the cost of each language, and even which simple-looking questions the model finds oddly hard.
9.1 Words, Characters, or Subwords
One token per word needs an enormous vocabulary and still meets unseen words. One token per character keeps the vocabulary small but makes sequences very long. Subword tokenization sits between: common words get one token, and rare words split into a few common pieces.
9.2 Byte-Pair EncodingRun this section
GPT-2 uses byte-level Byte-Pair Encoding (BPE). Its vocabulary has tokens: the 256 possible byte values, 50,000 learned merges, and one special end-of-text token.
In words. Start from single bytes and repeatedly merge the most frequent adjacent pair into a new token. Frequent words end up as one token; rare words stay in pieces.
Each step finds the most frequent adjacent pair in the corpus and merges it everywhere. Watch frequent words collapse into single tokens, and the test sentence get shorter.
The starting corpus repeats low, lower, newest and widest. Before stepping, predict which pair of symbols will be merged first, and which whole word will become a single token soonest.
In words. A small corpus split into symbols, a table of adjacent-pair counts, and the merges learned so far. Each step merges the most frequent pair everywhere. A test sentence below is retokenized after each merge, and its token count falls.
The BPE Merge Algorithm
- Split the training text into words, and write each word as a sequence of base symbols (characters, or bytes in GPT-2).
- Count how often each pair of adjacent symbols occurs across the whole corpus.
- Take the most frequent pair and replace every occurrence with a single new symbol .
- Add to the vocabulary and record the merge.
- Repeat steps 2 to 4 until the vocabulary reaches the target size.
New text is tokenized by applying the recorded merges in order. The original BPE paper (Sennrich et al., 2016) marks word ends with </w>; GPT-2 instead keeps the leading space as part of the token, so " lower" and "lower" are different tokens.
Since any text is a sequence of UTF-8 bytes, a byte-level vocabulary can tokenize every input and never needs an <UNK> ("unknown") token.
9.3 Other Subword Tokenizers
- WordPiece (Schuster & Nakajima, 2012), used by BERT with 30,522 tokens, merges the pair that most increases the training text's likelihood rather than the most frequent.
- Unigram (Kudo, 2018) starts from many candidate pieces and removes those whose loss hurts least, keeping a probability per piece so a word can split more than one way.
- SentencePiece (Kudo & Richardson, 2018) is a library that runs BPE or Unigram on raw text, treating the space as a symbol (
▁), so it needs no language-specific word splitting. LLaMA 1 and 2 use it with 32,000 BPE tokens; LLaMA 3 switched to 128,256 tokens built with OpenAI'stiktoken.
The algorithm matters less than the vocabulary size and the text it was built from.
9.4 Choosing a Vocabulary SizeRun this section
- Bigger vocabularies make sequences shorter, which is cheaper (attention cost grows with length, section 6.6) and fits more text in a fixed context window.
- Bigger vocabularies make the model's ends bigger. The embedding table and output layer each hold numbers, and the softmax runs over all at every position. At , going from 32,000 to 128,000 tokens adds about 393 million parameters to each.
- Rare tokens are poorly trained. A token seen a handful of times gets a handful of updates, and its embedding stays near random.
Vocabularies have grown with models, since the end matrices become a smaller share while the length savings apply to every token: GPT-2 used 50,257, LLaMA 2 32,000, LLaMA 3 128,256, and Gemma 256,000.
The text the tokenizer was built from matters as much. One built mostly from English learns few merges for other languages, so the same sentence can cost two to five times as many tokens in a language it saw little of: less text per context window and a higher price per request.
More merges, shorter sequences, but mostly for the training language
A tiny training text, so merges run out early (a pair must occur at least twice to be merged); real tokenizers learn tens of thousands of merges from gigabytes. The direction of the effect, not its size, is what carries over.
9.5 Quirks That Tokens CauseRun this section
The model sees token ids, never letters, and several apparent failures of reasoning are really tokenization:
- Spelling and counting letters.
strawberrymay be one or two tokens, and the model has no direct view of the letters inside, so "how many r's are in strawberry?" is harder than it looks. - Arithmetic.
1234might be one token and12345two (123,45), so digits do not line up in consistent columns and carrying is hard to learn. Some tokenizers, LLaMA's among them, split numbers into single digits for this reason. - Leading spaces. A prompt ending in a trailing space forces the next word to start without its usual leading-space token, a pattern rarely seen in training, and output often degrades.
- Under-trained tokens. A token merged from text later filtered out of the training data stays near random, and prompting with it can produce erratic output. Such "glitch tokens" exist in several released models.
When a model fails a simple-looking task, check how the input was tokenized first.
9.6 What the Reference Implementation Tokenizes
The reference implementation uses a character vocabulary of 67 tokens: <PAD> (padding), <UNK> (unknown), and every distinct character in the training text, sorted.
# src/infra/tokenizer.py
self.stoi = {self.PAD_TOKEN: 0, self.UNK_TOKEN: 1}
for character in sorted(set(text)):
self.stoi[character] = len(self.stoi)This keeps the tokenizer out of the results, so the labs' parameter counts and losses reflect the architecture. A 50,257-token vocabulary would not fit anyway: at width 64, its embedding table alone would be about 3.2 million numbers, nearly 30 times the whole 112k-parameter model.
Its costs are exactly what BPE fixes:
- Unseen characters fail. The Shakespeare text has no accented letters, so
"café"givesc,a,f,<UNK>. - Sequences are longer. An English word is five to six characters with its space, against about 1.3 BPE tokens, so text takes roughly four times the context.
- It does not transfer to any other text.
Measured (lab 1a). The vocabulary has 67 tokens, and its embedding table holds 4,288 of the model's 112,323 parameters, under . At that size the vocabulary is a design choice, not a cost worth worrying about.
Exercises
Why does a byte-level BPE tokenizer never need an <UNK> token?
The corpus is aaab aab ab (three words). Run BPE by hand: what are the first two merges?
With a character vocabulary, how many tokens does café cost if é was never seen in training? How many does a byte-level tokenizer with no merges need?
How many parameters does a 256,000-entry embedding table take at , in millions?
Why might a model answer "what is 4831 + 2967?" less reliably than "what is 4831 plus 2967, digit by digit?" Relate your answer to tokenization.
Core Academic References
- Sennrich, Haddow & Birch (2016): Neural Machine Translation of Rare Words with Subword Units (BPE). ACL 2016. arXiv:1508.07909
- Schuster & Nakajima (2012): Japanese and Korean Voice Search (WordPiece). ICASSP 2012.
- Kudo (2018): Subword Regularization (Unigram). ACL 2018. arXiv:1804.10959
- Kudo & Richardson (2018): SentencePiece. EMNLP 2018. arXiv:1808.06226
- Petrov et al. (2023): Language Model Tokenizers Introduce Unfairness Between Languages. NeurIPS 2023. arXiv:2305.15425
- Land & Bartolo (2024): Fishing for Magikarp: Automatically Detecting Under-trained Tokens in Large Language Models. arXiv:2405.05417
Measured labs
These labs test what this module says by running real code. Each card states the idea, what the experiment found, and what it does not prove. The numbers come from experiments/results.json, the output of running experiments/run_all.sh on the tiny-transformers reference code at version 137f7ef. The project checks fail if a measured number in the module text stops matching that file.
Lab 1a — how big the reference model is
What the module says
Module 1 explains that a token embedding is a table lookup: each token id picks one row, a vector of numbers. It also quotes parameter counts for large production models.
What the experiment found
The reference model has 112,323 parameters, which take 438.8 KiB when each is stored as a 32-bit float (fp32). That is small enough to inspect every weight yourself, which is the reason to study a model this size.
- total parameters
- 112,323
- size on disk in fp32
- 438.8 KiB
- vocabulary size
- 67
- position table parameters
- 4,096
- characters of training text
- 1,059,620
2 blocks, , feed-forward width , 2 attention heads
each token is one character, not a word piece
learned during training, not the fixed sine waves of section 7.1
What this does not prove
The two embedding tables (tokens and positions) hold 8,384 of the 112,323 parameters, about 7.5%. This lab counts parameters; it does not measure how the share changes in larger models, where the vocabulary and width are both far bigger.
Run it yourself
cd tiny-transformers && .venv/bin/python experiments/parameter_count.pyThe code being tested