Skip to content
mlmentorship

Tokenization: BPE, WordPiece, and the LLM era

Trace ranked BPE merges, byte coverage and the versioned text-to-ID contract shared by training and serving.

Published · 7 min read ·Core ·Intermediate
Reading depth

Summary

A tokenizer converts text to model IDs; preprocessing, vocabulary, merge rules and special-token handling belong to the model contract.

Consider a hypothetical corpus containing map five times, maps three times and cap twice. Use UTF-8 byte base pieces, forbid merges across word boundaries, and resolve ties by a declared fixed ordering. Which pairs merge, and how should the learned tokenizer encode maps, cap and unseen traps?

The main families

BPE (Byte-Pair Encoding)

Count adjacent pairs across the weighted corpus, merge the most frequent eligible pair, apply it, then recount. Training changes the segmentation; encoding later replays the learned priority without retraining on each input.

Weighted training counts; ranked encoding

Learn the merge order from the corpus, then reuse it on new words

Learn merge order once; reuse it during encodingThe corpus learns a prioritized merge table. A new word applies that fixed order rather than relearning frequencies from itself.A fixed merge order consumes the samecharactersOriginal word characterslowerApply learned l + o mergelowerApply learned lo + w mergelowerApply learned e + r mergelower
Derivation

START · COUNT ADJACENT PAIRS ACROSS ALL OCCURRENCES

Corpus: m a p × 5 · m a p s × 3 · c a p × 2

Largest count: a + p = 5 + 3 + 2 = 10
Rank 1 merge: add ap to the vocabulary.

ROUND 2 · RECOUNT AFTER APPLYING a + p → ap

Corpus now: m ap × 5 · m ap s × 3 · c ap × 2

Largest count: m + ap = 5 + 3 = 8
Rank 2 merge: add map to the vocabulary.

ROUND 3 · RECOUNT AFTER APPLYING m + ap → map

Corpus now: map × 5 · map s × 3 · c ap × 2

Largest count: map + s = 3
Rank 3 merge: add maps to the vocabulary.

APPLY · REPLAY MERGES IN LEARNED RANK ORDER

maps → [maps] · common whole word
cap → [c] [ap] · shared learned ending
traps → [t] [r] [ap] [s] · rarer word, more pieces

recount after each training merge. At encoding time, replay the fixed learned ranks; do not choose pairs using the new word's frequencies.
Check: Why does the second winning pair count become eight after the first merge? Why is the new word not used to relearn merge order?
Read it this way: The corpus learns a prioritized merge table. A new word applies that fixed order rather than relearning frequencies from itself.
Does encoding lower relearn pair priorities from that single word?

Initially, a+p occurs times. After merging it, m+ap occurs eight times; after that merge, map+s occurs three times. All three winners are unique, so the tie rule does not affect this example.

The frozen sequence yields maps → [maps], cap → [c, ap] and traps → [t, r, ap, s]. The last word needs available byte pieces t and r, even though the tiny training corpus never used them.

Byte-level BPE

Starting with all 256 byte values makes supported UTF-8 text representable before optional merges. An individual token’s bytes need not form a complete Unicode character; decode the whole sequence with the declared policy. Character-only vocabularies without fallback do not inherit this coverage guarantee.

Change base units or the deployed vocabulary

1. Encode previously unseen Unicode

New task: preserve supported text without an unknown-token fallback or silent normalization.

Picture and change: U+00E9 has UTF-8 bytes [195, 169] before merges. Its decomposed form, U+0065 followed by U+0301, has different bytes. Whether to normalize is a task requirement, not a universal cleanup step.

Code: use the existing tokenizer implementation, pinned here to tiktoken==0.12.0 and cl100k_base. This real encoding is separate from the toy corpus above.

import tiktoken

encoding = tiktoken.get_encoding("cl100k_base")
composed = "\u00e9"
decomposed = "e\u0301"
composed_ids = encoding.encode(composed, disallowed_special="all")
decomposed_ids = encoding.encode(decomposed, disallowed_special="all")
assert list(composed.encode("utf-8")) == [195, 169]
assert encoding.decode_bytes(composed_ids) == composed.encode("utf-8")
assert encoding.decode_bytes(decomposed_ids) == decomposed.encode("utf-8")
assert composed_ids != decomposed_ids
print(composed_ids, decomposed_ids)

Check: both forms round-trip to their original bytes, but their token IDs differ. Special-token-looking strings need a separate explicit policy; do not grant control-token status to arbitrary user text.

Use when: exact code, identifiers or source text must survive preprocessing.

2. Add domain-specific pieces

New task: reduce fragmentation for domain text without silently changing a deployed model’s symbols.

Picture and change: version the vocabulary, IDs, embedding/output rows, templates and corresponding continued training together. Preserving old IDs can avoid one incompatibility, but does not train new rows.

Check: reusing ID 17 for another piece without updating coupled weights changes the model’s input interpretation. Revalidate quality and context counts on the paired model/tokenizer artifact.

Use when: measured domain fragmentation justifies model adaptation and its costs.

Failure and transfer

Serving with different BOS/EOS IDs or a different role template can break compatibility despite ordinary text looking correct. Compare the final serialized IDs, not screenshots of prompts.

Does fewer tokens per document establish a better model?

Check your answer

No. Compression, coverage, quality and embedding/output parameter cost are different quantities; evaluate the complete paired artifact.

WordPiece

BERT-style inference commonly uses greedy longest-match pieces within pretokenized words, with ## marking continuation. Training details depend on the implementation; a family name does not pin a unique likelihood-based merge algorithm. Unknown-token behavior differs from complete byte coverage.

SentencePiece (Unigram)

SentencePiece is a toolkit supporting Unigram and BPE, not a synonym for Unigram. Unigram uses a probabilistic piece vocabulary and segmentation; training can prune candidates according to its objective. Normalization and whitespace treatment are configurable parts of the artifact.

Tiktoken / cl100k / o200k

Tiktoken implements byte-level BPE encodings, including cl100k_base and o200k_base. Encoding name, package version, merge assets and special-token policy matter more than a model-family nickname.

What an interviewer expects you to say

Explain base units, corpus weighting, boundaries, normalization, merge ordering, ties, IDs, special tokens and round-trip policy. Distinguish training from encoding and model adaptation from silent tokenizer replacement.

Common confusions

Tokens are not necessarily words, characters or complete UTF-8 sequences. Smaller vocabularies can increase sequence length; larger ones increase embedding/output rows and can reduce sharing. Neither tradeoff alone predicts final quality.

The LLM-era tokenization gotchas

Pin the training/serving contract, including template and BOS/EOS handling. Measure actual multilingual corpora, indentation/code and number formatting with the exact encoding. There is no universal characters-per-token ratio or guaranteed arithmetic benefit from digit splitting. Test normalization against exact-byte requirements rather than silently applying NFC everywhere.

Why interviewers ask

For inefficient domain tokenization, compare fragmentation, quality, memory and retraining costs before choosing vocabulary adaptation. Use only truthful production examples; neither vocabulary terminology nor a token-count improvement proves operational experience.

References: Sennrich et al., tiktoken, SentencePiece.

Related: Transformer architecture, LLM Evals essay.