Davidmg0815's picture
Omega-Messung: MATH-500 mit vollem Reasoning, Fehleranalyse, korrigierter Antwortvergleich
52467f4 verified
Raw
History Blame Contribute Delete
2.83 kB
#!/usr/bin/env python3
"""Gepaarter Vergleich der HumanEval+-Laeufe (McNemar, exakt).
WARUM: Bisher habe ich die Varianten ueber Binomial-Konfidenzintervalle
verglichen (+-4,4 bis +-4,9 Punkte) und daraus geschlossen, es sei nichts
unterscheidbar. Dieser Vergleich ist zu schwach, denn er behandelt die Laeufe
als unabhaengige Stichproben. Sie sind aber GEPAART: jede Variante bearbeitet
exakt dieselben 164 Aufgaben. Aufgaben, die alle loesen oder alle verfehlen,
tragen keine Information ueber den Unterschied — nur die Faelle, in denen genau
eine der beiden Varianten besteht (die "diskordanten Paare"), zaehlen.
Der exakte McNemar-Test schaut ausschliesslich auf diese Paare: unter der
Nullhypothese "beide gleich gut" ist ein diskordantes Paar ein Muenzwurf.
"""
import json
import glob
import math
import itertools
import os
PREP = os.path.dirname(os.path.abspath(__file__))
def lade(pfad):
d = json.load(open(pfad))
return d["modell"], {a["task"]: a["ok"] for a in d["aufgaben"]}
def binom_zweiseitig(k, n):
"""Exakter Zweiseitentest, p=0.5. k Erfolge aus n."""
if n == 0:
return 1.0
k = min(k, n - k)
einseitig = sum(math.comb(n, i) for i in range(k + 1)) / 2 ** n
return min(1.0, 2 * einseitig)
laeufe = [lade(f) for f in sorted(glob.glob(f"{PREP}/he-humanevalplus/*.json"))]
print(f"{len(laeufe)} Laeufe, je {len(laeufe[0][1])} Aufgaben\n")
print("Gepaarte Vergleiche — nur diskordante Paare tragen Information:\n")
print(f"{'A':38} {'B':38} {'nur A':>6} {'nur B':>6} {'p':>8}")
print("-" * 100)
zeilen = []
for (na, ra), (nb, rb) in itertools.combinations(laeufe, 2):
gemeinsam = set(ra) & set(rb)
nur_a = sum(1 for t in gemeinsam if ra[t] and not rb[t])
nur_b = sum(1 for t in gemeinsam if rb[t] and not ra[t])
p = binom_zweiseitig(nur_a, nur_a + nur_b)
zeilen.append((p, na, nb, nur_a, nur_b))
for p, na, nb, a, b in sorted(zeilen):
stern = " *" if p < 0.05 else ""
print(f"{na[:38]:38} {nb[:38]:38} {a:6} {b:6} {p:8.3f}{stern}")
print()
print("Kleinstes p:", f"{min(z[0] for z in zeilen):.3f}")
print("Signifikant bei 0,05:", sum(1 for z in zeilen if z[0] < 0.05), "von", len(zeilen))
# Wie gross muesste ein echter Unterschied sein, damit wir ihn saehen?
print()
print("Trennschaerfe: welche Aufteilung der diskordanten Paare waere signifikant?")
for n in (6, 10, 15, 20, 25, 30, 40):
# kleinstes k > n/2, das noch p < 0,05 liefert
kand = [k for k in range(n // 2 + 1, n + 1) if binom_zweiseitig(k, n) < 0.05]
if kand:
k = min(kand)
print(f" {n:2} diskordante Paare: ab {k}:{n-k} "
f"(p={binom_zweiseitig(k, n):.3f}) — Vorsprung {2*k-n} Aufgaben")
else:
print(f" {n:2} diskordante Paare: NIE signifikant, selbst bei {n}:0 "
f"(p={binom_zweiseitig(n, n):.3f})")