Davidmg0815 commited on
Commit
9258a4d
·
verified ·
1 Parent(s): b8638ac

Upload bench/bench_humaneval.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. bench/bench_humaneval.py +201 -0
bench/bench_humaneval.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """HumanEval+ / MBPP+ gegen einen llama-server.
3
+
4
+ Warum diese Datensaetze: beim selbstgebauten Aufgabensatz erreichten sechs von
5
+ sieben Modellen 10/10 — ein Test, den alle bestehen, unterscheidet nichts.
6
+ Die EvalPlus-Varianten haben ein Vielfaches der Testfaelle der Originale und
7
+ sieben Loesungen aus, die nur zufaellig richtig aussehen.
8
+
9
+ Bewertet wird durch Ausfuehren, in einem eigenen Prozess mit Zeitlimit.
10
+ Kein Modell benotet ein anderes.
11
+
12
+ ./bench_humaneval.py --port 8291 --modell "Qwen3.8 Q8_0" --satz humanevalplus \
13
+ --out ergebnis.json
14
+ """
15
+ import argparse
16
+ import json
17
+ import os
18
+ import re
19
+ import subprocess
20
+ import sys
21
+ import time
22
+ import urllib.request
23
+
24
+ DS = "/mnt/models/qwen38-prep/datasets"
25
+ # Die venv hat numpy, das Systempython nicht — die EvalPlus-Tests brauchen es.
26
+ PYTHON = "/mnt/models/skinnyJoe-venv/bin/python"
27
+
28
+ REASONING = False
29
+
30
+
31
+ def lade(satz, grenze):
32
+ pfad = os.path.join(DS, f"{satz}.jsonl")
33
+ zeilen = [json.loads(z) for z in open(pfad) if z.strip()]
34
+ return zeilen[:grenze] if grenze else zeilen
35
+
36
+
37
+ def bau_prompt(satz, a):
38
+ """Baut die Modellanfrage. Bewusst knapp und ohne Beispiele — sonst misst
39
+ man die Prompt-Kunst statt das Modell."""
40
+ if satz.startswith("humaneval"):
41
+ return ("Vervollstaendige die folgende Python-Funktion. Gib die "
42
+ "vollstaendige Funktion samt Signatur und noetigen Importen "
43
+ "aus, sonst nichts:\n\n" + a["prompt"])
44
+ # MBPP: Aufgabe in Prosa, der erwartete Funktionsname steckt im ersten
45
+ # assert. Ohne diesen Hinweis raet das Modell den Namen und faellt durch,
46
+ # was die Aufgabe nicht misst.
47
+ beispiel = ""
48
+ tl = a.get("test_list") or []
49
+ if isinstance(tl, str):
50
+ try:
51
+ tl = json.loads(tl)
52
+ except Exception:
53
+ tl = [tl]
54
+ if tl:
55
+ beispiel = f"\n\nDie Funktion muss so aufrufbar sein:\n{tl[0]}"
56
+ return (f"Schreibe eine Python-Funktion fuer diese Aufgabe:\n\n"
57
+ f"{a['prompt']}{beispiel}\n\nGib nur den Code aus, ohne Erklaerung.")
58
+
59
+
60
+ def frage(port, prompt, max_tokens, timeout):
61
+ b = {"messages": [{"role": "user", "content": prompt}],
62
+ "max_tokens": max_tokens, "cache_prompt": False,
63
+ "temperature": 0.2, "top_p": 0.9}
64
+ if not REASONING:
65
+ b["chat_template_kwargs"] = {"enable_thinking": False}
66
+ req = urllib.request.Request(
67
+ f"http://127.0.0.1:{port}/v1/chat/completions",
68
+ data=json.dumps(b).encode(),
69
+ headers={"Content-Type": "application/json"})
70
+ t0 = time.time()
71
+ try:
72
+ with urllib.request.urlopen(req, timeout=timeout) as r:
73
+ d = json.load(r)
74
+ except Exception as e:
75
+ return "", dict(tps=0.0, tokens=0, sekunden=time.time() - t0,
76
+ abbruch="fehler"), f"{type(e).__name__}: {e}"
77
+ m = d["choices"][0]["message"]
78
+ t = d.get("timings", {})
79
+ return (m.get("content") or ""), dict(
80
+ tps=t.get("predicted_per_second") or 0.0,
81
+ tokens=t.get("predicted_n") or 0,
82
+ denk_zeichen=len(m.get("reasoning_content") or ""),
83
+ abbruch=d["choices"][0].get("finish_reason") or "",
84
+ sekunden=round(time.time() - t0, 2)), ""
85
+
86
+
87
+ def schaele(text):
88
+ zaun = re.findall(r"```(?:python|py)?\s*\n(.*?)```", text, re.S)
89
+ if zaun:
90
+ return max(zaun, key=len).strip()
91
+ return text.strip()
92
+
93
+
94
+ def pruefe(satz, a, loesung, timeout=25):
95
+ """Fuehrt die Loesung gegen die Testfaelle aus. Liefert (ok, grund)."""
96
+ if not loesung.strip():
97
+ return False, "leere Antwort"
98
+ if satz.startswith("humaneval"):
99
+ # Das Modell liefert die ganze Funktion. Der Prompt-Kopf kommt trotzdem
100
+ # davor: er enthaelt die Importe, auf die der Docstring sich stuetzt.
101
+ # Doppelte def-Zeilen stoeren nicht, die zweite gewinnt.
102
+ programm = (a["prompt"] + "\n\npass\n\n" + loesung + "\n\n" +
103
+ a["test"] + f"\n\ncheck({a['entry_point']})\n")
104
+ else:
105
+ test = a.get("test") or ""
106
+ vorspann = a.get("test_imports") or []
107
+ if isinstance(vorspann, str):
108
+ vorspann = [vorspann]
109
+ programm = (loesung + "\n\n" + "\n".join(vorspann) + "\n" + test)
110
+ # MBPP+ definiert die Pruefroutine, ruft sie aber nicht auf.
111
+ if "def check(" in test and re.search(r"^check\(", test, re.M) is None:
112
+ m = re.search(r"assert\s+\w*\(?\s*(\w+)\s*\(", test)
113
+ programm += "\ncheck()\n" if "def check()" in test else ""
114
+ # Ueber eine Datei statt python -c: die EvalPlus-Testbloecke sind teils
115
+ # ueber 100 kB gross und sprengen das Argumentlimit der Kommandozeile
116
+ # (OSError 7). Genau diese Testmenge macht sie haerter als die Originale.
117
+ import tempfile
118
+ fd, pfad = tempfile.mkstemp(suffix=".py", prefix="evalplus-",
119
+ dir="/mnt/models/tmp")
120
+ try:
121
+ with os.fdopen(fd, "w") as f:
122
+ f.write(programm)
123
+ p = subprocess.run([PYTHON, pfad], capture_output=True,
124
+ text=True, timeout=timeout)
125
+ except subprocess.TimeoutExpired:
126
+ return False, f"Zeitueberschreitung ({timeout}s)"
127
+ except Exception as e:
128
+ return False, f"{type(e).__name__}: {e}"
129
+ finally:
130
+ try:
131
+ os.unlink(pfad)
132
+ except OSError:
133
+ pass
134
+ if p.returncode == 0:
135
+ return True, ""
136
+ letzte = (p.stderr.strip().splitlines() or ["?"])[-1]
137
+ return False, letzte[:100]
138
+
139
+
140
+ def main():
141
+ ap = argparse.ArgumentParser()
142
+ ap.add_argument("--port", type=int, default=8291)
143
+ ap.add_argument("--modell", required=True)
144
+ ap.add_argument("--satz", default="humanevalplus")
145
+ ap.add_argument("--out", required=True)
146
+ ap.add_argument("--grenze", type=int, default=0, help="0 = alle")
147
+ ap.add_argument("--max-tokens", type=int, default=1200)
148
+ ap.add_argument("--timeout", type=int, default=300)
149
+ ap.add_argument("--reasoning", action="store_true")
150
+ args = ap.parse_args()
151
+
152
+ global REASONING
153
+ REASONING = args.reasoning
154
+
155
+ aufgaben = lade(args.satz, args.grenze)
156
+ print(f"{args.modell} | {args.satz} | {len(aufgaben)} Aufgaben | "
157
+ f"Reasoning {'AN' if REASONING else 'AUS'}", file=sys.stderr)
158
+
159
+ zeilen, ok_n = [], 0
160
+ t_start = time.time()
161
+ for i, a in enumerate(aufgaben, 1):
162
+ txt, m, fehler = frage(args.port, bau_prompt(args.satz, a),
163
+ args.max_tokens, args.timeout)
164
+ if fehler:
165
+ zeilen.append(dict(task=str(a["task_id"]), ok=False, grund=fehler, **m))
166
+ print(f" [{i:3d}/{len(aufgaben)}] {a['task_id']}: SERVERFEHLER "
167
+ f"{fehler[:40]}", file=sys.stderr)
168
+ continue
169
+ loesung = schaele(txt)
170
+ ok, grund = pruefe(args.satz, a, loesung)
171
+ ok_n += ok
172
+ zeilen.append(dict(task=str(a["task_id"]), ok=ok, grund=grund, **m))
173
+ if i % 10 == 0 or not ok:
174
+ print(f" [{i:3d}/{len(aufgaben)}] {a['task_id']}: "
175
+ f"{'OK' if ok else 'FEHL ' + grund[:50]} "
176
+ f"(bisher {ok_n}/{i} = {ok_n/i*100:.0f}%)", file=sys.stderr)
177
+
178
+ dauer = time.time() - t_start
179
+ tps = sorted(x["tps"] for x in zeilen if x["tps"] > 0)
180
+ bericht = dict(
181
+ modell=args.modell, satz=args.satz, reasoning=REASONING,
182
+ n=len(aufgaben), bestanden=ok_n,
183
+ quote=round(ok_n / len(aufgaben) * 100, 1) if aufgaben else 0.0,
184
+ tps_median=round(tps[len(tps) // 2], 1) if tps else 0.0,
185
+ tokens_gesamt=sum(x["tokens"] for x in zeilen),
186
+ tokens_je_aufgabe=round(sum(x["tokens"] for x in zeilen) / len(zeilen), 1)
187
+ if zeilen else 0,
188
+ am_limit=sum(1 for x in zeilen if x.get("abbruch") == "length"),
189
+ dauer_s=round(dauer, 1),
190
+ aufgaben=zeilen)
191
+ with open(args.out, "w") as f:
192
+ json.dump(bericht, f, ensure_ascii=False, indent=1)
193
+ print(f"\n{args.modell} | {args.satz}: {ok_n}/{len(aufgaben)} = "
194
+ f"{bericht['quote']}% | {bericht['tps_median']} t/s | "
195
+ f"{bericht['tokens_je_aufgabe']:.0f} Token/Aufgabe | "
196
+ f"{bericht['am_limit']} abgeschnitten | {dauer/60:.1f} min",
197
+ file=sys.stderr)
198
+
199
+
200
+ if __name__ == "__main__":
201
+ main()