AI Infra
0%
Part I · Chapter 7

Tokenization

AuthorChangkun Ou
Reading time~13 min

Chapter 6 ended with token-id shards. Tokenization defines those ids: the map between text and the integers a model actually reads. This early map decides how expensive each language is to process, where code and numbers get split, and how much of a document's sequence length is spent encoding it. It is also why one preprocessing choice becomes one of the hardest decisions in the stack to revisit. And so byte-level BPE appears here as the dominant compromise, with Unigram and tokenizer-free schemes set beside it as different answers to the same constraints rather than cosmetic alternatives.

A model never sees text

A transformer does not see text. It sees a sequence of integers drawn from a fixed vocabulary, each mapped to a row of an embedding matrix, the lookup table mapping each token id to a learned vector, one row per vocabulary entry. Something has to turn a stream of Unicode into that sequence, and the tokenizer is that something. The choice looks like plumbing and behaves like a foundation.

The reason is a conjunction of three constraints, each harmless alone and unforgiving together.

The vocabulary must be finite and fixed before training begins, yet text is open. New words, code, emoji, and languages the designer never enumerated must still encode without failure. A table of words can never be complete, so completeness has to come from somewhere other than the table.

The segmentation must be cheap. Tokenization runs over the entire corpus on CPU fleets ahead of the GPU run, as covered in Chapter 6, so an elegant but slow scheme is not affordable at corpus scale.

And the result is permanent. The embedding and output matrices are sized to the vocabulary (the output matrices, or output projection, convert the model's final vectors back into a score per vocabulary entry), so the vocabulary cannot change without retraining the model. A tokenizer trained on a skewed corpus caps quality for everything under-represented in it, and that ceiling is invisible until evaluation.

Together, these constraints make tokenizer choice both quality-critical and close to irreversible. Everything that follows is an attempt to satisfy the first two constraints while living with the third.

The subword bargain

The dominant answer is subword tokenization: a vocabulary of pieces smaller than words but larger than characters, sized so common words are single tokens while rare words decompose into known parts. This is a bargain struck directly against the open-vocabulary constraint. Any string is representable because the pieces bottom out at units that always exist, and frequent text stays short because common sequences are merged into single ids.

Byte-pair encoding (BPE) is the workhorse construction. Start from a base alphabet, count adjacent pair frequencies across the corpus, merge the most frequent pair into a new symbol, and repeat until the vocabulary reaches its target size (Sennrich et al. 2015). The learned merge list is the tokenizer; encoding replays the merges in order.

# BPE training, conceptual
vocab = set(base_symbols)
merges = []
while len(vocab) < target_size:
    pair = most_frequent_adjacent_pair(corpus, vocab)
    merges.append(pair)
    vocab.add(merge(pair))

A worked example makes the loop concrete. Figure 7.2 runs two merges on a tiny corpus of the word low repeated, where the most frequent adjacent pair is absorbed into a new symbol each round until the common sequence is a single token.

Corpus is low low low; the base vocab is l, o, w.
Count adjacent pairs; (l, o) is the most frequent.
Merge (l, o) → lo, and append it to the merge list.
Recount; now (lo, w) is the most frequent pair.
Merge (lo, w) → low; the frequent sequence is now a single token.
Figure 7.1. BPE learns by counting pairs and merging the most frequent, round after round. Step through each round, or let it play.
bpe s0 Start: l o w  l o w  l o w vocab = { l, o, w } s1 Merge 1: pair (l, o) is most frequent lo w  lo w  lo w s0->s1  count + merge s2 Merge 2: pair (lo, w) is most frequent low  low  low s1->s2  count + merge s3 Result: 'low' is one token vocab = { l, o, w, lo, low } s2->s3  freeze merge list
Figure 7.2. BPE training as repeated greedy merges. Each round counts adjacent pairs, merges the most frequent one into a new symbol, and appends it to the merge list. Common sequences collapse into single tokens. After Sennrich et al. (2015).

Run the pseudocode for real: this trains BPE on a tiny corpus, prints the learned merges, then shows how common and rare words split into a different number of tokens.

from collections import Counter

def merge_pair(word, a, b):
    out, i = [], 0
    while i < len(word):
        if i < len(word) - 1 and word[i] == a and word[i + 1] == b:
            out.append(a + b); i += 2
        else:
            out.append(word[i]); i += 1
    return out

corpus = "low low low lower lowest newest wider".split()
words = [list(w) + ["_"] for w in corpus]
merges = []
for _ in range(6):
    pairs = Counter()
    for w in words:
        for a, b in zip(w, w[1:]):
            pairs[(a, b)] += 1
    if not pairs:
        break
    (a, b), _ = max(pairs.items(), key=lambda kv: (kv[1], kv[0]))
    merges.append((a, b))
    words = [merge_pair(w, a, b) for w in words]

print("learned merges:", [a + "+" + b for a, b in merges])
for w, toks in zip(corpus, words):
    print(f"{w:7s} -> {' '.join(toks)}  ({len(toks)} tokens)")

The decisive refinement is to run BPE over raw bytes rather than Unicode characters. With 256 byte values as the base alphabet, every possible input encodes with zero out-of-vocabulary risk, even text the tokenizer never saw, and there is no separate unknown token to handle. This is byte-level BPE, and it is why the same tokenizer can ingest English, code, a new emoji, and a script absent from its training data. Byte-fallback gives a near-equivalent guarantee in character-based schemes by decomposing any unseen character into its underlying bytes. The open-vocabulary constraint, which sank every word-level table, is now satisfied by construction rather than by hoping the table was large enough.

There is a second question the bargain leaves open: not what the pieces are, but how to choose a split when several are possible. BPE is greedy and frequency-driven, with no probabilistic model of how a string should split. The unigram language model offers an alternative: posit that a sentence is a bag of subword pieces with independent probabilities, start from a large candidate vocabulary, and prune pieces whose removal least hurts the corpus likelihood (Kudo 2018). Unigram yields a principled segmentation and, because it defines a distribution over segmentations, supports subword regularization: sampling alternative splits at training time as a form of augmentation (Kudo 2018).

Two parameters set by the data pipeline govern the rest. Vocabulary size trades sequence length against matrix size, and the tokenizer's own training corpus bakes in which languages and domains get short, efficient encodings. Both are frozen here and consumed, not chosen, downstream.

From words to bytes

The construction above did not arrive whole. Tokenization moved from words to bytes in three steps, each removing a failure of the last. Figure 7.3 traces the lineage, with the specific failure each stage carries and the one its successor removes.

evolution word Word-level fails: unknown token on any unseen word char Character-level fails: long sequences, relearns spelling word->char  remove unknown token subword Subword BPE / Unigram open vocabulary, short common text char->subword  shorten sequences byte Byte-level BPE zero out-of-vocabulary, any input encodes subword->byte  close OOV gap
Figure 7.3. The lineage from word-level to byte-level tokenization. Each arrow is labeled with the failure the next stage removes. Byte-level BPE closes the out-of-vocabulary gap entirely.

Word-level vocabularies came first and broke on the open-vocabulary problem: any word absent from the table became a single unknown token, discarding its content. Character-level models removed the unknown token but made sequences long and forced the model to relearn spelling from scratch, spending depth on what a vocabulary could have stored.

Sennrich et al. (2015) brought BPE, invented for data compression, to neural machine translation as the subword compromise, and it became the default (Sennrich et al. 2015). The unigram language model, introduced by Kudo (2018), offered a probabilistic alternative with subword regularization (Kudo 2018). Kudo and Richardson (2018) then packaged both in SentencePiece, which treats input as a raw stream including whitespace, so the tokenizer is language-independent and needs no pre-tokenization rules (Kudo and Richardson 2018). The final step was byte-level BPE, which made the base alphabet the 256 byte values and closed the out-of-vocabulary gap entirely. Byte-level BPE is the construction behind most current frontier tokenizers, with unigram and SentencePiece remaining common in multilingual and non-Latin-script settings.

The open frontier is whether to tokenize at all. tokenizer-free schemes operate directly on bytes or characters, learning the segmentation inside the model rather than fixing it in a preprocessing step. They remove the frozen vocabulary and its biases, at the cost of longer sequences and the compute to process them. This is a live research direction, not a settled replacement.

What's contested

Whether a fixed tokenizer should exist at all is genuinely unsettled. The case against it is that the vocabulary is a frozen, corpus-dependent artifact that bakes in language and domain bias, fragments numbers and code in ways that hurt arithmetic and program synthesis, and is the single component you cannot change after training. Tokenizer-free and byte-level approaches answer by learning structure inside the model, trading preprocessing bias for longer sequences and higher compute per character. That answer now has concrete systems behind it. The Byte Latent Transformer reads raw bytes and groups them into patches whose size tracks how predictable the next byte is, spending compute where the text is hard, and it matches BPE-based models in FLOP-controlled scaling comparisons (Pagnoni et al. 2024). H-Net goes further and learns the chunking itself, end to end, inside a hierarchical model (Hwang et al. 2025). Both still run their byte-level layers over sequences several times longer than a tokenized model sees, the tax a vocabulary exists to avoid. Byte-level BPE is so entrenched, and that sequence-length penalty so real, that the field has not converged. Treat tokenizer-free schemes as a promising open direction, not a drop-in replacement.

What you cannot take back

The history above reads like progress, and it is, but every choice it settled became a setting that someone has to make once and then live with. Each tokenizer setting is a balance with a knee, and several of them set a ceiling that only shows up later. Figure 7.4 makes the central one visible: a larger vocabulary shortens sequences but enlarges the matrices sized to it, so the two costs cross rather than agree.

2026-06-21T21:25:32.485829 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/
Figure 7.4. Schematic illustration of the vocabulary-size trade-off: a larger vocabulary shortens token sequences (cheaper per document) while enlarging the embedding and output matrices sized to it. The optimum is a knee, not an endpoint. Idealized data, not measured.
  • Vocabulary size. A larger vocabulary shortens sequences, which makes training and inference cheaper per document and improves multilingual fertility, the number of characters carried per token. It also enlarges the embedding and output matrices and can starve rare tokens of the occurrences they need to train a good embedding. The optimum is a function of corpus and budget, not a constant.
  • Training corpus for the tokenizer. Train it on the final data mixture or on a separate curated set? The choice permanently bakes the language and domain balance into the vocabulary. A mismatch with the real mixture spends vocabulary on the wrong things and leaves under-represented languages with long, expensive encodings.
  • BPE versus unigram. BPE is greedy, fast, and ubiquitous, with strong tooling and broad compatibility. Unigram is probabilistic, supports subword regularization, and often segments multilingual text more evenly, at the cost of a heavier training procedure and less ecosystem inertia behind it.
  • Multilingual fairness. Petrov et al. (2023) show that a tokenizer tuned on English-heavy data splits other languages into many more tokens for the same meaning (Petrov et al. 2023). That inflates cost and latency for those users and shortens their effective context window, a structural unfairness fixed in the vocabulary itself. This is the trade-off that most directly caps downstream quality, and it is set entirely here. Figure 7.5 shows the shape of the gap: the same sentence costs several times more tokens in some languages than in English.
2026-06-21T21:25:33.054799 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/
Figure 7.5. Schematic illustration of tokenizer fertility across languages: the same sentence costs many more tokens under an English-tuned tokenizer for some languages, with the multiple relative to English shown beside each bar. Idealized token counts, after Petrov et al. (2023).

The reason these matter more than ordinary hyperparameters is lock-in. A vocabulary trained on a skewed mixture caps multilingual quality and inflates token cost for under-served languages, and it cannot be changed without retraining the model, because the embedding and output matrices are sized to it. Vocabulary extension during continued training can graft on new tokens as a partial workaround, but it does not undo the original bias. In short, the tokenizer is decided once and lived with for the model's life, which is why a choice that looks like preprocessing deserves the scrutiny of an architecture decision.

Constraint arrow

The tokenizer is frozen in the data pipeline and dictates a choice one layer up. The vocabulary size set here fixes the row count of the embedding matrix and the output projection in Chapter 8: that layer consumes the vocabulary and sizes its matrices to match, it does not get to choose it. A token budget decided during data work therefore sets a hard parameter of the model architecture.

Knobs you set once

Naming the lock-in is not the same as dismissing the work. In practice a tokenizer is trained once, with a handful of decisions that are expensive to get wrong, and two recurring knobs deserve their own attention beyond vocabulary size and training corpus.

The first is digit handling: splitting numbers into individual digits so arithmetic does not depend on whether "1234" happened to merge into one token. The second is whitespace handling, where SentencePiece-style schemes encode spaces as ordinary symbols so detokenization is exact and reversible (Kudo and Richardson 2018). The build itself is the BPE training, conceptual loop above: count, merge, repeat, then freeze the merge list and vocabulary as the artifact every later stage consumes.

That artifact is the whole point. It is small, it is produced early, and it binds everything downstream to the language and domain balance present in the corpus that made it. The tokenizer arrives looking like a preprocessing step and turns out to be the first irreversible commitment in the model's life, and that is the lens worth carrying into the architecture that consumes it.

Further reading

  • Sennrich et al., “Neural Machine Translation of Rare Words with Subword Units” (BPE), 2015. arXiv:1508.07909
    This paper proposes using BPE to segment rare and unknown words into subword units, enabling open-vocabulary neural machine translation without back-off dictionaries.
  • Kudo, “Subword Regularization: Improving Neural Network Translation Models with Multiple Subword Candidates” (Unigram LM), 2018. arXiv:1804.10959
    Subword regularization trains NMT models on multiple probabilistically sampled subword segmentations and introduces a unigram language model segmentation algorithm as a probabilistic alternative to BPE.
  • Kudo & Richardson, “SentencePiece: A Simple and Language Independent Subword Tokenizer and Detokenizer for Neural Text Processing,” 2018. arXiv:1808.06226
    SentencePiece is a language-independent subword tokenizer and detokenizer that trains directly from raw sentences using BPE or unigram language model, enabling purely end-to-end text processing without pre-tokenization.
  • Pagnoni et al., “Byte Latent Transformer: Patches Scale Better Than Tokens” (BLT, tokenizer-free), 2024. arXiv:2412.09871
    BLT drops the tokenizer and groups raw bytes into dynamically sized patches based on next-byte entropy, matching tokenization-based models in FLOP-controlled scaling up to 8B parameters.
  • Hwang et al., “Dynamic Chunking for End-to-End Hierarchical Sequence Modeling” (H-Net, learned dynamic chunking), 2025. arXiv:2507.07955
    H-Net replaces the tokenization pipeline with a hierarchical model that learns content-dependent chunk boundaries end to end, outperforming byte-level and BPE Transformers at matched compute.
  • Petrov et al., “Language Model Tokenizers Introduce Unfairness Between Languages,” 2023. arXiv:2305.15425
    Tokenizers systematically encode the same text into up to 15 times more tokens for non-English languages, creating unfair cost, latency, and context-length disparities across language communities.

Comments

Log in to comment