Sampling

Compute what temperature, top-k and top-p actually do to a distribution — and choose them on purpose instead of by folklore.

03of 24
≈ 13Min read
5Parts

From logits to a choice

The model produces logits — one unnormalised score per token in the vocabulary. Softmax turns them into probabilities. The sampling parameters then cut that distribution down before a choice is made. Every knob you have acts on this one step, and nothing else.

text
logits        [ 2.0   1.0   0.0 ]        raw scores, any real number
  ↓ softmax(t)
probabilities [ 0.665 0.245 0.090 ]      sums to 1
  ↓ top-k / top-p
truncated     [ 0.625 0.375 0.000 ]      renormalised over what is left
  ↓ pick a random point
token

Temperature divides the logits

Temperature never changes the order of the candidates — it changes how much the leaders are favoured. Above about 1.2 the tail becomes reachable, and the tail is where the incoherence lives.

Temperature 0 is the right default for anything you intend to parse. It is usually called "deterministic", and in practice it is nearly so: batching and floating-point non-associativity on the provider’s hardware can still change a result between identical calls. Do not build a cache key on the assumption that it cannot.

js
softmax(logits.map((x) => x / temperature))
TemperatureEffect on [2, 1, 0]Use
0[1, 0, 0] — always the top tokenExtraction, classification, structured output, tests
0.5[0.87, 0.12, 0.02] — sharperCareful prose that should still read naturally
1.0[0.67, 0.24, 0.09] — the model as trainedGeneral use
1.5+[0.51, 0.26, 0.14] — flatterBrainstorming; and increasingly, nonsense

Top-k and top-p cut the tail

Top-k keeps a fixed number. Its weakness is that it is blind to shape: when the model is certain, 40 candidates drags in 39 bad ones; when it is genuinely uncertain, 40 may cut off good ones.

Top-p (nucleus) keeps a fixed mass, so the number of candidates adapts. On [0.9, 0.05, 0.05] at p = 0.9 it keeps one; on [0.34, 0.33, 0.33] at the same p it keeps all three. That is why it is the usual recommendation, and why most providers default to it.

They compose, and the common advice is to change one of them, not both — temperature or top-p. Two overlapping truncations are hard to reason about and easy to get into a state where nothing much is left.

js
topK(probs, 40)     // keep the 40 most likely, renormalise
topP(probs, 0.9)    // keep the smallest set that sums to 0.9, renormalise

What to set, by task

Two more that matter: max_tokens is a hard cut, not a hint — a reply truncated at the limit is not shorter, it is unfinished, and unfinished JSON does not parse. And stop sequences end generation when a string appears; they are checked on the decoded text, so a stop sequence that falls inside a token can behave unexpectedly.

TaskSettings
JSON, extraction, classificationtemperature: 0
Code generationtemperature: 0–0.2
Summarising, rewritingtemperature: 0.3–0.7
Creative drafting, ideastemperature: 0.8–1.0
Several candidates to choose betweenHigher temperature, n samples, then pick — see pass@k in the evaluation lesson

Sampling is not reasoning

A model that gets an answer wrong at temperature 1 will often get it right on a second sample, and that is sometimes read as the model "knowing" it. What it means is narrower: the correct answer had some probability and the sampler did not pick it.

That is a real property to exploit — sample several, take the majority, which is what self-consistency does — but it is a technique with a cost, not evidence of hidden competence. And it is worth remembering that the fix for an unreliable format is temperature 0 and a schema, not more samples.

Practice