Autoregressive Generation › The Generation Procedure
Language AI HandbookAll chapters Autoregressive Generation › The Generation ProcedureOne section · Sections in this chapter2 / 12 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Autoregressive generation follows a straightforward principle: at each step, the model predicts a probability distribution over possible next tokens, selects one token from that distribution, appends it to the sequence, and repeats until some stopping condition is met. This loop is deceptively simple to describe but rich with design decisions. Which token do you select from the distribution? Do you always take the highest-probability token, or do you sample? How do you decide when to stop? Each of these choices substantially affects text quality and diversity, as well as coherence.
The generation procedure has two distinct phases. The first is the prefill phase, where the model processes the entire input prompt in parallel. Because all prompt tokens are known in advance, the transformer can compute all their representations simultaneously, taking advantage of the GPU's parallel processing capabilities. This phase is relatively fast even for long prompts. The second is the decode phase, where the model generates new tokens one at a time. Each new token depends on all previous tokens, so this phase is inherently sequential: you cannot compute token until you have committed to token . This sequential bottleneck is what makes inference fundamentally different from training, and it is the primary engineering challenge in building fast language model serving systems.
Think of the prefill/decode split as analogous to reading a document versus dictating a response. Reading (prefill) is fast because your eyes can scan the entire page at once. Dictating (decode) is slower because each word must be chosen and spoken before you can choose the next. No matter how fast your thinking, you are bounded by the sequential nature of speech. Similarly, no matter how much GPU parallelism is available, decode is bounded by its step-by-step dependency structure.
Autoregressive generation produces sequences token-by-token, where each new token is conditioned on all previously generated tokens. The probability of generating a complete sequence factorizes as , meaning we multiply together the probability of each token given everything that came before it.
The process begins with a prompt, a sequence of tokens provided by the user. The model processes this prompt to produce hidden states, then uses the final hidden state to predict the first generated token. This token is appended to the prompt, the model processes the extended sequence, and the cycle continues. Each iteration of this loop produces exactly one new token. The model does not "plan ahead" or "commit" to a sentence structure in advance; it simply picks the locally most reasonable next token given everything so far. This myopic, one-step-at-a-time nature is both a feature and a limitation: it keeps generation computationally tractable, but it means the model can back itself into corners that would require backtracking to escape.
def greedy_generate(model, tokenizer, prompt: str, max_new_tokens: int = 50):
"""
Generate text using greedy decoding (always pick the most likely token).
This is the simplest generation strategy: at each step, select the token
with the highest probability.
"""
# Encode the prompt
input_ids = tokenizer.encode(prompt, return_tensors="pt")
# Generate tokens one at a time
for _ in range(max_new_tokens):
# Forward pass through the model
with torch.no_grad():
outputs = model(input_ids)
logits = outputs.logits
# Get logits for the last position
next_token_logits = logits[:, -1, :]
# Greedy selection: pick the highest probability token
next_token_id = next_token_logits.argmax(dim=-1, keepdim=True)
# Append to the sequence
input_ids = torch.cat([input_ids, next_token_id], dim=1)
# Check for end of sequence
if next_token_id.item() == tokenizer.eos_token_id:
break
# Decode and return the generated text
return tokenizer.decode(input_ids[0], skip_special_tokens=True)This implementation reveals several important aspects of generation. First, the model produces logits (unnormalized log-probabilities) for every position in the vocabulary at once. We only use the logits at the final position since that's where the next token prediction happens. Second, greedy decoding, which always selects the most probable token, is deterministic: the same prompt always produces the same output. Third, the loop continues until we either reach max_new_tokens or generate an end-of-sequence token.
Prompt: The future of artificial intelligence Generated: The future of artificial intelligence is uncertain. "We're not sure
The generated continuation flows naturally from the prompt. This shows that the model has learned meaningful language patterns. Notice how each word follows logically from the previous context. Greedy decoding often produces repetitive or generic text because it always takes the safe, high-probability path, converging on the same safe completions regardless of creative variation. For a prompt like "The future of artificial intelligence", greedy decoding will reliably produce something factual and generic because those patterns dominate the training distribution. We'll explore more sophisticated sampling strategies in subsequent chapters on temperature, top-k sampling, and nucleus sampling. For now, the main point is that all generation strategies share the same underlying loop: predict, select, append, repeat. The strategies differ only in how they implement the "select" step.

The Generation Loop in Detail
Let's trace through exactly what happens at each step of generation. The mechanics here are worth understanding precisely because they reveal both why generation works and why it is expensive. Consider a simple 4-token vocabulary and a prompt of 3 tokens:
Each iteration of the loop performs a complete forward pass through the transformer. The input is the entire sequence assembled so far, including both the original prompt tokens and any tokens generated in previous iterations. The model computes representations for every position using multi-head self-attention, propagates those representations through feed-forward layers, and finally uses the representation at the last position to produce a distribution over the next token. The representations at all other positions are computed too, even though we only care about the last one. This redundancy, computing representations for positions whose outputs we discard, is precisely the inefficiency that key-value caching eliminates.
It is worth pausing to appreciate what "conditioned on all previous tokens" means computationally. The attention mechanism at each layer allows every position to attend to every earlier position. This means that when predicting token , the model can draw on information from any token in the prompt or any previously generated token. The representation of the current position is not a fixed embedding; it is a function of the entire preceding context, computed fresh at every generation step. This is what allows transformers to maintain long-range coherence: they do not rely on a compressed "summary state" of the past (as RNNs do) but instead directly access any prior position through attention.

At each step, the model must process the entire sequence to maintain the correct attention patterns. The attention mechanism at position computes weighted sums over all positions , so earlier tokens influence later predictions. This creates a computational challenge: as the sequence grows, each forward pass becomes more expensive. A sequence that started at 10 tokens will be 60 tokens long after 50 generation steps, and each of those 60-token forward passes is approximately 6 times more expensive than the original 10-token pass. The total work grows quadratically, not linearly, with the number of tokens generated.
This diagram illustrates why the model is "coherent": at step 3, when predicting whether to generate "mat", "floor", or "bed", the attention mechanism at the final position can directly inspect "sat" (the verb that demands a landing place) and "The cat" (the subject that constrains the space). The generated context is not compressed or summarized; it is fully accessible. This direct access to all prior context is what distinguishes transformer-based generation from RNN-based generation, where the prior context is compressed into a fixed-size hidden state that loses information over long distances.
Mathematical Formulation
The generation loop we've been exploring raises a basic question: how do we assign a probability to an entire sequence of tokens? Consider the sentence "The cat sat on the mat". It would be impractical to maintain a probability table for every possible sentence, as natural language allows infinitely many valid sequences. Instead, we need a way to decompose sequence probability into manageable pieces.
From Intuition to the Chain Rule
Think about how you might estimate the likelihood of a sentence. You wouldn't evaluate it as a monolithic unit. Instead, you'd naturally consider: How likely is "The" as an opening word? Given that we started with "The", how likely is "cat" to follow? Given "The cat", how likely is "sat"? This intuitive approach of building probability step by step is exactly what autoregressive models formalize.
The mathematical foundation comes from the chain rule of probability, which states that any joint probability can be decomposed into a product of conditional probabilities. For a sequence of tokens, this gives us the autoregressive factorization:
where:
- : the token at position in the sequence
- : the total sequence length (number of tokens)
- : the conditional probability of token given all preceding tokens, often written as for brevity
- : the product over all positions from 1 to
This factorization is powerful because it turns the impossible task of modeling all possible sentences into a tractable one: we only need to model what comes next given what we've seen so far. Each factor in the product corresponds to exactly one step of the generation loop.
A Concrete Example
Let's trace through the factorization for our example sentence "The cat sat":
-
First token: asks how likely "The" is as an opening. Common sentence starters like "The", "A", "I" receive high probability.
-
Second token: asks what noun is likely after "The". Words like "cat", "dog", "man", "house" all receive some probability mass.
-
Third token: asks what action a cat might perform. "sat", "jumped", "ran", "slept" become likely candidates.
The full sequence probability is the product: . If any factor is near zero, the entire product collapses. This reflects that an unlikely transition makes the whole sequence improbable.
This explains a key challenge in evaluating language models: sequence probabilities get very small very quickly. A sequence of 10 tokens, each with conditional probability 0.1, has a joint probability of . This is why language models are typically evaluated using log-probability (summing log-probabilities rather than multiplying probabilities) and perplexity (the geometric mean inverse probability per token). These metrics avoid numerical underflow and give interpretable numbers even for long sequences.

From Hidden States to Probabilities
Now we need to understand how the model computes each conditional probability . This happens in two stages:
-
Encode the context: The transformer processes all tokens to produce a hidden state that summarizes the relevant context.
-
Project to vocabulary: A linear layer maps this hidden state to a score for every token in the vocabulary, then softmax converts these scores to probabilities.
The formula for this computation is:
where:
- : the hidden state vector at position from the final transformer layer, where is the hidden dimension (e.g., 768 for GPT-2 Small). This vector encodes everything the model "knows" about the context so far.
- : the language modeling head weight matrix that projects from hidden dimension to vocabulary size . Each row of this matrix can be thought of as a "template" for one vocabulary token.
- : the bias term (often zero or absent in modern implementations), which captures token-independent frequency preferences.
- : the probability assigned to token after normalization.
The product computes a score (logit) for each vocabulary token by measuring how similar the current context representation is to each token's template. Higher scores indicate more likely next tokens. The rows of can be thought of as "prototype vectors" for each vocabulary token: a token is predicted as likely when the current context representation aligns well (high dot product) with that token's prototype. This geometric intuition explains why related tokens often have similar logit scores: their prototype vectors point in similar directions in the hidden space.
The softmax function then performs two important operations:
- Exponentiation () ensures all values become positive
- Normalization (dividing by the sum) ensures probabilities sum to 1
Why does this formula make sense? Notice that exponentiation preserves the relative ordering of logits: if token A has a higher logit than token B, it will also have a higher probability after softmax. The exponential amplifies differences, so a logit advantage of 2.0 translates to roughly times the probability, rather than only twice the probability. This amplification is what allows the model to express strong preferences for particular tokens even when multiple tokens have plausible logits.


Why Generation Must Be Sequential
This mathematical structure reveals a basic constraint: we cannot parallelize generation across positions. To compute , the model must know tokens , because the transformer's causal attention mask prevents each position from seeing future tokens. The causal mask is not an arbitrary restriction imposed to make training efficient; it is the architectural guarantee that makes autoregressive generation self-consistent. If position 5 could attend to position 8 during generation, it would need to know what token occupies position 8 before that token has been chosen, creating a logical impossibility.
Concretely, if we want to predict the 10th token:
- We need , the hidden state at position 10
- Computing requires attention over positions 1 through 9
- But positions 1 through 9 must contain actual tokens, not placeholders
- Therefore, we must have already generated tokens 1 through 9
This sequential dependency is what makes autoregressive generation inherently step-by-step. Unlike tasks like classification where we process the entire input in parallel, generation must proceed one token at a time, with each new token depending on all previous decisions. This is a basic difference from training: during training, the target tokens are all known in advance, so the causal mask allows all positions to be computed simultaneously. During inference, each position's token is unknown until the model produces it, forcing strict left-to-right ordering. This training-inference asymmetry is one of the key reasons why training transformers is highly parallelizable while inference is not.
You might wonder whether there is some clever way around this constraint. Researchers have explored non-autoregressive generation, where all output tokens are predicted simultaneously, and masked diffusion models that iteratively refine a full-length output. These approaches trade off quality and flexibility for speed. In practice, for tasks requiring high-quality coherent text, autoregressive generation remains the dominant approach because the conditioning on previous tokens is precisely what ensures each new token fits naturally into the evolving context.
