Inference and Decoding

Inference uses a trained model with frozen weights; decoding decides how each next-token distribution becomes actual text.

The Big Question

By this point, training is finished. Pretraining gave the model broad language ability. Fine-tuning taught assistant behavior. Preference optimisation made preferred responses more likely than rejected ones.

How does a trained language model turn a probability distribution into an actual response?

Given a prompt, the model produces probabilities over vocabulary tokens. It has not yet written a response. Something must choose one token, append it to the context, and repeat the process. That decision process is decoding.

Possible next tokenProbability
Try0.31
I0.18
A0.15
You0.12
Cheddar0.07
all other tokens0.17

Inference is the full process of using the trained model to generate outputs. Decoding is the part of inference that chooses tokens from model probabilities.

Core Intuition

During training, gradient descent changes the model parameters. During inference, the weights are frozen. The model does not call loss.backward(), does not calculate parameter gradients, and does not learn from the current conversation just because it has seen it.

pθ(xtx<t)p_\theta(x_t\mid x_{<t})

The subscript θ\theta remains because the probabilities depend on learned weights. But θ\theta no longer changes.

Autoregressive Generation

Most LLMs generate autoregressively: each new token depends on the tokens before it. If the model chooses cheese after The best burger topping is, the future context is different than if it chose onion or subjective.

pθ(x1,,xT)=t=1Tpθ(xtx<t)p_\theta(x_1,\ldots,x_T)=\prod_{t=1}^{T}p_\theta(x_t\mid x_{<t})

During training, the next tokens come from the dataset. During inference, the model must live with its own choices. One early token can change every later probability distribution.

Interactive Demo

Decoding playground

Prompt and generated tokens

User: What should I put on a cheeseburger?
Assistant: Try
TokenLogitProbabilityAfter filters
cheddar4.20.459
0.509
pickles3.50.211
0.234
onion3.10.135
0.150
mustard2.80.097
0.107
lettuce2.10.044
0.000
bacon1.80.032
0.000
tomato1.40.020
0.000
quantum-0.80.002
0.000
Current decision
cheddar

Temperature reshapes probabilities. Top-k caps candidate count. Top-p keeps enough probability mass. The decoder then selects one token and appends it to the context.

Greedy trap

a side0.135

Greedy starts here because a has the highest first-step probability.

crispy fries0.320

This full sequence wins even though crispy was not the top first token.

fresh salad0.098

Plausible, but lower total probability in this toy tree.

KV cache intuition

Naive next step
reprocess all tokens
With cache
process 1 new token

The cache stores previous keys and values. It speeds up decoding but grows with context length and active requests.

Adjust the decoding controls. Notice that decoding changes selection behavior, not the model weights or the knowledge stored in the model.

Logits, Softmax, And Sequence Scores

The transformer usually outputs logits first: one real-valued score per vocabulary token.

z=[z1,z2,,zV]\mathbf{z}=[z_1,z_2,\ldots,z_V]

Logits are not probabilities. They can be negative and do not need to add to one. Softmax converts them into a probability distribution:

pi=ezij=1Vezjp_i=\frac{e^{z_i}}{\sum_{j=1}^{V}e^{z_j}}

For logits [2,1,0][2,1,0], exponentiating gives [7.39,2.72,1][7.39,2.72,1]. The total is 11.1111.11, so the probabilities are approximately:

[0.665,  0.245,  0.090][0.665,\;0.245,\;0.090]

A response contains many tokens, so its probability is a product of conditional probabilities. Systems usually work with log probabilities because products become sums:

logpθ(yx)=t=1Tlogpθ(ytx,y<t)\log p_\theta(y\mid x)=\sum_{t=1}^{T}\log p_\theta(y_t\mid x,y_{<t})

Decoding Strategies

MethodRuleUseful forRisk
GreedyChoose the highest-probability tokenStable extraction, narrow tasksCan be bland or locally suboptimal
SamplingDraw from the probability distributionConversation, brainstorming, varied outputsCan select bad low-probability tokens
TemperatureSharpen or flatten logits before samplingControl randomnessDoes not add knowledge or reorder tokens
Top-kKeep the k most likely tokensRemove long-tail weirdnessFixed candidate count cannot adapt to uncertainty
Top-pKeep enough tokens to reach cumulative probability pAdaptive nucleus samplingOften misunderstood as an individual threshold
Beam searchTrack several high-scoring sequencesTranslation, constrained generationCan prefer generic high-probability text

Greedy Decoding

Greedy decoding chooses the highest-probability token at every step:

xt=argmaxvVpθ(vx<t)x_t=\arg\max_{v\in\mathcal{V}}p_\theta(v\mid x_{<t})

It is deterministic, cheap, and easy to understand. But it chooses the best local token, not necessarily the best full sequence. A first token with slightly lower probability may lead to a much stronger continuation.

Sampling

Sampling draws from the probability distribution. A token with probability 0.300.30 appears about 30 percent of the time across many generations. Sampling preserves uncertainty and variation, but unrestricted sampling can select strange low-probability tokens from the long tail.

Temperature

Temperature reshapes logits before softmax:

pi(T)=ezi/Tj=1Vezj/Tp_i(T)=\frac{e^{z_i/T}}{\sum_{j=1}^{V}e^{z_j/T}}

Low temperature sharpens the distribution and makes the top token more dominant. High temperature flattens the distribution and gives lower-probability tokens more chance. Temperature does not change token rankings for positive TT. It changes the gaps, not the order.

Top-k And Top-p

Top-k keeps the kk highest-probability tokens and renormalises within that set. Top-p, or nucleus sampling, keeps the smallest set of highest-probability tokens whose cumulative probability reaches at least pp.

Top-p is not an individual token threshold. With p=0.9p=0.9, we do not require every token to have probability above 0.9. We keep tokens until their total probability mass reaches 0.9.

Beam Search

Beam search keeps several candidate sequences at once. Beam width B=1B=1 is greedy search. Larger beams retain more possible continuations.

Beam search compares accumulated log probabilities, often with length normalisation:

score(y)=logpθ(yx)yα\text{score}(y)=\frac{\log p_\theta(y\mid x)}{|y|^\alpha}

Without length correction, raw probabilities can prefer short outputs because every extra token multiplies by another probability less than or equal to one.

Stopping And Repetition Controls

Generation stops when the model selects an end-of-sequence token, reaches a maximum token limit, emits a stop string, calls a tool, or is interrupted by an external controller.

Repetition controls modify logits to discourage repeated tokens. A presence penalty depends on whether a token appeared at all:

zi=ziλp1[ci>0]z_i'=z_i-\lambda_p\mathbf{1}[c_i>0]

A frequency penalty depends on how often it appeared:

zi=ziλfciz_i'=z_i-\lambda_f c_i

These controls can reduce loops, but aggressive penalties can damage legitimate repetition. They change generation behavior; they do not give the model deeper understanding.

The Inference Loop

A simplified autoregressive generation loop looks like this:

tokens = tokenizer.encode(prompt)

for _ in range(max_new_tokens):
    logits = model(tokens)
    next_token_logits = logits[-1]
    probabilities = softmax(next_token_logits)
    next_token = decode(probabilities)
    tokens.append(next_token)

    if next_token == eos_token_id:
        break

response = tokenizer.decode(tokens)

The model usually produces logits for every input position. For generation, we care about the final position because it predicts the next token.

KV Cache

A naive inference loop recomputes old tokens again and again. After generating one new token, the earlier prompt tokens have not changed, but the model would still recompute their attention keys and values.

In self-attention:

Q=XWQK=XWKV=XWVQ=XW_Q\qquad K=XW_K\qquad V=XW_V
Attention(Q,K,V)=softmax(QKdk)V\operatorname{Attention}(Q,K,V)=\operatorname{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)V

During autoregressive decoding, previous keys and values remain useful to future tokens. The KV cache stores them for each layer. A new token computes its new query, key, and value, then attends to cached keys and values.

KV caching speeds decoding, but it costs memory:

KV memoryL×T×Hkv×dh×2\text{KV memory}\propto L\times T\times H_{kv}\times d_h\times 2

Longer context and more simultaneous users mean larger caches. KV caching should not intentionally change the answer. It changes how efficiently the same logits are computed.

Serving Performance

PrefillProcess the whole prompt and fill the KV cacheLong prompts increase time to first token
DecodeGenerate one new token at a timeLong answers increase time per response
KV cacheReuse previous attention keys and valuesTrades less recomputation for more memory
BatchingServe multiple requests togetherImproves throughput, but scheduling becomes harder
StreamingDisplay tokens as they arriveImproves perceived responsiveness, not necessarily compute speed
Speculative decodingUse a small draft model and large target verifierCan speed generation when draft acceptance is high

Speculative decoding uses a small draft model to propose several tokens and a larger target model to verify them. If many draft tokens are accepted, the system advances several positions after one target-model verification step. If the draft model is often wrong, the overhead can erase the speed-up.

Latency and throughput are different. Latency is how long one request waits. Throughput is how much total work the system completes over time. Batching and continuous batching improve hardware utilisation, but they are serving strategies, not changes to the model's conceptual generation process.

What Decoding Cannot Fix

Decoding chooses from the probabilities the model supplies. If the model assigns almost no probability to the correct answer, sampling controls may not rescue it. A sensible top-k or top-p filter may remove the correct token entirely.

Determinism is not truth. Greedy decoding can produce the same false answer every time. Randomness is not creativity. High temperature can produce surprising text, but it can also produce nonsense.

Decoding is a control surface. It is not a replacement for better training, better context, retrieval, tools, or evaluation.

Implementation

Greedy Decoding

import torch

def greedy_next_token(logits: torch.Tensor) -> int:
    if logits.ndim != 1:
        raise ValueError("Expected one-dimensional logits.")
    return int(torch.argmax(logits).item())

Softmax is not required before argmax because softmax preserves ranking.

Top-p Sampling

def sample_top_p(logits, p=0.9, temperature=1.0):
    adjusted_logits = logits / temperature
    probabilities = torch.softmax(adjusted_logits, dim=-1)

    sorted_probs, sorted_indices = torch.sort(
        probabilities,
        descending=True,
    )
    cumulative = torch.cumsum(sorted_probs, dim=-1)

    remove_mask = cumulative > p
    remove_mask[1:] = remove_mask[:-1].clone()
    remove_mask[0] = False

    filtered = sorted_probs.masked_fill(remove_mask, 0.0)
    filtered = filtered / filtered.sum()

    sampled_position = torch.multinomial(filtered, num_samples=1)
    return int(sorted_indices[sampled_position].item())

The mask shift keeps the first token that crosses the cumulative threshold, so the retained set actually reaches the requested probability mass.

Generation Function

@torch.inference_mode()
def generate(model, input_ids, eos_token_id, max_new_tokens=100):
    generated = input_ids.clone()

    for _ in range(max_new_tokens):
        outputs = model(input_ids=generated)
        next_token_logits = outputs.logits[0, -1]
        next_token_id = sample_top_p(
            next_token_logits,
            p=0.9,
            temperature=0.8,
        )
        next_token = torch.tensor(
            [[next_token_id]],
            dtype=generated.dtype,
            device=generated.device,
        )
        generated = torch.cat([generated, next_token], dim=-1)

        if next_token_id == eos_token_id:
            break

    return generated

@torch.inference_mode() tells PyTorch not to track gradients. Production systems also add KV caching, batching, streaming, mixed precision, memory management, and request scheduling.

Interview Discussion

What is the difference between inference and decoding?

Inference is the full process of using the trained model. Decoding is the token selection rule applied to next-token probabilities.

Why can greedy decoding be globally suboptimal?

It chooses the best local next token, even if another first token leads to a higher-probability full sequence.

Why does top-p adapt better than top-k?

Top-p changes the number of retained tokens depending on how concentrated the probability distribution is.

What does the KV cache store?

It stores previous attention keys and values for each layer so future tokens can reuse them.

How does speculative decoding speed generation?

A fast draft model proposes tokens, and the target model verifies several positions at once. Accepted tokens advance generation faster.

Active Recall

1. What changes during training that remains fixed during inference?

2. What is the difference between a logit and a probability?

3. Why can each generated token affect every later token?

4. What token does greedy decoding select?

5. What does low temperature do to the distribution?

6. How does top-p choose its candidate set?

7. Why does beam search often need length normalisation?

8. What does the KV cache save, and what does it cost?

9. What is the difference between prefill and decode?

10. Why can deterministic decoding still hallucinate?

Common Mistakes

  • Saying the model outputs words. It outputs logits over tokens; the decoder selects token IDs; the tokenizer converts IDs back into text.
  • Thinking temperature changes the model. It only transforms logits during decoding.
  • Thinking top-p keeps tokens with individual probability above p. It keeps cumulative probability mass.
  • Thinking beam search always produces better open-ended text. High model probability can be generic or repetitive.
  • Thinking the KV cache stores permanent knowledge. It stores temporary keys and values for the current sequence.

Connection To Generalisation

We now understand the complete path from text to response: tokenisation, token IDs, embeddings, transformer layers, logits, softmax probabilities, decoding, selected token, KV cache update, and repetition until stopping.

But understanding how a model works is not enough. Before adapting it, wrapping it in products, or deploying it, we need to ask whether it generalises, how to evaluate it, and how to choose between alternatives. That begins the Engineering Intelligence act.