Lesson 14 of 16
Section 4: Training, Generation & Deployment35 min read & walkthrough

Autoregressive Sampling: Greedy vs Temperature Scaling

01 / TEMPERATURE
Logits / T

Low T (<1.0) sharpens peaks; High T (>1.0) flattens for creativity.

02 / GREEDY
Argmax Sampling

Selects the single most probable token; prone to repetitive loops.

03 / MULTINOMIAL
Probabilistic Choice

Samples from the normalized Softmax probability distribution using PyTorch torch.multinomial.

The Big Idea

Now that our GPT model is trained from Lesson 4.1, how do you make it generate an original story? You provide a seed prompt (e.g. 'ROMEO:'), feed it through the network, and inspect the predicted scores for the next character.

The simplest strategy is 'greedy decoding': always pick the single token with the highest logit (argmax). While this sounds logical, greedy decoding is notoriously flawed: it gets trapped in sterile, repetitive infinite loops ('The King said the King said the King said...').

To introduce creativity, we divide raw logits by a positive number called Temperature (T) before applying softmax, as detailed in temperature-scaled Softmax. A low temperature (T = 0.2) sharpens differences, making the top choice 98% likely (conservative and factual). A high temperature (T = 1.5) flattens probabilities, giving surprising tokens a fighting chance (creative and poetic). We sample a token using torch.multinomial, append it to our sequence, crop to our context window, and repeat!

💡

Jargon Buster: Plain-English Translations

  • Greedy Decoding: Always choosing the token with the highest predicted probability (argmax).
  • Temperature: A hyperparameter that scales the sharpness or flatness of the probability distribution before sampling.
  • Multinomial Sampling: Drawing a random sample from a categorical probability distribution where each candidate has an exact percentage chance.
  • Context Window Cropping: Slicing the input sequence (idx[:, -block_size:]) so it never exceeds the maximum positional embedding length.

Architecture Blueprint & Tensor Mechanics

The autoregressive generation loop crops incoming context to block_size, divides final-step logits by temperature, and samples via torch.multinomial.

import torch
import torch.nn.functional as F

@torch.no_grad()
def generate(model, idx, max_new_tokens, block_size, temperature=0.8):
    model.eval()
    for _ in range(max_new_tokens):
        # 1. Crop current context to the maximum block_size
        idx_cond = idx if idx.size(1) <= block_size else idx[:, -block_size:]
        
        # 2. Get forward predictions
        logits, _ = model(idx_cond)
        
        # 3. Focus solely on the final time step and apply temperature
        logits = logits[:, -1, :] / max(temperature, 1e-5)
        
        # 4. Convert logits to probabilities
        probs = F.softmax(logits, dim=-1)
        
        # 5. Sample from the probability distribution
        idx_next = torch.multinomial(probs, num_samples=1)
        
        # 6. Append sampled token to the running sequence
        idx = torch.cat((idx, idx_next), dim=1)
        
    return idx

# Demonstration with dummy model
# prompt = torch.tensor([[10, 24, 32]], dtype=torch.long) # Encoded prompt
# out_tokens = generate(model, prompt, max_new_tokens=100, block_size=256, temperature=0.8)
# print("Generated text:", decode(out_tokens[0].tolist()))
⚠️

Beginner Traps & Gotchas

1. Setting temperature to 0.0 without a guard check

The Gotcha: Dividing logits by zero (logits / 0.0) yields NaN and Inf values, crashing the sampling step.

The Fix: Clamp temperature with max(temperature, 1e-5) or add an explicit branch for temperature == 0 that uses torch.argmax.

2. Forgetting to crop context sequence as generation length grows

The Gotcha: Generating 500 new tokens without cropping causes idx length to exceed block_size, triggering an IndexError in positional embeddings.

The Fix: Always crop context to the last block_size tokens before the forward pass: idx_cond = idx[:, -block_size:].