Chapter 15: Greedy and Sampling¶
Figure 15.1: Where we are: we are ready to generate new text using the trained model.
Our model is trained and loaded, and now we want to use it to generate new text. But the model doesn't give us one character. It produces a list of 65 scores (logits), one for each possible character in the vocabulary, so we need a strategy to pick the winner. In this chapter you will:
- Generate text by always choosing the highest-scoring character (greedy decoding).
- Generate text by choosing characters randomly based on their probabilities (sampling).
- Compare the trade-offs between deterministic and creative text generation.
Words to Know
- Greedy Decoding: always picking the single highest-probability token.
- Sampling: choosing the next token randomly, giving higher-probability tokens a better chance to be selected.
Theory¶
Greedy Decoding¶
The simplest strategy, called greedy decoding, is to always pick the character with the highest score.
While it seems logical to always pick the "best" answer, greedy decoding has a major flaw, which is that it often gets stuck in loops. Imagine the model learns a common phrase. It predicts the next character, and that character becomes part of the context. The context looks familiar, so it predicts the next character of the phrase, and soon it is repeating the same phrase forever.
Greedy decoding always gives the same result (it is deterministic). If you give it the same prompt, it will always produce the exact same text.
Sampling¶
To avoid repetitive loops, we can use a strategy called sampling. Instead of automatically taking the top character, we turn the raw scores (logits) into percentages (probabilities) and draw a winner randomly.
Figure 15.2: Greedy decoding always takes the top peak; sampling draws randomly from the distribution.
If the letter "e" has a 60% probability, it will be chosen 60% of the time. If "x" has a 1% probability, it is rarely chosen, but it still has a chance. This introduces variety and breaks repetitive loops, making the text feel more natural and creative. However, it also means the output is unpredictable, because a different random draw gives a different text. The scripts in this book fix the draw with a seed (torch.manual_seed(42)), so your run prints the same text as the book.
Generation loops the New Text back to the Tokens step on the "where we are" map. The model predicts one character, we add it to the prompt, and the cycle repeats.
In Business
In our house-style assistant, the choice between greedy and sampling depends on the task. If the assistant is answering a factual question from a company manual, you want greedy decoding, which is safe, predictable, and exact. If it is drafting a creative marketing email in the company voice, you want sampling, which is varied and creative and explores new options.
Code¶
We implement both strategies in the same generation loop. The core difference is how next_id is chosen.
Line 7 picks the character with the single highest score for greedy decoding. Lines 17 and 18 turn the scores into probabilities and draw a winner randomly for sampling.
Figure 15.3: The generation loop predicts one character at a time and appends it to the context.
Notice ids[:, -cfg.block_size:], which keeps only the last block_size tokens of the text so far (the context) before we pass it to the model. The model only learned to read a specific maximum length (our block size of 128) during training, so we must feed it at most that many tokens. We also take logits[:, -1, :] because only the scores at the last position predict the character that comes next.
We can see the two strategies in action with the prompt ROMEO:\n.
$ python src/ch14_generate_greedy.py
--- GREEDY (always picks highest-score token) ---
ROMEO:
I will the come the some the stand the son,
And the so shall the see the shall the see the stand
...
--- SAMPLING (picks randomly from distribution) ---
ROMEO:
My must greather, but what it? whom, he ere.
EBRUTUS:
Morcy there bagainVain, I will ever may
To epated as great that the appral tankswain
...
--- Observations ---
Greedy tends to be more repetitive.
Sampling is more varied but can make unexpected choices.
Figure 15.4: Greedy always picks the tallest bar; sampling can pick any bar based on its height.
What just happened:
- The greedy approach quickly got stuck in a repetitive loop ("the shall the shall...").
- The sampling approach produced varied, non-repetitive text, but it included some strange spellings ("greather", "bagainVain") because it occasionally picked low-probability characters.
Shape Check¶
Table 15.1 lists the shapes used during the character prediction step.
Table 15.1: Tensors used during the text generation loop.
| Tensor | Shape | What it means |
|---|---|---|
logits[:, -1, :] |
[1, 65] |
65 vocabulary scores for the final character in the sequence. |
next_id |
[1, 1] |
1 chosen character ID. |
Try It¶
Try It
Different starting prompts create different context for the model. The script below shows how sampling handles new prompts.
Lines 6 and 9 generate new text starting from two different prompts.
Key Takeaways¶
- Greedy decoding always chooses the highest-scoring token, which leads to predictable but repetitive text.
- Sampling chooses the next token randomly based on the probability distribution, creating varied and natural text.
- The generation loop works by predicting one character, appending it to the context, and repeating the process.
- We crop the input context to the model's maximum
block_sizebefore each prediction.
Check Your Understanding¶
- Why does greedy decoding often get stuck repeating the same phrase?
- What PyTorch function do we use to randomly pick a token based on its probabilities?
- Why do we slice the logits tensor with
[:, -1, :]in the generation loop?
Assignment¶
Assignment 15: Which half changes
About 15 minutes. You change one number and write no new code. Work in this chapter's Colab notebook or on your own laptop.
- Run it. Run
src/ch14_generate_greedy.pyas it is. Copy the text under the GREEDY heading and the text under the SAMPLING heading. - Change one number. In the demo part of the file, find
torch.manual_seed(42). The seed fixes the random draws, so every run of the script picks the same characters. Change42to any whole number you like and run the script again. - Compare. Which of the two texts changed? Which one is the same, character for character? In the greedy text, which words keep coming back?
- Explain it. In two or three sentences a manager could follow, say which rule you would choose for a task that must give the same answer every time, and what you give up by choosing it.
Hand in: both pairs of texts, your answers to step 3, and your sentences.
Further Reading¶
Why always picking the likeliest word goes wrong. Always taking the highest-scoring next token, which Chapter 15 calls greedy decoding, produces flat and repetitive text, and this paper shows why: human writing is not made of the most predictable word at every turn. Their alternative keeps the smallest set of tokens whose probabilities add up to a chosen share and samples from that. How you choose the next token matters as much as how well the model was trained.
Holtzman, A., Buys, J., Du, L., Forbes, M., & Choi, Y. (2019). The curious case of neural text degeneration (arXiv:1904.09751). arXiv. https://doi.org/10.48550/arXiv.1904.09751
Next up: Chapter 16: Temperature and Top-k