Where the numbers live
Last post covered:
- The loop. Guess the next token, measure how wrong the guess was, nudge every number a little in the direction that would have made it less wrong, repeat.
- The training data. Eighteen vintage computer names, split into words, sorted and numbered: 29 tokens. COMMODORE is 13 because it is thirteenth in that list. The number is a label, not a quantity.
- The window. Two tokens wide. The model’s only job is to guess the token that comes next, so the eighteen names become 58 examples.
- The parameters. 290 numbers that training changes.
- Twenty epochs. Enough training to invent plausible names, not so much that it repeats the training data.
This post is about the 290 numbers: where they live, what each one does, and what one training step does to them.
The token number is not one of them. The arithmetic is on three numbers per token, and they live in a table.
Two tables
Two tables, because the context window is two tokens wide and each position gets its own.
Each table has 29 rows, one per token, and each row holds three numbers. When the window holds END then COMMODORE, the machine fetches the END row from the first table and the COMMODORE row from the second table, and adds them:
position 1, <END> -0.0286 -0.0187 +0.0583
position 2, COMMODORE -0.0009 -0.0266 -0.0168
------- ------- -------
add them -0.0295 -0.0453 +0.0415

Those are the real rows, exported from the model before training by the Python that organized the experiments and pulled every number in this post out of a run; the repository map says where everything is. The three numbers at the bottom are what the training step works on.
Each row of three is a token’s embedding, placing it among other tokens according to their proximity. Using just one number in isolation wouldn’t help the model decide what should come next. Three numbers put it in a space. Think of them like coordinates in 3D space, so each token is a point in a box. To start they appear at random positions.
Here is the second table after 20 epochs, having been nudged into clusters:

APPLE and ATARI are 0.12 apart. COMMODORE is 0.44 from ATARI and 1.13 from 64. Nothing in the numbers says what a maker is. The makers ended up near each other because the same kind of token follows them. COLOR, BBC, ZX and MODEL sit high up on their own for the same reason: each is followed by another word, COMPUTER, MICRO, SPECTRUM, 100. That is what this space records: not what the words mean, but how and where they are encountered together. END is left off the figure because it sits far above everything else, and it should: it is the start of a name, and nothing else is.
COMMODORE also has a different row in each table. In position 1 its row is +0.0781, +0.0250, +0.0173. In position 2 it’s the row above. So the row places the token among the others and by where it sits relative to the token before it: the embedding is the token in its context. That is why the two words are everywhere in this field. A name becomes a position, and the position is where all the arithmetic happens.
Two positions, 29 tokens, three numbers each: 174 numbers, most of the model.
Why three?
GPT-3’s embedding is 12,288 numbers long where ours is three. Same idea, more room.
Every extra number is another 87 multiplies per example: 29 to score the tokens and 58 more in the training step. The 6809 has no multiply for 16-bit numbers, so each one is a routine built from its 8-bit MUL instruction, and I measured that routine on the machine at 133 cycles, about 150 microseconds. I priced the widths before choosing:
| numbers per word | parameters | multiplies to train | time in multiplies |
|---|---|---|---|
| 1 | 116 | 100,920 | 15 s |
| 2 | 203 | 201,840 | 30 s |
| 3 | 290 | 302,760 | 45 s |
| 4 | 377 | 403,680 | 60 s |
| 5 | 464 | 504,600 | 75 s |
| 6 | 551 | 605,520 | 90 s |
The run you watched last time took about 100 seconds to reach PRESS ANY KEY, and 45 of them were multiplies. The rest is computing probabilities, the updates and the display.
Six would probably still have fit my three-minute budget, but three was enough. Three numbers per token told 29 tokens apart well enough that the extra width never earned its cost in clock time.
The other choice I priced was the one I built first and threw away. Make a token a single character instead of a whole word and the model has to predict every letter: 12,859,560 multiplies. At eleven cycles each, the cost of the bare MUL instruction and the floor I priced it at, that is 158 seconds against a 180-second budget. At what the math costs in practice, it is half an hour.
What is a parameter?
It’s just something that can change in the model.
2 window positions x 29 tokens x 3 numbers = 174 the two tables
29 tokens x 3 numbers = 87 a row for every token it can predict
29 tokens = 29 a starting nudge per token
----
290
This model has 290. GPT-3 had 175 billion; DeepSeek-V3 has 671 billion. But the thing I enjoyed most about doing all this was seeing what I could do at the very small end of the scale.
In this computer field, we seem to get quickly obsessed with scale, buying the biggest computer, the biggest graphics card, training the biggest model. The whole world has gone crazy on this with gpu compute datacentres.
I enjoy using this old hardware because I think it’s easy to become wasteful these days, and there’s no challenge in that. Especially if you have money. Constraints make me more creative, teach me more about how things can work.
A score for every token
The 87 and the 29 in that count are the scoreboard: for every token the model might predict, three numbers called its weights, plus its starting nudge. To score a token, multiply the three context numbers by the token’s three weights and add the nudge. Do it 29 times and every token has a score.
That’s 87 multiplies, and the 6809’s MUL instruction multiplies two unsigned 8-bit numbers. Each weight is stored as 16 bits but scores with its top byte, the context number goes in as all 16, and one product takes two MUL instructions plus a step to fix the sign. Next post shows the 6809 doing one.
Before training all 290 numbers are random and small, so the scores are all near zero. Here are the shares they turn into for the window END, COMMODORE, where the right answer is AMIGA:
TRS-80 3.49% <- the largest share, barely
128 3.48%
ZX80 3.48%
ATARI 3.48%
...
AMIGA 3.45% <- the right answer, in the middle of the pack
Twenty-nine tokens, mostly indistinguished.
Scores become shares
Those percentages are what a score turns into once you push it through a softmax.
Suppose three tokens have raw scores of 3, 2 and 1. A score of 3 doesn’t mean 3%, or three votes. Softmax makes every score a positive weight, then divides each by the total, so the results are shares of 100%: about 67%, 24% and 9%. It keeps the order and it exaggerates the lead.
Why “soft”? A hard maximum would give the winner everything. Softmax lets the strongest choice lead while the others stay possible.
Softmax doesn’t choose. It hands you shares, and something else has to draw.
On the CoCo the shares add up to 256 instead of 100, so every token owns a stretch of a line from 0 to 255 as wide as its share. The machine draws one byte, and whatever stretch it lands on is the next token. That’s the die.
Training is a nudge
Back to the window END, COMMODORE, where the right answer is AMIGA and the model gave it 3.45%. Training is one step, done over and over: score, compare with the right answer, nudge every weight a little in the direction that would have raised the right answer’s share. Here is the step, with the real numbers.
AMIGA should have had 100% and got 3.45%, so the model was wrong by 0.9655. Every weight’s change is that, times a rate, times what the weight contributed.
Take AMIGA’s three weights from the scoreboard. To make AMIGA’s score, each was multiplied by one of the three context numbers, minus 0.0295, minus 0.0453 and plus 0.0415, the sum of the two rows fetched from the tables at the top of this post. Each weight’s contribution was its own context number, so each gets nudged by that number, scaled. The scale is the same for all three: 0.0625, a rate I chose, times 0.9655, how wrong the model was.

The rate, 0.0625, is a sixteenth. I chose it because dividing by sixteen is four shift instructions on a 6809.
The same step reaches back into the two tables, too. The END row in position 1 and the COMMODORE row in position 2 both get nudged, by how much each of the three context numbers contributed to every score that came out wrong. After the step, fetch and add those rows again and you get minus 0.0118, minus 0.0561, plus 0.0477. Those rows started random. Nudges like this are what make them mean something.
AMIGA now has 3.68%, up from 3.45. That is the size of one step, and there are 1,160 of them in a run. Every one of the 290 parameters moves by this rule.
Roll it
Here is the die being rolled, from the run you watched last time, after all 1,160 of those steps. Seed 6809 drew three bytes: 50, 12, 119.
The first draw, 50, landed on COMMODORE, which owned 27 of the 256. TANDY owned 131, more than half the line, and was not drawn. The second draw, 12, landed on 128, which owned 17; the widest stretch there was PET’s, 25. The third draw, 119, landed on END, which by then owned 228 of 256.

COMMODORE 128. A real name; it’s in the training data. It’s also the first name on the screen at the end of the video.
Seed 6810 drew 177, 110 and 29 from the same table, and got TANDY COMPUTER. Same weights, same rows, different bytes. The eleven names on that screen are eleven paths through one table.
Two things about the die. For the first two tokens, END’s stretch is handed to the widest, so a name is never one word long; that rule is in the code, not the weights. And you don’t have to roll. Take the widest stretch every time and you get the same name every time: greedy decoding, which is what temperature zero means. Temperature rescales the scores before they become shares. Higher narrows the fat stretch and widens the thin ones, lower does the reverse, and zero is greedy.
This is why it’s useful to think in probabilities. The model never answers. It hands over shares, and a draw picks. Take the biggest every time and you get one answer forever; draw, and you get variety, and now and then TANDY ZX80. It’s the die.
Two hundred and ninety numbers. A hundred and seventy-four are a lookup table, a hundred and sixteen are a scoreboard, and a byte from a die picks the word.
Next post: what 1,160 of those nudges do, epoch by epoch, and the 6809 instructions that do one of them.