Upload folder using huggingface_hub
Browse files- notes/project-report.md +190 -0
notes/project-report.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Can a small model check math better than it can do math?
|
| 2 |
+
|
| 3 |
+
A write-up of what we tried, why, and what we found. It builds on the two research
|
| 4 |
+
notes in this folder (`brainlift.md` and `research-small-vs-large-math.md`) and the
|
| 5 |
+
runs saved under `results/`.
|
| 6 |
+
|
| 7 |
+
## The question
|
| 8 |
+
|
| 9 |
+
We wanted to take a small open model, fine-tune it on data we generate, and get it to
|
| 10 |
+
reliably do one narrow thing that a plain prompt cannot: read a step-by-step math
|
| 11 |
+
solution and point to the first step that is wrong. This is usually called process
|
| 12 |
+
verification. We chose it because the background reading suggested it is one of the few
|
| 13 |
+
math tasks where a small specialist has a real chance against a much larger model.
|
| 14 |
+
|
| 15 |
+
The reason is the generator-verifier asymmetry. Checking a solution is generally easier
|
| 16 |
+
than producing one, so a model that is weak at solving competition problems can still be
|
| 17 |
+
useful at catching mistakes in someone else's work. If that holds, a small verifier is a
|
| 18 |
+
sensible bet.
|
| 19 |
+
|
| 20 |
+
We wrote the target as a spec so that a stranger could grade any output. Given a problem
|
| 21 |
+
and a numbered solution, the model returns the index of the first incorrect step, or -1
|
| 22 |
+
if every step is correct. We score this on ProcessBench, which reports the harmonic mean
|
| 23 |
+
of accuracy on erroneous solutions and accuracy on correct ones. GPT-4o scores about
|
| 24 |
+
61.9 there, so that was the number to have in mind.
|
| 25 |
+
|
| 26 |
+
## What the reading told us going in
|
| 27 |
+
|
| 28 |
+
Three findings from the research notes shaped the plan.
|
| 29 |
+
|
| 30 |
+
Data matters more than the training run. Results like LIMA and s1 come mostly from a
|
| 31 |
+
small, carefully chosen set of examples, so we put our effort into generating and
|
| 32 |
+
filtering data rather than tuning hyperparameters.
|
| 33 |
+
|
| 34 |
+
You should distill the process, not the style. Orca points out that copying a teacher's
|
| 35 |
+
final answers teaches surface imitation while copying the reasoning teaches the actual
|
| 36 |
+
skill. We ended up learning this the hard way.
|
| 37 |
+
|
| 38 |
+
Scale is a real constraint. The math-specific note was blunt that most "small beats
|
| 39 |
+
large" results live at 7B, and that genuine sub-4B wins are thin and usually need extra
|
| 40 |
+
machinery. The clearest sub-4B verifier, GenPRM at 1.5B, only beats GPT-4o when it also
|
| 41 |
+
runs generated Python to check arithmetic and votes over several samples. We noted this
|
| 42 |
+
and started at 1.5B anyway, partly to see the ceiling ourselves.
|
| 43 |
+
|
| 44 |
+
## How we built it
|
| 45 |
+
|
| 46 |
+
The pipeline has three parts, and we reused all three across every experiment.
|
| 47 |
+
|
| 48 |
+
Data. We took step-level labels from PRM800K, which are human judgments of where a
|
| 49 |
+
solution first goes wrong. For each solution a frontier model (Claude, through a company
|
| 50 |
+
gateway) wrote a paragraph-by-paragraph critique. We removed any training problem that
|
| 51 |
+
also appears in ProcessBench or MATH-500, so we were not training on the test set.
|
| 52 |
+
|
| 53 |
+
Training. QLoRA on Qwen2.5-Math, first the 1.5B instruct model and later the 7B. We
|
| 54 |
+
trained the verifier as an ordinary generative model with supervised fine-tuning. The
|
| 55 |
+
version of TRL we used has no dedicated process-reward trainer, and a generative verifier
|
| 56 |
+
is what the GenPRM result used, so this was both the practical and the principled choice.
|
| 57 |
+
|
| 58 |
+
Evaluation. ProcessBench for the main number and PRMBench as a harder, adversarial check.
|
| 59 |
+
Both use exact, objective scoring, so there is no model acting as judge and no judge bias
|
| 60 |
+
to worry about.
|
| 61 |
+
|
| 62 |
+
## What happened
|
| 63 |
+
|
| 64 |
+
### The first data recipe did not work
|
| 65 |
+
|
| 66 |
+
Our first version told the teacher the correct answer and asked for a matching critique.
|
| 67 |
+
This was a mistake. The critiques read well, but they were written backward from the
|
| 68 |
+
answer, so they contained no real error-finding. The 1.5B model trained on them picked up
|
| 69 |
+
the format and not the skill. On ProcessBench it caught errors about as often as the
|
| 70 |
+
untuned base, and the only thing it clearly improved at was confidently declaring
|
| 71 |
+
solutions correct. It learned to say "looks fine." That is the failure Orca describes.
|
| 72 |
+
|
| 73 |
+
### The second recipe was honest but hit a wall
|
| 74 |
+
|
| 75 |
+
We rebuilt the data so the teacher critiqued each solution without seeing the answer, and
|
| 76 |
+
we kept a critique only when its predicted error index matched the PRM800K label. This is
|
| 77 |
+
rejection sampling, and it gave us critiques that actually located the error. The keep
|
| 78 |
+
rate was about 77 percent, and the kept set came out roughly balanced between erroneous
|
| 79 |
+
and correct examples on its own. We also raised the LoRA rank, since a genuinely hard
|
| 80 |
+
skill should need more capacity than a small low-rank update.
|
| 81 |
+
|
| 82 |
+
It still did not move the score. At 1.5B the model kept defaulting to "all correct,"
|
| 83 |
+
predicting -1 about 90 percent of the time on the gsm8k subset. More data and more
|
| 84 |
+
capacity did not help, which usually means the problem is capability rather than data.
|
| 85 |
+
|
| 86 |
+
### Why it was stuck
|
| 87 |
+
|
| 88 |
+
The failure was not uniform across the test set, and that was the useful part. On
|
| 89 |
+
PRMBench the same model flagged an error about 41 percent of the time, much more often
|
| 90 |
+
than on gsm8k. The difference is the kind of error each set contains. PRMBench's injected
|
| 91 |
+
errors are often structural, like circular reasoning or a step that contradicts an
|
| 92 |
+
earlier one, and those can be caught by reading carefully. The gsm8k errors are
|
| 93 |
+
arithmetic, and a 1.5B model cannot reliably redo the arithmetic, so it falls back on
|
| 94 |
+
"looks fine." This matches GenPRM, which needed a Python interpreter for exactly this
|
| 95 |
+
reason.
|
| 96 |
+
|
| 97 |
+
### Two things that helped
|
| 98 |
+
|
| 99 |
+
We tried the code-execution idea first, because it can be run at inference on a model we
|
| 100 |
+
already had. We let the verifier write short Python snippets, ran them, and fed the
|
| 101 |
+
output back before it committed to an answer. On the 7B base this roughly doubled
|
| 102 |
+
error-localization on gsm8k, from about 11 to 21 (accuracy on erroneous steps), which is
|
| 103 |
+
the arithmetic-heavy split where we expected it to matter.
|
| 104 |
+
|
| 105 |
+
Moving from 1.5B to 7B helped on its own. The untuned 7B stopped collapsing to "all
|
| 106 |
+
correct" on the harder subsets and localized errors at a real rate, where the 1.5B could
|
| 107 |
+
not. This is the plainest evidence we have for the "wins live at 7B" point from the note.
|
| 108 |
+
|
| 109 |
+
### Results so far
|
| 110 |
+
|
| 111 |
+
Numbers are ProcessBench F1 on a 100-example sample per subset, greedy decoding, on the
|
| 112 |
+
0-100 scale (so GPT-4o's 61.9 is the reference). These are sensitive to decoding
|
| 113 |
+
settings; we say more about that below.
|
| 114 |
+
|
| 115 |
+
| model | gsm8k | math | olympiad | omnimath | avg | notes |
|
| 116 |
+
|---|---|---|---|---|---|---|
|
| 117 |
+
| 1.5B base | ~16 | | | | | prompt alone is weak |
|
| 118 |
+
| 1.5B tuned (v2) | ~16 | | | | | collapses to -1 (~90%) |
|
| 119 |
+
| 7B base | 12-21 | ~14 | ~13 | ~9 | ~12-21 | stops collapsing on hard subsets |
|
| 120 |
+
| 7B base + code-exec | ~32 | ~33 | | | | gsm8k acc-error ~11 to ~21 |
|
| 121 |
+
| 7B tuned | pending | | | | | final eval to fill in |
|
| 122 |
+
|
| 123 |
+
The gsm8k -1 rate tells the story cleanly: 1.5B tuned sits around 0.90, while the 7B base
|
| 124 |
+
is 0.83 on gsm8k but drops to about 0.21-0.25 on olympiad and omnimath, meaning it
|
| 125 |
+
actually attempts to localize on the harder problems.
|
| 126 |
+
|
| 127 |
+
## What we take from this
|
| 128 |
+
|
| 129 |
+
The honest headline is narrower than "small model beats big model," and that is fine. A
|
| 130 |
+
small fine-tuned model can help on the easy side of the generator-verifier asymmetry, but
|
| 131 |
+
only where the check is cheap for the model to run. For structural reasoning errors a
|
| 132 |
+
1.5B model can contribute. For arithmetic it cannot, at least not without a calculator,
|
| 133 |
+
and adding the calculator is what closes most of the gap. Capability (7B) and tools (code
|
| 134 |
+
execution) push in the same direction, which is the direction GenPRM already pointed.
|
| 135 |
+
|
| 136 |
+
On the project's own framing, the dataset really was the deliverable, but not the way we
|
| 137 |
+
first assumed. The first dataset was large and clean and still taught the wrong thing,
|
| 138 |
+
because the examples encoded justification instead of detection. Fixing what the examples
|
| 139 |
+
taught mattered more than making more of them.
|
| 140 |
+
|
| 141 |
+
We also spent more time on plumbing than expected. Getting QLoRA, the eval harness, and
|
| 142 |
+
the Colab setup to behave took several rounds of debugging, and a few of our early numbers
|
| 143 |
+
were held down by eval bugs rather than by the model (a repetition penalty that fought the
|
| 144 |
+
verifier's own format, a token budget that cut long critiques before they finished, and a
|
| 145 |
+
scale mix-up where our fractions were sitting next to a percentage). We mention this
|
| 146 |
+
because it changes how much to trust any single run.
|
| 147 |
+
|
| 148 |
+
## Evaluations
|
| 149 |
+
|
| 150 |
+
We ran these:
|
| 151 |
+
|
| 152 |
+
- ProcessBench, base versus tuned, on all four subsets. This is the main number and the
|
| 153 |
+
base-versus-tuned comparison the assignment asks for.
|
| 154 |
+
- PRMBench, which injects errors of specific types. This is the robustness check, and it
|
| 155 |
+
is where we found that the model does better on structural errors than arithmetic ones.
|
| 156 |
+
- A prediction-distribution readout (how often the model says -1, gives an index, or
|
| 157 |
+
fails to parse). This is a calibration check, and it is what exposed the "collapse to
|
| 158 |
+
all-correct" failure that a single F1 number hides.
|
| 159 |
+
- A code-execution ablation, the same model with and without the Python tool. This
|
| 160 |
+
isolates the arithmetic-verification effect.
|
| 161 |
+
|
| 162 |
+
Others that fit the assignment's rubric and are worth running:
|
| 163 |
+
|
| 164 |
+
- Test-time scaling with majority voting (Maj@k). GenPRM's headline needs it. Our harness
|
| 165 |
+
supports it, but it only helps once the base verdicts are decent, so it is a later step.
|
| 166 |
+
- The composition eval, where the verifier reranks a separate generator's samples and we
|
| 167 |
+
measure the lift in final-answer accuracy. This is the most direct "why does this
|
| 168 |
+
matter" test, since a verifier is only useful if it improves a downstream result. We
|
| 169 |
+
have the pieces but have not built the generator half yet.
|
| 170 |
+
- Consistency under perturbation. Take a solution the model handles, rename variables or
|
| 171 |
+
reorder independent steps, and check that the verdict does not change. This targets the
|
| 172 |
+
consistency dimension of the rubric and the fragilities the brainlift lists.
|
| 173 |
+
- A contamination-controlled held-out set. We already deduplicate training against the
|
| 174 |
+
eval sets, but a fresh set would make the numbers harder to argue with.
|
| 175 |
+
|
| 176 |
+
## What we would do next
|
| 177 |
+
|
| 178 |
+
The verifier is only half of the original plan. The other half is a generator, and the
|
| 179 |
+
point of having both was to measure the asymmetry directly by having the verifier rerank
|
| 180 |
+
the generator's samples. We have the code for this but have not run it.
|
| 181 |
+
|
| 182 |
+
After that, the clear next steps are the full GenPRM recipe on the 7B, meaning code
|
| 183 |
+
execution together with majority voting, and a held-out set to rule out contamination.
|
| 184 |
+
|
| 185 |
+
## Artifacts
|
| 186 |
+
|
| 187 |
+
- Datasets and model on Hugging Face: `42e/mathcompose-verifier` (data),
|
| 188 |
+
`42e/mathcompose-verifier-7b` (the tuned adapter), `42e/mathcompose` (code snapshot).
|
| 189 |
+
- Eval outputs in `results/`, and the earlier detailed failure analysis in
|
| 190 |
+
`results/M1_verifier_findings.md`.
|