n4nAI

What is nucleus sampling (top-p) in language models?

Nucleus sampling (top-p) dynamically selects tokens from the smallest probability mass covering p% of the distribution, balancing coherence and diversity in LLM output.

n4n Team3 min read751 words

Audio narration

Coming soon — every post will get a voice note here.

Nucleus sampling (top-p) is a decoding strategy that selects the next token from the smallest set of candidates whose cumulative probability exceeds a threshold p. Unlike top-k sampling which fixes the candidate pool size, nucleus sampling adapts the pool size to the model’s confidence at each step.

How nucleus sampling works

At each generation step, the model outputs a probability distribution over the vocabulary. Nucleus sampling sorts tokens by descending probability, then accumulates probability mass until the running total reaches or exceeds p. Only tokens in this accumulated set — the “nucleus” — are eligible for sampling. The selected token is drawn proportionally to its original probability, renormalized within the nucleus.

def nucleus_sampling(logits, top_p=0.9, temperature=1.0):
    """
    logits: raw model output [vocab_size]
    top_p: cumulative probability threshold (0 < top_p <= 1)
    temperature: sharpens (T < 1) or flattens (T > 1) the distribution
    """
    import torch
    
    # Apply temperature
    logits = logits / temperature
    
    # Convert to probabilities
    probs = torch.softmax(logits, dim=-1)
    
    # Sort descending
    sorted_probs, sorted_indices = torch.sort(probs, descending=True)
    
    # Cumulative sum
    cumsum_probs = torch.cumsum(sorted_probs, dim=-1)
    
    # Mask tokens outside the nucleus
    # Keep tokens where cumulative prob <= top_p
    # Also keep the first token that pushes us over the threshold
    nucleus_mask = cumsum_probs <= top_p
    # Ensure at least one token is kept
    nucleus_mask[0] = True
    
    # Zero out excluded tokens
    sorted_probs = sorted_probs * nucleus_mask
    
    # Renormalize
    sorted_probs = sorted_probs / sorted_probs.sum()
    
    # Sample from nucleus
    sampled_idx = torch.multinomial(sorted_probs, num_samples=1)
    token_id = sorted_indices[sampled_idx]
    
    return token_id

The threshold p typically ranges from 0.9 to 0.95. Lower values (0.7–0.8) produce more focused, deterministic output. Higher values (0.95–0.99) approach full distribution sampling, increasing diversity but risking incoherence.

Why top-p matters for generation quality

Fixed top-k sampling suffers from a fundamental mismatch: the model’s uncertainty varies dramatically across positions. After “The capital of France is”, the distribution is sharply peaked on “Paris” — top-k=50 wastes computation on irrelevant tokens. After “The poet wrote a beautiful”, the distribution is flat — top-k=50 may exclude plausible continuations.

Nucleus sampling adapts to this variance. When the model is confident, the nucleus contains few tokens. When uncertain, it expands automatically. This property makes top-p the default choice for most production LLM deployments.

The interaction with temperature is critical. Temperature reshapes the distribution before nucleus selection:

  • Temperature < 1.0: Sharpens peaks, shrinking the nucleus for a given p
  • Temperature > 1.0: Flattens distribution, expanding the nucleus
  • Temperature = 1.0: Uses raw model probabilities

In practice, most systems fix temperature=1.0 and tune top-p, or fix top-p=0.9–0.95 and tune temperature. Tuning both simultaneously creates a two-dimensional search space that’s harder to reason about.

Concrete example

Consider a model completing: “The detective examined the clue and realized”

With temperature=1.0, the top probabilities might be:

Token Probability Cumulative
“the” 0.35 0.35
“it” 0.18 0.53
“was” 0.12 0.65
“this” 0.08 0.73
“meant” 0.06 0.79
“something” 0.05 0.84
“important” 0.04 0.88
“had” 0.03 0.91
“been” 0.02 0.93
“missed” 0.01 0.94

With top-p=0.9, the nucleus includes tokens up to “had” (cumulative 0.91). The sampling pool is 8 tokens. “been” and “missed” are excluded despite non-trivial probability.

With top-p=0.95, the nucleus expands to include “been” (cumulative 0.93) and “missed” (0.94), giving 10 tokens.

With top-k=5, only “the”, “it”, “was”, “this”, “meant” are eligible — “something” and “important” are excluded despite combined 0.09 probability.

This illustrates why nucleus sampling preserves more semantic diversity than top-k at equivalent compute budgets.

Common misconceptions

Misconception: top-p and top-k are mutually exclusive

They compose cleanly. Most implementations apply both: first filter by top-k, then by top-p on the remaining tokens. This provides a hard ceiling on candidate count (useful for batching) while retaining adaptive behavior.

def combined_sampling(logits, top_k=50, top_p=0.9, temperature=1.0):
    logits = logits / temperature
    probs = torch.softmax(logits, dim=-1)
    
    # Top-k filter
    top_k_probs, top_k_indices = torch.topk(probs, min(top_k, probs.size(-1)))
    
    # Top-p filter on top-k subset
    sorted_probs, sorted_indices = torch.sort(top_k_probs, descending=True)
    cumsum = torch.cumsum(sorted_probs, dim=-1)
    nucleus_mask = cumsum <= top_p
    nucleus_mask[0] = True
    
    filtered_probs = sorted_probs * nucleus_mask
    filtered_probs = filtered_probs / filtered_probs.sum()
    
    sampled = torch.multinomial(filtered_probs, 1)
    return top_k_indices[sorted_indices[sampled]]

Misconception: top-p=1.0 equals greedy decoding

top-p=1.0 includes the entire vocabulary (minus any top-k cutoff). Sampling from the full distribution is not greedy — it still samples stochastically. Greedy decoding is argmax, equivalent to temperature→0 with any top-p.

Misconception: nucleus sampling eliminates repetition

Nucleus sampling reduces but does not eliminate repetition loops. Repetition arises from the model assigning high probability to recently generated tokens — a modeling issue, not a decoding issue. Repetition penalties (presence/frequency penalties) or contrastive search address this at the logit level, before sampling.

Misconception: higher top-p always means more creative

Beyond ~0.95, increasing top-p adds mostly low-probability noise tokens. The marginal diversity gain diminishes while hallucination risk increases. The sweet spot for most tasks is 0.9–0.95.

When to use nucleus sampling vs alternatives

Scenario Recommended approach
General chat/completion top-p=0.9, temperature=0.7–1.0
Code generation top-p=0.95, temperature=0.2–0.5 (more deterministic)
Creative writing top-p=0.9–0.95, temperature=0.8–1.0
Factual QA / extraction top-p=0.9, temperature=0.1–0.3 (near-greedy)
Classification / routing temperature=0 (greedy) or top-p=0.01
Beam search required Use beam search, not sampling

For structured output (JSON, function calls), avoid sampling entirely. Use constrained decoding or greedy with a grammar constraint. Sampling introduces non-determinism that breaks schema compliance.

Integration notes

When calling an OpenAI-compatible endpoint, the parameters map directly:

{
  "model": "gpt-4o-mini",
  "messages": [...],
  "top_p": 0.9,
  "temperature": 0.7,
  "max_tokens": 500
}

Most providers also support top_k as an extension parameter. If both are provided, the typical server-side logic applies top-k first, then top-p on the truncated distribution.

If you’re routing requests across multiple providers through a gateway, verify that each provider implements nucleus sampling identically. Some older APIs only support top-k, or apply the filters in reverse order. This can cause subtle output distribution shifts when failing over between providers.

Tagstop-pnucleus-samplingsampling-parameters

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All sampling parameters: top-p, top-k & penalties posts →