Quick review
Every part of our model on one page, in the order the sentence "Lily wanted to play with her" passes through it. Each card has the shapes, and where it helps, the formula and a line of code.
- Tokens in the example
- T = 6
- Numbers per token
- d = 128
- Blocks
- L = 4
- Heads per block
- 4
- Numbers per head
- d_k = 32
- Feed-forward width
- 512
- Vocabulary
- 4,096 tokens
- Longest text
- 128 tokens
One forward pass gives every value on this page.
const ids = tokenizer.encode('Lily wanted to play with her');
const trace = forward(model, ids); // every value below lives on trace
const probs = probabilities(rowView(trace.logits, ids.length - 1), 1);Inputs
Your text, split into tokens. Each token is an id number from the vocabulary.
The sentence "Lily wanted to play with her" is 6 token ids, each from 0 to 4,095.
const ids = tokenizer.encode('Lily wanted to play with her');Embedding
Each id picks one row from a table of learned numbers. That row is the token's starting list of numbers.
The table has 4,096 rows of 128 numbers. The sentence becomes 6 rows of 128.
trace.tokenEmbeddings // 6 × 128Position
Each position also gets a list of numbers, added to the token's list. That is how the model knows the order. GPT-2 learns a table of positions instead.
A table of 128 positions by 128 numbers. Row 0 goes to the first token, row 1 to the second.
trace.embeddings // tokenEmbeddings + positionEmbeddingsAdd and norm
Add, called a residual connection, keeps what came in and puts the new result on top, so earlier numbers are kept. Norm rescales each token's numbers to a steady range. GPT-2 moves the norm to the start of each step, before attention and before the feed-forward layer.
Each token's 128 numbers are handled on their own.
trace.layers[0].ln1.out // 6 × 128, normed before attentionMasked attention
Each token looks at itself and the tokens before it, and pulls in what it needs. Masked means it cannot look ahead.
Our model has 4 heads with 32 numbers each. Each head makes a 6 by 6 grid of weights.
trace.layers[0].heads[0].weights // 6 × 6, each row sums to 1Read the chapter on Masked attentionSee it on the map, Masked attention
Feed forward
Each token's numbers go through a small network on their own, without looking at other tokens.
128 numbers grow to 512, pass through GELU, and shrink back to 128.
trace.layers[0].mlpOut // 6 × 128Repeated blocks
Everything inside the frame is one block. The model repeats it, and each block refines the numbers further.
Our model stacks 4 blocks. The paper used 6, and GPT-2 small uses 12.
trace.layers.length // 4Final norm
One more norm after the last block, before the scores are made. GPT-2 added it. The paper has none.
The 6 rows of 128 numbers, rescaled.
trace.lnFinal.out // 6 × 128Linear
Turns each token's numbers into one score for every token in the vocabulary. It reuses the embedding table.
4,096 scores per position. Only the last position is used to guess the next token.
trace.logits // 6 × 4,096Softmax
Turns the scores into chances between 0 and 1 that add up to 1.
4,096 chances, one for each possible next token.
probabilities(rowView(trace.logits, ids.length - 1), 1)Output
The model's guess, a chance for every possible next token. Pick one, add it to the text, and run again.
One chance for each of the 4,096 tokens.
sample(probs, createRng(1)) // the next token id