mathcompose / notes /project-report.md
42e's picture
Upload folder using huggingface_hub
ca5eeeb verified
|
Raw
History Blame Contribute Delete
10.8 kB

Can a small model check math better than it can do math?

A write-up of what we tried, why, and what we found. It builds on the two research notes in this folder (brainlift.md and research-small-vs-large-math.md) and the runs saved under results/.

The question

We wanted to take a small open model, fine-tune it on data we generate, and get it to reliably do one narrow thing that a plain prompt cannot: read a step-by-step math solution and point to the first step that is wrong. This is usually called process verification. We chose it because the background reading suggested it is one of the few math tasks where a small specialist has a real chance against a much larger model.

The reason is the generator-verifier asymmetry. Checking a solution is generally easier than producing one, so a model that is weak at solving competition problems can still be useful at catching mistakes in someone else's work. If that holds, a small verifier is a sensible bet.

We wrote the target as a spec so that a stranger could grade any output. Given a problem and a numbered solution, the model returns the index of the first incorrect step, or -1 if every step is correct. We score this on ProcessBench, which reports the harmonic mean of accuracy on erroneous solutions and accuracy on correct ones. GPT-4o scores about 61.9 there, so that was the number to have in mind.

What the reading told us going in

Three findings from the research notes shaped the plan.

Data matters more than the training run. Results like LIMA and s1 come mostly from a small, carefully chosen set of examples, so we put our effort into generating and filtering data rather than tuning hyperparameters.

You should distill the process, not the style. Orca points out that copying a teacher's final answers teaches surface imitation while copying the reasoning teaches the actual skill. We ended up learning this the hard way.

Scale is a real constraint. The math-specific note was blunt that most "small beats large" results live at 7B, and that genuine sub-4B wins are thin and usually need extra machinery. The clearest sub-4B verifier, GenPRM at 1.5B, only beats GPT-4o when it also runs generated Python to check arithmetic and votes over several samples. We noted this and started at 1.5B anyway, partly to see the ceiling ourselves.

How we built it

The pipeline has three parts, and we reused all three across every experiment.

Data. We took step-level labels from PRM800K, which are human judgments of where a solution first goes wrong. For each solution a frontier model (Claude, through a company gateway) wrote a paragraph-by-paragraph critique. We removed any training problem that also appears in ProcessBench or MATH-500, so we were not training on the test set.

Training. QLoRA on Qwen2.5-Math, first the 1.5B instruct model and later the 7B. We trained the verifier as an ordinary generative model with supervised fine-tuning. The version of TRL we used has no dedicated process-reward trainer, and a generative verifier is what the GenPRM result used, so this was both the practical and the principled choice.

Evaluation. ProcessBench for the main number and PRMBench as a harder, adversarial check. Both use exact, objective scoring, so there is no model acting as judge and no judge bias to worry about.

What happened

The first data recipe did not work

Our first version told the teacher the correct answer and asked for a matching critique. This was a mistake. The critiques read well, but they were written backward from the answer, so they contained no real error-finding. The 1.5B model trained on them picked up the format and not the skill. On ProcessBench it caught errors about as often as the untuned base, and the only thing it clearly improved at was confidently declaring solutions correct. It learned to say "looks fine." That is the failure Orca describes.

The second recipe was honest but hit a wall

We rebuilt the data so the teacher critiqued each solution without seeing the answer, and we kept a critique only when its predicted error index matched the PRM800K label. This is rejection sampling, and it gave us critiques that actually located the error. The keep rate was about 77 percent, and the kept set came out roughly balanced between erroneous and correct examples on its own. We also raised the LoRA rank, since a genuinely hard skill should need more capacity than a small low-rank update.

It still did not move the score. At 1.5B the model kept defaulting to "all correct," predicting -1 about 90 percent of the time on the gsm8k subset. More data and more capacity did not help, which usually means the problem is capability rather than data.

Why it was stuck

The failure was not uniform across the test set, and that was the useful part. On PRMBench the same model flagged an error about 41 percent of the time, much more often than on gsm8k. The difference is the kind of error each set contains. PRMBench's injected errors are often structural, like circular reasoning or a step that contradicts an earlier one, and those can be caught by reading carefully. The gsm8k errors are arithmetic, and a 1.5B model cannot reliably redo the arithmetic, so it falls back on "looks fine." This matches GenPRM, which needed a Python interpreter for exactly this reason.

Two things that helped

We tried the code-execution idea first, because it can be run at inference on a model we already had. We let the verifier write short Python snippets, ran them, and fed the output back before it committed to an answer. On the 7B base this roughly doubled error-localization on gsm8k, from about 11 to 21 (accuracy on erroneous steps), which is the arithmetic-heavy split where we expected it to matter.

Moving from 1.5B to 7B helped on its own. The untuned 7B stopped collapsing to "all correct" on the harder subsets and localized errors at a real rate, where the 1.5B could not. This is the plainest evidence we have for the "wins live at 7B" point from the note.

Results so far

Numbers are ProcessBench F1 on a 100-example sample per subset, greedy decoding, on the 0-100 scale (so GPT-4o's 61.9 is the reference). These are sensitive to decoding settings; we say more about that below.

model gsm8k math olympiad omnimath avg notes
1.5B base ~16 prompt alone is weak
1.5B tuned (v2) ~16 collapses to -1 (~90%)
7B base 12-21 ~14 ~13 ~9 ~12-21 stops collapsing on hard subsets
7B base + code-exec 32.0 24.2 ~28 calculator ~doubles it; gsm8k acc-error ~11 to ~21
7B tuned pending final eval to fill in
7B tuned + code-exec pending expected best

The gsm8k -1 rate tells the story cleanly: 1.5B tuned sits around 0.90, while the 7B base is 0.83 on gsm8k but drops to about 0.21-0.25 on olympiad and omnimath, meaning it actually attempts to localize on the harder problems.

What we take from this

The honest headline is narrower than "small model beats big model," and that is fine. A small fine-tuned model can help on the easy side of the generator-verifier asymmetry, but only where the check is cheap for the model to run. For structural reasoning errors a 1.5B model can contribute. For arithmetic it cannot, at least not without a calculator, and adding the calculator is what closes most of the gap. Capability (7B) and tools (code execution) push in the same direction, which is the direction GenPRM already pointed.

On the project's own framing, the dataset really was the deliverable, but not the way we first assumed. The first dataset was large and clean and still taught the wrong thing, because the examples encoded justification instead of detection. Fixing what the examples taught mattered more than making more of them.

We also spent more time on plumbing than expected. Getting QLoRA, the eval harness, and the Colab setup to behave took several rounds of debugging, and a few of our early numbers were held down by eval bugs rather than by the model (a repetition penalty that fought the verifier's own format, a token budget that cut long critiques before they finished, and a scale mix-up where our fractions were sitting next to a percentage). We mention this because it changes how much to trust any single run.

Evaluations

We ran these:

  • ProcessBench, base versus tuned, on all four subsets. This is the main number and the base-versus-tuned comparison the assignment asks for.
  • PRMBench, which injects errors of specific types. This is the robustness check, and it is where we found that the model does better on structural errors than arithmetic ones.
  • A prediction-distribution readout (how often the model says -1, gives an index, or fails to parse). This is a calibration check, and it is what exposed the "collapse to all-correct" failure that a single F1 number hides.
  • A code-execution ablation, the same model with and without the Python tool. This isolates the arithmetic-verification effect.

Others that fit the assignment's rubric and are worth running:

  • Test-time scaling with majority voting (Maj@k). GenPRM's headline needs it. Our harness supports it, but it only helps once the base verdicts are decent, so it is a later step.
  • The composition eval, where the verifier reranks a separate generator's samples and we measure the lift in final-answer accuracy. This is the most direct "why does this matter" test, since a verifier is only useful if it improves a downstream result. We have the pieces but have not built the generator half yet.
  • Consistency under perturbation. Take a solution the model handles, rename variables or reorder independent steps, and check that the verdict does not change. This targets the consistency dimension of the rubric and the fragilities the brainlift lists.
  • A contamination-controlled held-out set. We already deduplicate training against the eval sets, but a fresh set would make the numbers harder to argue with.

What we would do next

The verifier is only half of the original plan. The other half is a generator, and the point of having both was to measure the asymmetry directly by having the verifier rerank the generator's samples. We have the code for this but have not run it.

After that, the clear next steps are the full GenPRM recipe on the 7B, meaning code execution together with majority voting, and a held-out set to rule out contamination.

Artifacts

  • Datasets and model on Hugging Face: 42e/mathcompose-verifier (data), 42e/mathcompose-verifier-7b (the tuned adapter), 42e/mathcompose (code snapshot).
  • Eval outputs in results/, and the earlier detailed failure analysis in results/M1_verifier_findings.md.