4.2.2 · D4Tokenization & Language Modeling

Exercises — Byte-Pair Encoding (BPE)

2,985 words14 min readBack to topic

Before we start, one shared convention (used in every problem):


L1 — Recognition

Exercise 1.1

Which of these describes what BPE learns from a corpus? (a) A fixed list of whole words. (b) A neural network. (c) An ordered list of merge rules plus the vocabulary they build. (d) A dictionary of synonyms.

Recall Solution

(c). BPE training produces two coupled things: a vocabulary (the set of tokens) and an ordered list of merge rules (which pair to glue, in what priority). The order is not decoration — it is part of the answer, because encoding replays these rules from rule 1 downward. See Byte-Pair Encoding (BPE) "BPE Encoding (Inference)". Answer (a) is word-level tokenization (the thing BPE avoids), and (b) is unrelated — BPE is a deterministic counting algorithm, not a trained network.

Exercise 1.2

In the string n e w es t </w>, list every adjacent pair.

Recall Solution

Slide a two-symbol window left to right: . Five pairs, because a string of 6 symbols has gaps between neighbours. Note es is one symbol here (an earlier merge already fused it), so (w,es) is a single pair, not (w,e) then (e,s).

Exercise 1.3

True or false: after enough merges, BPE can emit an <UNK> (unknown) token for a word it never saw in training.

Recall Solution

False. The base vocabulary contains every single character. Any word, however weird, can always fall back to individual characters, so a valid tokenization always exists — no <UNK>. This is BPE's headline advantage over word-level tokenization. (Detail: modern byte-level BPE goes even lower, to raw bytes, guaranteeing coverage of any Unicode input.)


L2 — Application

Exercise 2.1

Corpus: 4× "cat" 4× "cap". Split into characters with </w> and compute the frequency of pair (c, a).

Recall Solution

Split:

4×:  c a t </w>
4×:  c a p </w>

(c, a) appears once per word. Frequency .

Exercise 2.2

Same corpus (4× "cat" 4× "cap"). Which pair is merged first? Give the new token.

Recall Solution

Count all pairs:

  • :
  • :
  • :
  • :
  • :

Winner is at 8, uncontested. Merge → new token ca. Corpus becomes:

4×:  ca t </w>
4×:  ca p </w>

Exercise 2.3

Continue Exercise 2.2 for one more merge. What is the second rule, and why is there a tie?

Recall Solution

Recount after ca:

  • :
  • :
  • :
  • :

Everything ties at 4. The corpus is perfectly balanced. Break the tie alphabetically on the pair's written form. Comparing the four candidates by their concatenated strings cat, cap, p</w>, t</w>: alphabetical order puts cap before cat before p</w> before t</w>. So merge cap. Rule 2 is ca p → cap.

Exercise 2.4

Using only the merge rules learned in Ex 2.2–2.3 (c a → ca, then ca p → cap), encode the new word caps.

Recall Solution
Input:            c a p s </w>
Apply rule 1 (c a→ca):   ca p s </w>
Apply rule 2 (ca p→cap): cap s </w>
No rule mentions (cap, s) or (s, </w>) → stop.
Output tokens: [cap, s, </w>]

The lonely s survives as a character token — perfectly legal, and exactly why BPE never fails on new words.


L3 — Analysis

Exercise 3.1

In the parent's hug/pug/bug example, iteration 1 had a tie between (u,g) and (g,</w>), both at 5. Explain why they have identical frequency and which wins.

Recall Solution

Every word in that corpus ends in ...u g </w>. So whenever (u,g) occurs, (g,</w>) occurs right after it in the same word — they are locked together across all three words. Their counts are therefore forced equal: each. The tie-break is alphabetical on ug vs g</w>: comparing character by character, g < u, so g</w> would sound like it wins — but the standard convention compares the pair joined as a token string, ug vs g</w>, and ug < g</w> is false (g < u). The parent chose (u,g); the honest reading is that implementations differ, and the parent's convention picks ug. Key insight for the exam: state your tie-break rule explicitly, because the corpus does not decide it for you.

Exercise 3.2

Why does the parent note insist merges be applied in learned order during encoding? Give a concrete failure if you ignored order.

Recall Solution

Later rules are built on top of earlier ones. Rule es t → est presupposes the token es already exists, which only rule e s → es can create. If you applied est first, the symbol es isn't in the string yet, so the rule can't fire — you'd get stuck with e s t unmerged. See

Figure — Byte-Pair Encoding (BPE)
: the left path applies rules in order and reaches est; the right path skips ahead and jams. Order encodes a dependency chain.

Exercise 3.3

The compression ratio is , the average characters per token. Explain why doubling the vocabulary from 25k to 50k barely changes , using the notion of pair frequency.

Recall Solution

BPE merges pairs in descending frequency. The first thousands of merges eat the very common pairs — each fires in a huge fraction of the corpus, so each one shortens many sequences at once, pushing up fast. By the time you reach the 25,000th merge, the remaining pairs are rare: each fires in only a sliver of the corpus, so gluing it barely moves the average token length. Formally, the marginal gain per merge tracks the frequency of the pair being merged, and frequencies fall off steeply (Zipf-like). So going 25k → 50k merges only rare pairs → tiny → tiny . This is the diminishing returns the parent describes. See

Figure — Byte-Pair Encoding (BPE)
.


L4 — Synthesis

Exercise 4.1

Design your own corpus. Build a 3-word corpus (with counts) so that BPE's first two merges produce the tokens lo then low. Show the two iterations.

Recall Solution

Use 6× "low" 3× "lower". (Any corpus where l o w dominates works.)

6×:  l o w </w>
3×:  l o w e r </w>

Iteration 1 pair counts:

  • ← highest
  • (tie!) — break alphabetically: lo < ow, merge .
  • , , , .

Merge l o → lo:

6×:  lo w </w>
3×:  lo w e r </w>

Iteration 2:

  • ← highest, uncontested at 9.

Merge lo w → low. Rules: l o → lo, then lo w → low. ✔

Exercise 4.2

Take your corpus from 4.1 and its two rules. Encode the unseen word slow.

Recall Solution
Input:            s l o w </w>
Apply rule 1 (l o→lo):   s lo w </w>
Apply rule 2 (lo w→low): s low </w>
Stop (no rule touches (s, low) or (low, </w>)).
Output: [s, low, </w>]

Even though slow never appeared, its frequent inner chunk low is recovered — the whole point of subword tokenization. This same token feeds straight into an 3.1.05-Embedding-Layer, where low gets one shared vector across every word that contains it.

Exercise 4.3

Two tokenizers, same corpus, differ only by tie-break rule (one alphabetical, one "keep-first-seen"). Argue whether they can produce different final vocabularies, and why this matters for 6.2.02-Inference-Optimization.

Recall Solution

Yes, they can diverge. A tie chooses a different merge, which changes which pairs exist next round, which changes future counts — the effect cascades. So two "valid" BPE runs can yield different vocabularies and different merge orders. Why it matters downstream: a deployed model is bound to its tokenizer forever. If you swap tokenizers (even by a tie-break tweak), previously trained token IDs no longer mean the same thing — the embedding table and all cached KV states become invalid. For 6.2.02-Inference-Optimization, the tokenizer is a frozen contract: reproducibility ⇒ pin the tie-break rule.


L5 — Mastery

Exercise 5.1 (Degenerate input)

What does BPE do with the empty string "" and with a single-character word "a"? Walk both through encoding.

Recall Solution
  • Empty string: chars = []. Adding </w> gives just </w> (or nothing, per convention). No pairs exist (need ≥2 symbols for a pair), so no rule fires. Output: [</w>] or []. The algorithm is total — it never crashes on empty input.
  • "a": split → a </w>. Exactly one pair (a, </w>). If no learned rule matches it, output is [a, </w>]. If a rule a </w> → a</w> exists, output [a</w>]. Either way, defined and finite. Takeaway: the merge loop always terminates because each merge strictly reduces the symbol count, which is bounded below by 1.

Exercise 5.2 (The s-interruption edge case)

In the parent's hug/pug/bug model, encoding hugs gave [h, ug, s, </w>] — no hug</w> token. Reproduce the reasoning, then predict the encoding of bugs.

Recall Solution

Rules (in order): u g → ug, ug </w> → ug</w>, b ug</w> → bug</w>, p ug</w> → pug</w>. For hugs:

Input:          h u g s </w>
Rule 1 (u g→ug):   h ug s </w>
Rule 2 needs (ug,</w>) adjacent — but 's' sits between ug and </w>. FAILS.
Rules 3,4 need ug</w>, which never formed. FAIL.
Output: [h, ug, s, </w>]

For bugs, the same wall appears — s blocks (ug, </w>):

Input:          b u g s </w>
Rule 1 (u g→ug):   b ug s </w>
Rule 2 blocked by 's'. Rules 3,4 need ug</w>. All FAIL.
Output: [b, ug, s, </w>]

Crucial lesson: ug</w> and bug</w> are word-ending tokens. A trailing s changes the ending, so those end-anchored tokens can't apply. The </w> boundary is doing real work here.

Exercise 5.3 (Sequence-length arithmetic)

A corpus has characters. After training, the average characters-per-token is . Give the BPE sequence length and the compression ratio versus character-level. Then, if better vocabulary raised to , how many tokens do you save?

Recall Solution

From the parent's formula and :

  • tokens. (sequences are 4× shorter than character-level).
  • At : tokens.
  • Saved: tokens (a 20% reduction). Notice the ratio only rose from 4 to 5 for that saving — the diminishing-returns effect of Ex 3.3 in numbers.

Exercise 5.4 (Full mini-training, from scratch)

Train BPE to vocab size 8 on 3× "ab" 2× "abc". Give the base vocab, every merge, and the final vocabulary.

Recall Solution

Split with </w>:

3×:  a b </w>
2×:  a b c </w>

Base vocab → size 4.

Iteration 1 pairs:

  • ← highest
  • , , .

Merge a b → ab. , size 5.

3×:  ab </w>
2×:  ab c </w>

Iteration 2 pairs:

  • ← highest
  • , .

Merge ab </w> → ab</w>. , size 6.

3×:  ab</w>
2×:  ab c </w>

Iteration 3 pairs (only the abc word still has pairs):

  • , — tie at 2. Alphabetical on abc vs c</w>: abc < c</w>, merge .

Merge ab c → abc. , size 7.

3×:  ab</w>
2×:  abc </w>

Iteration 4 pairs:

  • ← only pair left.

Merge abc </w> → abc</w>. , size 8 → target reached, stop.

Final vocabulary (size 8): Merge rules in order: a b → ab, ab </w> → ab</w>, ab c → abc, abc </w> → abc</w>.


Recall Self-test cloze

BPE merges the most frequent adjacent pair each round. Ties are broken alphabetically (by convention — state it explicitly). BPE can never emit an ==<UNK> token because the base vocab holds every character. Merge rules must be applied during encoding in the order they were learned. The compression ratio equals , the average characters per token, with diminishing returns as vocab grows. Merges must be recounted from the rewritten (post-merge)== corpus, not the original characters.

Q: Why can't hugs produce a hug</w> token in the parent model?
The trailing s sits between ug and </w>, so the pair (ug, </w>) is never adjacent and its merge rule can't fire.
Q: What single fact guarantees the BPE merge loop terminates?
Every merge strictly reduces the total symbol count, which is bounded below, so the process must stop.

Related: 4.3.01-Language-Model-Basics · 4.2.02 Byte-Pair Encoding (BPE) (Hinglish)