Softmax and Probability Distributions over a Vocabulary
Text generation maps input text to token IDs, obtains probability vectors, locates the highest-probability indices, and decodes them back into text.
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…
Putting a Number on Generated Text
A practical question comes up as soon as a language model produces text: how do you put a number on how good that text is? The answer is a loss value, but a loss can only be computed after the model has produced its output. So the first job is to run the model on a small batch and look closely at what it actually generates. Doing this on a small batch makes every step visible before any scoring happens.
The sequence that runs the model, converts its raw outputs into probabilities, picks token IDs, and decodes them back into words is the same sequence that the generate_text_simple function performs internally. This article walks through that sequence on a concrete batch, then shows how the loss is built from the probabilities the model assigned to the correct tokens.
The running batch contains two inputs, 'every effort moves' and 'I really like'. Each has already been converted to token IDs. Each input has three tokens, so the batch holds six token positions in total. That count of six comes back later when the loss is averaged.
Tracing the Generation Pipeline
Text generation is a five-step procedure. First, the input text is mapped to token IDs using a vocabulary. Second, the model produces a probability score for every token in the vocabulary at every input position. Third, for each position, the index with the highest probability is located. Fourth, those highest-probability indices are collected as the predicted token IDs. Fifth, the predicted token IDs are mapped back into text using an inverse vocabulary that goes from IDs to words.
The same five steps apply whether the vocabulary is tiny or large. The pipeline diagram uses a seven-word vocabulary so that every probability vector fits on one page. The GPT model used in this work has a far larger vocabulary, and that difference matters when you interpret the numbers, as the next section explains.
Probability Vectors Indexed by Vocabulary
The output of the model is a probability tensor, and its shape encodes three things at once. The first dimension is the batch size, which is the number of input examples. The second dimension is the number of tokens in each input. The last dimension matches the vocabulary size, because each input position receives one probability for every possible next token. With the two-example batch here and a vocabulary of 50,257 tokens, the probability tensor has shape torch.Size([2, 3, 50257]).
Read the diagram as a lookup table. Each slot in one probability vector is an index, and each index corresponds to a vocabulary token ID. Token IDs for this model range from 0 to 50,256. To choose the predicted token at a position, the third step of the pipeline locates the index whose probability is highest in that vector. The index itself is the token ID, which is why the predicted IDs can be decoded back into words through the inverse vocabulary.
Targets as Shifted Inputs
The targets are the token IDs the model should produce. They are built from the inputs by shifting every sequence one position forward, so that each input token is paired with the token that follows it. The input 'every effort moves' has the target 'effort moves you'. The input 'I really like' has the target 'really like chocolate'. Each target token is the next word after the corresponding input token.
This shifting is what teaches a language model to predict the next token in a sequence. Without it, the model would be asked to reproduce its input, which is a different task.
Why the Loss Reads Target Probabilities
The loss does not look at the single most likely token. It looks at how much probability the model gave to the correct token at each position. For every input position, the probability vector has one entry per vocabulary token. The entry at the index of the target token is the value that matters. The training goal is to raise that entry, so that the correct token becomes more likely relative to the others.
| Question | Argmax prediction | Target-token probability |
|---|---|---|
| What it reads | The single index with the highest probability | The value at the index of the correct target token |
| What it tells the learner | Which token the model would pick | How much probability the model gave to the right answer |
| Changes smoothly when training nudges values | Not necessarily; the winning index can stay the same while the correct one gains ground | Yes; raising the correct entry raises this value directly |
| Role in the loss | Not used to compute the loss | The values that the logarithm, averaging, and negation are applied to |
Argmax versus target-token probability
Before any training, the model's vectors are essentially random. Each input token produces a vector, each vector index corresponds to a vocabulary word, and training is meant to maximize the value at the index of the target token. Because the vectors start random, the values at the target indices are not special yet.
Applied to the two-example batch, indexing the probability tensor with the target IDs gives the untrained probabilities assigned to the correct tokens. For the first input these are roughly 7.45e-05, 3.11e-05, and 1.16e-05. For the second input they are roughly 1.03e-05, 5.68e-05, and 4.76e-06. Every value is tiny, which matches the expectation for a large vocabulary.
Computing the Loss Step by Step
The loss is computed in a fixed sequence that starts from the six target probabilities collected above. First, a logarithm is applied to each probability, which makes the values easier to optimize than the raw probabilities. Second, the six log probabilities are averaged into a single number. Third, that average is multiplied by -1. The result is the cross entropy loss, which is the quantity training reduces.
In the formula, $p_{i}$ is the probability the model assigned to the correct target token at position $i$, and $n$ is the number of positions, which is six for this batch. The source does not fix a base for the logarithm, so the worked numbers below use the natural logarithm.
Loss for the Two-Example Batch
Using the six untrained target probabilities for the batch (7.45e-05, 3.11e-05, 1.16e-05, 1.03e-05, 5.68e-05, 4.76e-06), compute the cross entropy loss.
- 1Take the logarithm of each value
Natural logs are approximately -9.505, -10.378, -11.365, -11.483, -9.776, and -12.255.
- 2Sum the six log values
The sum is approximately -64.762.
- 3Average over six positions
Dividing the sum by 6 gives approximately -10.794.
- 4Negate the average
Multiplying by -1 gives a loss of approximately 10.79.
The untrained loss is approximately 10.79. Exact digits depend on how the listed probabilities are rounded.
Notice how the steps respond to the numbers. Every target probability is far below 1, so every logarithm is strongly negative. Averaging and negating turn those negative values into a positive loss. A larger probability on the correct token pulls its logarithm toward zero, which lowers the loss. That is the direction training pushes.
Finding Where Generated Text Diverges
When generated text is compared with its target, the first useful step is to decode both sides and read them position by position. Decoding the first batch's targets gives ' effort moves you'. Decoding the model's predicted token IDs for the same batch gives ' Armed heNetflix'. The two strings differ throughout, which is consistent with an untrained model.
A divergence report should identify the first position where the predicted token differs from the target, because that is where the sequence departs from what was expected. The loss value for that position is then the place to investigate. In this batch every decoded output differs from its target, so the investigation starts at the first position of each sequence.
A trained model would be expected to match the target at more positions, and the first mismatch would move further along the sequence. Finding that point is the debugging step: it tells you where the model's behaviour first breaks from the target.
Mistakes Beginners Make With This Pipeline
Common mistake Scoring the model with the argmax prediction only
What it looks likeChecking whether the highest-probability index equals the target and stopping there.
Why it failsThe loss is built from the probability at the target index, so a near miss and a far miss look identical to an argmax check.
Do this insteadRead the probability assigned to the target token at each position and compute the loss from those values.
Common mistake Expecting large starting probabilities
What it looks likeThinking an untrained model with values near 0.00002 is broken.
Why it failsTiny values are the normal starting point when the vocabulary has about 50,257 entries.
Do this insteadCompare against 1 divided by the vocabulary size, not against the seven-word teaching example.
Common mistake Forgetting the shift between inputs and targets
What it looks likeUsing the input 'every effort moves' as its own target.
Why it failsWithout the one-position shift, the model is asked to reproduce its input instead of predicting the next token.
Do this insteadPair each input token with the token that follows it, giving 'effort moves you' for the first input.
Common mistake Comparing whole strings instead of positions
Why it failsTwo strings can look different overall while the first mismatch sits at an early position that deserves investigation.
Practice Locating the First Divergence
Given the aligned target token IDs [5, 9, 2, 7] and predicted token IDs [5, 9, 4, 7], identify the first position where the generated output diverges from the target, and say which loss value you would inspect first.
Hints
- Compare the two lists one position at a time, starting from position 0.
- The first position where the two values differ is the divergence point.
What do you think happens?
Before reading on, predict the first divergence position for the lists above, counting positions from 0.
Reveal answer
Position 2. The target has 2 and the prediction has 4 there, while positions 0, 1, and 3 match.
The loss value at position 2 is the one to inspect first, since that is where the generated sequence departs from the target.
Key Takeaways
- Text generation maps input text to token IDs, computes a probability for every vocabulary token at each position, selects the highest-probability indices, and decodes them back into text.
- Targets are the inputs shifted one position forward, so the model learns to predict the next token.
- The loss is built from the probabilities assigned to the correct target tokens, not from the argmax prediction.
- The cross entropy loss takes the logarithm of each target probability, averages the results, and negates the average.
- When debugging, decode both sequences and locate the first position where the predicted token differs from the target.