Transformers from the ground up
Tokenisation in depth
A tokenizer is a learned merge table, not a rule about words. Read the merges, measure fertility, and see why a vocabulary change is a model change.
Merges, not rules
Byte-pair encoding learns a vocabulary instead of being handed one. It starts from a table of single characters. It counts every adjacent pair in a corpus and merges the most frequent pair into a new symbol. It repeats until the vocabulary reaches its target size. The result is an ordered merge table. That table is the tokenizer.
Order matters, because the rules are applied in the order they were learned. A later merge can only combine symbols that earlier merges produced. Two tokenizers with the same vocabulary size can therefore disagree on almost every id. The corpus, the split into words and the merge order all differ. Reproducing a model means shipping the merge table with it.
The frequency that drives a merge is counted over words, weighted by how often they appear. It is not counted over distinct words. A pair that appears once in ten thousand rare words matters less than a pair inside “the”. Code keeps a dictionary from word to count. The pair count runs over that dictionary. That is what makes training on billions of tokens tractable.
Bytes, so nothing is unknown
A character vocabulary cannot cover every script. A word vocabulary cannot cover a new word. Byte-level BPE avoids both problems by starting from the 256 possible bytes. Every UTF-8 string is a sequence of bytes. Every string can therefore be encoded, and there is no unknown token at all.
The cost moves rather than disappears. A character outside the languages the corpus favoured takes several bytes. So it takes several tokens. The same sentence in English and in a rare script can take three times as many tokens. That triples the cost, and the share of the context window it fills.
The printable mapping is a detail with consequences. Bytes are mapped to visible characters so the tokenizer file stays text. So a token can look like “Ġthe”, a space marker followed by letters. Reading the raw token list is useful. It is not the same as reading the original string.
The same merges in a library
from tokenizers import Tokenizer
tokenizer = Tokenizer.from_file("tokenizer.json")
encoded = tokenizer.encode("unhappiness costs three tokens")
print(encoded.tokens) # the pieces, including markers
print(encoded.ids) # what the model reads
print(encoded.offsets) # where each piece came from
print(tokenizer.decode(encoded.ids))Learn the merges
Run five merge steps over the four weighted words, then add a fifth word to the corpus and see which merge changes first.
Fertility and the vocabulary budget
Vocabulary size trades sequence length against row quality. A larger vocabulary turns common words into one token. That shortens sequences and cuts attention cost. It also gives each token a rarer row to learn and widens the output softmax. A smaller vocabulary keeps rows well trained. It makes every sentence longer.
Fertility is the average number of tokens per word. English prose often lands near 1.3 with a modern vocabulary. Code, numbers and other scripts run higher. Measure it on your own data rather than trusting a published figure. The tokenizer that matters is the one attached to the model you are using.
Compression ratio is characters per token. It is the same measurement inverted, and it is the one to log in a pipeline. A sudden drop after a data change means the new data is being shredded. The first symptom is usually a bill or a truncated prompt, not an error message.
Changing the tokenizer invalidates the embedding table. The ids no longer point at the same rows, so a checkpoint cannot be reused with a new vocabulary without retraining at least the embeddings.
Normalisation and special tokens
Before any merge, the text is normalised, or cleaned up, and split. Normalisation may lower-case, strip accents or collapse whitespace. Pre-tokenisation, the split into words, cuts on spaces, punctuation and digit boundaries. A merge then never crosses a boundary it should respect. Both steps are part of the tokenizer, and both change the ids.
Special tokens occupy ids in the same vocabulary. A beginning-of-sequence marker, a padding token, a mask token and chat role markers are all rows. The model can read them and predict them unless they are frozen. The attention mask must exclude padding, and the loss must ignore it. Otherwise the model learns to produce padding as if it were text.
The split between normalisation and pre-tokenisation also decides what the model can never see. A tokenizer that strips accents cannot tell apart two words that differ only by one. A tokenizer that lower-cases loses case at the first step. Those choices are cheap to make and expensive to reverse.
Encode with the ranks
Encode two sentences with the merge table, then reorder two merges and see which words are split differently and how fertility moves.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What is a trained byte-pair encoding tokenizer?
