A language model never sees letters. It sees a list of integers, one per token, and the first thing it does with each integer is fetch a row from a table of learned vectors. Everything downstream, from attention to sampling, works on those vectors. In 2013, Mikolov and colleagues showed that such vectors can capture meaning well enough that vec("Madrid") − vec("Spain") + vec("France") lands closer to vec("Paris") than to any other word vector. Their model turned two vectors into a probability with a dot product and a softmax over the whole vocabulary: the same shape every LLM's output layer still has.
Spotted in the wild
This chamber builds both halves: how text is cut into tokens, and how each token becomes a vector.
- “V”Vocabulary size: the number of distinct token ids.
- “d”Embedding dimension: the length of every token's vector.
- “E, a V by d real matrix”The embedding matrix. Row is the learned vector for token id .
- “x sub i”The one-hot row vector for token id : a 1 in position and zeros elsewhere.
- “merge a and b”A BPE merge rule: wherever is followed by , write the single new token .
- “cosine similarity of u and v”: the cosine of the angle between two vectors, from to .
- “n context”The context window: the most tokens the model can attend over, prompt and output together.
- “v sub w and v prime sub w”Word2vec's input and output vectors for word , kept in two separate tables.
| Symbol | Say it | Meaning | LaTeX |
|---|---|---|---|
| “V” | Vocabulary size: the number of distinct token ids. | ||
| “d” | Embedding dimension: the length of every token's vector. | ||
| “E, a V by d real matrix” | The embedding matrix. Row is the learned vector for token id . | ||
| “x sub i” | The one-hot row vector for token id : a 1 in position and zeros elsewhere. | ||
| “merge a and b” | A BPE merge rule: wherever is followed by , write the single new token . | ||
| “cosine similarity of u and v” | : the cosine of the angle between two vectors, from to . | ||
| “n context” | The context window: the most tokens the model can attend over, prompt and output together. | ||
| “v sub w and v prime sub w” | Word2vec's input and output vectors for word , kept in two separate tables. |
Why not characters, or words?
A tokeniser maps text to ids with each . The two obvious units both fail. The figures below are rough, for English prose.
| Unit | Vocabulary size | Tokens per English word | Unknown words? |
|---|---|---|---|
| Characters or bytes | about 100, or exactly 256 | about 5–6 | Never with bytes |
| Whole words | hundreds of thousands, and never complete | 1 | Constantly: names, typos, code, new coinages |
| Subwords (BPE and relatives) | tens of thousands to a few hundred thousand | roughly 1.3 | Never, given a byte fallback |
Characters make sequences long, and attention, which the next chamber builds, costs time that grows with the square of sequence length; each position also carries little meaning, so the model must spend layers reassembling words. Whole words make the vocabulary enormous, leave rare words with too few examples to learn from, treat run, runs and runner as unrelated, and still meet words they have never seen, which older systems replaced with an [UNK] token. Chinese, Japanese and Thai are written without spaces, so there is not even an obvious word to look up. Subwords sit between: frequent words become single tokens, and rare words split into reusable pieces.
Byte-pair encoding
Byte-pair encoding (BPE) was a 1994 compression trick before Sennrich, Haddow and Birch used it to build subword vocabularies. Training is a loop: split the corpus into words (pre-tokenisation) and each word into base symbols; count every adjacent pair, weighting each word by its frequency; merge the most frequent pair everywhere and record the rule ; repeat until the vocabulary, base symbols plus one token per merge, reaches its target size.
def merge(symbols, pair): # left to right, never overlapping
out, i = [], 0
while i < len(symbols):
if tuple(symbols[i:i + 2]) == pair:
out.append(symbols[i] + symbols[i + 1]); i += 2
else:
out.append(symbols[i]); i += 1
return out
def train_bpe(words, num_merges): # words: {("l", "o", "w"): 5, ...}
merges = []
for _ in range(num_merges):
pairs = {}
for w, c in words.items():
for p in zip(w, w[1:]):
pairs[p] = pairs.get(p, 0) + c
best = max(pairs, key=pairs.get)
merges.append(best)
words = {tuple(merge(list(w), best)): c for w, c in words.items()}
return merges
def encode(word, merges): # replay merges in learned order
symbols = list(word)
for pair in merges:
symbols = merge(symbols, pair)
return symbols
Encoding recounts nothing. It splits new text into base symbols and replays the merge list in order, so a word never seen in training still encodes, as a sequence of learned pieces.
Why the most frequent pair? Suppose a merge replaces occurrences in a sequence of symbols. Each replacement turns two symbols into one, and the left-to-right scan never reuses a position, so the result has symbols. Summed over the corpus, a pair's non-overlapping count is exactly the number of tokens its merge saves, and greedy BPE takes the largest one-step saving. Overlaps are the only subtlety: in the pair occurs twice but only one replacement fits. Greedy is also myopic: the best merge for the corpus now need not be best later, and it is certainly not best for every text you will encode. The lab below makes you beat it.
The sequence a a a a a has five symbols. Applying the merge left to right, how many tokens remain?
Bytes, SentencePiece and vocabulary size
Byte-level BPE. GPT-2 ran BPE on UTF-8 bytes rather than Unicode characters. Its paper notes that character-level BPE would need a base vocabulary of over 130,000 symbols, while bytes need only 256, and every possible string is a byte sequence. It also stopped merges across character categories (except spaces), so dog, dog. and dog! would not each claim a vocabulary slot. Its vocabulary has 50,257 tokens.
SentencePiece (Kudo and Richardson, 2018) treats the input as a raw stream, with whitespace as an ordinary symbol written ▁, so it needs no language-specific splitting on spaces and decoding is exactly reversible. It implements BPE and the unigram language model, which starts from a large vocabulary and prunes it, and can fall back to bytes for unseen characters. Families such as T5 and Llama 2 used it.
| Larger vocabulary | Smaller vocabulary |
|---|---|
| Shorter sequences: more text per window, fewer decoding steps | Longer sequences |
| Embedding and output matrices with parameters each | Cheaper tables and softmax |
| Many rare tokens that each receive little training | Every token seen often |
| Room for merges in many scripts | Frequent languages, often English, take the merges |
A common rule of thumb for English prose is about 0.75 words, or 4 characters, per token. Code, numbers, unusual names and most other languages cost more. Treat these as rough averages that vary by tokeniser, and count with the real one whenever it matters.
Why can a byte-level BPE tokeniser encode any string without an unknown token?
The token tax and other quirks
Tokenisers are not neutral. Petrov et al. (2023) found that the same text translated into different languages can differ in tokenised length by up to 15 times, and even character-level and byte-level models show more than 4-fold differences for some language pairs. Part of the gap is UTF-8 itself, so byte-level models are not immune; subword tokenisers widen it by spending their merges on whatever was frequent in their training data.
UTF-8 spends one byte on each ASCII character, two on accented Latin, Greek, Cyrillic, Arabic or Hebrew letters, three on Devanagari, Thai, Chinese, Japanese or Korean characters, and four on most emoji. A user writing in a heavily taxed language pays more per message, waits through more decoding steps and fits less text into the same window. Other quirks to know:
- Numbers. LLaMA split every number into single digits; other tokenisers group up to three digits; early byte-level tokenisers kept whatever digit strings were frequent, so neighbouring numbers could split differently. Arithmetic is harder when tokens do not line up with place value.
- Leading spaces. In GPT-style tokenisers
helloandhelloare different tokens, because the space travels with the word. A prompt that ends in a stray space can push the model towards unusual continuations. - Glitch tokens. Rumbelow and Watkins found tokens such as
SolidGoldMagikarpthat GPT-2 and GPT-3 models handled bizarrely, often unable even to repeat them. The likely cause: strings that were frequent in the tokeniser's training data but almost absent from the model's, so their embeddings were barely trained. - Special tokens. Reserved ids never produced by merges mark the end of a text (GPT-2's
<|endoftext|>), padding, or chat roles. A chat template flattens a conversation into one token sequence; one common style looks like this:
<|im_start|>system
You are a concise assistant.<|im_end|>
<|im_start|>user
How many tokens is this?<|im_end|>
<|im_start|>assistant
Each model family has its own template, and the wrong one quietly degrades output. Check how your tokeniser treats a literal <|im_end|> typed by a user, because libraries differ. Chamber 6, Prompting as Programming, goes deeper.
Embeddings: a lookup table that learns
Write token id as the one-hot row vector and let . Then
because only contributes: multiplying by a one-hot vector selects row . Frameworks skip the multiplication and index the row directly (an embedding layer is a gather), and for a sequence of tokens the transformer consumes , where stacks the one-hot rows. The embedding says nothing about position; transformers add that separately.
starts random and is trained by backpropagation like any other weight. Since , the chain rule gives : row of the gradient sums the gradients at the positions where token occurred, and every token absent from the batch gets exactly zero. Rare tokens are therefore rarely updated, which is how glitch tokens survive.
At the other end, the final hidden state is scored against every token, , and a softmax turns the logits into next-token probabilities: Mikolov's Equation (2) with the rows of as the output vectors . Tied embeddings set (Press and Wolf, 2017), saving parameters: with an illustrative and , that is about 524 million. GPT-2 ties them; many larger models keep two tables.
Token id 7 has the one-hot row vector . What is ?
Comparing embeddings: cosine similarity
By Cauchy–Schwarz, , so the cosine lies in ; you will prove the equality cases below. It compares directions and ignores lengths, whereas a raw dot product also rewards long vectors. For word2vec-style embeddings, nearest neighbours by cosine tend to be related words, and differences can encode relations, as in the Madrid example; in code, normalise every row of once and a single matrix product gives all the cosines. An LLM's input embedding rows are a weaker notion of meaning than its contextual hidden states; dedicated embedding models produce one vector per passage, and Chamber 8, Retrieval, Tools and Agents, searches with them.
What is the cosine similarity of and ?
Context windows and paying per token
The context window is the most tokens a model attends over in one call, prompt and output together: . GPT-2's held 1,024 tokens; current models accept far more, but every token still costs compute, time and money. APIs bill per token, usually quoted per million, with input and output priced separately and output typically dearer; many discount input that repeats across calls through caching. Self-hosting pays in GPU time per token instead, which Chamber 9 prices out.
- Count with the model's own tokeniser:
tiktokenfor OpenAI models, Hugging Facetokenizersfor open-weight models, or the token-counting endpoints that APIs such as Anthropic's and Google's Gemini provide. - Budget the window: system prompt, history, retrieved documents, tool definitions and the reserved output must all fit.
- Measure tokens per message for each language your users write in, not per English word.
Read beyond
Lecture notes · free online · ~25 min
Byte-Pair Encoding tokenizationHugging Face LLM Course · Chapter 6, section 5
Train BPE by hand on their toy corpus of hug, pug, pun, bun and hugs, then check your merges against their code.
Book · free online · ~40 min
Speech and Language Processing (3rd edition draft)Dan Jurafsky & James H. Martin · Chapter “Embeddings” (Chapter 5 in the August 2026 draft)
Read the section on cosine similarity and note why the chapter prefers it to the raw dot product.
Paper · free online · ~30 min
Language Model Tokenizers Introduce Unfairness Between LanguagesPetrov, La Malfa, Torr & Bibi · Introduction and the tokenisation-length comparisons
Find which languages pay the largest premium over English, and whether byte-level models escape it.
Article · free online · ~25 min
The Illustrated Word2vecJay Alammar · From “Skipgram” to the end
Identify the embedding and context matrices: they are the v and v′ tables of Equation (2).
Read the equation in context
Neural Machine Translation of Rare Words with Subword UnitsRico Sennrich, Barry Haddow & Alexandra Birch · ACL 2016, 2016Sennrich and colleagues wanted translation without a fixed word list: names, compounds and loanwords can be translated piece by piece. Their BPE learner is a few lines of Python, and segmenting rare words this way improved over a back-off dictionary baseline by up to 1.1 and 1.3 BLEU on English→German and English→Russian.
Distributed Representations of Words and Phrases and their CompositionalityTomas Mikolov, Ilya Sutskever, Kai Chen, Greg Corrado & Jeffrey Dean · NeurIPS 2013, 2013Skip-gram learns vectors by predicting the words around each word. Section 2 maximises the average log probability (Equation 1)
where is the window size, and defines each probability by the softmax of the opening Glimpse, with separate "input" and "output" vectors per word. The authors call that softmax impractical because its gradient costs time proportional to , often – terms, and replace it with hierarchical softmax and negative sampling. LLM training does compute the full softmax over at every position: GPUs made the price worth paying.
Decode the paper · Section 2, Equation (2): the basic Skip-gram softmax
Distributed Representations of Words and Phrases and their CompositionalityTomas Mikolov, Ilya Sutskever, Kai Chen, Greg Corrado & Jeffrey Dean · NeurIPS 2013, 2013
Skip-gram learns word vectors by predicting the words around each word. Equation (1) averages over a window of words either side, and Equation (2) defines that probability as a softmax over dot products between one input vector and every output vector. The paper calls this full softmax impractical, because the cost of its gradient is proportional to , often – terms, and replaces it with hierarchical softmax or negative sampling.
Options
Your turn
Train a tokeniser by hand. Greedy BPE merges whatever is most frequent in the corpus; your job is to choose merges that squeeze three particular phrases into their token budgets, the last built from words the corpus never contains. Watch your curve against greedy's as you go.
Interactive lab
Token squeezer
Training corpus: The sirens sing and the sailors sail. Singing sirens, sailing sailors. The singer sings and the sailor sails. The rowers row, the sails sing in the wind.
Fit “singing sailors” into 10 tokens using at most 3 merges. Find a merge that pays off more than once.
Phrase tokens
16 / 10
Merges used
0 / 3
Vocabulary
14
Corpus · greedy
148 · 148
Pairs in the corpus (count) · tap to merge
Teal pairs occur in the phrase; −n is how many tokens that merge would save there. Below, merges in violet are the ones greedy BPE would also pick at that step.
Match · Term ↔ Meaning
Tokenisation terms
Options
Match · Expression ↔ Meaning
Embedding maths
Options
Proof puzzle
What one merge saves
Claim
If applying the merge to a sequence of symbols replaces occurrences, the result has exactly symbols.
Tap lines in the order they should appear. Not every line belongs. Tap a line in your proof to send it back.
Your proof
- Pick the first line below.
Available lines
Prove it yourself
Cosine similarity is bounded
Claim
For nonzero , prove , and that exactly when is a positive multiple of .
Your typeset proof appears here.
Coding problems
Problem 1·Warm-up
Nearest by cosine
Toy 4-dimensional embeddings (illustrative, not from a real model): king , queen , man , woman , prince , apple . Form . Among the tokens other than king, man and woman, find the one with the largest cosine similarity to , and give that similarity to 4 decimal places.
Problem 2·Standard
Train, then encode the unseen
Train BPE on the word counts low 5, lower 2, newest 6, widest 3. Write each word as its characters preceded by a word-start marker _ (so low is _ l o w). Repeat 10 times: count every adjacent pair of symbols, weighting each word by its count; pick the pair with the highest count, breaking ties by the lexicographically smallest pair (compare first symbols, then second symbols, as Python compares tuples of strings); then replace its non-overlapping occurrences, scanning each word left to right, by the concatenated symbol. Finally encode the unseen words lowest, newer, wider and slowest (each with its _ marker) by applying the 10 merges in the order learned. How many tokens do the four words take in total?
Problem 3·Challenge
The token tax, simulated
Two toy languages share one tokeniser. Language A builds words from the syllables ka, lo, mi, ne, ru, to (in that order); language B from zu, xe, qa, vo, jy, wi. Use the Park–Miller generator with , where each call advances and returns the new value. Generate 1000 words. For each word: call once, and the word is in B if the value mod 10 is 0, otherwise in A; call again, and the word has syllables; then, for each syllable, call once and take syllable number value mod 6 (counting from 0) from that language's list. Join the syllables with no marker. Train 30 BPE merges on all 1000 words: count adjacent pairs over every word occurrence, take the most frequent pair, break ties by the lexicographically smallest pair, and replace non-overlapping occurrences left to right; merges never cross words. Finally compute the mean number of tokens per word for the B words and for the A words. Give the ratio, B over A, to 4 decimal places.
Key takeaways
- Models read integer token ids. Subword tokenisers trade vocabulary size against sequence length, and with a byte fallback they never meet an unknown word.
- BPE learns merges by frequency and encodes by replaying them in order; each merge saves exactly its non-overlapping count.
- Tokenisers are not neutral: other languages, numbers, leading spaces and rare tokens cost or behave differently, so count with the real tokeniser.
- An embedding is a row of a learned matrix, selected by a one-hot vector; tying it to the output layer saves parameters.
- Cosine similarity compares directions and lies in ; context windows and bills are both measured in tokens.
Checkpoint
Prove it to the labyrinth
Answer every question to clear this chamber. First-try answers earn the most XP.
Why does a tokeniser trained mostly on English text usually spend more tokens on the same message in, say, Burmese or Amharic?
A byte-level BPE tokeniser starts from 256 byte tokens, learns 50,000 merges and adds one special end-of-text token. What is its vocabulary size?
A model has a context window of 32,768 tokens. The system prompt takes 1,500 tokens and you reserve 2,000 tokens for the answer. Retrieved passages cost 600 tokens each. How many whole passages fit?
Why could asking an early GPT model to repeat the token ' SolidGoldMagikarp' produce bizarre output?
What does it mean to tie a model's input and output embeddings?
In a GPT-style byte-level tokeniser, why are hello and hello (with a leading space) usually different tokens?
A string has 10 ASCII letters, 5 Greek letters (2 bytes each in UTF-8) and 2 emoji (4 bytes each). Before any merges, how many base tokens does a byte-level tokeniser need?
When a trained BPE tokeniser encodes new text, how does it choose which merges to apply?
End of the chamber
Clear this chamber
- Questions in this chamber (0/12 solved)Next unsolved
- Bonus: Token squeezer (+40 XP)
- Bonus: Problem 1: Nearest by cosine (+20 XP)
- Bonus: Problem 2: Train, then encode the unseen (+35 XP)
- Bonus: Problem 3: The token tax, simulated (+50 XP)
- Bonus: Proof: What one merge saves (+25 XP)
- Bonus: Proof: Cosine similarity is bounded (+35 XP)
- Bonus: Decode the paper (+25 XP)
- Bonus: Match: Tokenisation terms (+20 XP)
- Bonus: Match: Embedding maths (+20 XP)