Module 2. Tokenization and Embeddings
Try each exercise first. Expand worked answers or project guidance when needed; their open state is shared between RU/EN.
Download course files (.zip) 2588.1 KBThe archive preserves the source folder structure, both languages, and supporting files.
E1. UTF-8 accounting
Before merges, how many byte-level tokens do Привет, мир!, Hello, world!, and наивный 🚀 use? Explain every byte.
Show answer and solution
Solution. Before BPE merges, each UTF-8 byte is one base token. Привет, мир! has nine Cyrillic letters (Привет has 6 and мир has 3), hence bytes, plus a comma, a space, and !: 21. Hello, world! contains 13 ASCII characters and therefore uses 13 bytes. In наивный 🚀, seven Cyrillic letters use 14 bytes, the space uses 1, and the emoji uses 4, for 19 total. A tokenizer-free check is len(text.encode("utf-8")).
E2. Ranked encoding
Implement an encoder that repeatedly merges the pair with the lowest available rank:
Show answer and solution
Solution.
def encode_by_rank(text, merges):
chunks = re.findall(PAT, text)
output = []
for chunk in chunks:
ids = list(chunk.encode("utf-8"))
while len(ids) > 1:
pairs = set(zip(ids[:-1], ids[1:]))
best = min(pairs, key=lambda p: merges.get(p, float("inf")))
if best not in merges:
break
ids = merge([ids], best, merges[best])[0]
output.extend(ids)
return outputThe regex first splits the text into pre-token chunks, which must be encoded independently. Within each chunk, enumerate adjacent pairs, choose the present pair with the smallest numeric rank, and merge all valid occurrences. Stop when no present pair belongs to merges. Corpus frequency is not recomputed at inference: training has already encoded its effect in the learned ranks.
E3. A reproducible fertility protocol
Fix the corpus, word segmentation rule, tokenizer revision, and aggregation method. Report total tokens divided by total words, plus bytes/token. Explain why averaging per-sentence fertility assigns disproportionate weight to short sentences.
Show answer and solution
Solution. Freeze the exact corpus snapshot and strata, normalization and word rule, tokenizer name and revision, and whether special tokens are added. For each line record UTF-8 bytes , words , and tokenizer IDs (normally excluding beginning-of-sequence and end-of-sequence markers, BOS and EOS), then compute
This micro-average weights every word equally. The alternative weights every line equally, so a one-word line affects the result as much as a long sentence; zero-word lines also need a separate policy. Publish , , , and excluded-line counts so another reader can reproduce the result.
E4 [project]. Multilingual map
Use FLORES-200 with published tokenizers for o200k_base, Llama 4, Qwen3, Gemma 3, DeepSeek V4, and Kimi K3. Record exact revisions and let the measurements determine the ranking rather than predicting a winner.
Project demos and guidance
Demo for this project exercise: English project-demo notebook for Module 2, section “1. What does the denominator count?”. It shows a compact reproducible example on small inputs, not a completed solution to the full-scale project.
Specify suitable word segmentation for each language, or compare tokens per aligned sentence. A regex matching letter runs treats a Chinese string as one run, not one linguistic word. The demo exposes denominators and units per sentence; its wordrun_proxy is not WordPiece.
E5 [project]. Harmony audit
In the main notebook’s optional network cell, load o200k_harmony, count names and unique IDs, identify the alias at 200018, and classify protocol versus reserved spellings.
Project demos and guidance
Demo for this project exercise: English project-demo notebook for Module 2, section “2. Why can names outnumber IDs?”. It shows a compact reproducible example on small inputs, not a completed solution to the full-scale project.
Expected for the 11 October 2026 snapshot: 1,091 names, 1,090 unique special IDs, 10 protocol names, 1,081 reserved-form names, and n_vocab=201088 after adding 199,998 mergeable IDs.
E6. Lookup and one learning step
Using from §11, compute E[[0,2,0]], E[0] @ E.T, and the tied BF16 weight payload for . Then minimize with zero targets and IDs [0,2,0]: what gradient does each row receive?
Show answer and solution
Solution. Lookup gives [[1,0],[0,1],[1,0]]. The logits are [1,2,0], so ID 1 wins. There are parameters occupying BF16 bytes, or 16 MiB. Repeated ID 0 accumulates two contributions: the gradient is [[2,0],[0,0],[0,1]]. A step of 0.1 gives [[0.8,0],[2,0],[0,0.9]], lowering from 1.5 to 1.045. The notebook checks the gradient with finite differences; this is a lookup exercise rather than semantic model training.