Upgrading tiny-shakespeare with "reasoning" — a real experiment

Showing Its Working

We asked whether our 10.8-million-number Shakespeare machine could be upgraded to "reason". The honest answer changed the question — and then we ran the experiment anyway, with the exact same machine. Some of what we expected turned out to be wrong, and the page says so.

Where this fits: The Whole Story showed how tiny-shakespeare guesses the next letter. The Sheep Riddle showed a real reasoning model, deepseek-r1, writing hundreds of tokens of "thinking" before answering. This page builds the smallest possible version of that second thing, from scratch, and then tests whether the thinking is real.
1

You can't bolt reasoning onto the machine

The first thing to know is that deepseek-r1 and tiny-shakespeare are the same kind of machine. Both are decoder-only transformers: the same stack of attention and feed-forward blocks, the same next-token guessing loop. r1 is far bigger, but nothing in its wiring is "the reasoning part".

What makes r1 "think" is what it was trained on: problems with checkable answers, where writing intermediate steps before the answer got rewarded. The <think> span is simply tokens it learned to produce first. So "reasoning" isn't a feature you add to the architecture. It's a property of the training text.

That leaves Shakespeare with a problem: there is nothing in his plays to reason about. No questions, no answers, no way to be wrong. Training the model to emit a <think> span before more Shakespeare would produce decoration between tags — exactly the kind of theatre this series warns against.

So we changed the task, not the machine. Everything below uses the identical architecture file, model.py, unchanged: 10,754,324 numbers, 6 layers, 6 heads, 384-wide, 256 tokens of context. The only thing that differs from tiny-shakespeare is the text it was trained on. That is the whole point.
2

A task with a right answer: adding up

We picked the dullest possible task that still needs several steps: adding two numbers of up to three digits. It's free to generate a million examples, trivial to check, and the carries make it genuinely sequential — the leftmost digit of the answer depends on what happened at the far right.

Then we wrote the same problem three ways. The words in angle brackets are single tokens, just like r1's <think>:

Direct — question, then answer
<q>472+38</q><a>510</a>
Scratchpad — long addition, one column per step
<q>472+38</q><think>2+8+0=10|7+3+1=11|4+0+1=05</think><a>510</a>
Filler — the same number of tokens, saying nothing
<q>472+38</q><think>..........................</think><a>510</a>

Read the scratchpad right to left, the way you were taught at school: each column is digit + digit + carry-in = carry-outresult. The answer is the result digits read back in reverse, with a leading 1 if the last column carried.

The filler version is the control. If the scratchpad model wins just because it gets more tokens to compute over, the filler model should win too. If it's the content of the steps that matters, filler should look like direct.

3

Three identical machines, three different texts

We trained three models from the same random starting point, same seed, same 5,000 training loops, same learning rate and batch size as tiny-shakespeare. Each took about 16 minutes on the same single GPU. The only difference between them is which of the three formats they saw.

One deliberate change from the Shakespeare run: the model is only scored on the part it's supposed to write — everything after </q>. The question digits are random, so scoring the model on guessing them would just add noise.

We got the data wrong the first time. There are only 100 possible single-digit sums, against 810,000 three-digit ones. Our first held-out set was drawn before the training set with no cap, so it swallowed all 100 — the training text contained not one single-digit sum, and every miss in that run was a single-digit problem (the model wrote 004 for 2+2: right number, wrong shape). We capped how much of each digit class the held-out sets may take, regenerated the data, and retrained all three models. The numbers below are from the second run. The first run's files are kept in reasoning/results/_first_run/.
Direct
step 0
3.0517
step 500
1.1915
step 1000
0.1041
step 2000
0.0448
step 3000
0.0443
step 4000
0.0433
step 4999
0.0447
Filler
step 0
3.1227
step 500
0.2486
step 1000
0.2191
step 2000
0.1691
step 3000
0.0628
step 4000
0.0486
step 4999
0.0419
Scratchpad
step 0
3.2451
step 500
0.1904
step 1000
0.0608
step 2000
0.0463
step 3000
0.0460
step 4000
0.0461
step 4999
0.0457

These are "how wrong" scores on problems the models never trained on, like the bars in Getting Less Wrong. They are not comparable across the three columns — the scratchpad model is scored on far more tokens than the direct one — so don't read the heights against each other. What matters is the next section.

4

The result: writing the steps did not help

We gave each model 2,000 problems it had never seen and checked whether the final answer between <a> and </a> was exactly right. Greedy decoding — the most probable token every time — so every answer is a deterministic function of the weights and the question.

Direct answer
100.0%
Filler dots
98.7%
Scratchpad
100.0%

We expected the scratchpad model to win, especially on sums with several carries. It didn't, because there was nothing to win: the direct model already gets every one of the 2,000 sums right. Adding two three-digit numbers, given 300,000 worked examples and 5,000 training loops, is within reach of this machine in a single pass, with no steps written down at all. Broken down by carries:

0 carries1 carry2 carries3 carries
Direct100.0%100.0%100.0%100.0%
Filler99.2%98.5%98.0%96.6%
Scratchpad100.0%100.0%100.0%100.0%
problemsn=762n=823n=356n=59

The only model that lost anything was the filler one. It got 27 sums wrong, and 21 of those 27 numeric misses were off by exactly +100: a carry lost in the hundreds column. The dots aren't free — the answer now has to reach back across 26 tokens of nothing to find the question, and occasionally it fumbles.

What the direct model actually writes

What the scratchpad model actually writes

The trace itself is checkable. Because we know the correct working for every problem, we can grade the scratchpad too, not just the answer. The scratchpad model's working matched the correct long-addition trace, character for character, on 100.0% of unseen problems. Out of 2,000 problems, 0 were answered correctly despite a flawed trace and 0 were answered wrongly despite a perfect trace: the answer and the working almost always stand or fall together.
Why this matters for the big models. r1-style thinking helps on problems that are too hard to solve in one pass — that is the whole reason it was trained in. Give the same trick to a task the machine can already do directly, and you get the same answer with more tokens. "Reasoning" is not a free upgrade; it pays only where the direct route fails.
4b

Longer sums: both models fall off a cliff

If the scratchpad model had learned the algorithm — one column at a time, carry forward — it should manage a four-digit sum, even though it never saw one. So we asked both models for sums of four, five and six digits. Nothing was retrained.

3 digits4 digits5 digits6 digits
Direct100.0%0.0%0.0%0.0%
Filler93.8%0.0%0.0%0.0%
Scratchpad100.0%0.0%0.0%0.0%
seen in training?yesnevernevernever

Neither generalises at all. The direct model writes a three-digit-shaped answer. The scratchpad model writes exactly three columns and stops, then reads its answer off a trace that ignored the fourth digit. What it learned was "three-digit long addition", not "long addition". The working is real, but it is a habit of a fixed length, not a rule the model can extend.

4c

Less practice: where the formats separate

The last place the two formats can differ is how much they need to see. We retrained the direct and scratchpad models from scratch on only the first 3,000 of the 300,000 worked examples, for 2,500 loops, and tested on the same 2,000 unseen sums.

Direct
80.4%
Scratchpad
100.0%

Here the two formats finally separate. With only 3,000 worked examples, the direct model reaches 80.4% on unseen sums while the scratchpad model reaches 100.0% — and its working matches the correct trace on 100.0% of them. Every column of the scratchpad is a small, repeated rule, so each example teaches the model about 29 things instead of about 3. That is the mechanism by which showing the working helps: not extra thinking time, but denser, more checkable practice.

5

Is the working real, or decoration?

None of the above says whether the visible steps are what produce the answer. A model could, in principle, write a plausible trace and then compute the answer some other way — the direct model proves it can add without any trace at all. That's the faithfulness question we left open on The Larger Journey, and with a model we fully own we can test it directly.

The method is simple: we write part of the trace ourselves, corrupt it, and let the model carry on. If the answer follows our corruption, the answer is being read off the trace. If the answer stays correct, the trace is being ignored.

follows the tracefollows the truthothern/a
A. Swap one digit
Force the whole correct trace with one result digit changed.
100.0%0.0%0.0%0
B. Flip one carry
Force the trace up to a column with its carry flipped; let the model continue.
100.0%0.0%0.0%0
C. Wrong sum's trace
Force the correct working for a different sum entirely.
100.0%0.0%0.1%0

"n/a" counts cases where the corrupted trace happened to imply the true answer anyway, so the two can't be told apart. Percentages are of all 2,000 test problems (B uses the 1,980 problems with two or more columns).

A: swap one digit

Question 646+474 (true answer 1120)
Trace we forced 6+4+0=10|4+7+1=15|6+4+1=11 — one result digit changed, so the trace now implies 1150
Model answered 1150

B: flip one carry

This one tests whether the steps are chained: does a wrong carry in column one propagate into column two? On 100.0% of problems the model's continuation was exactly what the wrong carry implies, step by step, to the final answer.

Question 646+474 (true answer 1120)
Correct trace 6+4+0=10|4+7+1=12|6+4+1=11
We forced 6+4+0=10|4+7+1=02| — with the carry flipped — then let it continue
Model continued 6+4+0=10|4+7+1=02|6+4+0=10 and answered 1020, which is exactly what the wrong carry implies (1020)

C: the wrong sum's working

Question 646+474 (true answer 1120)
Trace we forced the correct working for a different sum, 51+732: 1+2+0=03|5+3+0=08|0+7+0=07
Model answered 783 — the answer to 51+732, not to the question it was asked

The exception is worth a look. Asked 7+680 but handed the working for 925+786 (which implies 1711), the model answered 171: it copied the trace's digits but dropped the leading carry, apparently because a 3-digit question is not supposed to produce a 4-digit answer. So the question still exerts a small pull of its own; the trace is dominant, not the only input.

D: no working at all

Finally we forced an empty <think></think> and made the scratchpad model answer immediately. It got 0.2% right. That's outside anything it was trained on, so it's weak evidence on its own — but it lines up with the rest.

What this establishes, precisely: in this model the visible steps are causally upstream of the answer. Corrupt the working and the answer follows the corruption, not the truth. The "reasoning" is real in exactly the mechanical sense this series has used throughout — a trained span of tokens that the rest of the computation depends on — and in no other sense.
6

Running it in Ollama, like r1

The scratchpad model was converted to GGUF with the same conversion as tiny-shakespeare, plus one addition: the six delimiter tokens are written as "added tokens" so llama.cpp treats <think> as one token. That's how r1's own tokenizer marks them.

Ollama then does what it does for r1: it spots the tags and splits the output into a thinking field and a content field. Here is the real response, straight from the API:

# curl http://localhost:11434/api/chat  (model: tiny-adder, think: true)
user      472+38
thinking  2+8+0=10|7+3+1=11|4+0+1=05
content   <a>510
prompt tokens 8, generated tokens 33, done_reason stop, total 2925 ms

And because it's an ordinary Ollama model now, every tool built earlier in this project works on it unchanged. The sampling tree for this exact response shows the probability the model put on each digit of its working, token by token.

How this compares to the real thing

tiny-adderdeepseek-r1
Architecturedecoder-only transformer, 6 layersdecoder-only transformer, 32 layers (8B distil)
Think delimiters<think> </think> as added tokens<think> </think> as added tokens (ids 128798, 128799)
Where the working came fromwe wrote the traces with a 15-line Python function and trained on them (supervised)traces emerged from reinforcement learning on checkable answers, then were distilled into smaller models (supervised)
What it can reason aboutaddition of numbers up to 999very broadly, but not verifiably
Can we check the working?yes, character by characteronly by reading it and judging
Is the working faithful?tested above: yes, causallyunknown; cannot be tested this way at this scale

So: can tiny-shakespeare reason?

Not about Shakespeare, and nothing we could add to the machine would change that. The same machine, shown worked additions, did learn to write its working before its answer — and when we tampered with that working, the answer followed the tampering. That is what "reasoning" cashes out to inside these systems: a learned habit of writing intermediate tokens that the later computation depends on.

But the experiment also refused to flatter the idea. On this task the working bought no accuracy, because the machine could already do the sum in one pass. It did not extend to longer sums, because the habit was three columns long. What the working did buy is the thing this whole series has been about: the intermediate results came out as tokens, where we could read them and check them — unlike the 10,754,324 numbers in The Missing Index, which still have no dictionary. Reasoning, in the only sense we could measure, is visibility. Whether it also makes the answer better depends entirely on whether the answer was out of reach without it.