Decode autoregressively
A language model only ever answers one question: what comes next? To write a whole text you ask it again and again. Give it the text so far, take the token it rates highest, add that token to the end, and ask again with the longer text. It stops when it has written enough tokens, or when it produces a special token that means the text is finished.
Learn it
Words to know
- autoregressive
- Producing output one token at a time, with each new token added to the input for the next step.
- greedy decoding
- Always choosing the token with the highest score.
- end-of-sequence token
- A special token that means the text is finished. It is often written eos.
- bigram model
- The simplest language model: it predicts the next token from the current token alone.
How it works
- Start with the prefix. Here the model is a bigram table: the scores for the next token are the row of
logitsat the last token's ID. - Pick the position of the largest score in that row. On a tie, the smallest position wins. Append it to the sequence.
- Stop after
max_newnew tokens. - Also stop as soon as the sequence ends with
eos, whether that token was in the prefix or was added a moment ago.
Worked example
Input prefix = [0] logits = [[0, 3, 1], [0, 0, 4], [0, 0, 1]] max_new = 5 eos = 2 Output [0, 1, 2]
| Round | Last token | Its row of scores | Picked | Sequence |
|---|---|---|---|---|
| 1 | 0 | [0, 3, 1] | 1 | [0, 1] |
| 2 | 1 | [0, 0, 4] | 2 | [0, 1, 2] |
| 3 | 2 | - | Stop: 2 is eos | [0, 1, 2] |
Five new tokens were allowed, but only two were written. The end token stopped the loop early.
Why it matters
The workshop's GPT uses this loop with two changes. It has no end token, so it always writes a fixed number of tokens. And it draws each token at random, likelier tokens more often, instead of taking the top one.
Your turn
Extend a sequence of token IDs by greedy decoding from a bigram score table.
prefix- the token IDs to start from; at least one
logits- a square table: row
iholds the scores for the token after tokeni max_new- the largest number of tokens to add; 0 or more
eos- the ID of the end token
- Returns
- The whole sequence: the prefix followed by the new tokens. Nothing is added once the sequence ends with
eos.
Example 1
Input prefix = [0] logits = [[0, 3, 1], [0, 0, 4], [0, 0, 1]] max_new = 5 eos = 2 Output [0, 1, 2]
After 0 the best score is at position 1. After 1 it is at position 2, which is the end token.
Example 2
Input prefix = [2] logits = [[0, 1, 2], [0, 1, 2], [0, 1, 2]] max_new = 4 eos = 2 Output [2]
The prefix already ends with the end token, so nothing is added.
Example 3
Input prefix = [0] logits = [[1, 1], [0, 1]] max_new = 2 eos = 1 Output [0, 0, 0]
Row 0 is a tie between positions 0 and 1, and the smaller wins. Token 0 is picked twice, and then the limit of 2 is reached.
Hints
Each hint gives away a little more. Open one only when you need it.
Hint 1: Where to start
Copy the prefix into a new list, then loop at most
max_newtimes. Each pass adds one token or stops.Hint 2: The key step
At the top of each pass, check whether the last token is
eos. If not, take the rowlogits[last]and find the index of its largest value.Hint 3: The outline
Repeat
max_newtimes: ifresult[-1] == eos, break. Otherwise setrow = logits[result[-1]]and appendrow.index(max(row)):max(row)is the largest score andindexgives the first position that holds it. Returnresult.
Solution
From here on the page gives the method away. If you are still solving, go back to the hints.
Step by step
- Work on a copy, so the caller's prefix is not changed.
list(prefix)makes a new list. Appending to it leaves the original as it was. - The end token means the text is finished. Checking at the top of each pass covers both cases: an end token already in the prefix, and one appended on the pass before.
- The scores for what follows the last token. A bigram model looks only at the last token. A GPT would run the whole sequence through the network here.
- Greedy: take the position of the largest score.
max(row)is the largest score, androw.indexreturns the first position that holds it. On a tie that is the smallest ID.
The same numbered steps are the comments in the solution.
Python
def solve(prefix, logits, max_new, eos):
# 1. Work on a copy, so the caller's prefix is not changed
result = list(prefix)
for _ in range(max_new):
# 2. The end token means the text is finished
if result[-1] == eos:
break
# 3. The scores for what follows the last token
row = logits[result[-1]]
# 4. Greedy: take the position of the largest score
best = row.index(max(row))
result.append(best)
return resultWatch out
- Always cap the number of new tokens. A model that never produces the end token would otherwise loop for ever.
max(row)gives the largest score, not its position. The token ID is the position.
Check yourself
What does autoregressive generation mean?
The model writes one token at a time, and each new token is added to the input for the next step.
What are the two ways the generation loop stops?
It reaches the limit on new tokens, or the sequence ends with the end token.