19 — Inference

Decoding Strategies#

Greedy · Beam SearchMathematical 1 min read
◆ The PatternDeterministic methods for converting logits to text

Greedy decoding picks the highest-probability token at each step. Simple but often suboptimal — it can miss globally better sequences. Beam search maintains K candidate sequences (beams) and prunes at each step.

Greedy: token_t = argmax P(token | context)
Fastest, but prone to repetitive or locally-trapped outputs.
Beam search: maintain top-K partial sequences ranked by Σ log P
At each step, expand all K beams, score, keep top K. Beam width K=4–5 is typical.

Beam search was dominant pre-LLM (translation, summarization) but is rarely used for chat/creative generation — it produces too safe, repetitive text. Modern LLMs use sampling (next topic) for most tasks.

Beam search still wins for tasks with a "correct" answer — code generation, math, structured output. For open-ended text, sampling produces more natural and diverse output.
Interactive — beam search tree
Pattern bridge: Greedy, beam search, nucleus sampling — trading off quality vs. diversity. The same tradeoff as bias-variance: greedy = high bias, random = high variance. In markets, fear and greed drive conservative vs. aggressive strategies.
Worked example

A two-step choice. Greedy decoding takes A (0.6) and then the best continuation of A (0.4): a sequence probability of 0.24. B starts less likely (0.4) but continues with 0.9, for 0.36. A beam of width 2 keeps both first steps and finds B–x. Greedy commits too early; beam search looks a little further.

Where it misleads
  • The most probable text is often bland or repetitive; for open-ended generation, maximising probability is the wrong goal (Holtzman et al. 2020).
  • Wider beams can make translation worse beyond small widths (the beam search curse).
  • Deterministic decoding still varies across hardware and batch sizes because of floating-point non-determinism.
In code
step1 = {'A': 0.6, 'B': 0.4}
step2 = {'A': {'x': 0.4, 'y': 0.35, 'z': 0.25}, 'B': {'x': 0.9, 'y': 0.05, 'z': 0.05}}
greedy_first = max(step1, key=step1.get)
greedy_best = max(step2[greedy_first].values())
greedy = (greedy_first, step1[greedy_first] * greedy_best)
beam = max((((a, b), step1[a] * pb) for a in step1 for b, pb in step2[a].items()), key=lambda c: c[1])   # width 2 keeps both first steps
Python, in your browser — edit the code and run it again

Assumes import numpy as np. Configurations named after real models use their published shapes; other numbers are made up or simulated, as the comments say.

The same idea with a library
def beam_search(model, prompt_ids, beam_width=4, max_len=50):
    beams = [(prompt_ids, 0.0)]  # (sequence, log_prob)
    for _ in range(max_len):
        candidates = []
        for seq, score in beams:
            logits = model(seq)[:, -1, :]
            log_probs = F.log_softmax(logits, dim=-1)
            topk = log_probs.topk(beam_width)
            for i in range(beam_width):
                new_seq = torch.cat([seq, topk.indices[:, i:i+1]], dim=-1)
                new_score = score + topk.values[:, i].item()
                candidates.append((new_seq, new_score))
        # Keep top-K beams
        beams = sorted(candidates, key=lambda x: -x[1])[:beam_width]
    return beams[0][0]  # best sequence
Sources
  • A. Holtzman, J. Buys, L. Du, M. Forbes & Y. Choi, “The Curious Case of Neural Text Degeneration”, ICLR, 2020
  • C. Meister, R. Cotterell & T. Vieira, “If Beam Search Is the Answer, What Was the Question?”, EMNLP, 2020
  • M. Freitag & Y. Al-Onaizan, “Beam Search Strategies for Neural Machine Translation”, Workshop on Neural Machine Translation, 2017

Last reviewed October 2026 · How this site checks its content · Report a mistake

Linked from 1
← Previous
Data Curation
Open in the full reader, with the topic sidebar