Building a Tokenizer
BPE from scratch; vocab size as a tunable knob.
Byte-Pair Encoding from scratch in ~80 lines. Train at multiple vocabulary sizes, visualize what gets merged, and watch the sweet spot emerge between "everything is bytes" and "everything is rare fragments."
The Concept
A BPE tokenizer starts with the 256 possible byte values. It counts which adjacent pairs of bytes are most frequent, merges the winner into a new token ID, and repeats until the vocabulary reaches the desired size. That is the whole engine: count pairs, pick the winner, replace it, repeat.
Encoding new text re-applies the learned merges in the same priority order they were discovered. Decoding concatenates the byte sequences each token stands for. The tokenizer is just a compression scheme — but the compression units are the things the language model later predicts.
Why It Matters
Vocabulary size is a knob nobody explains until it bites them. Too small and the model wastes context budget reassembling common chunks character-by-character. Too large and rare tokens never accumulate enough gradient signal to become useful. The sweet spot exists because two costs pull in opposite directions, and the only way to feel that tradeoff is to train at multiple sizes and look.
The Build
build.py$python projects/03_building-a-tokenizer/build.py --tiny
"""
Project 3: Building a Tokenizer — complete working build.
A byte-level Byte-Pair-Encoding (BPE) tokenizer, trained from scratch on a small
built-in corpus of mixed English prose, code, and URLs. Trains multiple
vocabulary sizes and reports the compression vs. fragmentation tradeoff.
Run:
python build.py --tiny # vocab sizes [288, 512, 1024], CPU <30s
python build.py --full # vocab sizes [288, 512, 1024, 2048, 4096]
"""
from __future__ import annotations
import argparse
from collections import Counter
from pathlib import Path
# Built-in mixed corpus: prose + code + URLs + identifiers. ~3KB.
DEFAULT_CORPUS = """
The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.
She sells seashells by the seashore. She sells seashells by the seashore.
A language model predicts the next token given the previous tokens.
A language model predicts the next token given the previous tokens.
Compression is what tokenizers actually optimize for, not meaning.
def main():
user_id = 42
name = "alice"
return user_id, name
def fetch(url: str):
response = httpx.get(url)
return response.json()
class Embedding:
def __init__(self, vocab_size, dim):
self.weight = torch.randn(vocab_size, dim)
def forward(self, ids):
return self.weight[ids]
SELECT id, name, created_at FROM users WHERE active = TRUE ORDER BY created_at DESC LIMIT 100;
SELECT id, name, email FROM users WHERE id = 42;
https://example.com/api/v1/users/42
https://example.com/api/v1/users/123
https://docs.python.org/3/library/argparse.html
The training loop iterates over batches.
The training loop iterates over batches.
loss.backward()
optimizer.step()
optimizer.zero_grad()
replaying the song, replaying the song
playing playing playing
internationalization, internationalization
the cat sat on the mat. the cat sat on the mat.
color colour, color colour
favorite favourite
"""
def get_stats(ids: list[int]) -> Counter[tuple[int, int]]:
"""Count adjacent pair occurrences across `ids`."""
counts: Counter[tuple[int, int]] = Counter()
for a, b in zip(ids, ids[1:]):
counts[(a, b)] += 1
return counts
def merge(ids: list[int], pair: tuple[int, int], new_id: int) -> list[int]:
"""Replace every occurrence of `pair` in `ids` with `new_id`."""
out: list[int] = []
i = 0
while i < len(ids):
if i < len(ids) - 1 and ids[i] == pair[0] and ids[i + 1] == pair[1]:
out.append(new_id)
i += 2
else:
out.append(ids[i])
i += 1
return out
class BPETokenizer:
"""Byte-level BPE: starts from 256 byte tokens, learns merges greedily."""
def __init__(self) -> None:
self.merges: dict[tuple[int, int], int] = {}
self.vocab: dict[int, bytes] = {i: bytes([i]) for i in range(256)}
def train(self, text: str, vocab_size: int, verbose: bool = False) -> int:
"""Train merges until the corpus has no more multi-occurrence pairs
OR vocab_size is reached. Returns the actual final vocab size."""
if vocab_size < 256:
raise ValueError(f"vocab_size must be >= 256 for byte-level BPE (got {vocab_size})")
ids = list(text.encode("utf-8"))
self.merges = {}
self.vocab = {i: bytes([i]) for i in range(256)}
n_merges = vocab_size - 256
for k in range(n_merges):
stats = get_stats(ids)
if not stats or max(stats.values()) < 2:
# No pair occurs more than once → further merges would just
# invent one-off tokens. Stop early.
break
pair = max(stats, key=lambda p: stats[p])
new_id = 256 + k
ids = merge(ids, pair, new_id)
self.merges[pair] = new_id
self.vocab[new_id] = self.vocab[pair[0]] + self.vocab[pair[1]]
if verbose and (k < 5 or k % 100 == 0):
piece = self.vocab[new_id].decode("utf-8", errors="replace")
print(f" merge {new_id}: {pair} (count={stats[pair]}) -> {piece!r}")
return len(self.vocab)
def encode(self, text: str) -> list[int]:
ids = list(text.encode("utf-8"))
while len(ids) >= 2:
# Find the lowest-rank merge present among adjacent pairs.
stats = {(a, b) for a, b in zip(ids, ids[1:])}
best = None
best_rank = float("inf")
for pair in stats:
if pair in self.merges and self.merges[pair] < best_rank:
best = pair
best_rank = self.merges[pair]
if best is None:
break
ids = merge(ids, best, self.merges[best])
return ids
def decode(self, ids: list[int]) -> str:
raw = b"".join(self.vocab[i] for i in ids)
return raw.decode("utf-8", errors="replace")
def compression_ratio(text: str, tokenizer: BPETokenizer) -> tuple[int, int, float]:
raw_bytes = len(text.encode("utf-8"))
n_tokens = len(tokenizer.encode(text))
return raw_bytes, n_tokens, raw_bytes / max(n_tokens, 1)
def token_frequency_stats(tokenizer: BPETokenizer, text: str) -> dict:
ids = tokenizer.encode(text)
counts = Counter(ids)
n_tokens = len(counts)
n_lt_5 = sum(1 for c in counts.values() if c < 5)
n_eq_1 = sum(1 for c in counts.values() if c == 1)
return {
"unique_tokens_used": n_tokens,
"median_freq": sorted(counts.values())[n_tokens // 2] if n_tokens else 0,
"lt_5_frac": n_lt_5 / max(n_tokens, 1),
"eq_1_frac": n_eq_1 / max(n_tokens, 1),
}
def visualize_tokens(tokenizer: BPETokenizer, text: str) -> str:
"""Pretty-print a string as `[token][token][token]` boundaries."""
ids = tokenizer.encode(text)
pieces = []
for tid in ids:
s = tokenizer.vocab[tid].decode("utf-8", errors="replace")
# Make whitespace visible
s = s.replace(" ", "·").replace("\n", "↵")
pieces.append(f"[{s}]")
return "".join(pieces)
def write_compression_plot(results: list[tuple[int, float, float]], path: Path) -> None:
"""results: list of (vocab_size, avg_tokens_per_sample, compression_ratio)."""
try:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
except ImportError:
return
sizes = [r[0] for r in results]
avg_tokens = [r[1] for r in results]
ratios = [r[2] for r in results]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
ax1.plot(sizes, avg_tokens, "o-")
ax1.set_xscale("log")
ax1.set_xlabel("vocab size (log)")
ax1.set_ylabel("avg tokens per sample sentence")
ax1.set_title("Tokens per sentence vs vocab size")
ax1.grid(True, alpha=0.3)
ax2.plot(sizes, ratios, "o-", color="tab:green")
ax2.set_xscale("log")
ax2.set_xlabel("vocab size (log)")
ax2.set_ylabel("compression ratio (bytes/token)")
ax2.set_title("Compression ratio vs vocab size")
ax2.grid(True, alpha=0.3)
fig.tight_layout()
fig.savefig(path, dpi=120)
plt.close(fig)
SAMPLE_SENTENCES = [
"The quick brown fox jumps over the lazy dog.",
"She sells seashells by the seashore.",
"def main(): user_id = 42",
"SELECT id, name FROM users WHERE active = TRUE;",
"https://example.com/api/v1/users/42",
"replaying the song",
"internationalization",
"The cat sat on the mat.",
"loss.backward()",
"optimizer.zero_grad()",
]
def main() -> int:
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument("--tiny", action="store_true")
parser.add_argument("--full", action="store_true")
parser.add_argument(
"--vocab-sizes",
type=int,
nargs="+",
default=None,
help="Override the vocab sizes to train.",
)
parser.add_argument(
"--output-dir",
type=Path,
default=Path(__file__).parent / "outputs",
)
args = parser.parse_args()
if args.vocab_sizes is None:
args.vocab_sizes = [288, 512, 1024, 2048, 4096] if args.full else [288, 512, 1024]
args.output_dir.mkdir(parents=True, exist_ok=True)
corpus = DEFAULT_CORPUS
print(f"Corpus size: {len(corpus.encode('utf-8'))} bytes")
print(f"Vocab sizes to train: {args.vocab_sizes}")
results: list[tuple[int, float, float]] = []
visualizations: dict[int, list[tuple[str, str]]] = {}
stats_table: list[dict] = []
for vsize in args.vocab_sizes:
print(f"\n=== Training BPE @ vocab_size={vsize} ===")
tok = BPETokenizer()
actual_size = tok.train(corpus, vocab_size=vsize, verbose=False)
print(
f" requested vocab_size={vsize}, actual={actual_size} (learned {len(tok.merges)} merges)"
)
if actual_size < vsize:
print(
f" (BPE stopped early: corpus exhausted multi-occurrence pairs at "
f"vocab={actual_size}; further merges would only invent one-off tokens.)"
)
# Roundtrip sanity: encode -> decode should match original.
for sample in SAMPLE_SENTENCES:
assert tok.decode(tok.encode(sample)) == sample, (
f"roundtrip failed at vocab_size={vsize} on {sample!r}"
)
# Average tokens per sample sentence + compression on corpus.
token_counts = [len(tok.encode(s)) for s in SAMPLE_SENTENCES]
avg_tokens = sum(token_counts) / len(token_counts)
b, t, ratio = compression_ratio(corpus, tok)
print(f" avg tokens/sentence: {avg_tokens:.2f}")
print(f" corpus: {b} bytes -> {t} tokens; compression ratio = {ratio:.3f}")
# Token frequency stats on the corpus.
freq = token_frequency_stats(tok, corpus)
print(
f" unique tokens used: {freq['unique_tokens_used']} "
f"median_freq: {freq['median_freq']} "
f"<5 freq frac: {freq['lt_5_frac']:.2%} "
f"==1 freq frac: {freq['eq_1_frac']:.2%}"
)
results.append((vsize, avg_tokens, ratio))
stats_table.append(
{
"vocab_size": vsize,
"avg_tokens": avg_tokens,
"compression_ratio": ratio,
**freq,
}
)
# Visualization on 4 sample sentences.
viz = [(s, visualize_tokens(tok, s)) for s in SAMPLE_SENTENCES[:4]]
visualizations[vsize] = viz
for sentence, rendered in viz:
print(f" {sentence!r}")
print(f" -> {rendered}")
# === Outputs ===
write_compression_plot(results, args.output_dir / "compression.png")
log = args.output_dir / "run_log.txt"
lines = [
"# Project 3 run log",
f"corpus_size_bytes: {len(corpus.encode('utf-8'))}",
f"vocab_sizes : {args.vocab_sizes}",
"",
"# per vocab size",
]
for row in stats_table:
lines.append(
f" vocab_size={row['vocab_size']}: "
f"avg_tokens={row['avg_tokens']:.2f} "
f"compression={row['compression_ratio']:.3f} "
f"unique={row['unique_tokens_used']} "
f"<5freq={row['lt_5_frac']:.2%} "
f"==1freq={row['eq_1_frac']:.2%}"
)
lines.append("")
lines.append("# token boundary visualizations (largest vocab size)")
largest = max(visualizations.keys())
for sentence, rendered in visualizations[largest]:
lines.append(f" {sentence}")
lines.append(f" {rendered}")
log.write_text("\n".join(lines) + "\n", encoding="utf-8")
print(f"\nOutputs written to {args.output_dir.resolve()}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
The Steps
8 reference filesEach step is the code for one piece, in isolation — read them as the book introduces them. They assemble into build.py above.
Step 01Start From Bytes Not Charactersstep_01_start-from-bytes-not-characters.py
"""
Project 3: Step 1 — Start from bytes, not characters
Pedagogical reference: this file shows the code for this step in isolation.
For the full assembled, runnable build, use build.py in this same folder.
"""
text = "hello 👋"
data = text.encode("utf-8")
print(list(data))
# [104, 101, 108, 108, 111, 32, 240, 159, 145, 139]
Step 02Count Adjacent Pairsstep_02_count-adjacent-pairs.py
"""
Project 3: Step 2 — Count adjacent pairs
Pedagogical reference: this file shows the code for this step in isolation.
For the full assembled, runnable build, use build.py in this same folder.
"""
[104, 101, 108, 108, 111]
from collections import Counter
def get_stats(ids: list) -> Counter:
counts = Counter()
for a, b in zip(ids, ids[1:]):
counts[(a, b)] += 1
return counts
Step 03Merge The Most Frequent Pairstep_03_merge-the-most-frequent-pair.py
"""
Project 3: Step 3 — Merge the most frequent pair
Pedagogical reference: this file shows the code for this step in isolation.
For the full assembled, runnable build, use build.py in this same folder.
"""
def merge(ids: list, pair: tuple, new_id: int) -> list:
out = []
i = 0
while i < len(ids):
if i < len(ids) - 1 and ids[i] == pair[0] and ids[i + 1] == pair[1]:
out.append(new_id)
i += 2
else:
out.append(ids[i])
i += 1
return out
Step 04Train The Tokenizer To A Target Vocabulary Sizestep_04_train-the-tokenizer-to-a-target-vocabulary-size.py
"""
Project 3: Step 4 — Train the tokenizer to a target vocabulary size
Pedagogical reference: this file shows the code for this step in isolation.
For the full assembled, runnable build, use build.py in this same folder.
"""
ids = list(text.encode("utf-8"))
merges = {}
vocab = {i: bytes([i]) for i in range(256)}
for new_id in range(256, V):
stats = get_stats(ids)
pair = max(stats, key=stats.get)
ids = merge(ids, pair, new_id)
merges[pair] = new_id
vocab[new_id] = vocab[pair[0]] + vocab[pair[1]]
Step 05Build Encode And Decodestep_05_build-encode-and-decode.py
"""
Project 3: Step 5 — Build encode and decode
Pedagogical reference: this file shows the code for this step in isolation.
For the full assembled, runnable build, use build.py in this same folder.
"""
def decode(ids: list, vocab: dict) -> str:
raw = b"".join(vocab[i] for i in ids)
return raw.decode("utf-8", errors="replace")
def encode(text: str, merges: dict) -> list:
ids = list(text.encode("utf-8"))
while len(ids) >= 2:
stats = {(a, b): i for i, (a, b) in enumerate(zip(ids, ids[1:]))}
pair = min(stats, key=lambda p: merges.get(p, float("inf")))
if pair not in merges:
break
ids = merge(ids, pair, merges[pair])
return ids
Step 08Plot Tokens Per Sentence Vs Vocabulary Sizestep_08_plot-tokens-per-sentence-vs-vocabulary-size.py
"""
Project 3: Step 8 — Plot tokens-per-sentence vs vocabulary size
Pedagogical reference: this file shows the code for this step in isolation.
For the full assembled, runnable build, use build.py in this same folder.
"""
sizes = [256, 1024, 4096, 8192]
results = []
for vocab_size in sizes:
tokenizer = train_bpe(corpus_text, vocab_size)
token_counts = []
for sentence in sample_sentences:
ids = tokenizer.encode(sentence)
token_counts.append(len(ids))
avg_tokens = sum(token_counts) / len(token_counts)
results.append((vocab_size, avg_tokens))
Step 09Build A Token Visualizerstep_09_build-a-token-visualizer.py
"""
Project 3: Step 9 — Build a token visualizer
Pedagogical reference: this file shows the code for this step in isolation.
For the full assembled, runnable build, use build.py in this same folder.
"""
tokens = tokenizer.encode("replaying the song")
for tid in tokens:
piece = tokenizer.vocab[tid].decode("utf-8", errors="replace")
print(f"[{piece}]")
Step 10Compare To A Production Tokenizerstep_10_compare-to-a-production-tokenizer.py
"""
Project 3: Step 10 — Compare to a production tokenizer
Pedagogical reference: this file shows the code for this step in isolation.
For the full assembled, runnable build, use build.py in this same folder.
"""
import tiktoken
enc = tiktoken.get_encoding("gpt2")
print(enc.encode("replaying the song"))
Break It
break_it.pymode avg tokens/sent compression ==1freq
----------------------------------------------------------------------
baseline (vocab=512) 6.70 3.540 33.6%
too small (vocab=256) 28.30 1.000 -
too large (req 8192) 4.90 4.656 42.5%
(too-large requested 8192, got actual=556 after BPE exhausted pairs)
Too small (vocab=256). No merges at all. Every byte is its own token. A 45-character sentence becomes 45 tokens. The model would have to do all the work of recognizing words from their constituent bytes — context budget wasted.
Too large (vocab=8192 requested → 556 actual). BPE stopped early on this 1299-byte corpus because no remaining pair appeared more than once. Even at vocab=556, 42.5% of tokens used in the corpus appear exactly once. On a real corpus this fraction wouldn't be quite that bad — but the trend is the same: pushing vocabulary size past what the corpus can support creates one-off "fossils" that won't accumulate enough gradient signal to learn useful embeddings.
Lesson: the tokenizer exists to manage a tradeoff, not eliminate it. Modern vocabularies land in the tens of thousands because that's the size where there are enough pieces to compress common patterns without each piece becoming statistically lonely.
$python projects/03_building-a-tokenizer/break_it.py --tiny
"""
Project 3: BREAK IT — vocab too small vs. vocab too large.
Two failure modes:
1. **vocab_size=256** (no merges, pure byte-level): sequences become 4x longer,
the model wastes context reassembling common chunks.
2. **vocab_size=8192** (way too many for this tiny corpus): BPE stops early
when pairs exhaust, but the merges it does learn become one-off "fossils"
that won't get useful gradient signal in any downstream model.
Run:
python break_it.py --tiny
"""
from __future__ import annotations
import argparse
from pathlib import Path
from build import (
DEFAULT_CORPUS,
SAMPLE_SENTENCES,
BPETokenizer,
compression_ratio,
token_frequency_stats,
visualize_tokens,
)
def main() -> int:
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument("--tiny", action="store_true")
parser.add_argument("--full", action="store_true")
parser.add_argument(
"--output-dir",
type=Path,
default=Path(__file__).parent / "outputs",
)
args = parser.parse_args()
args.output_dir.mkdir(parents=True, exist_ok=True)
corpus = DEFAULT_CORPUS
# === Sane baseline: vocab=512 ===
base = BPETokenizer()
base.train(corpus, vocab_size=512)
base_avg = sum(len(base.encode(s)) for s in SAMPLE_SENTENCES) / len(SAMPLE_SENTENCES)
_, _, base_ratio = compression_ratio(corpus, base)
base_freq = token_frequency_stats(base, corpus)
# === Too small: vocab=256 (no merges; pure bytes) ===
tiny = BPETokenizer()
tiny.train(corpus, vocab_size=256)
tiny_avg = sum(len(tiny.encode(s)) for s in SAMPLE_SENTENCES) / len(SAMPLE_SENTENCES)
_, _, tiny_ratio = compression_ratio(corpus, tiny)
# === Too large: vocab=8192 (will stop early but yields many rare tokens) ===
huge = BPETokenizer()
huge_actual = huge.train(corpus, vocab_size=8192)
huge_avg = sum(len(huge.encode(s)) for s in SAMPLE_SENTENCES) / len(SAMPLE_SENTENCES)
_, _, huge_ratio = compression_ratio(corpus, huge)
huge_freq = token_frequency_stats(huge, corpus)
print(f"{'mode':25s} {'avg tokens/sent':>16s} {'compression':>12s} {'==1freq':>10s}")
print("-" * 70)
print(
f"{'baseline (vocab=512)':25s} {base_avg:>16.2f} {base_ratio:>12.3f} "
f"{base_freq['eq_1_frac']:>10.1%}"
)
print(f"{'too small (vocab=256)':25s} {tiny_avg:>16.2f} {tiny_ratio:>12.3f} {'-':>10s}")
print(
f"{'too large (req 8192)':25s} {huge_avg:>16.2f} {huge_ratio:>12.3f} "
f"{huge_freq['eq_1_frac']:>10.1%}"
)
print(f"\n(too-large requested 8192, got actual={huge_actual} after BPE exhausted pairs)")
print("\nVisualization at vocab=256 (pure bytes — no merges):")
print(f" '{SAMPLE_SENTENCES[0]}'")
print(f" -> {visualize_tokens(tiny, SAMPLE_SENTENCES[0])}")
print("\nVisualization at vocab=512 (sane baseline):")
print(f" '{SAMPLE_SENTENCES[0]}'")
print(f" -> {visualize_tokens(base, SAMPLE_SENTENCES[0])}")
log = args.output_dir / "break_it_log.txt"
log.write_text(
"# Project 3 BREAK IT — vocab too small vs. too large\n\n"
f"baseline (vocab=512): avg_tokens={base_avg:.2f} "
f"compression={base_ratio:.3f} ==1freq={base_freq['eq_1_frac']:.1%}\n"
f"too small (vocab=256): avg_tokens={tiny_avg:.2f} "
f"compression={tiny_ratio:.3f}\n"
f"too large (req 8192, got {huge_actual}): avg_tokens={huge_avg:.2f} "
f"compression={huge_ratio:.3f} ==1freq={huge_freq['eq_1_frac']:.1%}\n"
"\n"
"Lessons:\n"
"1. Too-small vocab (256, no merges): byte-level tokens force every common\n"
" word to be reassembled from scratch every time. Sentences are ~4x longer.\n"
" A model would waste context budget on local reassembly.\n"
"\n"
"2. Too-large vocab (8192 requested): BPE stops early because no pair\n"
" occurs more than once. Most of the late merges are one-off fragments\n"
" that wouldn't get reusable embeddings in any downstream model.\n"
"\n"
"The sweet spot exists because two costs pull in opposite directions:\n"
"small vocab causes long sequences and wasted context; large vocab causes\n"
"sparse token frequencies and weak embeddings. Modern tokenizers land in\n"
"the tens of thousands — enough to compress common patterns, not so many\n"
"that each piece becomes statistically lonely.\n",
encoding="utf-8",
)
print(f"\nLog written to {log.resolve()}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
Outputs
# Project 3 BREAK IT — vocab too small vs. too large
baseline (vocab=512): avg_tokens=6.70 compression=3.540 ==1freq=33.6%
too small (vocab=256): avg_tokens=28.30 compression=1.000
too large (req 8192, got 556): avg_tokens=4.90 compression=4.656 ==1freq=42.5%
Lessons:
1. Too-small vocab (256, no merges): byte-level tokens force every common
word to be reassembled from scratch every time. Sentences are ~4x longer.
A model would waste context budget on local reassembly.
2. Too-large vocab (8192 requested): BPE stops early because no pair
occurs more than once. Most of the late merges are one-off fragments
that wouldn't get reusable embeddings in any downstream model.
The sweet spot exists because two costs pull in opposite directions:
small vocab causes long sequences and wasted context; large vocab causes
sparse token frequencies and weak embeddings. Modern tokenizers land in
the tens of thousands — enough to compress common patterns, not so many
that each piece becomes statistically lonely.
# Project 3 run log
corpus_size_bytes: 1299
vocab_sizes : [288, 512, 1024]
# per vocab size
vocab_size=288: avg_tokens=20.80 compression=1.385 unique=92 <5freq=29.35% ==1freq=5.43%
vocab_size=512: avg_tokens=6.70 compression=3.540 unique=149 <5freq=87.25% ==1freq=33.56%
vocab_size=1024: avg_tokens=4.90 compression=4.656 unique=146 <5freq=95.89% ==1freq=42.47%
# token boundary visualizations (largest vocab size)
The quick brown fox jumps over the lazy dog.
[The·quick·brown·fox·jumps·over·the·lazy·dog][.]
She sells seashells by the seashore.
[She·sells·seashells·by·the·seashore][.]
def main(): user_id = 42
[def·][ma][in][()][:][·][user_][id·=·42]
SELECT id, name FROM users WHERE active = TRUE;
[SELECT·id,·nam][e·][FROM·users·WHERE·][act][iv][e·=·][T][R][U][E][;]Read it in the book
This page ships the code. The full derivation — why the chain rule is mechanical not magical, what the computational graph buys you, the long-form reasoning — lives in Chapter 3 of Under the Hood.
Get the book →