From vector to next token

At the top of the stack, the last token’s vector is normalised and multiplied by one big matrix that gives a score to every token in the vocabulary. Softmax turns the scores into probabilities. Applying the same read-out to the middle layers, the logit lens, shows the prediction taking shape.

Key idea
logits = LN(x_final) · W_U (d × vocab); probabilities = softmax(logits); W_U is often the embedding table transposed
Real world
A compass needle against a dial. The needle (the final vector) points somewhere; you read how well it lines up with each of the marked directions (each word), and the best match is your answer.
In an LLM
GPT-2 multiplies a 768-number vector by a 768 × 50,257 matrix: 50,257 dot products, one score per token. For “The capital of France is”, the top scores go to “ the”, “ now”, “ a”, “ France” and “ Paris”.

The last three steps

After the final block, every position holds a vector of dd numbers. To predict the next token from the last position, the model:

  1. Normalises it with one final LayerNorm (or RMSNorm). In a pre-norm model the stream has grown a lot by now (see the residual stream), and this brings it back to a standard size.
  2. Unembeds it: multiplies by the matrix WUW_U, of shape d×vocabd \times \text{vocab}. Each column of WUW_U belongs to one token, so the result is one score per token: a logit.
  3. Softmax turns the logits into probabilities that add up to 1.
p(next token)=softmax⁡(LN⁡(xfinal) WU)p(\text{next token}) = \operatorname{softmax}\big(\operatorname{LN}(x_{\text{final}})\, W_U\big)

Each logit is a dot product: the final vector against one token’s column. A token scores high when the final vector points the same way as its column. Try it with a toy vocabulary of eight words:

final x1 × 4
thingact.smallfirst1.60.20.30.1
→
norm × 21 × 4
thingact.smallfirst3.4−1.1−0.8−1.5
norm × 21 × 4
thingact.smallfirst3.4−1.1−0.8−1.5
×
W_U = Eᵀ4 × 8
·cat·dog·mat·sat·ran·the·a·onthing1.00.90.80.10.00.10.0−0.1act.0.10.3−0.31.00.90.00.00.3small0.0−0.20.10.0−0.31.00.90.8first0.00.00.00.00.00.3−0.20.0
=
logits1 × 8
·cat·dog·mat·sat·ran·the·a·on3.32.93.0−0.8−0.8−0.9−0.4−1.3
softmax → probabilities
·cat3.3340%
·mat3.0130%
·dog2.9227%
·a−0.441%
·ran−0.791%
·sat−0.801%
·the−0.921%
·on−1.340%
Each logit is a dot product: the final vector against one word’s column of W_U. “cat” wins with logit 3.33 and 40%. With tied embeddings, a word’s output column is its own embedding, so a final vector that points like a word’s embedding predicts that word.
The last token’s final residual vector (4 toy dimensions) becomes a probability for each of 8 words. Move the sliders to change the vector; toggle tied embeddings to swap the output matrix.

Tied embeddings

At the bottom of the model, the embedding table EE (vocab × dd) turns token IDs into vectors. At the top, WUW_U (dd × vocab) turns vectors back into token scores. These are opposite jobs, and many models use one matrix for both: WU=E⊤W_U = E^\top. This is .

It saves a lot of parameters (38.6 million in GPT-2 small, almost a third of the model), and it makes sense: a final vector that points like the embedding of “ Paris” should predict “ Paris”. GPT-2 and Gemma tie their embeddings. Llama 3 8B does not: with 8 billion parameters the extra 525 million for a separate output matrix is cheap, and an untied output layer gives the model more freedom. Untick “tied embeddings” in the widget to see a separately learned WUW_U.

example · a compass and a dial

A hiker reads a compass. The needle points in some direction, and the dial has marks for N, NE, E and so on. The hiker checks how well the needle lines up with each mark and says “north-east”. The unembedding does the same in 768 dimensions with 50,257 marks: the final vector is the needle, each token’s column is a mark, and the dot product measures how well they line up. Softmax then says “mostly NE, a bit of E”.

example · a real prediction

At the last position of “The capital of France is”, GPT-2 small multiplies its normalised 768-number vector by the 768 × 50,257 matrix. After softmax, “ the” gets 8.5%, “ now” 4.8%, “ a” 4.6%, “ France” 3.2% and “ Paris” 3.2%. A small 2019 model hedges: “the” could start “the city of Paris”. Larger models are far more confident about “ Paris”.

The logit lens

The read-out is just “final norm, then WUW_U”. Nothing stops you applying it to the residual stream in the middle of the model, as if the model stopped after layer 5. That is the (nostalgebraist, 2020). It works because of the residual stream: every layer adds to the same vector, and the output reads that vector, so the intermediate vectors live in roughly the same “language” as the final one.

Here it is on GPT-2 small, with real numbers. Rows are layers, columns are positions; each cell is the top guess for the next token.

Loading GPT-2 data…
The logit lens on GPT-2 small.

What the lens shows, and what it doesn’t:

  • Layer 0 is junk. It is just the token embedding (plus position) put through the final norm and WUW_U. GPT-2 decodes it to odd tokens like “ destro” and “ mathemat”. Nothing has been computed yet, and the read-out was trained for the top of the stack.
  • Early layers are rough. Layers 1 to 4 give generic, high-frequency guesses (“ now”, “ not”, “ also” after “ is”) that would fit almost any sentence.
  • The answer forms in the late middle. At the last position of “The capital of France is”, “ France” becomes the top guess at layer 10 (62%), with “ Paris” second (18%). In the Mary and John sentence, layers 9 and 10 guess “ them”, and layer 11 switches to “ Mary” (61%), the correct name.
  • The last layer often hedges. GPT-2’s final layer spreads the probability out again: after “France is” the output puts “ the” first. The lens shows that the model had the facts in mind earlier than its final answer suggests.

The logit lens is a rough tool: it assumes every layer speaks the output’s language, which is only roughly true. The tuned lens (Belrose et al., 2023) learns a small correction for each layer and gives cleaner readings. More in interpretability.

going deeper

In code

logits = model.lm_head(model.transformer.ln_f(x))   # x: (T, 768) → (T, 50257)
probs = logits.softmax(-1)
 
# logit lens: the same read-out on every layer's hidden state
out = model(ids, output_hidden_states=True)
for layer, h in enumerate(out.hidden_states[:-1]):
    p = model.lm_head(model.transformer.ln_f(h)).softmax(-1)

In Hugging Face’s GPT-2, the last entry of hidden_states already has the final norm applied, so it is decoded without ln_f.

Tying

model.lm_head.weight is model.transformer.wte.weight is True for GPT-2: the same tensor is used for the input embedding and the output layer.

In papers

Related

  • the-block - Put the pieces together: LayerNorm, attention, add; LayerNorm, MLP, add. That is one transformer block, and a model is a stack of identical blocks between an embedding table and an output layer. Here is one block computed number by number, and an exact parameter count for real models.
  • embeddings - A token ID is just a name. The model turns it into a vector by looking up one row of a big learned table, the embedding matrix. Training places tokens that are used alike near each other, so the table ends up holding a map of meaning. Here is the lookup step by step, and GPT-2’s real table.
  • residual-stream - Every token carries one vector up through the model. Each attention layer and each MLP reads that vector, computes something, and adds the result back. Nothing is overwritten. This running sum is the residual stream, and it is why deep transformers can be trained at all.
  • training-objective - A language model is trained on one task: predict the next token. Shift the text one place to get the answers, and score every position at once by the negative log of the probability the model gave the right token. Average, and that is the loss.