unparallelled commited on
Commit
0b89d77
·
verified ·
1 Parent(s): 7cb8e0f

Upload scripts/longeval_score.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. scripts/longeval_score.py +105 -0
scripts/longeval_score.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ longeval_score.py -- score CP_GENERATE output against longeval_gen.py metadata.
4
+
5
+ This is REAL benchmark scoring (substring match on generated text), not the NLL
6
+ proxy needle_score.py computes. A point is awarded only when every expected
7
+ string appears in the generation, which is RULER's recall-style criterion.
8
+
9
+ usage: longeval_score.py --gen <generations.csv> --meta <task_meta.json>
10
+ [--out results.csv] [--show 3]
11
+
12
+ Reports overall accuracy plus a per-depth breakdown -- the depth axis is the
13
+ lost-in-the-middle curve, so a model that only retrieves from the end of the
14
+ window shows a strong depth gradient even at high overall accuracy.
15
+ """
16
+
17
+ import argparse
18
+ import csv
19
+ import json
20
+ import sys
21
+
22
+ try:
23
+ import tiktoken
24
+ except ImportError:
25
+ sys.exit("longeval_score.py needs tiktoken: pip install tiktoken")
26
+
27
+ ENC = tiktoken.get_encoding("gpt2")
28
+
29
+
30
+ def norm(s):
31
+ return "".join(ch for ch in s.lower() if ch.isalnum())
32
+
33
+
34
+ def main():
35
+ ap = argparse.ArgumentParser()
36
+ ap.add_argument("--gen", required=True, help="CP_GEN_OUT csv: i,tok,tok,...")
37
+ ap.add_argument("--meta", required=True, help="<task>_meta.json")
38
+ ap.add_argument("--out", default="", help="optional per-item csv")
39
+ ap.add_argument("--show", type=int, default=3, help="print N sample generations")
40
+ args = ap.parse_args()
41
+
42
+ meta = json.load(open(args.meta))
43
+ items = {it["i"]: it for it in meta["items"]}
44
+
45
+ gens = {}
46
+ with open(args.gen) as f:
47
+ for row in csv.reader(f):
48
+ if not row:
49
+ continue
50
+ i = int(row[0])
51
+ gens[i] = [int(t) for t in row[1:] if t.strip() != ""]
52
+
53
+ missing = sorted(set(items) - set(gens))
54
+ if missing:
55
+ print(f" WARN: {len(missing)} prompts have no generation "
56
+ f"(first: {missing[:5]}) -- scoring only what is present")
57
+
58
+ rows, n_ok = [], 0
59
+ by_depth = {}
60
+ for i in sorted(gens):
61
+ if i not in items:
62
+ continue
63
+ it = items[i]
64
+ text = ENC.decode(gens[i])
65
+ tn = norm(text)
66
+ hits = [e for e in it["expected"] if norm(e) in tn]
67
+ ok = len(hits) == len(it["expected"])
68
+ n_ok += ok
69
+ d = it["depth"]
70
+ by_depth.setdefault(d, [0, 0])
71
+ by_depth[d][0] += ok
72
+ by_depth[d][1] += 1
73
+ rows.append({"i": i, "depth": d, "ok": int(ok),
74
+ "n_expected": len(it["expected"]), "n_hit": len(hits),
75
+ "expected": "|".join(it["expected"]),
76
+ "generated": text.replace("\n", "\\n")[:200]})
77
+
78
+ n = len(rows)
79
+ if n == 0:
80
+ sys.exit(" no scorable prompts -- check --gen and --meta pair up")
81
+
82
+ print(f"\n=== {meta['task']} (prompt_len={meta['prompt_len']}, n={n}) ===")
83
+ print(f" accuracy: {n_ok}/{n} = {100.0 * n_ok / n:.1f}%")
84
+ print(f" {'depth':>7} {'acc':>8} (needle position in context)")
85
+ for d in sorted(by_depth):
86
+ ok, tot = by_depth[d]
87
+ print(f" {d:>7.2f} {ok:>3}/{tot:<3} {100.0 * ok / tot:5.1f}%")
88
+
89
+ if args.show:
90
+ print(f"\n --- {min(args.show, n)} sample generations ---")
91
+ for r in rows[:args.show]:
92
+ mark = "HIT " if r["ok"] else "MISS"
93
+ print(f" [{mark}] expect={r['expected']}")
94
+ print(f" got={r['generated'][:150]}")
95
+
96
+ if args.out:
97
+ with open(args.out, "w", newline="") as f:
98
+ w = csv.DictWriter(f, fieldnames=list(rows[0]))
99
+ w.writeheader()
100
+ w.writerows(rows)
101
+ print(f"\n wrote {args.out}")
102
+
103
+
104
+ if __name__ == "__main__":
105
+ main()