← All posts

Model development

Post 4 of 26

Tokenization: what a tokenizer decides before training starts

Posts in this series · 26

Before a single weight is trained, a decision has already been made about every piece of text the model will ever see: how it is cut into tokens. The Foundations post on language models shows how byte-pair encoding learns its pieces, merge by merge. This post picks up from there and asks what that decision costs.

The answer matters more than it first appears. The model computes per token, its context window is measured in tokens, the training budget from the previous post is counted in tokens, and API prices are charged per token. A tokenizer that needs three times as many tokens for one language as another makes that language three times as expensive in every one of those ways.

What a tokenizer does

A tokenizer has two jobs: encode a string into a list of integers, and decode the integers back into the string. Encoding happens in up to four steps:

  1. Normalize the text: for example, apply a Unicode normalization form, or replace unusual spaces.
  2. Pre-tokenize: split the text into chunks that merges may not cross, typically words with their leading space, numbers and punctuation. GPT-2’s tokenizer does this with a regular expression, which is why ” world” with its space is a single token.
  3. Apply the model, BPE or Unigram, inside each chunk.
  4. Add special tokens: markers such as end-of-text, or the role markers of a chat template, which never come from ordinary text.

Most of the choices that matter later hide in steps 1, 2 and 4, not in the merge algorithm.

Two cases of step 3 deserve care. Byte-level tokenizers (GPT-2, Qwen2.5) start from the 256 possible bytes, so every string can be encoded and decoded exactly. Byte fallback gives a character-level or subword tokenizer a way out: a character missing from the vocabulary is spelled with byte tokens instead of an unknown token. LLaMA, for example, used SentencePiece BPE with byte fallback, and split all numbers into individual digits (LLaMA, §2).

BPE and Unigram

BPE builds a vocabulary upwards: start from characters or bytes and repeatedly add the most frequent adjacent pair. Encoding replays the learned merges in order.

Unigram, the other algorithm in the SentencePiece library, works downwards (Kudo, 2018, §3.2):

  1. Start from a large seed vocabulary of candidate pieces.
  2. Give each piece a probability, fitted so that the training text is as likely as possible when each sentence is a sequence of independently chosen pieces.
  3. Remove the pieces whose removal costs the least likelihood (keeping the top 80%, for example, and always keeping single characters), and repeat until the vocabulary has the target size.

To encode, Unigram picks the segmentation with the highest product of piece probabilities. With illustrative probabilities un 0.1, happy 0.05, unh 0.01 and appy 0.02, the word “unhappy” could be un + happy, with probability 0.1×0.05=0.0050.1 \times 0.05 = 0.005, or unh + appy, with probability 0.01×0.02=0.00020.01 \times 0.02 = 0.0002, so un + happy wins. Real implementations find the best segmentation efficiently with the Viterbi algorithm rather than listing every split. Because Unigram has probabilities, it can also sample alternative segmentations during training, a regularization trick BPE does not offer directly.

In practice both work well. The consequential decisions are the training data, the vocabulary size and the normalization, not the algorithm.

What the same text costs

Tokenizers are usually compared by compression, bytes or characters per token, and across languages by the premium: how many tokens a language needs relative to English for the same content (Petrov et al., 2023, §3). The fair way to measure it is on a parallel corpus, where every sentence has the same meaning in every language. FLORES-200 provides one. Here are the 1,012 sentences of its devtest set under three tokenizers:

LanguageGPT-2 (50k vocabulary)XLM-R (250k)Qwen2.5 (152k)
English27,044 tokens30,66127,621
French×2.0×1.3×1.6
Chinese×3.1×1.0×1.0
Hindi×7.4×1.2×4.4
Khmer×15.2×1.6×6.5

Premiums are relative to English under the same tokenizer.

GPT-2’s tokenizer was trained mostly on English web text, so Khmer has almost no merges and falls back to about one token per byte: 1.06 bytes per token, where every Khmer character takes 3 bytes. Qwen2.5 was built for a multilingual model and does much better, yet still needs 6.5 times as many tokens for Khmer as for English. XLM-R, whose tokenizer was trained on text in 100 languages (Conneau et al., 2020), comes closest to parity. Petrov et al. found differences of up to 15 times across languages and tokenizers, including tokenizers trained for multilingual use.

These numbers are costs, three times over:

Try it: predict context capacity. On average an English FLORES sentence is 26.7 GPT-2 tokens. How many sentences fit in an 8,192-token context window in English? In Khmer under GPT-2, where the same content takes 15.2 times as many tokens? And under Qwen2.5, at 176.6 tokens per Khmer sentence?

Answer. English: 8,192 / 26.7 ≈ 307 sentences. Khmer under GPT-2: each sentence is about 406 tokens, so about 20 sentences. Under Qwen2.5: 8,192 / 176.6 ≈ 46. The same window carries fifteen times less Khmer content under the English-centred tokenizer, and nearly seven times less even under a multilingual one. A retrieval system that fills the context with documents, or a summarizer of long reports, inherits this directly.

Vocabulary size

A bigger vocabulary means fewer tokens per sentence, but every entry needs a row in the embedding matrix and in the output layer: V×dV \times d parameters each, for vocabulary size VV and model width dd. At width 4,096, a 32,000-entry vocabulary costs 131 million parameters per matrix and a 128,000-entry one 524 million.

For small models this is a large share. Qwen2.5-0.5B has a vocabulary of 151,936 rows at width 896, so its embedding matrix holds 136 million parameters, about 28% of the model’s 494 million (the output layer reuses the same matrix). A bigger vocabulary also makes the output softmax more expensive and spreads training data over more rows, so rare tokens are seen less often. Choosing a vocabulary trades sequence length against parameters, compute in the output layer and how well each row is trained.

When text and tokenizer disagree

Decoding should give back the original string. Checking that on the FLORES sentences turned up three ways it can fail, all from normalization:

GPT-2’s byte-level tokenizer has no normalizer and round-tripped every sentence exactly. Normalization is often what you want, merging look-alike forms so they share tokens, but it means the text the model sees is not the text you wrote. That matters for anything that compares strings: exact-match evaluation, citation of source spans, code, and invisible characters like those in Khmer text.

Other consequential failures:

Everyday picture: a tokenizer is like a postage scale with different rates per alphabet. The letter says the same thing in every language, but some senders pay several times more to post it, and the box it has to fit into, the context window, is the same size for everyone.

What to remember

Try it yourself

Sources

Further reading