datarekha

Sampling — temperature, top-k, top-p

How the model actually picks the next token from its probability distribution, and when to use which sampling strategy in production.

6 min read Intermediate Generative AI Lesson 3 of 63

What you'll learn

  • What greedy, temperature, top-k, and top-p sampling actually do
  • Why temperature=0 is not truly deterministic
  • Which sampling setting to pick for structured outputs vs creative writing

Before you start

The model outputs a probability distribution over ~100k tokens. Sampling is the rule for picking one. This is the dial you tune to trade off between “predictable and correct” and “creative and varied”.

TrySampling · temperature, top-k, top-p

Reshape the distribution, then draw a token

The prompt The weather today is ___ has these ~10 plausible next tokens. Each knob just reshapes the probabilities before you sample: temperature scales, top-k truncates by count, top-p truncates by mass.

Temperature T1.00
peakyrandom
k = 5
keep 5 tallestby count
p = 0.90
cumulative ≥ 0.90by mass
sunny33.3%
cloudy20.2%
nice13.5%
warm10.0%
rainy8.2%
cold5.5%
mild3.7%
hot2.7%
windy1.8%
terrible1.1%
10 of 10 in playpeakyrandom
0 draws
What got sampled
Hit Sample a token a few times — watch how the tally tracks the reshaped bars above. Crank temperature up or narrow top-p and sample again to feel the difference.

Greedy: pick the top

The simplest strategy: pick whichever token has the highest probability.

probs = {"Paris": 0.62, "the": 0.18, "located": 0.12, "a": 0.08}

greedy_pick = max(probs, key=probs.get)   # always the argmax
print("greedy picks:", greedy_pick)
greedy picks: Paris

Greedy is deterministic given the same probabilities. It’s fast and boring — exactly what you want for classification, extraction, and structured outputs.

Temperature: the creativity dial

Temperature T rescales the logits (pre-softmax scores) by dividing them by T. T = 1.0 is the model’s “natural” distribution. T < 1 makes the distribution sharper (peaks get peakier). T > 1 makes it flatter (rare tokens get more chance). The intuition: dividing by a small T amplifies the gap between high and low logits before softmax, so the already-highest score dominates even more; dividing by a large T compresses the gap, so scores converge and all tokens become nearly equally likely.

import math

logits = {"Paris": 4.0, "the": 2.5, "located": 2.0, "a": 1.0}

def softmax_with_temperature(logits, T):
    if T <= 0:                                  # T=0 collapses to argmax
        top = max(logits, key=logits.get)
        return {k: (1.0 if k == top else 0.0) for k in logits}
    scaled = {k: v / T for k, v in logits.items()}
    m = max(scaled.values())
    exps = {k: math.exp(v - m) for k, v in scaled.items()}
    Z = sum(exps.values())
    return {k: v / Z for k, v in exps.items()}

for T in [0.1, 0.7, 1.0, 2.0]:
    p = softmax_with_temperature(logits, T)
    print(f"T={T}:", {k: round(v, 3) for k, v in p.items()})
T=0.1: {'Paris': 1.0, 'the': 0.0, 'located': 0.0, 'a': 0.0}
T=0.7: {'Paris': 0.841, 'the': 0.099, 'located': 0.048, 'a': 0.012}
T=1.0: {'Paris': 0.71, 'the': 0.158, 'located': 0.096, 'a': 0.035}
T=2.0: {'Paris': 0.485, 'the': 0.229, 'located': 0.178, 'a': 0.108}

Watch the distribution flatten as T climbs. At T=0.1 Paris is effectively certain; by T=2.0 even “a” carries real weight and the tail tokens have a genuine chance. The same three distributions, drawn as bars:

T=0.1 — sharpT=1.0 — naturalT=2.0 — flataccent bar = “Paris”; rising temperature lets the tail tokens catch up

Top-k: keep the best k

Top-k truncates the distribution to the k highest-probability tokens before sampling. Everything else gets zero probability. Cuts off the long tail of unlikely garbage.

# Pseudocode for top-k
sorted_tokens = sorted(probs.items(), key=lambda x: -x[1])
top_k = dict(sorted_tokens[:k])
# Renormalize so they sum to 1
total = sum(top_k.values())
top_k = {t: p / total for t, p in top_k.items()}

Typical k is 40-50. Modern APIs use top-p more than top-k, but both exist.

Top-p (nucleus): keep tokens up to cumulative p

This is the most useful one to understand. Top-p sorts tokens by probability, then keeps only the smallest set whose cumulative probability exceeds p (typically 0.9 or 0.95). The “nucleus” of the distribution.

def top_p_sample(probs, p_threshold=0.9):
    sorted_items = sorted(probs.items(), key=lambda x: -x[1])
    cumulative = 0.0
    nucleus = []
    for tok, prob in sorted_items:
        cumulative += prob
        nucleus.append((tok, prob))
        if cumulative >= p_threshold:       # stop once we cross p
            break
    total = sum(p for _, p in nucleus)      # renormalise the nucleus
    return {t: p / total for t, p in nucleus}

probs = {
    "Paris": 0.45, "Lyon": 0.20, "Marseille": 0.15,
    "Nice": 0.08, "Lille": 0.05, "Toulouse": 0.04,
    "Bordeaux": 0.02, "Nantes": 0.01,
}

print("nucleus (p=0.9):")
for tok, p in top_p_sample(probs, 0.9).items():
    print(f"  {tok}: {p:.3f}")
nucleus (p=0.9):
  Paris: 0.484
  Lyon: 0.215
  Marseille: 0.161
  Nice: 0.086
  Lille: 0.054

The cumulative probability crossed 0.9 at Lille (0.45 + 0.20 + 0.15 + 0.08 + 0.05 = 0.93), so the three rare tail tokens — Toulouse, Bordeaux, Nantes — were dropped entirely, and the surviving five were renormalised to sum to 1.

Why top-p over top-k? Top-p adapts. When the model is confident (“the capital of France is __”), the nucleus might be just 1-2 tokens. When it’s open-ended (“once upon a time __”), the nucleus might be 50. Top-k forces the same k regardless of how peaked the distribution is.

A crucial reminder the three together make clear: the knobs do not pick tokens, they reshape the distribution you then draw from. Temperature always applies; top-k and top-p are filters that trim the candidate set first. Set the shape, then sample.

What to use when

This is a cheat-sheet, not gospel. Most APIs default to reasonable values; you only need to override for specific reasons.

Use caseSettings
Classification, extraction, JSON outputstemperature=0
Code generationtemperature=0.2, top_p=0.95
Q&A on documents (RAG)temperature=0.1-0.3
Conversational chattemperature=0.7, top_p=0.95 (typical defaults)
Creative writingtemperature=0.9-1.1, top_p=0.95
Brainstorming variationstemperature=1.0, sample N times

A subtle gotcha

If you’re A/B testing prompts, run each prompt multiple times unless temperature is 0. A single sample at T=0.7 is noisy. You can convince yourself prompt B is better when it just got lucky once. Average over at least 5-10 samples per prompt, or run a real eval.

In one breath

  • Greedy picks the argmax — deterministic, ideal for classification, extraction, and JSON.
  • Temperature rescales logits before softmax: T < 1 sharpens, T > 1 flattens.
  • Top-k keeps the k best tokens; top-p keeps the smallest set summing to p — and adapts to how peaked the step is.
  • temperature=0 is mostly but not bit-reproducible (GPU float order + ties); add a seed if you need more.
  • Tune either temperature or top_p, not both; and run >1 sample when T > 0 before trusting an A/B.

Quick check

Quick check

0/3
Q1What's the main advantage of top-p over top-k sampling?
Q2Why isn't temperature=0 always exactly reproducible?
Q3Best sampling setting for extracting structured JSON from a document?

Next

The next lesson covers structured outputs — how to actually get the model to emit valid JSON every time, using schemas instead of hoping.

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Practice this in an interview

All questions
What is the difference between temperature, top-k, and top-p sampling in LLMs?

Temperature rescales the logits before softmax — lower values sharpen the distribution toward the most likely token, higher values flatten it. Top-k restricts sampling to the k highest-probability tokens; top-p (nucleus sampling) restricts it to the smallest set of tokens whose cumulative probability reaches p. In practice top-p adapts the candidate pool dynamically while top-k uses a fixed count.

How do temperature, top-k, and top-p sampling control LLM generation?

Temperature rescales the logits before softmax: low values sharpen the distribution toward greedy deterministic output and high values flatten it for more randomness. Top-k restricts sampling to the k most likely tokens, and top-p or nucleus sampling restricts it to the smallest set of tokens whose cumulative probability exceeds p, both trimming the unlikely tail.

How would you reduce the cost of serving an ML or LLM model in production without hurting quality?

Work top-down: start at the model layer with quantization, distillation, or routing cheaper models for easy requests, since model choices drive every downstream cost. Then optimize the runtime with batching, caching, and techniques like prompt caching for LLMs, and finally match infrastructure to the load using autoscaling on queue depth and spot or batch capacity. Track cost per token or per prediction alongside latency percentiles and accuracy so optimizations never silently degrade quality.

What techniques reduce LLM cost and latency in production?

Cost scales with input plus output tokens; latency scales with output tokens and model size. The highest-leverage levers are: model routing (use a small model when the task is simple), prompt caching (reuse expensive prefix computation), output length control, and batching. Together these can cut spend 60–90% without quality regression.

Related lessons

Explore further

Skip to content