The problem in brief
Each row is a short arithmetic story in English ("a school has 485 pupils, 232 are girls, how many boys?") and you must output the numerical answer. Every answer comes from at most three of the numbers in the text combined with , , , , but the text may contain extra numbers that are not used. You get 740 solved stories for training and 333 unsolved ones for the test. The rules are part of the challenge: pretrained encoder-only or encoder-decoder Transformers are allowed, decoder-only models (GPT-style LLMs) are forbidden, and recurrent networks are encouraged.
The data comes from the Kaggle competition archive. The test answers are private and we never submitted anything, so every score below is measured on a held-out part of the 740 training stories.
How you are scored
Plain accuracy, an answer counts only if it is exactly right:
What to aim for. The organisers publish no threshold. The public leaderboard of this simulation round has five entries, and the best one is 0.30 (the others: 0.27, 0.13, 0.06, 0.00). It is a hard task for a two-hour exam with these rules.
First look at the data
The stories are short (about 50 tokens), have 2 to 5 numbers (most often 3), and every training answer is a positive integer. Many stories are near-copies of each other with one twist: "how many more crayons than erasers", "how many did he give", "how many does his brother have". Changing one question word changes the operation, so a model has to read the question, not just recognise the topic.
A model that outputs a number directly has to do two jobs at once: understand the story and do the arithmetic. Transformers of the allowed size are bad at the second job. We checked: flan-t5-base asked zero-shot for the answer gets 5 % of the training stories right.
So we split the jobs. The computer is perfect at arithmetic; the model only has to say which numbers to combine and how. Replace each number by a placeholder in order of appearance:
A school has n0 pupils. There are n1 girls and the rest are boys. How many boys are there? →
n0 - n1
The training set gives answers, not equations, but we can recover an equation for free: enumerate every expression of up to three placeholders (an operand may be reused, as in "the girls plus the boys, who are the girls minus 281") and keep those that evaluate to the answer. 739 of the 740 stories are reachable this way.

The left panel shows the shapes: two-number equations dominate ( alone is 40 %), and only about 15 % need three numbers. The right panel shows a catch. Only 318 stories have exactly one equation that matches; for the others, several expressions hit the same number by coincidence (in a story like "James bought 12 pencils and gave 6 to his brother; how many does his brother have?", both n1 and n0 - n1 give 6). We pick the simplest match (fewest operands, then distinct operands), which is right most of the time, but some labels are wrong: this is the "spurious program" problem of weak supervision.
A first baseline
Treat it as classification over equation templates: TF-IDF of the masked story (words and word pairs) and a logistic regression over the 73 templates seen in training, scored by 5-fold cross-validation.
| model | accuracy (5-fold CV) |
|---|---|
always the most frequent template (n0 - n1) | 0.124 |
| TF-IDF + logistic regression, top template | 0.158 |
| same, first template that gives a positive integer | 0.268 |
The last line is the first trick that matters. The answers are counts of things, so they are positive integers. Going down the classifier's ranked list and skipping templates that give or adds 11 points. That alone almost reaches the best leaderboard score. But a bag of words cannot tell "how many more crayons than erasers" from "how many more erasers than crayons", which have the same words and opposite equations.
The key idea
Translate the story into an equation with a pretrained encoder-decoder, then let Python compute it. The rules allow encoder-decoder Transformers, and T5 is one: its encoder reads the whole story with attention (so word order and the link between "n1" and "girls" are visible), and its decoder writes a short sequence. Generating ( n0 + n1 ) - n2 token by token is far easier than generating "1455", because:
- The output vocabulary is tiny and the outputs are short: placeholders, four operators and brackets, at most 11 tokens. With 740 examples a pretrained model can learn this mapping; it could never learn multi-digit arithmetic from 740 examples.
- Placeholders make the problem invariant to the values. "Mary has n0 apples" is the same task whatever is. The model sees the same pattern across stories with different numbers, which multiplies the effective data.
- Beam search gives a ranked list of equations for free, and we can apply the integer check to it: take the most likely equation whose value is a positive integer. This filter uses knowledge the model does not have (the actual values) at zero cost.
We fine-tune flan-t5-small (77 M parameters), whose instruction tuning has already taught it to follow "Write the equation:" style prompts. The loss is the usual cross-entropy of the target equation tokens given the masked story:
The full solution
- Mask the numbers (regular expression
\d+(,\d{3})*(\.\d+)?, so "1,250" and "2.5" are one number) and remember their values. - Recover a label for each training story: the simplest expression over the placeholders (1, 2 or 3 operands, , both bracketings) that equals the answer.
- Fine-tune flan-t5-small: 30 epochs, batch 8, Adafactor with learning rate , inputs padded to a fixed 96 tokens.
- Decode with 10 beams and keep the first beam that is a valid expression with a positive integer value:
vals = [evaluate(e, numbers(q)) for e in beams] # Python does the arithmetic
pred = next((v for v in vals if valid(v)), vals[0]) # first positive integer, else top beam
- Train on all 740 stories and write
submission.csvfor the 333 test stories (solution.pydoes it; we did not submit, and the scores below come from the held-out runs).
A practical note, since it cost us time. On an Apple GPU (MPS), PyTorch caches a buffer for every new tensor shape. With dynamic padding, every batch has a new length and memory grew without limit: a flan-t5-base run reached 14 GB. Padding every batch to the same length keeps the footprint flat (about 1.6 GB for flan-t5-small). flan-t5-base needed more than 3 GB in any configuration we tried (full fine-tuning, or LoRA on the CPU at 4 GB), which was over our memory budget, so the final model is the small one.
Results
Accuracy on held-out training stories (official test labels are private). "Fold 0" is the same held-out 20 % (148 stories) for every row that mentions it; with 148 stories the standard error is about points.
| approach | evaluated on | accuracy |
|---|---|---|
| most frequent template | 5-fold CV | 0.124 |
| TF-IDF + logistic regression | 5-fold CV | 0.158 |
| TF-IDF + logistic regression + integer filter | 5-fold CV | 0.268 |
| flan-t5-base, zero-shot, direct numeric answer | all 740 | 0.053 |
| flan-t5-large + LoRA | (abandoned: 10 min per epoch on our machine) | |
| flan-t5-small fine-tuned, top beam only | fold 0 | 0.534 |
| flan-t5-small fine-tuned + integer filter (final) | fold 0 | 0.588 |
| flan-t5-base fine-tuned + filter (over memory budget) | fold 0 / fold 0 rerun / fold 1 | 0.662 / 0.723 / 0.662 |
| same, numbers kept next to placeholders ("n0 (485)") | fold 0 | 0.649 |
The final model gets 0.588 on the held-out 20 %, about twice the best public leaderboard entry (0.30). The bigger flan-t5-base reaches about 0.68 on average, so model size is the main lever left; showing the actual values to the model did not help. Two runs of the same base configuration on the same fold differed by 6 points, which tells you how noisy single 148-question evaluations are.

Where does it fail? By equation size (right panel), the model gets 71 % of one-number answers and 65 % of two-number equations, but only 11 % of three-number equations: those are rare in training (about 15 %) and require a longer chain of reasoning. About a third of the errors have the right shape but the wrong numbers (n2 - n1 instead of ( n2 - n0 ) - n1 for "6 fewer than Steven and 3 more than Jill"), and some "errors" are noisy labels: the training set contains stories whose given answer is not what the text says.
What to remember
- Do not make a neural network do arithmetic. Ask it for the program (here an equation) and execute the program exactly. This "predict the structure, compute the value" split is behind most strong math-word-problem solvers.
- Abstract away what does not matter. Replacing numbers by placeholders turns 740 different stories into a few dozen patterns the model can share across examples.
- Recover labels you are not given. The equations were not in the data, but a brute-force search over all small expressions created them. Keep in mind that such weak labels can be right for the wrong reason (spurious matches) and choose a tie-breaking rule on purpose.
- Use free knowledge as a filter. Answers are positive integers; re-ranking beams with this constraint gave +5 points on top of the model and +11 points on the TF-IDF baseline.
- Fixed shapes on GPUs. If memory grows from epoch to epoch, pad to a fixed length before blaming the model.
Note on the code. The full
solution.pybelow reproduces the setup that produced the reported score, but it was not re-run end to end after it was assembled.