Large Language Models (LLMs) are (mostly) autoregressive models: they predict the next token given the tokens so far, and repeat this loop until stopping. This post focuses on inference: the overall flow, what vectors get computed, and how the attention mechanism combines information across tokens. We also focus on the what data is generated in each phase of an LLC and how they are used in other phases of an inference process.


2. Inference: the end-to-end loop

At a high level, inference is:

  1. Tokenize the prompt into token IDs.
  2. Embed token IDs into vectors (plus positional information).
  3. Run NlayersN_{\mathrm{layers}} Transformer blocks (attention + MLP).
  4. Produce logits for the next token; convert to probabilities.
  5. Select the next token (argmax or sampling), append it, and repeat.

The loop runs until an end condition: max tokens, an EOS token, or a stopping rule.

2.1 Overview

Prompt text flows through tokenization, embeddings, repeated Transformer blocks, and next-token sampling before being displayed.
Figure 1. End-to-end LLM inference. Each Transformer block applies causal self-attention and a per-token MLP before the final hidden state produces the next-token logits.

3. Tokenize and Positions embedding

Here, there are LL tokens in each prompt, each embedded in a DD-dimensional space. A batch contains BB such prompts.

3.1 Tokenization (text → IDs)

Modern LLMs typically use a subword tokenizer (BPE, Unigram, WordPiece, etc.). The prompt becomes a sequence of token IDs:

(t1,t2,,tL),ti{1,,V} (t_1, t_2, \dots, t_L), \quad t_i \in \{1, \dots, \|V\|\}

where V\|V\| is vocabulary size.

Practical notes:

  • Tokenization defines the model’s “alphabet.” It impacts context length, efficiency, and what counts as a single step of generation.
  • Special tokens (BOS/EOS, system delimiters, etc.) are often inserted by the serving stack.

3.2 Turning token IDs into vectors

3.3 Embedding lookup

The model stores an embedding table ERV×DE \in \mathbb{R}^{\|V\| \times D}.

For each token ID tit_i, we take a row of this table:

xi=E[ti]RD x_i = E[t_i] \in \mathbb{R}^D

Stacking all tokens gives:

X(b)=[x1Tx2TxLT]RL×D,XRB×L×D X^{(b)} = \begin{bmatrix} x_1^T \\ x_2^T \\ \vdots \\ x_L^T \end{bmatrix} \in \mathbb{R}^{L \times D}, \qquad X \in \mathbb{R}^{B \times L \times D}

3.4 Positional information

Transformers need position information because attention alone is permutation-invariant. Common approaches:

  • Absolute positional embeddings: XX+PX \leftarrow X + P with PRB×L×DP \in \mathbb{R}^{B \times L \times D}.
  • Rotary / relative position methods (e.g., RoPE): position is injected into the attention computation (often into QQ and KK) rather than added to XX.

Either way, the result is an initial tensor X0RB×L×DX_0 \in \mathbb{R}^{B \times L \times D}.

4. Inside one Transformer block (MHA + FFN)

A transformer block is the key block for the success of LLMs. At a high-level, it mixes information across tokens (via attention) and then applies a nonlinear transformation to each token vector (via the MLP/FFN). The mixing step allows the model to build up contextual understanding, due to the presence of positional encoding. While the MLP step allows it to create new features and representations that are not just linear combinations of the input.

4.1 What a Transformer block computes (vectors everywhere)

Each block maps XX+1X_\ell \mapsto X_{\ell+1} using (i) multi-head self-attention and (ii) an MLP, typically with residual connections and layer normalization. Here, {1,,Nlayers}\ell \in \{1, \ldots, N_{\mathrm{layers}}\} indexes the block number, and XRB×L×DX_\ell \in \mathbb{R}^{B \times L \times D} is the hidden-state tensor at that layer.

The exact ordering varies (pre-norm vs post-norm), but the core computations are consistent.

4.2 Complete prefill chain: from XX to context-mixed vectors

Let:

  • BB: batch size
  • LL: prompt sequence length
  • DD: model hidden dimension
  • HH: number of attention heads
  • dh=D/Hd_h = D / H: dimension per head
  • DffD_{\mathrm{ff}}: FFN intermediate dimension
  • LN\operatorname{LN}: layer normalization

Step 1. Input hidden states. The block receives one DD-dimensional vector for every token position.

XRB×L×D X \in \mathbb{R}^{B \times L \times D}
normalize before attention\Downarrow\quad\text{normalize before attention}

Step 2. Pre-attention normalization. Normalize each token vector before forming its attention projections.

X~=LN(X),X~RB×L×D \widetilde{X} = \operatorname{LN}(X), \quad \widetilde{X} \in \mathbb{R}^{B \times L \times D}
QKV projections\Downarrow\quad\text{QKV projections}

Step 3. Query, key, and value projections. Learned linear maps create three views of each normalized token.

Q=X~WQ,K=X~WK,V=X~WVWQ,WK,WVRD×DQ,K,VRB×L×D \begin{aligned} Q &= \widetilde{X}W_Q, \quad K = \widetilde{X}W_K, \quad V = \widetilde{X}W_V \\ W_Q, W_K, W_V &\in \mathbb{R}^{D \times D} \\ Q, K, V &\in \mathbb{R}^{B \times L \times D} \end{aligned}
split into H heads\Downarrow\quad\text{split into H heads}

Step 4. Split into attention heads. Reshape each projection into HH independent dhd_h-dimensional subspaces.

Q,K,VRB×H×L×dhdh=DH \begin{aligned} Q, K, V &\in \mathbb{R}^{B \times H \times L \times d_h} \\ d_h &= \frac{D}{H} \end{aligned}
for every head h\Downarrow\quad\text{for every head h}

Step 5. Causal attention. Each head scores only current and earlier positions, then forms a weighted sum of values.

Sh=QhKhTdh+Mcausal,ShRB×L×LPh=softmax(Sh),PhRB×L×LAh=PhVh,AhRB×L×dh \begin{aligned} S_h &= \frac{Q_h K_h^T}{\sqrt{d_h}} + M_{\mathrm{causal}}, \quad S_h \in \mathbb{R}^{B \times L \times L} \\ P_h &= \operatorname{softmax}(S_h), \quad P_h \in \mathbb{R}^{B \times L \times L} \\ A_h &= P_h V_h, \quad A_h \in \mathbb{R}^{B \times L \times d_h} \end{aligned}
concatenate heads and project\Downarrow\quad\text{concatenate heads and project}

Step 6. Merge the heads. Concatenate the per-head results and project them back to the model dimension.

A=Concat(A1,,AH),ARB×L×DO=AWO,WORD×D,ORB×L×D \begin{aligned} A &= \operatorname{Concat}(A_1, \ldots, A_H), \quad A \in \mathbb{R}^{B \times L \times D} \\ O &= AW_O, \quad W_O \in \mathbb{R}^{D \times D}, \quad O \in \mathbb{R}^{B \times L \times D} \end{aligned}
add attention residual\Downarrow\quad\text{add attention residual}

Step 7. Attention residual. Add the attention update to the original, unnormalized block input.

Y=X+O,YRB×L×D Y = X + O, \quad Y \in \mathbb{R}^{B \times L \times D}
normalize before FFN\Downarrow\quad\text{normalize before FFN}

Step 8. Pre-FFN normalization. Normalize the residual state before the position-wise feed-forward network.

Y~=LN(Y),Y~RB×L×D \widetilde{Y} = \operatorname{LN}(Y), \quad \widetilde{Y} \in \mathbb{R}^{B \times L \times D}
apply FFN and add residual\Downarrow\quad\text{apply FFN and add residual}

Step 9. Feed-forward update. Transform each normalized token independently, then add that update to the residual state.

F1=ϕ(Y~W1+b1),F1RB×L×DffF2=F1W2+b2,F2RB×L×DXout=Y+F2,XoutRB×L×D \begin{aligned} F_1 &= \phi(\widetilde{Y}W_1 + b_1), \quad F_1 \in \mathbb{R}^{B \times L \times D_{\mathrm{ff}}} \\ F_2 &= F_1W_2 + b_2, \quad F_2 \in \mathbb{R}^{B \times L \times D} \\ X_{\mathrm{out}} &= Y + F_2, \quad X_{\mathrm{out}} \in \mathbb{R}^{B \times L \times D} \end{aligned}

This is a pre-norm block: layer normalization occurs before attention and before the FFN. Some architectures use a different normalization placement or RMSNorm instead.

Why multiple heads? A single attention head can only learn one “type” of relationship between positions (e.g., syntactic adjacency). With HH heads, the model can attend to different aspects simultaneously — one head might track subject–verb agreement, another might focus on nearby tokens, yet another on coreference.

Multi-head attention repeats the attention computation for every head in every transformer layer. For example, Llama 2 7B has 32 layers with 32 attention heads per layer: at each layer, those 32 heads run in parallel on the same token sequence. Conceptually, head hh has its own learned WQ(h)W_Q^{(h)}, WK(h)W_K^{(h)}, and WV(h)W_V^{(h)}, so it forms different queries, keys, values, and attention weights from the same input.

During decoding, each head also produces its own key and value history. The layer’s KV cache keeps these separately along its head dimension: Kcache,VcacheRB×H×t×dhK_{\mathrm{cache}}, V_{\mathrm{cache}} \in \mathbb{R}^{B \times H \times t \times d_h}. Thus, the key/value vectors for one head are not shared with another head; the serving system stores the HH distinct per-head histories and reads the appropriate one when computing the next token.

4.4 The causal mask: why it matters

In autoregressive generation the model must not peek at future tokens — token ii can only attend to positions 1,,i1, \dots, i. The causal mask enforces this by adding -\infty to illegal positions before softmax, which drives those attention weights to zero.

Why is this critical?

  • Without the mask, the model could “cheat” during training by reading the token it is supposed to predict, making the learned weights useless.
  • At inference the future tokens don’t exist yet, so the mask matches reality — but it must be present during training so the model learns correct causal distributions.

The residual connection then adds the original input back: X=X+MHA(X)X' = X + \text{MHA}(X), so information is never lost. The FFN runs next (§4.5).

4.5 MLP: per-token nonlinearity

The feed-forward network (MLP) applies independently to each position:

MLP(x)=W2σ(W1x+b1)+b2 \operatorname{MLP}(x) = W_2\,\sigma(W_1 x + b_1) + b_2

where σ\sigma is often GELU or a gated variant (e.g., SwiGLU). This increases representational capacity beyond linear mixing.

4.6 FFN (feed-forward network): the “feature factory”

In many write-ups, the per-token MLP is called the FFN layer. It is conceptually simple but does a lot of the model’s nonlinear work.

Typical shape choices use an expansion factor DffDD_{\mathrm{ff}} \gg D (often Dff4DD_{\mathrm{ff}} \approx 4D):

FFN(X)=W2σ(XW1+b1)+b2 \operatorname{FFN}(X) = W_2\,\sigma(X W_1 + b_1) + b_2

with W1RD×DffW_1 \in \mathbb{R}^{D \times D_{\mathrm{ff}}} and W2RDff×DW_2 \in \mathbb{R}^{D_{\mathrm{ff}} \times D}.

Key points for inference:

  • The FFN is position-wise: it does not mix tokens (no L×LL \times L interaction). It transforms each token vector independently.
  • The compute is dominated by dense GEMMs (B×L×DB \times L \times D by D×DffD \times D_{\mathrm{ff}}, then B×L×DffB \times L \times D_{\mathrm{ff}} by Dff×DD_{\mathrm{ff}} \times D), which GPUs handle efficiently.
  • Gated variants (e.g., SwiGLU) change the exact formula but keep the same idea: expand DDffD \to D_{\mathrm{ff}}, apply a nonlinearity/gate, then project back to DD.

5. From final vectors to the next token

After NlayersN_{\mathrm{layers}} blocks, we have XoutRB×L×DX_{\mathrm{out}} \in \mathbb{R}^{B \times L \times D}. To predict the next token for one request, we use its last-position vector xLRDx_L \in \mathbb{R}^D.

Compute logits over the vocabulary:

z=xLWU+b,WURD×V z = x_L W_U + b, \quad W_U \in \mathbb{R}^{D \times \|V\|}

Many models tie weights so that WUETW_U \approx E^T.

Convert logits to probabilities with temperature TT:

p(y=vcontext)=softmax(zT)v p(y = v \mid \text{context}) = \operatorname{softmax}\left(\frac{z}{T}\right)_v

Temperature rescales every logit before softmax. At T=1T=1, the model uses its original distribution; T<1T<1 sharpens it toward high-probability tokens, making output more predictable, while T>1T>1 flattens it and makes lower-probability tokens more likely. It is useful when the application needs to trade deterministic behavior for variety without changing the model’s weights.

Then choose the next token:

  • Greedy decoding: choose argmaxvp(v)\arg\max_v p(v). This is deterministic and useful for reproducible outputs, but can sound repetitive because it always chooses the locally most likely token.
  • Top-kk sampling: retain only the kk most likely tokens, renormalize their probabilities, and sample from that set. This removes an arbitrarily long tail of implausible tokens.
  • Nucleus (top-pp) sampling: retain the smallest set of tokens whose cumulative probability is at least pp, then renormalize and sample. Unlike top-kk, the candidate-set size adapts: it grows when the model is uncertain and shrinks when it is confident.
  • Repetition penalties: lower the score of tokens that have already appeared in the generated context before sampling. They help prevent loops and copied phrases, but an overly strong penalty can make necessary names or technical terms less likely.

Generation also needs stopping controls: a maximum-token limit bounds runtime and output size; an EOS token lets the model signal completion; and a serving application can define stop sequences such as a chat delimiter to stop at a protocol boundary.

This yields a new token ID tL+1t_{L+1}, appended to the context, and the loop repeats.


6. Worked example: prefill and two-token decoding

This deliberately small model has one attention head with D=dh=2D=d_h=2 and identity projections, so Q=K=V=XQ=K=V=X. It generates two tokens after the prompt I like: first tea, then ..

Use these token vectors:

xI=[1,0],xlike=[0,1],xtea=[1,1] x_{I} = [1, 0],\quad x_{like} = [0, 1],\quad x_{tea} = [1, 1]

For the toy output projection, use the following vocabulary vectors:

etea=[0,1],e.=[1,0.4],ecoffee=[0.2,0.5] e_{tea} = [0, 1],\quad e_{.} = [1, 0.4],\quad e_{coffee} = [0.2, 0.5]

6.1 Prefill: process the prompt at once

Prefill runs the full prompt through the model in parallel. For I like, the input is:

Xprompt=[1001] X_{\mathrm{prompt}} = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}

The causal mask prevents I from seeing like, while like can see both positions. With Q=K=V=XpromptQ=K=V=X_{\mathrm{prompt}}, the masked attention scores and probabilities are:

Sprefill=12[101],Pprefill[1.000.000.330.67] S_{\mathrm{prefill}} = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & -\infty \\ 0 & 1 \end{bmatrix}, \qquad P_{\mathrm{prefill}} \approx \begin{bmatrix} 1.00 & 0.00 \\ 0.33 & 0.67 \end{bmatrix}

The final prompt position produces:

hlike=0.33[1,0]+0.67[0,1][0.33,0.67] h_{like} = 0.33\,[1, 0] + 0.67\,[0, 1] \approx [0.33, 0.67]

At the same time, prefill stores the prompt keys and values for this layer:

Kcache=Vcache=[1001] K_{\mathrm{cache}} = V_{\mathrm{cache}} = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}

Use hlikeh_{like} to score the next token:

ztea=0.67,z.0.60,zcoffee0.40 z_{tea} = 0.67,\quad z_{.} \approx 0.60,\quad z_{coffee} \approx 0.40

After softmax, tea has the highest probability, so it is the first generated token. Notice that the first generated token is selected from the last prefill output; it does not require reprocessing the prompt.

6.2 Decode: append tea and generate the second token

The next decode step processes only the newly generated token tea. Its query, key, and value are q3=k3=v3=[1,1]q_3=k_3=v_3=[1,1]. The query attends over the two cached prompt keys plus its new key:

s3=12[1,  1,  2]p3[0.248,  0.248,  0.503] s_3 = \frac{1}{\sqrt{2}}[1,\; 1,\; 2] \qquad\Longrightarrow\qquad p_3 \approx [0.248,\; 0.248,\; 0.503]

The new hidden state is the weighted value sum:

htea=0.248[1,0]+0.248[0,1]+0.503[1,1][0.751,  0.751] h_{tea} = 0.248\,[1, 0] + 0.248\,[0, 1] + 0.503\,[1, 1] \approx [0.751,\; 0.751]

Append the new key and value rather than recomputing the prompt’s entries:

Kcache=Vcache=[100111] K_{\mathrm{cache}} = V_{\mathrm{cache}} = \begin{bmatrix} 1 & 0 \\ 0 & 1 \\ 1 & 1 \end{bmatrix}

Now the same output projection selects the second generated token:

ztea0.75,z.1.05,zcoffee0.53 z_{tea} \approx 0.75,\quad z_{.} \approx 1.05,\quad z_{coffee} \approx 0.53

The period has the highest probability, so the completed generated suffix is tea .. In a real system, the period would be passed to another one-token decode step unless an EOS token or stop sequence ends generation.


7. What changes during real inference

Real deployments add a few important engineering layers:

  • KV cache: during autoregressive decoding, you don’t recompute KK and VV for all past tokens each step. You cache them per layer and only compute the new token’s projections. What that cache costs, and the attention variants built to shrink it, is the subject of the next article.
  • Batching: serving stacks batch multiple requests/tokens to use GPU efficiently.
  • Precision: inference often uses FP16/BF16, sometimes INT8/FP8 quantization.
  • Decoding policy: sampling settings (temperature, top-pp, repetition penalties) strongly affect outputs without changing model weights.

8. KV caching: decoding as append-only attention

The key observation is that in autoregressive decoding, at step tt you only need one new query (for the new position) but you need keys/values for all positions so far.

For each layer \ell, maintain cached matrices:

Kcache(),Vcache()RB×H×t×dh K^{(\ell)}_{\mathrm{cache}}, V^{(\ell)}_{\mathrm{cache}} \in \mathbb{R}^{B \times H \times t \times d_h}

When a new token arrives, you compute only its projections and append:

kt=xtWK,  vt=xtWVKcache[Kcache;kt],  Vcache[Vcache;vt] k_t = x_t W_K,\; v_t = x_t W_V\quad\Rightarrow\quad K_{\text{cache}} \leftarrow [K_{\text{cache}};\, k_t],\; V_{\text{cache}} \leftarrow [V_{\text{cache}};\, v_t]

Then compute the new query qt=xtWQq_t = x_t W_Q and attend over the cached keys/values:

αt=softmax(qt(Kcache)Tdh+Mt)RB×H×1×t,ht=αtVcacheRB×H×1×dh \alpha_t = \operatorname{softmax}\left(\frac{q_t (K_{\mathrm{cache}})^T}{\sqrt{d_h}} + M_t\right) \in \mathbb{R}^{B \times H \times 1 \times t},\quad h_t = \alpha_t V_{\mathrm{cache}} \in \mathbb{R}^{B \times H \times 1 \times d_h}

Here MtM_t is the (trivial) causal mask for this single query row; in practice you just ensure the new token cannot attend to “future” positions (which do not exist yet).

8.1 ASCII timeline

Prefill (prompt length = L): compute all layers on all tokens

tokens:   [ 1   2   3  ...  L ]
cache K:  [k1  k2  k3  ...  kL]
cache V:  [v1  v2  v3  ...  vL]

Decode step L+1 (generate one token): compute only the new token’s q,k,v

new token:                  [ L+1 ]
append K:   [k1  k2  ...  kL | k(L+1)]
append V:   [v1  v2  ...  vL | v(L+1)]

attention for the new token:
q(L+1)  x  [k1..k(L+1)]^T  -> weights over positions 1..L+1  -> weighted sum of [v1..v(L+1)]

8.2 Why it matters

  • Compute: without caching, each new token would re-run attention over all previous tokens and recompute their K,VK,V repeatedly. With KV caching, you reuse K,VK,V and only do the “new query against all cached keys” work.
  • Asymptotics: prompt processing (“prefill”) is still quadratic in prompt length due to full self-attention, but token-by-token decoding avoids recomputing past projections and is much closer to linear-in-context per generated token.
  • Memory: caching costs memory proportional to context length, number of layers, and number of heads (this is often the main throughput limiter at long context).

9. What’s next

Now that you know what each inference step computes, see Intel TPP for LLM Inference for a deep dive into how these operations are accelerated on Intel CPUs — fused GEMM kernels, blocked weight layouts, flash attention with BRGEMM, and OpenMP parallelism.