|
Download notes/project-report.md from 42e/mathcompose: direct link, hf CLI and curl.
- Browser
- Download file 10.8 kB
-
https://huggingface.co/42e/mathcompose/resolve/main/notes/project-report.md
- Command line
-
hf download hf://42e/mathcompose/notes/project-report.md
-
curl -L -o project-report.md https://huggingface.co/42e/mathcompose/resolve/main/notes/project-report.md
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`. | |