5.1 — Tokenization: words, characters, subwords (BPE)
Goal: Understand why every modern LLM uses byte-pair encoding (BPE) tokenisation: derive the algorithm from first principles, see what it solves that word-level and character-level tokenisers don't, and recognise the practical consequences for training and inference.
1. Why this session exists
A neural network operates on real-valued tensors. Text is a string of Unicode code points. Something has to convert one into the other, and that something is the tokeniser: a deterministic, learned-once function that maps text sequences of integer token IDs.
Tokenisation is the only part of a modern LLM that is not itself a differentiable model. It is fixed before training, shared between training and inference, and it shapes everything downstream:
- The vocabulary size sets the row count of the embedding matrix and the column count of the output projection (5.2).
- The token efficiency — how many tokens it takes to encode a typical sentence — directly determines training and inference cost, because cost scales with sequence length, not character count.
- The inductive bias the tokeniser imposes determines what the model finds easy or hard. A tokeniser that splits numbers into individual digits makes arithmetic easier; one that lumps
12345into a single token makes it almost impossible.
This session derives byte-pair encoding (BPE) — the family of tokenisers used by GPT, LLaMA, Mistral, Claude, and essentially every production LLM since 2019 — and contrasts it with the two natural baselines (word-level and character-level).[1, 2, 8]
2. The two extremes: word-level and character-level
2.1 Word-level
Split text on whitespace and punctuation, build a vocabulary of all distinct word forms in the training corpus, map each form to an integer.
The fatal problems:
- Out-of-vocabulary (OOV). Any word not seen at training time gets mapped to a special
<unk>token. Proper nouns, typos, neologisms, and rare inflections are all lost. The model cannot read them. - Morphology is hidden. "run", "runs", "running", "ran" are four unrelated integers. The model must rediscover that they share meaning purely from co-occurrence statistics.
- Vocabulary explosion. A web-scale corpus contains millions of distinct word forms. Storing an embedding row for each balloons memory; restricting to the top- pushes everything else to
<unk>. - No graceful degradation. No way to read an unknown word "approximately." If you've never seen "unpresidented" (a typo of "unprecedented"), it is indistinguishable from a foreign word or random noise.
2.2 Character-level
Map every Unicode code point (or every byte) to a token. Vocabulary is tiny (256 for bytes; ~150k for full Unicode but typically capped much lower in practice).
This fixes OOV — every text becomes representable — and trivially handles morphology, since the network sees every character of every word. The cost:
- Sequences are very long. A 1000-word document is ~5000 characters. Cost in attention layers (Chapter 6) is in sequence length, so a 5× longer sequence is 25× more expensive per layer.
- The model has to learn that "the" is a unit from scratch. Characters are syntactically meaningful but the unit of meaning in language is roughly the morpheme. A character-level model spends capacity rediscovering this at every layer.
- Sample efficiency suffers. A given amount of training compute spans far fewer "concepts" if each concept takes 5–10 tokens to express.
We want a middle road: a vocabulary large enough that common words become single tokens (so sequences stay short), but composable enough that rare words fall back to subword pieces (so nothing is OOV). That middle road is BPE.
3. Byte-pair encoding: the algorithm
BPE was originally a 1994 compression algorithm (Gage)[3]. Sennrich, Haddow, and Birch (2016)[4] repurposed it for neural machine translation, and it became the universal tokeniser for LLMs by 2019.
3.1 The training algorithm
Inputs: a training corpus (a long string) and a target vocabulary size .
Initial vocabulary. Start with every byte (or every distinct character) appearing in . Call this . For byte-level BPE, .
Initial tokenisation. Tokenise the corpus by splitting it into the units of . Each word becomes a sequence of bytes/characters.
Iterate. For :
- Count, across the whole tokenised corpus, how often each adjacent pair of tokens co-occurs inside a word. Call this count .
- Find the pair with the largest count.
- Create a new token (the concatenation), add it to .
- Replace every adjacent occurrence of in the tokenised corpus with the new single token.
After iterations, , and the algorithm also produces an ordered list of merge rules — the sequence of pair-merges, in the order they were learned.
3.2 Encoding new text at inference
Tokenise the new text into the initial vocabulary (bytes or characters), then apply the merge rules in order: for each merge , scan the sequence and replace every adjacent pair with . The result is the BPE token sequence.
The same merge rules give a deterministic, reversible mapping. Decoding is trivial: each token is a string of bytes; concatenate the strings of all tokens in a sequence to recover the text.
4. Worked example
Take a tiny corpus consisting of four words, each with its frequency in parentheses:
Initial tokenisation (by character, with a word-end marker to keep word boundaries):
Pair counts: is the most frequent pair (it appears in newer and wider). Merge: introduce token er.
New counts. The most frequent pair is now . Merge: introduce er_.
Next: wins. Merge: lo.
Continue until you reach the desired vocabulary size. You can see the structure emerging: common prefixes (lo), common suffixes (er_), and common short words become single tokens, while rare words (wider) remain split into subword pieces.
Encoding the unseen word "lower" after these three merges: by characters l, o, w, e, r; apply merges in order: (e, r) \to er gives l, o, w, er; the merge (er, \_) \to er_ requires the trailing ; the merge (l, o) \to lo gives lo, w, er. So "lower" tokenises to three pieces even though it was never seen at training time — graceful fallback.
5. Modern variants
5.1 Byte-level BPE (GPT-2 onward)
If is the set of 256 bytes (Radford et al., 2019)[7], the tokeniser can represent any string of Unicode code points, in any language, with no out-of-vocabulary failure mode — every byte is in the base vocabulary. This is the standard for GPT, LLaMA, Mistral. Vocabularies for these models are typically 32k–128k.
5.2 WordPiece (BERT)
Same family. Differs in the merge criterion: instead of "most frequent pair", WordPiece picks the merge that most increases the likelihood of the training data under a unigram language model. The end result is similar; the difference matters mostly for the order in which merges happen.
5.3 SentencePiece (LLaMA, T5, multilingual)
A wrapper around BPE or unigram-LM tokenisation that operates directly on the raw byte stream including spaces — no word-pre-tokenisation step. This is critical for languages without explicit word boundaries (Chinese, Japanese, Thai) and makes the tokeniser truly language-agnostic.
5.4 tiktoken (OpenAI)
OpenAI's production BPE library, used for GPT-3.5/4 and several open weights. Implements byte-level BPE with optimised lookups; pre-tokenises with a regex pattern that controls how digits, contractions, and whitespace are handled.
[5, 6, 9, 10, 12]6. Practical consequences
Token efficiency varies by domain. English prose with a standard 32k BPE tokeniser averages ~0.75 tokens per word. Code, JSON, and math expressions are far less efficient — 4 + 8 = 12 might be 7 tokens. Non-Latin scripts and rare languages can be 2–4× less efficient than English on a tokeniser trained mostly on English data, which directly translates into compute cost asymmetries.
Arithmetic is tokenisation-sensitive. If the tokeniser merges 123 into a single token but leaves 124 as 1 + 24, the model sees no structural similarity between the two. Modern tokenisers (LLaMA-3, recent GPT) deliberately split digits individually for this reason — it gives the model uniform access to digit-level structure.
Whitespace is tricky. GPT-style tokenisers represent the leading space of a word as part of the token (e.g. Ġhello is " hello" as a single token). This makes detokenisation trivial but means "hello" (start of sentence) and " hello" (mid-sentence) are different tokens.
Special tokens. Every model adds non-text tokens to the vocabulary: <bos> (beginning of sequence), <eos> (end), <pad> (padding), and increasingly many control tokens (<|im_start|>, <|im_end|>, <|tool_call|>, etc.). These are reserved IDs at vocab edges; the model learns special behaviour around them during fine-tuning.
Tokenisation is irreversible information loss in one direction only. Text tokens is many-to-one (different texts can collide if the tokeniser strips information, e.g. some Unicode normalisations); tokens text is deterministic. The forward direction can be "weird" — leading spaces, control characters — and is the source of many subtle prompt-engineering bugs.
[11]7. What we keep going forward
From this point on, "input to the model" means a sequence of integer token IDs drawn from a fixed vocabulary of size . The neural network never sees text; the tokeniser is an opaque, fixed preprocessor. Everything that follows — embeddings (5.2), the language-modeling objective (5.3), attention (Chapter 6) — operates on this integer sequence.
(a) Re-run the worked example of §4 for two more merge steps. What is the vocabulary after five merges, and how would "newer" tokenise after those five merges?
(b) Suppose a byte-level BPE tokeniser has vocabulary size . How many merge rules does it contain?
(c) A model with vocabulary has an embedding matrix and an output projection . For and , how many parameters are in these two matrices alone? Compare to the parameter count of a single Transformer block (we'll see this is roughly in Chapter 6).
(d) Explain in one paragraph why splitting digits individually (each digit its own token) helps arithmetic in language models, while merging frequent multi-digit numbers (like years: 2023, 2024) into single tokens hurts it.
(e) A user's prompt is 400 English words and a 200-line Python script. Roughly estimate the token count using English tokens/word and code tokens/line. What does this imply about the cost of pasting a long code snippet versus a long natural-language prompt?
References
- [1]
Jurafsky, D., & Martin, J. H. (2024). Speech and Language Processing (3rd ed., draft). — Canonical reference for tokenisation (5.1), n-grams and smoothing (5.4), and language-modeling fundamentals (5.3). stanford.edu/~jurafsky/slp3
↩ - [2]
Raschka, S. (2024). Build a Large Language Model (From Scratch). Manning. — Chapter 2 on tokenisation (5.1) and the practitioner-level walk-through of BPE.
↩ - [3]
Gage, P. (1994). "A new algorithm for data compression". The C Users Journal, 12(2), 23–38. — Original BPE algorithm as a compression scheme (5.1).
↩ - [4]
Sennrich, R., Haddow, B., & Birch, A. (2016). "Neural machine translation of rare words with subword units". ACL. arXiv:1508.07909 — Adapted BPE to neural sequence models; the paper that put BPE on the LLM map (5.1).
↩ - [5]
Schuster, M., & Nakajima, K. (2012). "Japanese and Korean voice search". ICASSP. — WordPiece tokeniser (used by BERT) (5.1 §5).
↩ - [6]
Kudo, T., & Richardson, J. (2018). "SentencePiece: A simple and language-independent subword tokenizer and detokenizer for neural text processing". arXiv:1808.06226 — SentencePiece tokeniser (LLaMA, T5) (5.1 §5).
↩ - [7]
Radford, A., Wu, J., Child, R., et al. (2019). "Language models are unsupervised multitask learners". OpenAI technical report. — GPT-2 and the introduction of byte-level BPE (5.1 §5).
↩ - [8]
Manning, C., et al. CS224N: Natural Language Processing with Deep Learning. Stanford. — Lectures on subword tokenisation and language modelling (5.1, 5.3).
↩ - [9]
Shazeer, N. (2019). "Fast transformer decoding: One write-head is all you need" (Multi-Query Attention). arXiv:1911.02150 — MQA: share K, V across heads to shrink the KV cache (6.14 §5.1).
↩ - [10]
Ainslie, J., Lee-Thorp, J., de Jong, M., et al. (2023). "GQA: Training generalized multi-query transformer models from multi-head checkpoints". arXiv:2305.13245 — Grouped-query attention; the modern default in LLaMA-2-70B, LLaMA-3, Mistral, Qwen (6.14 §5.1; full treatment Cap 8.3).
↩ - [11]
Liu, H., Zaharia, M., & Abbeel, P. (2023). "Ring Attention with blockwise transformers for near-infinite context". arXiv:2310.01889 — Context parallelism for 1M+ context training (7.9 §6, 7.13 §5.1).
↩ - [12]
Dao, T., & Gu, A. (2024). "Transformers are SSMs: Generalized models and efficient algorithms through structured state space duality" (Mamba-2). arXiv:2405.21060 — SSM-attention duality; Mamba-2 architecture (8.7 §5.1).
↩