File size: 5,359 Bytes
994182c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | #!/usr/bin/env python3
"""HumanEval pass@1 — a coding guardrail (did security tuning help or hurt general coding?).
Loads a model in-process (HF generate), prompts each HumanEval problem, extracts the
generated function, executes it against the official unit tests in a subprocess, and
reports pass@1. Emits reports/eval/<label>_coding.{json,md} in the same shape as the
other evals so base vs merged is directly comparable, and includes published reference
numbers for context.
Inference is forward-only (unaffected by any backward-kernel issue). Generated code is
executed in a subprocess with a timeout on a disposable GPU VM.
Usage:
python training/scripts/eval_coding.py --model Qwen/Qwen3.6-27B --label base_a100
python training/scripts/eval_coding.py --model /workspace/checkpoints/qwen36_a100_stage1_merged --label merged
"""
from __future__ import annotations
import argparse
import json
import re
import subprocess
import sys
import tempfile
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from eval_hf_model import load_model # noqa: E402
from intraining_eval import _generate # noqa: E402
# Published HumanEval pass@1 (greedy/0-shot, approximate, for context only).
REFERENCE = {
"Qwen2.5-Coder-32B-Instruct": 0.92,
"GPT-4o": 0.90,
"DeepSeek-V3": 0.91,
"Llama-3.1-70B-Instruct": 0.80,
"Qwen2.5-7B-Instruct": 0.84,
"CodeLlama-34B": 0.51,
}
CODE_BLOCK = re.compile(r"```(?:python)?\s*(.*?)```", re.DOTALL | re.IGNORECASE)
def extract_code(text: str, entry_point: str, prompt: str) -> str:
body = text.rsplit("</think>", 1)[-1] if "</think>" in text else text
m = CODE_BLOCK.search(body)
code = m.group(1).strip() if m else body.strip()
# If the model returned the full function, use it; otherwise treat it as the body
# continuation of the given prompt signature.
if f"def {entry_point}" in code:
return code
return prompt + "\n" + code
def run_program(program: str, timeout: int) -> bool:
with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False) as f:
f.write(program)
path = f.name
try:
proc = subprocess.run([sys.executable, path], capture_output=True, timeout=timeout)
return proc.returncode == 0
except Exception:
return False
finally:
try:
Path(path).unlink()
except OSError:
pass
def parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument("--model", required=True)
p.add_argument("--label", required=True)
p.add_argument("--report-dir", default="reports/eval")
p.add_argument("--limit", type=int, default=0, help="0 = all 164 problems.")
p.add_argument("--max-new-tokens", type=int, default=640)
p.add_argument("--dtype", default="bfloat16")
p.add_argument("--device-map", default="auto")
p.add_argument("--timeout", type=int, default=12, help="Per-problem execution timeout (s).")
return p.parse_args()
def main() -> int:
args = parse_args()
from datasets import load_dataset
from transformers import AutoTokenizer
ds = load_dataset("openai/openai_humaneval", split="test")
if args.limit:
ds = ds.select(range(min(args.limit, len(ds))))
tokenizer = AutoTokenizer.from_pretrained(args.model, trust_remote_code=True)
if tokenizer.pad_token_id is None:
tokenizer.pad_token = tokenizer.eos_token
model = load_model(args.model, args.dtype, args.device_map)
sys_msg = (
"You are an expert Python programmer. Complete the function. Respond with a single "
"```python code block containing the full function definition and any needed imports, "
"and no explanation."
)
passed = 0
total = len(ds)
for i, ex in enumerate(ds):
messages = [{"role": "system", "content": sys_msg}, {"role": "user", "content": ex["prompt"]}]
out = _generate(model, tokenizer, messages, args.max_new_tokens, enable_thinking=False)
code = extract_code(out, ex["entry_point"], ex["prompt"])
program = code + "\n\n" + ex["test"] + f"\n\ncheck({ex['entry_point']})\n"
if run_program(program, args.timeout):
passed += 1
if (i + 1) % 20 == 0:
print(f"[coding] {i+1}/{total} pass@1 so far {passed/(i+1):.3f}", flush=True)
pass_at_1 = passed / total if total else 0.0
result = {"kind": "humaneval", "n": total, "pass@1": round(pass_at_1, 4), "passed": passed}
report_dir = Path(args.report_dir)
report_dir.mkdir(parents=True, exist_ok=True)
payload = {"label": args.label, "model": args.model, "results": [result], "reference_pass@1": REFERENCE}
(report_dir / f"{args.label}_coding.json").write_text(json.dumps(payload, indent=2), encoding="utf-8")
lines = [f"# HumanEval — {args.label}", "", f"- Model: `{args.model}`",
f"- pass@1: **{pass_at_1:.2%}** ({passed}/{total})", "", "## Reference pass@1 (published, approx)"]
for k, v in REFERENCE.items():
lines.append(f"- {k}: {v:.0%}")
(report_dir / f"{args.label}_coding.md").write_text("\n".join(lines) + "\n", encoding="utf-8")
print(json.dumps(payload, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())
|