Research log Small Model Experimentation
GitHub

Qwen3.5-4B Oracle-Distilled Semantic Verifier

Training a code judge helps at home

The one idea you need

Several draft programs all pass the one test you can run, but the real grading tests stay hidden. A small model plays judge, betting which draft secretly passes. It learns by studying past programs already labeled by whether they cleared those hidden tests.

The question

When several candidate programs all pass the one test you can see, can a small model learn to pick the one that secretly passes the hidden tests?

What we found

Yes, but only on home turf. On the problem set it trained on, the trained judge picked a genuinely-correct program 81% of the time, up from 72% for the same model untrained and just 44% for grabbing the first candidate that passes the visible test. On a fresh, unfamiliar problem set, though, the untrained model judged better, 94% versus 90% — training sharpened its home instinct while dulling its general one.

Why it matters

If you fine-tune a small model to judge which code is correct, expect gains only on problems resembling your training set. An off-the-shelf model may already be the stronger judge elsewhere, so train on broad, varied code.

Right program picked, familiar problems44% → 81%grabbing the first visible-passing candidate versus the trained judge
Trained versus untrained, familiar problems72% → 81%the extra gain training added on home problems
Unfamiliar problems: who judged better94% vs 90%the untrained model beat the trained one on a fresh problem set
Best any judge could do100%a perfect picker always finds a hidden-test-passing program in the pool
On this page
  1. Results at a glance
  2. Overview
  3. Report
    1. Objective
    2. Data
    3. Key Result
    4. Readout
    5. Overall
    6. Interpretation
    7. Figures
    8. Reproduction
  4. Experiment log
  5. Figures
  6. Data files
  7. Reproduce
  8. Related

Results at a glance 2

How often each way of picking lands a fully-correct program

How to read

Bars show how often the chosen program also passes the hidden tests (taller is better) across six picking methods, from naive rules to the two model judges to a perfect picker. Two colors compare the familiar problem set against a fresh one.

0%25%50%75%100%shortest visible-passshortest visible-pass25%32.3%random visible-passrandom visible-pass40.6%64.5%first visible-passfirst visible-pass43.8%77.4%frozen base verifierfrozen base verifier71.9%93.5%SFT verifierSFT verifier81.2%90.3%oracle ceilingoracle ceiling100%100%

Takeaway → The trained judge's bar is tallest on the familiar set but slightly shorter than the untrained judge on the fresh set; every judge towers over naive rules.

Data table
selection policyMBPP validationHumanEval transfer
shortest visible-pass25%32.3%
random visible-pass40.6%64.5%
first visible-pass43.8%77.4%
frozen base verifier71.9%93.5%
SFT verifier81.2%90.3%
oracle ceiling100%100%

Numbers from reports/summary_overall.csv

Technical framing

Which candidate-selection policy picks hidden-test-passing programs? — SFT beats the frozen verifier in-domain on MBPP (+9.4pp) but trails it by 3.2pp on HumanEval transfer; both crush naive selection.

Most programs that pass the visible test still fail the hidden ones

How to read

For each problem set, a tall bar counts programs per task that pass the visible test and a shorter bar counts how many of those also pass the hidden tests. A bigger gap means picking the right one is harder.

051015HumanEval (transfer)HumanEval (transfer)11.18.16MBPP validationMBPP validation5.663.12

Takeaway → On the familiar set barely half the visible-passing programs survive the hidden tests, versus about three-quarters on the fresh set, so choosing well matters more there.

Data table
Eval setVisible-test-passing candidatesAlso pass hidden tests
HumanEval (transfer)11.18.16
MBPP validation5.663.12

Numbers from experiments/qwen35_4b_oracle_distilled_semantic_verifier/reports/summary_overall.csv

Technical framing

How hard is candidate selection? Pool size vs hidden-pass candidates per task — MBPP pools are harder: barely half of visible-passing candidates also pass hidden tests, vs ~73% on HumanEval, so selection matters more there.

In the author’s words from the Overview · “Summary”

Train Qwen3.5-4B as a deployable verifier for Python candidate programs. The training oracle labels visible-test-passing candidates by hidden-test execution. At inference, the model sees only the task, public tests, public-test status, and candidate code; it ranks candidates by the probability that they pass hidden tests.

Overview

This top-level README was generated during repository normalization because the imported experiment did not include one.

How To Read

Start with the primary report, then inspect data/, reports/, analysis/, src/, and scripts/ as available. This folder remains self-contained; do not move its run data into shared directories.

Summary

Train Qwen3.5-4B as a deployable verifier for Python candidate programs. The training oracle labels visible-test-passing candidates by hidden-test execution. At inference, the model sees only the task, public tests, public-test status, and candidate code; it ranks candidates by the probability that they pass hidden tests.

Report

Rendered from reports/qwen35_4b_oracle_distilled_semantic_verifier_report.md

Objective

Train Qwen3.5-4B as a deployable verifier for Python candidate programs. The training oracle labels visible-test-passing candidates by hidden-test execution. At inference, the model sees only the task, public tests, public-test status, and candidate code; it ranks candidates by the probability that they pass hidden tests.

Data

  • Train records: 90 MBPP tasks.
  • Validation records: 32 MBPP tasks.
  • Transfer eval records: 31 HumanEval tasks.
  • Visible tests per task: 1.
  • Candidate implementations per task: up to 18.
  • Train verifier examples: 489.

Key Result

  • HumanEval first-visible baseline: 77.4%.
  • HumanEval base Qwen verifier: 93.5%.
  • HumanEval candidate-pool coverage: 100.0%.
  • HumanEval SFT verifier: 90.3%.
  • MBPP validation first-visible baseline: 43.8%.
  • MBPP validation base Qwen verifier: 71.9%.
  • MBPP validation SFT verifier: 81.2%.

Readout

The SFT verifier improves MBPP validation selection by +9.4 points over the frozen base verifier and +37.5 points over first visible-pass selection. On HumanEval it improves +12.9 points over first visible-pass selection, but trails the frozen base verifier by 3.2 points. This is a useful but not complete transfer result: oracle-distilled posttraining teaches an in-domain candidate verifier, while the frozen model is already a very strong out-of-domain verifier under this candidate-generation setup.

Overall

DatasetPolicyCandidate-pool coverageSelected hidden-passCoverage capturedVisible candidatesHidden-pass candidates
humanevalFirst visible-pass100.0%77.4%77.4%11.138.16
humanevalShortest visible-pass100.0%32.3%32.3%11.138.16
humanevalRandom visible-pass100.0%64.5%64.5%11.138.16
humanevalOracle coverage100.0%100.0%100.0%11.138.16
humanevalBase Qwen verifier100.0%93.5%93.5%11.138.16
humanevalSFT Qwen verifier100.0%90.3%90.3%11.138.16
mbppFirst visible-pass100.0%43.8%43.8%5.663.12
mbppShortest visible-pass100.0%25.0%25.0%5.663.12
mbppRandom visible-pass100.0%40.6%40.6%5.663.12
mbppOracle coverage100.0%100.0%100.0%5.663.12
mbppBase Qwen verifier100.0%71.9%71.9%5.663.12
mbppSFT Qwen verifier100.0%81.2%81.2%5.663.12

Interpretation

The primary question is whether oracle-labeled posttraining teaches a semantic candidate verifier that captures candidate-pool coverage under leak-free public evidence. The answer here is mixed. The adapter clearly improves in-domain MBPP selection, which means the hidden-test labels provide a learnable signal. The HumanEval result is weaker: SFT remains above simple selection baselines, but the frozen Qwen verifier is better on this eval set. The most likely reading is that this small MBPP-only SFT run partly overfits the mutation and task distribution instead of improving the model's general verifier prior. The next iteration should either train on a larger and more varied code corpus, or distill preferences from a stronger oracle while preserving the frozen model's general-code prior.

Figures

Reproduction

python scripts/build_dataset.py --mbpp-train 90 --mbpp-valid 32 --humaneval-eval 31 --visible-tests 1 --candidate-count 18
python scripts/eval_verifier.py --records data/mbpp_valid_records.jsonl --policy first --name first_visible --out reports/eval/mbpp_first_visible.json
python scripts/eval_verifier.py --records data/mbpp_valid_records.jsonl --policy shortest --name shortest_visible --out reports/eval/mbpp_shortest_visible.json
python scripts/eval_verifier.py --records data/mbpp_valid_records.jsonl --policy random --name random_visible --out reports/eval/mbpp_random_visible.json
python scripts/eval_verifier.py --records data/mbpp_valid_records.jsonl --policy oracle --name oracle_coverage --out reports/eval/mbpp_oracle_coverage.json
python scripts/eval_verifier.py --records data/humaneval_eval_records.jsonl --policy first --name first_visible --out reports/eval/humaneval_first_visible.json
python scripts/eval_verifier.py --records data/humaneval_eval_records.jsonl --policy shortest --name shortest_visible --out reports/eval/humaneval_shortest_visible.json
python scripts/eval_verifier.py --records data/humaneval_eval_records.jsonl --policy random --name random_visible --out reports/eval/humaneval_random_visible.json
python scripts/eval_verifier.py --records data/humaneval_eval_records.jsonl --policy oracle --name oracle_coverage --out reports/eval/humaneval_oracle_coverage.json
python scripts/eval_verifier.py --records data/mbpp_valid_records.jsonl --policy base --name base_verifier --out reports/eval/mbpp_base_verifier.json
python scripts/eval_verifier.py --records data/humaneval_eval_records.jsonl --policy base --name base_verifier --out reports/eval/humaneval_base_verifier.json
python scripts/train_verifier_sft.py --max-steps 220 --batch-size 2 --grad-accum 2
python scripts/eval_verifier.py --records data/mbpp_valid_records.jsonl --policy adapter --name sft_verifier --out reports/eval/mbpp_sft_verifier.json --adapter-dir /workspace/large_artifacts/qwen35_4b_oracle_distilled_semantic_verifier/models/verifier_sft_lora
python scripts/eval_verifier.py --records data/humaneval_eval_records.jsonl --policy adapter --name sft_verifier --out reports/eval/humaneval_sft_verifier.json --adapter-dir /workspace/large_artifacts/qwen35_4b_oracle_distilled_semantic_verifier/models/verifier_sft_lora
python scripts/make_report.py

Experiment log 7

Show the running log (7 entries)

Objective

Train a deployable Qwen3.5-4B semantic verifier for Python candidate programs. Training labels come from hidden-test execution. Inference uses only public task information and candidate code.

Initial Design

  • Training substrate: MBPP.
  • Transfer substrate: HumanEval.
  • Action interface: A = candidate will pass hidden tests, B = candidate will fail hidden tests.
  • Candidate pool: canonical solution, source mutations, and generic default-return variants.
  • Train examples: visible-test-passing candidates only, balanced to keep hard negatives.
  • Evaluation metric: selected hidden-pass rate and fraction of candidate-pool coverage captured.
  • Large artifacts: LoRA adapter and tokenizer under /workspace/large_artifacts/qwen35_4b_oracle_distilled_semantic_verifier.

Reproduction

python scripts/build_dataset.py --mbpp-train 90 --mbpp-valid 32 --humaneval-eval 31 --visible-tests 1 --candidate-count 18
python scripts/train_verifier_sft.py --max-steps 220 --batch-size 2 --grad-accum 2
python scripts/make_report.py

Run Notes

  • Dataset construction initially targeted 40 HumanEval eval records. With leak-free public tests and a required visible-passing hidden-failing hard negative, only 31 HumanEval tasks were usable. I kept the hard-negative and leak-free constraints and reduced the HumanEval eval target to 31.
  • MBPP train and validation targets remained at 90 and 32 records.

Dataset Manifest

  • MBPP train: 90 records, 489 balanced verifier examples, 5.86 visible-passing candidates per task on average.
  • MBPP validation: 32 records, 181 verifier examples, 5.66 visible-passing candidates per task on average.
  • HumanEval eval: 31 records, 345 verifier examples, 11.13 visible-passing candidates per task on average.
  • Candidate-pool coverage is 100% for all retained splits.
  • HumanEval skips before reaching 31 usable records: 36 tasks had no public doctest-style tests, 33 had no visible-passing hidden-failing hard negative.

Evaluation Results

DatasetPolicySelected hidden-pass
MBPP validationFirst visible-pass43.8%
MBPP validationShortest visible-pass25.0%
MBPP validationRandom visible-pass40.6%
MBPP validationFrozen Qwen verifier71.9%
MBPP validationSFT Qwen verifier81.2%
MBPP validationOracle coverage100.0%
HumanEvalFirst visible-pass77.4%
HumanEvalShortest visible-pass32.3%
HumanEvalRandom visible-pass64.5%
HumanEvalFrozen Qwen verifier93.5%
HumanEvalSFT Qwen verifier90.3%
HumanEvalOracle coverage100.0%

Interpretation

The SFT verifier learned a real in-domain selection signal: MBPP validation improved from 71.9% for the frozen Qwen verifier to 81.2% for the adapter. The HumanEval result is weaker. SFT stayed above simple baselines, but it underperformed the frozen Qwen verifier by 3.2 points. This suggests the small MBPP-only oracle-distillation run partly overfit the candidate mutation and task distribution instead of cleanly improving the general code-verification prior.

The next iteration should keep the oracle-labeled verifier idea, but train on a broader code-candidate distribution and include an explicit regularizer or distillation term that preserves the frozen model's strong HumanEval behavior.

Figures 4

coverage captured
coverage captured · reports/figures/
selected hidden pass
selected hidden pass · reports/figures/
selection vs coverage
selection vs coverage · reports/figures/
verifier sft loss
verifier sft loss · reports/figures/

Data files 8

Result tables and metrics copied from the experiment folder — preview inline or open the raw file.

Reproduce

Run steps are documented inside the experiment folder (README and scripts).

Browse the experiment folder on GitHub ↗