Tokens & Embeddings
How text becomes vectors: BPE, tokenizer pathologies, and the geometry of embedding space.
- → Run BPE merges by hand and explain why tokenizers exist
- → Diagnose real LLM failures caused by tokenization
- → Describe embedding space geometry and tied unembeddings
The model never sees your text
Before a transformer does anything, a completely separate program chops your string into pieces and looks each piece up in a table. That program is the tokenizer, the pieces are tokens, and the model only ever sees the results. It has no access to the original characters. Almost every surprising low-level failure in this course traces back to that sentence.
Why not feed the model characters? You could, and some models do, but an average English word is 4–5 characters, so every sentence becomes 4–5× longer. Attention cost grows with the square of sequence length, so character models spend their compute re-deriving spelling instead of meaning.
Why not whole words? Then the vocabulary is unbounded (every typo, every name, every URL is a new word), rare words get almost no training signal, and anything unseen becomes an <UNK> hole in the input.
·cat and cat are two different entries with two different vectors.BPE: build a vocabulary by merging
Byte-pair encoding learns the vocabulary from data with an almost embarrassingly simple loop. Start with every character as its own symbol. Count every adjacent pair of symbols in the corpus. Merge the most frequent pair into one new symbol. Repeat until you have as many merges as you wanted.
The payoff shows up on words the corpus never contained. Feed lowest to the four merges above, in order, and you get low + est — two learned pieces, no <UNK>, and a segmentation that happens to be morphologically sensible. That is the whole trick: frequent things get short, rare things get decomposed.
Real implementations add two wrinkles. First, a pre-tokenization regex splits text into words, numbers, and punctuation runs before any merging, so merges can never straddle a space or glue a word to a comma. Second, byte-level fallback: GPT-2 works on the 256 raw bytes rather than Unicode characters, so any input at all — emoji, Klingon, binary — is encodable, just expensively. The widget below has the first wrinkle and not the second; characters missing from its small corpus get flagged instead.
Where tokenizers bite
A tokenizer is a lossy interface between the world and the model. Four failure families come straight out of that interface, and you can predict all four from the mechanism.
1. Arithmetic. A model can only learn a clean addition algorithm if digits arrive in consistent chunks. GPT-2 and GPT-3 gave hundreds of multi-digit numbers their own tokens with no consistent grouping, so 380 + 42 and 381 + 42 can have completely different token shapes. Beren Millidge's Integer tokenization is insane walks through how bad it was; his 2024 follow-up notes that newer tokenizers adopted consistent digit grouping, which is one reason arithmetic improved without anything changing in the architecture.
2. Spelling and counting letters. Asking how many rs are in strawberry asks the model to report on characters it never received. It can often answer anyway — spelling is recoverable from context and from the token string itself — but it is doing inference, not lookup, and it fails in the way that inference fails. Tokenization is not the whole story here, but it is why the task is hard at all.
3. Glitch tokens. In 2023 Jessica Rumbelow and Matthew Watkins found tokens like SolidGoldMagikarp sitting near the centre of GPT-2's embedding cloud. These strings were frequent in the tokenizer's training data (Reddit usernames from counting threads) but essentially absent from the model's training data, so their embedding vectors were never trained away from their random initialisation. Prompting with them produced evasion, insults, hallucinated completions, and broke determinism at temperature 0. Land & Bartolo later automated detection of such under-trained tokens across many production models — they are not a historical curiosity.
4. Language inequity. Petrov et al. measured tokenized length for the same text across languages and found differences of up to 15×. Since APIs bill per token and context windows are counted in tokens, speakers of under-represented languages pay more money for less context and slower responses — a fairness problem baked in before inference starts.
“The capital of France is” and “The capital of France is ” are genuinely different inputs. The second one has already committed to a token boundary the training data almost never contains, and quality drops. If a prompt behaves strangely, look at its last character first.From ids to geometry
A token id is just a row number. The embedding matrix holds one learned vector per token, and “embedding a token” means taking that row:
Written as a matrix product it looks like computation; it is a table lookup. For GPT-2 small, million parameters — roughly 31% of the model's 124M, spent before any thinking happens.
Because tokens with similar contexts get similar gradient pressure, embedding space acquires structure: numbers cluster with numbers, punctuation with punctuation, code keywords with code keywords. The famous stronger claim is analogy arithmetic — — which says related pairs are separated by a consistent offset vector, not just placed nearby.
At the other end of the model, the unembedding turns the final residual vector back into 50,257 logits — and each logit is a dot product between the residual stream and one row, exactly the similarity operation from Module 0.1:
That last observation is the whole idea behind the logit lens: if the unembedding can decode the final residual stream, point it at an intermediate layer and watch the prediction form. Module 3.1 does this properly. For now, notice that it is only possible because input and output live in the same vector space.
Play: tokenize and map
The first widget trains a genuine BPE tokenizer in your browser — 200 merges over a small corpus, learned when the page loads — and re-segments whatever you type on every keystroke. The second is a hand-built picture of embedding-space structure, which is honest about being a diagram rather than a projection of real weights.
- rank 1T|h|e
- rank 69T|he
- finalThe
- 0· + t → ·t×117
- 1h + e → he×71
- 2· + a → ·a×68
- 3·t + he → ·the×56
- 4e + n → en×55
- 5e + r → er×54
- 6i + n → in×51
- 7· + s → ·s×44
- 8o + r → or×42
- 9· + m → ·m×38
- 10o + k → ok×36
- 11ok + en → oken×35
- 12· + o → ·o×35
- 13a + r → ar×34
- 14o + d → od×31
- 15· + i → ·i×31
- 16·t + oken → ·token×31
- 17e + l → el×29
- 18·a + n → ·an×29
- 19a + t → at×28
- 20·an + d → ·and×27
- 21e + s → es×26
- 22n + e → ne×24
- 23od + el → odel×23
- 24· + b → ·b×23
- 25r + e → re×22
- 26· + w → ·w×22
- 27· + c → ·c×22
- 28in + g → ing×22
- 29· + l → ·l×20
- 30·m + odel → ·model×20
- 31e + c → ec×20
- 32· + · → ··×20
- 33·i + s → ·is×19
- 34or + d → ord×18
- 35·o + f → ·of×18
- 36· + p → ·p×18
- 37a + n → an×17
- 38i + t → it×17
- 39a + c → ac×17
- 40· + d → ·d×15
- 41v + er → ver×14
- 42o + n → on×14
- 43o + m → om×14
- 44·s + t → ·st×13
- 45· + v → ·v×13
- 46· + e → ·e×13
- 47· + f → ·f×13
- 48· + in → ·in×13
- 49·i + t → ·it×12
- 50·w + ord → ·word×12
- 51o + t → ot×11
- 52i + z → iz×11
- 53s + t → st×11
- 54s + e → se×11
- 55a + l → al×11
- 56a + in → ain×11
- 57g + e → ge×10
- 58·l + e → ·le×10
- 59· + u → ·u×10
- 60·token + iz → ·tokeniz×10
- 61· + ne → ·ne×10
- 62·s + p → ·sp×10
- 63·t + r → ·tr×10
- 64m + b → mb×10
- 65x + t → xt×9
- 66·t + h → ·th×9
- 67·tokeniz + er → ·tokenizer×9
- 68e + d → ed×9
- 69T + he → The×9
- 70· + re → ·re×8
- 71a + d → ad×8
- 72er + s → ers×8
- 73·th + at → ·that×8
- 74·o + ne → ·one×8
- 75⏎ + ⏎ → ⏎⏎×8
- 76u + l → ul×8
- 77·b + ec → ·bec×8
- 78·tr + ain → ·train×8
- 79en + t → ent×8
- 80ec + t → ect×8
- 81l + y → ly×8
- 82u + c → uc×8
- 83·· + ·· → ····×8
- 84· + n → ·n×7
- 85·token + s → ·tokens×7
- 86u + n → un×7
- 87e + xt → ext×7
- 88a + s → as×7
- 89o + c → oc×7
- 90·o + n → ·on×7
- 91·e + ver → ·ever×7
- 92i + on → ion×7
- 93·v + ect → ·vect×7
- 94·vect + or → ·vector×7
- 95·word + s → ·words×6
- 96·c + h → ·ch×6
- 97·p + r → ·pr×6
- 98t + s → ts×6
- 99· + h → ·h×6
- 100·v + oc → ·voc×6
- 101·voc + a → ·voca×6
- 102·voca + b → ·vocab×6
- 103·vocab + ul → ·vocabul×6
- 104·vocabul + ar → ·vocabular×6
- 105·vocabular + y → ·vocabulary×6
- 106·ever + y → ·every×6
- 107ac + e → ace×6
- 108·b + e → ·be×6
- 109·train + ing → ·training×6
- 110it + h → ith×6
- 111·m + er → ·mer×6
- 112f + or → for×6
- 113·n + ot → ·not×5
- 114t + ers → ters×5
- 115· + A → ·A×5
- 116u + t → ut×5
- 117en + c → enc×5
- 118·sp + ace → ·space×5
- 119· + r → ·r×5
- 120at + ion → ation×5
- 121·bec + om → ·becom×5
- 122a + i → ai×5
- 123·mer + g → ·merg×5
- 124·merg + es → ·merges×5
- 125·le + ar → ·lear×5
- 126d + ing → ding×5
- 127· + The → ·The×5
- 128·st + r → ·str×5
- 129t + u → tu×5
- 130i + m → im×5
- 131·d + o → ·do×4
- 132·re + ad → ·read×4
- 133·t + ext → ·text×4
- 134o + p → op×4
- 135·h + as → ·has×4
- 136e + p → ep×4
- 137·s + it → ·sit×4
- 138·f + r → ·fr×4
- 139·fr + om → ·from×4
- 140·d + at → ·dat×4
- 141·dat + a → ·data×4
- 142·w + ith → ·with×4
- 143·c + o → ·co×4
- 144ac + h → ach×4
- 145m + e → me×4
- 146ne + d → ned×4
- 147·m + at → ·mat×4
- 148·f + or → ·for×4
- 149mb + ed → mbed×4
- 150mbed + ding → mbedding×4
- 151·ne + ar → ·near×4
- 152an + ge → ange×4
- 153⏎ + ···· → ⏎····×4
- 154·do + es → ·does×3
- 155·ch + un → ·chun×3
- 156·chun + k → ·chunk×3
- 157t + he → the×3
- 158·st + ar → ·star×3
- 159·u + se → ·use×3
- 160ar + ac → arac×3
- 161m + odel → model×3
- 162en + d → end×3
- 163·st + ep → ·step×3
- 164·s + e → ·se×3
- 165·c + an → ·can×3
- 166w + ord → word×3
- 167·bec + a → ·beca×3
- 168·beca + u → ·becau×3
- 169·becau + se → ·because×3
- 170·a + l → ·al×3
- 171a + y → ay×3
- 172ac + k → ack×3
- 173·p + ai → ·pai×3
- 174·pai + r → ·pair×3
- 175enc + od → encod×3
- 176encod + ing → encoding×3
- 177i + l → il×3
- 178o + l → ol×3
- 179p + e → pe×3
- 180· + ord → ·ord×3
- 181·ord + er → ·order×3
- 182·lear + ned → ·learned×3
- 183·a + p → ·ap×3
- 184p + l → pl×3
- 185·s + a → ·sa×3
- 186for + e → fore×3
- 187·d + i → ·di×3
- 188·di + f → ·dif×3
- 189·dif + f → ·diff×3
- 190·diff + er → ·differ×3
- 191·differ + ent → ·different×3
- 192·e + ach → ·each×3
- 193·becom + es → ·becomes×3
- 194·mat + r → ·matr×3
- 195·matr + i → ·matri×3
- 196·matri + x → ·matrix×3
- 197·l + o → ·lo×3
- 198·u + p → ·up×3
- 199p + r → pr×3
Highlighted merges are the ones that fired on your text. The count is how often the pair appeared in the corpus when it was merged — notice how fast it falls, and imagine that curve continuing to merge 50,000.
Switch the overlay to pair offsets: every arrow is the same length and direction, which is the property that makes vector arithmetic like king − man + woman land near queen. Real embeddings show a weaker, noisier version of this — see the caveat in the lesson above.
Things to try: (1) Drag the merge slider from 0 to 200 with the prose sample loaded and watch tokens fuse — at 0 merges this is a character-level model, and the token count falls by more than half by the end. (2) Switch to the numbers sample and look at how 1024 and 2048 get carved up; the corpus contains both, so ask yourself what an addition algorithm would have to learn from these shapes. (3) Type the same word twice, once after a space and once at the start of a line, and confirm they produce different tokens. (4) In the map, hover actor and 42 and notice that nearest-neighbour structure is about role in text, not meaning in the world.
Problem set
Problem 1 is the one to do on paper — running the merge loop by hand once is worth an hour of reading about it. The two code problems can share a notebook.
Corpus (word: count): low: 5, lower: 2, newest: 6, widest: 3. Start with characters as symbols; treat words as independent (no merges across word boundaries). Break count ties in favour of the pair encountered first, scanning words in the order listed.
- Carry out merges 1–4, writing the pair, its count, and the state of all four words after each merge.
- Using exactly those four merges in order, encode the unseen word
lowest. How many tokens? - What would the vocabulary need for
lowestto be a single token, and why is that a bad trade?
A tokenizer has learned these merges, in this order: 0: t+h→th, 1: h+e→he, 2: th+e→the, 3: ·+the→·the (where · is a space).
- Encode
·thestep by step. Which merges fire, and in which order? - Now suppose merges 0 and 1 were swapped in rank. Encode
·theagain. Do you get the same tokens? - Explain why
theat the start of a document and·themid-sentence are different tokens with different embedding vectors, and give one practical consequence for prompting.
Open tiktokenizer and switch between models (GPT-2 and a modern GPT-4-class tokenizer at minimum). For each of the following, record the token count and the actual split:
1234567,1,234,567, and380 + 42 = 422- The same sentence in English and in a non-Latin-script language you can get a translation of
- A short Python function, and the same function with the indentation doubled
strawberry, andSolidGoldMagikarp
Write two sentences per item on what the split predicts about model behaviour.
In a notebook, implement train(corpus, num_merges), encode(text) and decode(ids) for byte-level BPE. Train 500 merges on a few hundred KB of text (Tiny Shakespeare, or any file on your disk).
Success checks:
decode(encode(s)) == sfor a dozen strings including emoji, accented characters, and tabs.- Compression: report characters per token before and after training. With 500 merges on English you should land around 3–4 characters per token, versus 1 at merge zero.
- Encode a word your corpus never contained and show the segmentation is sensible pieces rather than bare characters.
Write a short paragraph on each of three real LLM failures that are caused or amplified by tokenization. For each: name the observable behaviour, give the mechanism at the token level, and state one intervention that would fix or reduce it. At least one of your three must be something you can reproduce yourself today.
Load GPT-2 small in a notebook (transformers or TransformerLens) and pull out W_E, shape 50257×768.
- Verify the tying claim: is the unembedding matrix the same tensor as the embedding matrix?
- Compute cosine nearest neighbours for
king,Paris,seven, anddef. Do the clusters from the widget show up? - Compute the mean cosine similarity between 1,000 random token pairs. Compare with from Module 0.1. Explain any discrepancy.
Success check: nearest neighbours are recognisably related, and you can state whether real embeddings are more or less spread out than random directions.
Check yourself
0: t+h→th, 1: h+e→he, 2: th+e→the. Encoding the gives: SolidGoldMagikarp make GPT-2 and GPT-3 behave strangely?strawberry. The most accurate diagnosis is:Go deeper
Do the Karpathy video as a build-along; everything else here is either the primary source for a claim in the lesson or a tool you will keep open while prompting.