Majid Al-RaimiTokenization units and byte-pair encoding

ICS 582Lecture 02Part 06

Tokenization units and byte-pair encoding

Words, characters or subwords as the unit of text, the tradeoffs among them, and the BPE algorithm that learns a subword vocabulary by merging frequent pairs, traced by hand on a toy corpus.

Concepts
5
Slides
38-48
Reading
30 min
Understood
0/5 concepts

Why this part matters

Every model you train or evaluate in this course fixes a tokenizer before it sees a single example. That choice decides the vocabulary size, the sequence length, the cost per token, and whether an Arabic clitic such as wa- or the article al- ends up as a clean unit or as a fragment glued to whatever follows it.

This part first asks what a good tokenization is trying to achieve, then compares the three obvious units (words, characters, subwords) against those goals. The second half is the byte-pair encoding algorithm that modern language models use to learn subwords from data: how it trains, a full hand trace on the toy corpus from the slides with every pair count shown, and how the learned merges are replayed on new text. Tracing BPE by hand is a standard exam exercise, and reading a merge list is what you will do when you debug a Hugging Face tokenizer on Arabic text in your research.

By the end you can

  1. Name the four properties a tokenization balances and place words, characters and subwords on the coverage versus compactness spectrum.
  2. Write the BPE training pseudocode from memory and explain the role of k.
  3. Run BPE by hand for k merges on a small corpus, showing pair counts, stating the tie rule, and listing the vocabulary and final corpus.
  4. Encode an unseen word by replaying the merge list in order and explain why order matters.
  5. Explain how a leading space marker versus an end-of-word marker changes the learned units.

What a tokenizer must balance

Take the six-word string that the rest of this part will keep returning to: set new new renew reset renew. Split it at spaces and you have one token per word. Split it into characters, keeping a marker for each space, and you have one token per character. Split it with the subword vocabulary this part will learn and you get something in between.

SplitTokens NDistinct typesVocabulary |V|
Words644
Characters with marker2977
BPE, k = 415611
The same six-word string under three tokenizations

Same text, three very different values of N and |V|. That is the whole subject of Tokenization in one example. Tokenization converts raw text into a sequence of tokens, the units that a model or a pipeline operates on, and the choice of unit fixes three things at once: the Vocabulary the model must learn embeddings for, the length of every input, and what the model can represent at all. Earlier parts of this lecture treated the unit as given and asked what a word is. This part reverses the question: given that we may pick the unit, what should we optimise for?

Four properties a tokenization balances

Coverage
How rarely new text contains a token the vocabulary cannot represent. Characters and bytes win outright; words lose.
Compactness
How short the token sequence is for a given text. Words win; characters lose by a factor of four to six.
Meaningfulness
How well one token lines up with one unit of meaning. Words win; characters carry almost none on their own.
Cross-lingual applicability
Whether the same scheme works for Arabic, Chinese and English alike. Data-driven subwords and bytes win; whitespace words fail for scripts without spaces.

The four criteria pull in different directions, which is why no single unit wins. Coverage asks how often new text contains something the vocabulary cannot represent, the out-of-vocabulary problem from the Zipf and Heaps part: with word tokens the answer is always, because Heaps' law says the word vocabulary never closes. Compactness asks how many tokens the model must process; every token costs attention, memory and, in a hosted API, money. Meaningfulness asks whether one token corresponds to one unit of meaning, which makes downstream tasks such as tagging and parsing easier. Cross-lingual applicability asks whether the same scheme works for Chinese, which has no spaces, and for Arabic, whose clitics glue several words into one whitespace token.

There is also a quieter reason to care. Jurafsky and Martin note that a standardised tokenization is essential for replicability: a perplexity or an F1 score only means something if the units are the same, so the tokenizer is part of the experimental protocol, not a preprocessing detail (SLP3, chapter 2).

Recall

Name the four properties a good tokenization balances, and say which one word tokens fail on.

Coverage (few or no unknown tokens), compactness (short sequences), meaningfulness (units correlate with meaning) and cross-lingual applicability. Word tokens fail on coverage: the word vocabulary never closes, so new text always brings out-of-vocabulary words.

Words, characters and subwords: the trade-offs

Take the word renewed. As a word token it is one unit, but only if it appeared in training; if it did not, it is an unknown. As characters it is 7 letters, eight tokens with the leading space marker, none of which means anything alone. With the subword vocabulary learned later in this part it is four tokens, _re new e d, and the model has seen every one of them before even though it has never seen the word. Those three outcomes are the three granularities, and each buys something by paying for something else.

The same string as 29 character tiles, 15 subword tiles or 6 word tiles. The bar under each row is its token count. The vocabulary each scheme needs on a real corpus runs the other way, dozens of characters against tens of thousands of subwords and an open-ended set of words, which a six-word corpus is too small to show.
WordsCharactersSubwords
Vocabulary sizeVery large and open-ended; grows with the corpus under Heaps' lawTiny: the letters of a script, or 256 byte valuesFixed by design, typically 32k to 200k units
Sequence lengthShortest: one token per wordLongest: one token per characterIn between: frequent words stay whole, rare words split
Out-of-vocabularyYes: rare words, misspellings, new namesNone: every string is spelled from the base symbolsAlmost none: unseen words are composed from pieces
MeaningfulnessHighest; aligns with lexicons and syntaxSpread across many tokens; the model must learn to reassemble itOften morpheme-like, but only by accident of frequency
Best usePOS tagging, parsing, lexicon-driven systems that need explicit word boundariesNoisy OCR or typo-heavy text, very low-resource languages, specialised tasksModern language models and machine translation
Words, characters and subwords on the four criteria

Words: meaningful, but the vocabulary never closes

Word tokens are the natural first choice. They are meaningful, they line up with dictionaries and with the lexicons that rule-based systems consult, and tasks that need explicit word boundaries, such as part-of-speech tagging, syntactic parsing and lexicon-driven pipelines, are built around them. The problems are the ones the first four parts of this lecture already exposed. "Word" is ambiguous across languages: punctuation, contractions, compounds, clitics and scripts without spaces all give different answers. And the vocabulary is enormous and open-ended. Heaps' law says |V| keeps growing with N, so however large the training corpus, test text brings rare and unseen words, and every one of them is an OOV token the model must lump into a single UNK symbol.

Characters: nothing is unknown, but everything is long

Go to the other extreme and tokenize into characters. The vocabulary is tiny, a few dozen letters for Latin script or, at byte level, exactly 256 values. There is no OOV at all, because any string is spelled from the base symbols, and the tokenizer is robust to spelling variants, typos and OCR noise since a damaged word still shares most of its characters with the clean one. The price is paid in sequence length and in meaning. The 6-word string became 29 tokens, roughly five times longer, and attention cost grows with the square of that length. Two words that sit a sentence apart become dozens of positions apart, so a model with a fixed context window sees less of the surrounding text and must learn dependencies across far longer distances. Meaning is spread across many tokens, so the model must spend capacity learning to reassemble words before it can learn anything about them, which makes training slower. Characters are the right call for noisy text, for extremely low-resource languages where no subword vocabulary can be trusted, and for specialised tasks such as transliteration where the character really is the unit.

Subwords: the middle ground modern models use

Subword tokenization keeps a vocabulary smaller than a word vocabulary and produces sequences shorter than a character sequence, and its decisive property is the third row of the table: an unseen word is composed from pieces that were seen. The GPT-2 paper describes byte-pair encoding as a practical middle ground that interpolates between word-level inputs for frequent symbol sequences and character-level inputs for infrequent ones (Radford et al., 2019). Frequent words stay whole, rare words shatter into recognisable parts, and nothing is ever unknown.

There are two ways to decide what the pieces are. The linguistically natural way is to cut at morphemes, since morphemes carry meaning by definition. But the morphology parts of this lecture showed why that is hard: allomorphy changes the shape of a morpheme by context, non-concatenative morphology interleaves an Arabic root with a pattern rather than concatenating anything, and clitics blur the boundary between word and affix. Morpheme segmentation needs a linguist, or a model, per language, and it remains ambiguous even then. It is useful in some applications but it is not the common approach in modern language models. The language-agnostic way is to learn frequent substrings from data with no linguistic knowledge at all. That is what byte-pair encoding does, and Jurafsky and Martin name BPE and unigram language modelling as the two families widely used in modern language models (SLP3, section 2.4). They work across languages, reduce OOV and stay efficient, which is why the rest of this part is about one of them.

Quick check

Which statement about tokenization granularity is correct?

Recall

Give one reason to prefer characters over words, and one reason to prefer subwords over both.

Characters have no OOV and a tiny vocabulary, and they are robust to typos and OCR noise, so they suit noisy or very low-resource text. Subwords keep a fixed mid-sized vocabulary with sequences far shorter than characters and still compose unseen words from pieces, so they get most of the coverage benefit at a fraction of the length.

Here is the smallest corpus that shows the whole idea, borrowed from Jurafsky and Martin: the ten-character string A B D C A B E C A B with the vocabulary {A, B, C, D, E}. Look for the pair of adjacent symbols that occurs most often. It is A B, three times. Replace every A B with a new symbol AB and add it to the vocabulary. Now look again.

Worked example

Two merges on a ten-character corpus

  1. Merge (A, B)

    The corpus becomes AB D C AB E C AB, seven symbols instead of ten, and the vocabulary grows to six: {A, B, C, D, E, AB}.
  2. Merge (C, AB)

    The most frequent adjacent pair is now C AB, twice. The corpus becomes AB D CAB E CAB, five symbols, and the vocabulary grows to seven with CAB.
  3. Two merges

    Vocabulary 5 to 7, corpus 10 to 5 symbols. Each merge adds exactly one vocabulary entry and shortens the corpus by the number of non-overlapping occurrences of the pair it replaces (for pairs of two different symbols, simply the pair count).

That loop is the entire training algorithm of byte-pair encoding. The slide's pseudocode, which is Figure 2.6 of SLP3 adapted from Bostrom and Durrett (2020), reads as follows. Start with V equal to the set of unique characters (or bytes) in the Corpus C. Then repeat k times: find the most frequent pair of adjacent tokens tL, tR in C, form the new token tNEW = tL + tR by concatenation, add it to V, and replace every occurrence of tL tR in C with tNEW. Return V. One iteration of the loop is a Merge.

Vfinal=Vinitial+k|V_{\text{final}}| = |V_{\text{initial}}| + k
Each merge adds exactly one entry, so k fixes the vocabulary size

Sennrich, Haddow and Birch, who introduced BPE for neural machine translation, state the consequence plainly: the final symbol vocabulary size is equal to the size of the initial vocabulary plus the number of merge operations, and the number of merges is the only hyperparameter of the algorithm (Sennrich et al., 2016). In the toy example k is 2 or 4. In practice Jurafsky and Martin describe tens of thousands of merges on a very large corpus to produce vocabularies of 50,000, 100,000 or even 200,000 tokens, and GPT-2's vocabulary is 50,257 entries (Radford et al., 2019).

Two practical details shape the counting. First, merges never cross word boundaries. Sennrich's implementation says it does not consider pairs that cross word boundaries for efficiency, so the corpus is stored as a table of distinct words with their counts, and a pair inside a word that occurs twice in the corpus is counted twice. That is exactly why the slides list the toy corpus as four rows with counts rather than as running text. Second, the base symbols can be characters or bytes. GPT-2 works on UTF-8 bytes because a Unicode Code point vocabulary would start at over 130,000 entries, whereas byte-level BPE starts at exactly 256 and, since every byte is in the vocabulary, there can never be an unknown token (Radford et al., 2019; SLP3, section 2.4).

Four brackets close in the order the trainer learned them, then the character row gives way to four tokens. Merged pieces are accent, leftover characters teal.

Where the algorithm came from, and its two halves

The name is older than NLP. Philip Gage published byte pair encoding in 1994 as a data compression method: find the most frequently occurring pair of adjacent bytes in the data, replace all instances with a byte that was not in the original data, and repeat until no further compression is possible (Gage, 1994). Sennrich and colleagues adapted it for word segmentation, and two things changed. The units became characters rather than bytes, and the stopping rule became a fixed k rather than exhaustion, so the merges are no longer a throwaway table for one file but a reusable vocabulary shared by every text the model will ever see.

That reuse is why BPE has two parts. The trainer runs once, over the training corpus, and produces the vocabulary together with the ordered list of merges it performed. The encoder runs every time new text arrives and applies that list. The two must never be confused, and the last concept of this part is devoted to the encoder. The simulator below is the trainer: step through the merges and watch the pair table decide each one, then encode a word of your own in its lower panel; the encoder concept returns to that panel on its own.

SimulatorBPE merge stepper: train on a corpus, then encode a new word
Next: merge 1
Merges k0applied
Vocabulary7entries7 initial + 0
Corpus29symbolsfrom 29
Shorter by0%
Corpus after 0 merges
  • 2_new
  • 2_renew
  • 1set
  • 1_reset
Vocabulary (7)
_enrstw

Grey chips are the initial symbols, teal chips are learned merges in the order they were learned, and the accent chip is the newest.

Pair counts deciding merge 1
RankPairCountFirst seen atNote
1(n, e)4position 1winner, tie broken by rule
2(e, w)4position 2tie at the maximum
3(_, r)3position 4
4(r, e)3position 5
5(_, n)2position 0
6(e, n)2position 6
7(s, e)2position 10
8(e, t)2position 11
9(e, s)1position 15
  1. characters_renewed
  2. output_ r e n e w e d (8 tokens)

The encoder never counts anything. It replays the merge list top to bottom; a merge whose pair does not occur in the word is skipped.

Quick check

In one BPE training step, what exactly does the trainer merge?

Recall

State the difference between the BPE trainer and the BPE encoder in one sentence each.

The trainer counts adjacent pair frequencies on the training corpus and produces a vocabulary plus an ordered merge list. The encoder splits new text into characters or bytes and replays that merge list in order without counting anything.

Now run the BPE trainer by hand on the slide Corpus, set new new renew reset renew, for k = 4. This is the same example as the current SLP3 draft (example 2.11 in section 2.4), so the tables here match both the slides and the book symbol for symbol. The slides show the corpus and the Vocabulary after each Merge but never the pair counts that decided it, so those are worked out in full below.

First the setup. Every word except the first carries a leading space marker, drawn as an open box on the slides and written _ here. The first word, set, starts the string, so it has none. Grouping equal words gives four rows with counts: 2 × _ n e w, 2 × _ r e n e w, 1 × s e t and 1 × _ r e s e t. The initial vocabulary is the seven symbols _, e, n, r, s, t, w, and the corpus holds 2 × 4 + 2 × 6 + 3 + 6 = 29 symbols. Pair counts are weighted by row count: a pair that occurs once inside _ n e w counts 2.

Worked example

Four merges on set new new renew reset renew

  1. Merge 1: count every adjacent pair

    PairCountNote
    (n, e)4winner: tied with (e, w), seen first in the corpus
    (e, w)4tie at the maximum
    (_, r)3
    (r, e)3
    (_, n)2
    (e, n)2
    (s, e)2
    (e, t)2
    (e, s)1

    (n, e) occurs once in _ n e w (count 2) and once in _ r e n e w (count 2), so it scores 4. So does (e, w). The pseudocode says nothing about ties; the slide merges n e, which is the pair encountered first when scanning the corpus. The corpus becomes 2 × _ ne w, 2 × _ r e ne w, 1 × s e t, 1 × _ r e s e t and ne joins the vocabulary.

  2. Merge 2: recount on the new corpus

    PairCountNote
    (ne, w)4winner: the only pair at 4 once ne exists
    (_, r)3
    (r, e)3
    (_, ne)2
    (e, ne)2
    (s, e)2
    (e, t)2
    (e, s)1

    The old pair (e, w) no longer exists because the e in front of every w is now inside ne. (ne, w) scores 4 alone, so new is merged with no tie. The corpus becomes 2 × _ new, 2 × _ r e new, 1 × s e t, 1 × _ r e s e t.

  3. Merge 3: a second tie

    PairCountNote
    (_, r)3winner: tied with (r, e), seen first
    (r, e)3tie at the maximum
    (_, new)2
    (e, new)2
    (s, e)2
    (e, t)2
    (e, s)1

    (_, r) and (r, e) both score 3: twice from _ r e new and once from _ r e s e t. The slide merges _ r, again the pair seen first. The corpus becomes 2 × _ new, 2 × _r e new, 1 × s e t, 1 × _r e s e t.

  4. Merge 4: the tie resolves itself

    PairCountNote
    (_r, e)3winner: the only pair at 3 once _r exists
    (_, new)2
    (e, new)2
    (s, e)2
    (e, t)2
    (e, s)1

    With _r in place, (r, e) has disappeared and (_r, e) inherits its count of 3, the unique maximum. Merging it gives _re. Slide 47 shows merges 3 and 4 together for this reason: the second is forced by the first.

  5. After k = 4

    Vocabulary _, e, n, r, s, t, w, ne, new, _r, _re (11 entries, 7 + 4). Corpus 2 × _ new, 2 × _re new, 1 × s e t, 1 × _re s e t, which is 15 symbols instead of 29. Merge list, in order: ne, new, _r, _re.
MergeNew token (count)Corpus symbols|V|Corpus
0start2972 _ n e w, 2 _ r e n e w, 1 s e t, 1 _ r e s e t
1ne (4, tie)2582 _ ne w, 2 _ r e ne w, 1 s e t, 1 _ r e s e t
2new (4)2192 _ new, 2 _ r e new, 1 s e t, 1 _ r e s e t
3_r (3, tie)18102 _ new, 2 _r e new, 1 s e t, 1 _r e s e t
4_re (3)15112 _ new, 2 _re new, 1 s e t, 1 _re s e t
The trace at a glance

Ties: the pseudocode is silent, so state your rule

Two of the four steps were decided by a tie, and the algorithm as written never says how to break one. Real implementations do have a rule, and it is worth knowing which. The minimal listing in the Sennrich paper (Algorithm 1) picks the best pair with Python's max(pairs, key=pairs.get), which in modern Python (3.7 and later, where dictionaries keep insertion order) returns the first pair encountered while scanning. The released subword-nmt implementation instead selects with max(stats, key=lambda x: (stats[x], x)), which prefers the lexicographically largest pair among the tied ones. On this corpus that rule agrees with the slides at merge 1, choosing (n, e) because n sorts after e, but at merge 3 it chooses (r, e) over (_, r). The Hugging Face course states the first-encountered convention explicitly: when there is a choice of the most frequent pair, the first one encountered is selected. The 🤗 Tokenizers library breaks ties by internal token ids, and the simulator's alphabetical option takes the smallest pair.

The simulator lets you switch between first-seen and alphabetical order, and on this corpus the very first merge already differs: alphabetical order picks (e, w) over (n, e), so the vocabulary ends with ew where the slides have ne, even though the corpus rows after four merges are identical. That is exactly why an exam answer must state its rule.

First pair seen (slides)Alphabetical (smallest first)
Merge 1ne (tied with ew, seen first)ew (tied with ne, ew < ne)
Merge 2new from (ne, w)new from (n, ew)
Merge 3_r (tied with re, seen first)_r (tied with re, _ sorts first)
Merge 4_re_re
Learned tokensne, new, _r, _reew, new, _r, _re
Corpus after k = 42 × _ new, 2 × _re new, s e t, _re s e t2 × _ new, 2 × _re new, s e t, _re s e t
The same corpus under two tie rules, k = 4

The order of the rows matters too, because "first encountered" depends on it. The slides list the four rows by count, most frequent first, and so does SLP3. Jurafsky and Martin continue the same trace past the slides: merges five to seven are _new, _renew and se, each decided by a tie at count 2, and merge eight, set, is then forced because (se, t) is the only pair left at 2. The simulator reproduces that order exactly when its rows are listed the same way.

What the space marker does to the learned units

The marker is not cosmetic. Because this deck attaches it to the start of a word, the trainer learned _re, a word-initial piece, and that token is different from a re that occurs inside a word, as in _ c a re, which never merges into _re. Jurafsky and Martin put it this way: the system has essentially induced that there is a word-initial prefix re- (SLP3, section 2.4). This leading-marker convention is the one used by GPT-2, whose byte-level tokenizer shows the space as Ġ, and by SentencePiece, which shows it as (U+2581); in both, the space rides on the following word (Hugging Face tokenizers documentation). It also explains why set in the trace never carries a marker: it is the first word of the string, so no space precedes it, and s e t and _ s e t are different strings to the trainer.

The original paper did it the other way round. Sennrich, Haddow and Birch appended a special end-of-word symbol to every word so that the original tokenization could be restored after translation, and older SLP3 drafts followed them with the corpus low lowest newer wider new (Sennrich's own listing uses low lower newest widest). Under that convention every word gets a marker, the marker sits at the end, and the prefix would be learned as plain re.

Leading marker (this deck)End-of-word suffix (Sennrich 2016)
Where the marker sitsBefore the word: _new, _reAfter the word: new·, set·
First word of the stringNo marker: set stays s e tMarker like every word: s e t ·
Prefix re- is learned as_re, distinct from word-internal rere, identical to word-internal re
Used byThis deck, current SLP3, GPT-2 (Ġ), SentencePiece (▁)Sennrich et al. 2016, older SLP3 drafts
Leading space marker versus end-of-word suffix

Quick check

After merging ne and new on 'set new new renew reset renew', which pair wins merge 3 under first-seen tie-breaking?

Recall

After merges ne, new, _r and _re on 'set new new renew reset renew', list the vocabulary and the corpus.

Vocabulary: _, e, n, r, s, t, w, ne, new, _r, _re (11 items). Corpus: 2 × _ new, 2 × _re new, 1 × s e t, 1 × _re s e t, 15 symbols instead of 29.

Recall

Step 1 counts (n, e) = 4 and (e, w) = 4. What does the pseudocode say about the tie, and what do real implementations do?

Nothing. The paper's minimal listing and the Hugging Face tutorial take the first pair encountered in the corpus. The released subword-nmt code takes the lexicographically largest tied pair, and other libraries use yet other rules, so an exam answer should state the rule it uses.

Recall

How does the leading space marker change what is learned?

Word-initial pieces such as _re become tokens distinct from word-internal re. The first word of the string carries no marker, so set and _set are different strings. With Sennrich's end-of-word suffix instead, every word ends in the marker and the prefix would be learned as plain re.

Training is over. The merge list ne, new, _r, _re is frozen, and a word arrives that the trainer never saw: renewed, with its leading space. The encoder does not count anything. It writes the word as characters and replays the four merges, first to last.

Worked example

Encoding three words with the four learned merges

  1. _renewed, an unseen word

    Start from _ r e n e w e d, eight symbols. Merge 1 (n e) applies: _ r e ne w e d. Merge 2 (ne w) applies: _ r e new e d. Merge 3 (_ r) applies: _r e new e d. Merge 4 (_r e) applies: _re new e d. Four tokens, all in the vocabulary, no unknown.
  2. _newest, where two merges do not apply

    _ n e w e s t becomes _ ne w e s t, then _ new e s t. Merges 3 and 4 look for _ r and _r e, find neither, and are skipped. Output _ new e s t. The marker stays separate because (_, new) was merge 5 in the book's longer run and is not in this list.
  3. set at the start of a sentence

    No leading marker, so the input is s e t. None of the four merges mentions s or t, so the output is s e t, three tokens. With k = 8 it would be the single token set.
  4. The encoder is a replay

    Same list, same order, every input. _renewed is a word the model has never seen, tokenized entirely from pieces it has.

Try the same replay on words of your own. The panel below starts from the four slide merges; type newest, reset or an Arabic transliteration, tick sentence-initial to drop the marker, and raise k to watch _new and set collapse into single tokens once merges five to eight exist.

SimulatorEncoder replay: a frozen merge list applied to any word
k = 4
Merge list, in learned order
  1. 1.ne
  2. 2.new
  3. 3._r
  4. 4._re
  1. characters_renewed
  2. 1. n+e_renewed
  3. 2. ne+w_renewed
  4. 3. _+r_renewed
  5. 4. _r+e_renewed
  6. output_re new e d (4 tokens)

The encoder never counts anything. It replays the merge list top to bottom; a merge whose pair does not occur in the word is skipped.

A playhead sweeps the frozen merge list left to right while the string below changes at each stamp. The crossed-out tally is the rule: the encoder never counts.

The rule behind the trace is short. To tokenize a new string, start from characters or bytes, apply the learned merges in the order they were learned, and output the resulting subword sequence. The slide calls this "greedy": each merge fires on every place it matches as soon as its turn comes, the encoder never goes back, and it never searches for the longest vocabulary entry. Jurafsky and Martin are exact about what the encoder does not do: it just runs on the test data the merges learned from the training data, in the order they were learned, and the frequencies in the test data play no role, only the frequencies in the training data (SLP3, section 2.4). Sennrich describes the same procedure: split words into sequences of characters, then apply the learned operations to merge the characters into larger known symbols. Because the list is fixed, the output is deterministic: the same input always yields the same tokens, which is what lets a model trained on one tokenization be served with it later.

encode(w)=mk(m2(m1(chars(w))))\text{encode}(w) = m_k(\cdots m_2(m_1(\text{chars}(w))))
The merges compose in learned order; nothing is recomputed

Order is not a convention but a dependency. Merge 2 joins ne and w, and ne only exists if merge 1 has already run; merge 4 needs _r from merge 3 in the same way. Replaying the list out of order would produce segmentations that never occurred during training, so the model would receive token ids it has no experience of, even though every id is technically in the vocabulary. With tens of thousands of merges, most words in ordinary text are covered by a single token and only rare words break into pieces, which is the interpolation between word and character level that the GPT-2 paper describes (Radford et al., 2019).

TrainerEncoder
InputA training corpus with word countsOne new string
Counts pair frequenciesYes, at every stepNever
OutputA vocabulary plus an ordered merge listA token sequence
RunsOnce, before any model is trainedOn every input, forever after
Hyperparameterk, the number of mergesNone; the merge list is fixed
Trainer and encoder side by side

One last coverage detail depends on the base symbols. A character-level BPE trained on English letters still has a hole: the Hugging Face course shows mug encoding as [UNK] ug because m never appeared in its toy corpus, so it is not in the vocabulary at all. A byte-level tokenizer starts from all 256 byte values, so any UTF-8 string, including Arabic, emoji and typos, is encodable and the UNK special token is never needed (Radford et al., 2019; Hugging Face tokenizers documentation). The next part looks at what this does to non-Latin scripts.

Quick check

When BPE encodes new text, what decides which merges are applied?

Recall

With merges ne, new, _r, _re learned in that order, how is the unseen word '_renewal' encoded, and why can no other segmentation occur?

Start from _ r e n e w a l. Merge 1 gives _ r e ne w a l, merge 2 gives _ r e new a l, merge 3 gives _r e new a l, and merge 4 gives _re new a l, four tokens. Nothing in the list ever joins a to l, and no merge produces a bare re, so segmentations such as _r e new a l or _ re new al are unreachable.

Recall

Why must the encoder apply merges in the order they were learned?

Later merges are built from earlier ones: new can only form after ne exists, and _re only after _r. The model was also trained on token sequences produced by that exact order, so a different order produces segmentations it has never seen.

Recap

If you remember nothing else

  • Tokenization converts text into the units a model operates on; the unit choice trades coverage, compactness, meaningfulness and cross-lingual reach.
  • Words: meaningful but ambiguous, huge vocabularies, OOV. Characters: tiny vocabulary, no OOV, very long sequences. Subwords: the middle ground modern LMs use.
  • BPE has a trainer and an encoder. The trainer starts from characters or bytes and merges the most frequent adjacent pair k times, so |V| = initial symbols + k.
  • On 'set new new renew reset renew' the first four merges are ne (4), new (4), _r (3), _re (3), giving 11 vocabulary items and a corpus of 15 symbols instead of 29.
  • Two of those four steps were ties. The pseudocode does not break ties; first-seen order gives ne, alphabetical order gives ew, so always state your rule.
  • The leading space marker (GPT-2's Ġ, SentencePiece's ▁) makes _re a word-initial token. Sennrich's original code used an end-of-word suffix instead.
  • The encoder replays merges in learned order and never counts frequencies. '_renewed' becomes _re new e d with no unknown token.
  • BPE units are frequent substrings, morphemes only by accident.

Sources