Instructions to use Duke-CEI-SVD/traj-mc with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Duke-CEI-SVD/traj-mc with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Duke-CEI-SVD/traj-mc", device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 5,939 Bytes
3fffa60 | 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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | """
mcnemar.py -- paired McNemar test for BASE vs OURS on a single benchmark.
Reads two per-item JSONL files (base, ours), pairs by item_id, and computes:
b = #(BASE wrong, OURS correct)
c = #(BASE correct, OURS wrong)
chi2 = (b - c)^2 / (b + c) [the pre-registered statistic]
p_chi2 : chi-square (df=1) p-value
p_exact : exact binomial McNemar p (recommended when b+c is small)
Reports raw acc for both arms + (b, c) + paired p. Non-paired z tests are FORBIDDEN.
Self-test (`python mcnemar.py --self_test`) validates p_chi2 and p_exact against
scipy (chi2.sf and binomtest / statsmodels.mcnemar).
"""
import os
import sys
import json
import argparse
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import common as C
def _chi2_sf_df1(x):
"""Survival function of chi-square df=1 = erfc(sqrt(x/2)). No scipy needed."""
import math
return math.erfc(math.sqrt(x / 2.0))
def _exact_binom_two_sided(b, c):
"""
Exact two-sided McNemar p = P(X<=min | n=b+c, 0.5) tail, doubled/clamped.
Computed in log space (lgamma) so it is stable for large n -- the naive
math.comb(n,i)*0.5**n overflows float for n in the thousands (e.g. MMLU).
"""
import math
n = b + c
if n == 0:
return 1.0
k = min(b, c)
ln_half = math.log(0.5)
ln_nfac = math.lgamma(n + 1)
cum = 0.0
for i in range(0, k + 1):
logpmf = ln_nfac - math.lgamma(i + 1) - math.lgamma(n - i + 1) + n * ln_half
cum += math.exp(logpmf)
return min(1.0, 2.0 * cum)
def mcnemar_from_pairs(base_correct, ours_correct):
assert len(base_correct) == len(ours_correct)
b = sum(1 for bb, oo in zip(base_correct, ours_correct) if (not bb) and oo)
c = sum(1 for bb, oo in zip(base_correct, ours_correct) if bb and (not oo))
n = len(base_correct)
chi2 = ((b - c) ** 2) / (b + c) if (b + c) > 0 else 0.0
return {
"n_paired": n,
"acc_base": sum(base_correct) / n if n else None,
"acc_ours": sum(ours_correct) / n if n else None,
"b_base_wrong_ours_right": b,
"c_base_right_ours_wrong": c,
"chi2": chi2,
"p_chi2": _chi2_sf_df1(chi2) if (b + c) > 0 else 1.0,
"p_exact": _exact_binom_two_sided(b, c),
"direction": "ours>base" if b > c else ("base>ours" if c > b else "tie"),
}
def read_items(path):
d = {}
with open(path) as f:
for line in f:
line = line.strip()
if not line:
continue
r = json.loads(line)
d[r["item_id"]] = r
return d
def pair_and_test(base_path, ours_path, field="correct"):
base = read_items(base_path)
ours = read_items(ours_path)
common_ids = sorted(set(base) & set(ours))
if len(common_ids) != len(base) or len(common_ids) != len(ours):
print(f"[mcnemar] WARNING: base={len(base)} ours={len(ours)} "
f"paired={len(common_ids)}; using intersection.")
# prompt_hash sanity: same item must be the same prompt across arms
mism = [i for i in common_ids
if base[i].get("prompt_hash") != ours[i].get("prompt_hash")]
if mism:
print(f"[mcnemar] WARNING: {len(mism)} items have mismatched prompt_hash "
f"(base vs ours); protocol drift?")
bc = [bool(base[i][field]) for i in common_ids]
oc = [bool(ours[i][field]) for i in common_ids]
res = mcnemar_from_pairs(bc, oc)
res["prompt_hash_mismatches"] = len(mism)
res["field"] = field
return res
def self_test():
import random
random.seed(0)
bc = [random.random() < 0.6 for _ in range(500)]
oc = [random.random() < 0.65 for _ in range(500)]
res = mcnemar_from_pairs(bc, oc)
b, c = res["b_base_wrong_ours_right"], res["c_base_right_ours_wrong"]
ok = True
try:
from scipy.stats import chi2 as sp_chi2, binomtest
sp_p_chi2 = float(sp_chi2.sf(res["chi2"], 1))
sp_p_exact = float(binomtest(min(b, c), b + c, 0.5).pvalue)
d_chi2 = abs(sp_p_chi2 - res["p_chi2"])
d_exact = abs(sp_p_exact - res["p_exact"])
print(f"[self_test] b={b} c={c} chi2={res['chi2']:.4f}")
print(f"[self_test] p_chi2 ours={res['p_chi2']:.6e} scipy={sp_p_chi2:.6e} "
f"|d|={d_chi2:.2e}")
print(f"[self_test] p_exact ours={res['p_exact']:.6e} scipy={sp_p_exact:.6e} "
f"|d|={d_exact:.2e}")
ok = d_chi2 < 1e-9 and d_exact < 1e-9
except ImportError:
print("[self_test] scipy not available; statsmodels fallback")
try:
from statsmodels.stats.contingency_tables import mcnemar as sm_mc
import numpy as np
table = np.array([[0, b], [c, 0]])
sm = sm_mc(table, exact=True)
d = abs(float(sm.pvalue) - res["p_exact"])
print(f"[self_test] p_exact ours={res['p_exact']:.6e} "
f"statsmodels={float(sm.pvalue):.6e} |d|={d:.2e}")
ok = d < 1e-9
except ImportError:
print("[self_test] neither scipy nor statsmodels; cannot validate")
ok = False
print(f"[self_test] {'PASS' if ok else 'FAIL'}")
return ok
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--base_items")
ap.add_argument("--ours_items")
ap.add_argument("--benchmark", default=None)
ap.add_argument("--field", default="correct", choices=["correct", "correct_norm"],
help="acc (correct) vs acc_norm (correct_norm)")
ap.add_argument("--out", default=None)
ap.add_argument("--self_test", action="store_true")
args = ap.parse_args()
if args.self_test:
sys.exit(0 if self_test() else 1)
res = pair_and_test(args.base_items, args.ours_items, args.field)
res["benchmark"] = args.benchmark
print(json.dumps(res, indent=2))
if args.out:
C.dump_json(res, args.out)
if __name__ == "__main__":
main()
|