Softmax and Probabilities for Token Selection
The lines that follow the logits selection are where the added diversity comes from.
Try it: Text Vectorization: Tokens to Vectors
The text-to-tensor pipeline: standardize, split into tokens, build a frequency-ranked vocabulary with padding and [UNK], map tokens to integer ids, pad or truncate, then one-hot or multi-hot encode and look the ids up in an embedding table.
How it works
- Standardize: lowercase and delete punctuation.
- Split on whitespace into tokens.
- Build the vocabulary: 0 = padding, 1 = [UNK], then words by frequency (ties: first appearance), capped at max tokens.
- Map each token to its id (1 if it did not make the vocabulary), then pad with 0 or truncate to the sequence length.
- Encode: one-hot rows per position or one multi-hot row per text; embed: id k selects row k of the embedding table.
Default run (18 steps): Raw text (59 characters): "The movie was great. The plot was great, the acting wasn't!". A model cannot read strings, so it is turned into numbers step by step. … Embedding lookup: id k selects row k of a fixed 8 x 3 table, giving a (8, 3) dense matrix instead of (8, 8) sparse one-hots. Row 0 (padding) is looked up too; a trained Embedding layer would learn these rows.
Simplified: Bounded ASCII text (up to 200 characters, 40 tokens), whitespace splitting only, and a small FIXED embedding table (a real Embedding layer learns its rows). The multi-hot vector keeps the padding slot (always 0) so its positions line up with the ids.
Loading the simulation…
Why a Greedy Generator Repeats Itself
Earlier in this module, the text generation loop produced each new token by looking only at the last time step of the model's output and choosing the single most likely token. That approach is simple and predictable, but it always makes the same choice when it is given the same input. A generator that always picks the top candidate tends to produce repetitive and narrow text.
The modified generate function addresses this by adding two controls to the same loop. Temperature scaling changes how random the choice is, and top-k sampling restricts the set of tokens the choice can come from. Both controls live inside one function, so the whole mechanism can be read in one place.
What do you think happens?
If temperature is 0.0 and the prompt and model are the same on two runs, will the generated tokens be the same both times?
Reveal answer
Yes, the same tokens every time.
The source says that when temperature is 0.0 or lower, the code skips the scaling and sampling lines and calls torch.argmax instead. Taking the argmax of the same logits always selects the same token, so this path is deterministic. The randomness only enters through the sampling branch.
Tracing One Pass Through the Loop
Each pass through the loop starts the same way as the earlier generator. The function crops the context to context_size, runs the model, and keeps only the logits for the last time step. Everything after that line is new. The lines that follow the logits selection are where the added diversity comes from.
On each pass, the data moves through one fixed sequence of operations. Top-k filtering runs first, then temperature scaling, then softmax converts the logits into probabilities, and finally torch.multinomial samples one token from those probabilities. Whether the temperature step runs at all depends on the temperature value, which is why the flowchart below shows a branch.
Read the diagram from the top. The top-k step always comes before the branch. The temperature decision then sends the flow either to the sampling lines, which divide, normalize, and draw one token at random, or to torch.argmax, which takes the highest-scoring token. Both paths lead to the same end-of-sequence check, which decides whether the loop continues.
Top-k Filtering and Negative Infinity
Top-k sampling restricts the candidates to the k largest logits. The function finds the cutoff, which is the smallest of the top k logits. Any logit below that cutoff is replaced with negative infinity. Every other logit is kept as it is.
In this generated example, the two highest logits, 3.2 and 2.7, stay in place. The other two are set to negative infinity. The vocabulary arrays are not shortened. The remaining positions still exist, but they carry the negative infinity value instead of their original scores.
Temperature Scaling Divides the Logits
Temperature scaling is applied to the logits before softmax. In the formula, z stands for the logits of the candidate tokens, T stands for the temperature, and z' is the scaled result that the function passes to softmax. In the code, this is the line logits = logits / temperature.
- 1z is the vector of logits for the candidate tokens
- 2T is the temperature, which is greater than 0.0 on the sampling path
- 3z' is the scaled logits, which are passed to softmax to produce probabilities
The purpose of this division is to change how sharply the probabilities differ after softmax. The source does not show the probability values directly, so the exact effect depends on the logits at each step. The direction of the mechanism is what matters: the temperature is the divisor, and the scaled logits are what softmax turns into probabilities for sampling. Reasoning from the formula, dividing by a larger T shrinks the gaps between the logits, which pushes the probabilities closer together. Dividing by a smaller T enlarges the gaps.
In this generated example, the temperature is 2.0. The gap between the two logits drops from 2.0 to 1.0 after division. Because the gap is smaller, softmax assigns the two tokens more similar probabilities, so the sampling step has more variety to draw from.
Choosing Between Greedy and Sampling
The temperature value decides which selection path runs. With temperature greater than 0.0, the function samples a token from the probabilities. With temperature 0.0 or lower, it selects the token with the highest logit. The earlier generator always made the greedy choice, so this branch keeps that behavior as a special case.
| Setting | Path taken | Same tokens every run? | What controls the outcome |
|---|---|---|---|
| Temperature 0.0 or lower | torch.argmax, greedy | Yes, for the same logits | The highest logit |
| Temperature greater than 0.0 | Divide, softmax, torch.multinomial | Not guaranteed without a fixed seed | Probabilities and the random number generator |
| Temperature greater than 0.0 with torch.manual_seed fixed | Sampling path with a fixed random number generator | Reproducible for that run | The seed and the probabilities |
Determinism under different temperature settings
The seed matters because the source's example calls torch.manual_seed(123) before generating. That call fixes the random number generator, so the sampling step behaves reproducibly on that run. Without the seed, the same sampling settings are not guaranteed to produce the same tokens each time.
Tracing the Source's Generation Call
The source runs the modified function with a fixed seed, top_k set to 25, and temperature set to 1.4. Both controls are active, so the loop takes the sampling path on every pass. The output is the prompt followed by up to 15 newly generated tokens. The source notes that the text reads as plausible words that drift in meaning, which is the variety the function is designed to allow.
torch.manual_seed(123)fixes the random number generator for a reproducible sampling run- top_k=25 keeps only the 25 highest logits as candidates
- temperature=1.4 is greater than 0.0, so the logits are divided and the sampling branch runs
To trace a single pass, follow the same order each time. Filter to the top 25 logits, divide the surviving logits by 1.4, apply softmax, and let torch.multinomial draw one token. Append that token, then check whether it equals eos_id. If it does, the loop breaks early and returns the sequence so far. If not, the loop continues until max_new_tokens iterations have run.
- The if test is the branch point between sampling and greedy selection
- The division is the temperature step, which runs only on the sampling path
- The else branch is the greedy path that the earlier generator always used
Mistakes with Temperature and Top-k
Common mistake Assuming temperature always changes the output
What it looks likeSetting temperature to 0.0 and expecting different text on each run
Why it failsAt 0.0 or lower, the code skips the scaling and sampling lines and calls torch.argmax, which is deterministic for the same logits.
Do this insteadCheck the temperature value against the 0.0 threshold before predicting variety.
Common mistake Thinking top-k removes tokens from the vocabulary
What it looks likeBelieving that the tokens below the cutoff are deleted from the arrays
Why it failsThe function sets the lower logits to negative infinity and keeps the vocabulary arrays in place.
Do this insteadRead top-k as masking: the excluded tokens still exist but receive no probability.
Common mistake Expecting temperature to run before top-k
What it looks likeDividing all logits by temperature and then picking the top k
Why it failsThe source states that top-k filtering runs first, then temperature scaling, then softmax, then sampling.
Do this insteadFollow the pipeline order: filter, scale, softmax, sample.
Common mistake Treating a seeded sampling run as the general case
What it looks likeAssuming every sampled run with temperature 1.4 gives the same text
Why it failsThe seed in the source fixes the random number generator for that run only.
Do this insteadWithout a fixed seed, the sampling path is not guaranteed to repeat.
Predicting Whether a Run Is Deterministic
A run uses top_k=5 and temperature=0.0 with the same prompt on two machines. Predict whether both runs produce the same tokens, and name the line that decides the outcome.
Hints
- Compare the temperature value with the 0.0 threshold
- Think about which function the greedy path calls
- Consider whether top_k changes the answer on the greedy path
A second run uses top_k=25 and temperature=1.4 with no fixed seed. Identify the branch it takes, and explain why its tokens may differ from one run to the next.
Hints
- Temperature greater than 0.0 selects the sampling path
- torch.multinomial draws from the probabilities
- The seed is what makes the source's run reproducible
Key Takeaways
- The modified generate function applies top-k filtering first, then temperature scaling, then softmax, then torch.multinomial sampling on each pass through the loop.
- Top-k filtering keeps the k largest logits and sets the rest to negative infinity, so those tokens receive no probability.
- Temperature scaling divides the logits by the temperature when it is greater than 0.0, which changes how sharply the probabilities differ after softmax.
- A temperature of 0.0 or lower takes the torch.argmax greedy path and is deterministic for the same logits, while a sampling run is reproducible only when its random number generator is fixed.