Temperature and Sampling, Demystified

AILLMSamplingInference
Share on LinkedIn

You set temperature to 0.7 because the blog post said to. Your chatbot gives different answers to the same question. Your extraction pipeline occasionally returns malformed JSON. Your creative writing feature produces the same three opening sentences repeatedly.

Temperature and sampling parameters control how the model chooses the next token from its probability distribution. They are not magic knobs — they are precise mathematical operations on logits. Understanding what they do lets you pick the right settings per task instead of copying defaults.

From logits to tokens

At each generation step, the model outputs a logit vector — one score per token in the vocabulary (50,000–128,000 entries). Sampling converts logits to a token:

logits → softmax(temperature) → filter (top-k/top-p) → sample → next token

Every parameter in that pipeline changes which tokens are eligible and how likely each is.

Temperature: sharpening or flattening the distribution

Temperature divides logits before softmax:

import torch
import torch.nn.functional as F

logits = model_output  # shape: [vocab_size]

def sample_with_temperature(logits, temperature=1.0):
    scaled = logits / temperature
    probs = F.softmax(scaled, dim=-1)
    return torch.multinomial(probs, num_samples=1)

# temperature = 0.01 ≈ greedy (always picks highest)
# temperature = 1.0  = model's natural distribution
# temperature = 2.0  = flatter, more random
Temperature Effect Use case
0.0 Greedy — always highest probability token Extraction, SQL, classification, code
0.1–0.3 Low randomness, mostly deterministic Factual Q&A, summarization
0.5–0.7 Moderate creativity Chatbots, general assistants
0.8–1.0 High diversity Creative writing, brainstorming
> 1.0 Very flat distribution, often incoherent Rarely useful in production

At temperature 0, "The capital of France is" almost always produces " Paris". At temperature 1.5, it might produce " Lyon", " a city", or " located".

Top-k: hard cap on candidates

Top-k keeps only the K highest-probability tokens and zeroes the rest:

def top_k_filter(logits, k=40):
    top_k_values, top_k_indices = torch.topk(logits, k)
    filtered = torch.full_like(logits, float('-inf'))
    filtered.scatter_(0, top_k_indices, top_k_values)
    return filtered

With k=40, the model samples only from the 40 most likely tokens. This prevents low-probability tail tokens (which cause hallucinations and incoherence) from being selected.

Typical values: k=40–50 for general use, k=1 equivalent to greedy.

Top-p (nucleus sampling): adaptive filtering

Top-p selects the smallest token set whose cumulative probability exceeds p:

def top_p_filter(logits, p=0.9):
    sorted_logits, sorted_indices = torch.sort(logits, descending=True)
    cumulative_probs = torch.cumsum(F.softmax(sorted_logits, dim=-1), dim=-1)

    # Remove tokens beyond the nucleus
    sorted_indices_to_remove = cumulative_probs > p
    sorted_indices_to_remove[1:] = sorted_indices_to_remove[:-1].clone()
    sorted_indices_to_remove[0] = False

    filtered = logits.clone()
    filtered[sorted_indices[sorted_indices_to_remove]] = float('-inf')
    return filtered

When the model is confident (one token at 95%), top-p=0.9 might keep only 2–3 tokens. When uncertain (flat distribution), it might keep 500+. This adaptiveness is why top-p generally outperforms fixed top-k.

Most production configs use top-p=0.9 or top-p=0.95 combined with temperature.

Frequency and presence penalties

These modify logits based on tokens already generated:

# Conceptual: reduce logit of tokens that already appeared
for token_id in generated_tokens:
    if token_id in logits:
        count = generated_tokens.count(token_id)
        logits[token_id] -= frequency_penalty * count  # per occurrence
        logits[token_id] -= presence_penalty              # flat penalty if seen

Use frequency penalty 0.3–0.6 when the model repeats phrases in long outputs. Avoid high penalties on extraction tasks — they can suppress valid repeated field names.

Recommended settings by task

TASK_CONFIGS = {
    "extraction": {"temperature": 0.0, "top_p": 1.0},
    "classification": {"temperature": 0.0, "top_p": 1.0},
    "code_generation": {"temperature": 0.2, "top_p": 0.95},
    "summarization": {"temperature": 0.3, "top_p": 0.9},
    "chatbot": {"temperature": 0.7, "top_p": 0.9},
    "creative_writing": {"temperature": 0.9, "top_p": 0.95, "frequency_penalty": 0.5},
}

Do not tune these in isolation. Temperature and top-p interact — low temperature with low top-p is nearly greedy; high temperature with high top-p produces chaos.

Reproducibility in production

For deterministic pipelines:

# OpenAI
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[...],
    temperature=0,
    seed=42,  # fixed seed for reproducibility
)

# vLLM
params = SamplingParams(temperature=0.0, seed=42)

Log the full sampling configuration with every request. When output quality shifts after a model update, you need to know exactly which parameters were used to isolate whether the model or the settings changed.

Common production mistakes

Teams get temperature sampling explained wrong in predictable ways:

LLM features around temperature sampling explained break in production when prompts assume deterministic output, context windows are sized for dev datasets, or token costs are never budgeted per user session. Always log prompt hash, model version, and latency—not raw prompts with PII.

Resources

Frequently asked questions

Should I always use temperature 0 for deterministic output?

Temperature 0 (greedy decoding) picks the highest-probability token at each step. Use it for extraction, classification, code generation, and any task where consistency matters. It can produce repetitive text in open-ended generation because the model never explores alternative phrasings.

What is the difference between top-p and top-k?

Top-k limits sampling to the K highest-probability tokens (e.g., top 40). Top-p (nucleus sampling) dynamically selects the smallest set of tokens whose cumulative probability exceeds P (e.g., 0.9). Top-p adapts to the distribution shape — narrow when the model is confident, wide when uncertain.

Why does my output change between identical requests at temperature 0?

True greedy decoding at temperature 0 should be deterministic on the same hardware and software version. Non-determinism usually comes from floating-point operation ordering in batched inference, different CUDA kernels, or the provider not actually using temperature 0 internally. Set a fixed seed where supported for reproducibility.

Hiring a senior Android / Flutter engineer?

I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.

Get in touch →