Upload source/files/in2_gate.py with huggingface_hub
Browse files- source/files/in2_gate.py +141 -0
source/files/in2_gate.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
in2_gate.py -- the EXACT layout gate for in2_gen.py output. No GPU, no CUDA.
|
| 4 |
+
|
| 5 |
+
This is the only exact pre-flight check in the IN2 pilot. The step-0 loss check
|
| 6 |
+
was demoted to a diagnostic because its two hypotheses sit only ~0.22 nats apart
|
| 7 |
+
(no-induction 4.147 vs strong-induction 3.925, against a 3.901 plain baseline),
|
| 8 |
+
which cannot validate a layout. These assertions can.
|
| 9 |
+
|
| 10 |
+
Checks, per arm dir:
|
| 11 |
+
1 file size is exactly examples*T*2 bytes (headerless uint16)
|
| 12 |
+
2 every token in [0, VOCAB)
|
| 13 |
+
3 no answer span intrudes into the last PAD_TAIL offsets of its example
|
| 14 |
+
4 planted spans (needle / probe+answer / decoy) never overlap
|
| 15 |
+
5 RETRIEVAL IS ACTUALLY POSSIBLE: the tokens at the probe equal the needle's
|
| 16 |
+
key, and the tokens at the answer equal the needle's value. If this fails
|
| 17 |
+
the task is unsolvable and the whole experiment is void.
|
| 18 |
+
6 batch alignment: re-derive x/y exactly as DataLoaderLite::next_batch does
|
| 19 |
+
(x=buf[i], y=buf[i+1], stride B*T*dp) and assert every example boundary
|
| 20 |
+
lands on a row boundary
|
| 21 |
+
7 arm-specific distance invariants (b: all == span, c: all >= d_min)
|
| 22 |
+
|
| 23 |
+
Cross-arm:
|
| 24 |
+
8 arms B and C have BIT-IDENTICAL answer_start histograms -- the discipline
|
| 25 |
+
the whole B-vs-C contrast rests on
|
| 26 |
+
|
| 27 |
+
Usage
|
| 28 |
+
python3 in2_gate.py --dirs /tmp/in2b /tmp/in2c --B 2 --dp 2
|
| 29 |
+
"""
|
| 30 |
+
import argparse, array, json, os, sys
|
| 31 |
+
|
| 32 |
+
VOCAB = 50304
|
| 33 |
+
fails = []
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def check(cond, msg):
|
| 37 |
+
print((" ok " if cond else " FAIL ") + msg)
|
| 38 |
+
if not cond:
|
| 39 |
+
fails.append(msg)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def load(d, split="train"):
|
| 43 |
+
js = [f for f in os.listdir(d) if f.endswith(".json") and f"_{split}_" in f]
|
| 44 |
+
if len(js) != 1:
|
| 45 |
+
raise SystemExit(f"{d}: expected exactly one {split} manifest, found {js}")
|
| 46 |
+
man = json.load(open(os.path.join(d, js[0])))
|
| 47 |
+
toks = array.array("H")
|
| 48 |
+
for sp in man.get("shards", [man["bin"]]):
|
| 49 |
+
with open(sp, "rb") as f:
|
| 50 |
+
toks.fromfile(f, os.path.getsize(sp) // 2)
|
| 51 |
+
return man, toks
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def gate_one(d, B, dp, split="train"):
|
| 55 |
+
man, toks = load(d, split)
|
| 56 |
+
T, n_ex, arm, span = man["T"], man["examples"], man["arm"], man["span"]
|
| 57 |
+
kl, vl, pad = man["key_len"], man["val_len"], man["pad_tail"]
|
| 58 |
+
print(f"\n=== arm {arm} T={T} examples={n_ex} K={man['K']} ===")
|
| 59 |
+
|
| 60 |
+
tot = sum(os.path.getsize(sp) for sp in man.get("shards", [man["bin"]]))
|
| 61 |
+
check(tot == n_ex * T * 2,
|
| 62 |
+
f"1 total size across {len(man.get('shards',[1]))} shard(s) == examples*T*2 "
|
| 63 |
+
f"({n_ex*T*2} bytes, got {tot})")
|
| 64 |
+
lo, hi = min(toks), max(toks)
|
| 65 |
+
check(lo >= 0 and hi < VOCAB, f"2 tokens in [0,{VOCAB}) (min {lo}, max {hi})")
|
| 66 |
+
|
| 67 |
+
bad_pad = bad_ovl = bad_ret = bad_dist = 0
|
| 68 |
+
for ei, recs in enumerate(man["records"]):
|
| 69 |
+
base = ei * T
|
| 70 |
+
spans = []
|
| 71 |
+
for r in recs:
|
| 72 |
+
a0, a1 = r["answer_start"], r["answer_start"] + vl
|
| 73 |
+
if a1 > T - pad:
|
| 74 |
+
bad_pad += 1
|
| 75 |
+
spans.append((r["needle_start"], r["needle_start"] + span))
|
| 76 |
+
spans.append((r["probe_start"], r["probe_start"] + span))
|
| 77 |
+
# 5: the probe must reproduce the needle's key, the answer its value
|
| 78 |
+
nk = toks[base + r["needle_start"]: base + r["needle_start"] + kl]
|
| 79 |
+
nv = toks[base + r["needle_start"] + kl: base + r["needle_start"] + span]
|
| 80 |
+
pk = toks[base + r["probe_start"]: base + r["probe_start"] + kl]
|
| 81 |
+
av = toks[base + a0: base + a1]
|
| 82 |
+
if nk != pk or nv != av:
|
| 83 |
+
bad_ret += 1
|
| 84 |
+
if arm == "b" and r["distance"] != span:
|
| 85 |
+
bad_dist += 1
|
| 86 |
+
if arm == "c" and r["distance"] < man["d_min"]:
|
| 87 |
+
bad_dist += 1
|
| 88 |
+
spans.sort()
|
| 89 |
+
for (s0, e0), (s1, _) in zip(spans, spans[1:]):
|
| 90 |
+
if s1 < e0:
|
| 91 |
+
bad_ovl += 1
|
| 92 |
+
check(bad_pad == 0, f"3 no answer within the last {pad} offsets ({bad_pad} bad)")
|
| 93 |
+
check(bad_ovl == 0, f"4 planted spans never overlap ({bad_ovl} bad)")
|
| 94 |
+
check(bad_ret == 0, f"5 RETRIEVAL POSSIBLE: probe==key and answer==value ({bad_ret} bad)")
|
| 95 |
+
check(bad_dist == 0, f"7 arm-{arm} distance invariant ({bad_dist} bad)")
|
| 96 |
+
|
| 97 |
+
# 6: DataLoaderLite windowing. next_batch reads B*T+1 tokens and sets
|
| 98 |
+
# y[i]=buf[i+1]; pos_ advances by B*T*world. Example boundaries must land on
|
| 99 |
+
# row boundaries or an example would straddle two rows.
|
| 100 |
+
stride = B * T * dp
|
| 101 |
+
check(stride % T == 0, f"6a batch stride {stride} is a multiple of T={T}")
|
| 102 |
+
check((n_ex * T) % stride == 0,
|
| 103 |
+
f"6b total {n_ex*T} tokens is a whole number of batches (stride {stride})")
|
| 104 |
+
if len(toks) >= B * T + 1:
|
| 105 |
+
x0 = toks[0:B * T]
|
| 106 |
+
y0 = toks[1:B * T + 1]
|
| 107 |
+
rows_ok = all(x0[r * T] == toks[r * T] for r in range(B))
|
| 108 |
+
check(rows_ok, f"6c row r starts at token r*T for all B={B} rows")
|
| 109 |
+
check(y0[T - 1] == toks[T], "6d y[T-1] targets the next example's first token "
|
| 110 |
+
"(why PAD_TAIL exists)")
|
| 111 |
+
return man
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def main():
|
| 115 |
+
ap = argparse.ArgumentParser()
|
| 116 |
+
ap.add_argument("--dirs", nargs="+", required=True)
|
| 117 |
+
ap.add_argument("--B", type=int, default=2)
|
| 118 |
+
ap.add_argument("--dp", type=int, default=2)
|
| 119 |
+
ap.add_argument("--split", default="train", choices=["train", "val"])
|
| 120 |
+
args = ap.parse_args()
|
| 121 |
+
|
| 122 |
+
mans = {}
|
| 123 |
+
for d in args.dirs:
|
| 124 |
+
m = gate_one(d, args.B, args.dp, args.split)
|
| 125 |
+
mans[m["arm"]] = m
|
| 126 |
+
|
| 127 |
+
if "b" in mans and "c" in mans:
|
| 128 |
+
print("\n=== cross-arm ===")
|
| 129 |
+
hb, hc = mans["b"]["answer_start_hist"], mans["c"]["answer_start_hist"]
|
| 130 |
+
check(hb == hc, "8 arms B and C have BIT-IDENTICAL answer_start histograms "
|
| 131 |
+
f"({len(hb)} vs {len(hc)} distinct offsets)")
|
| 132 |
+
check(mans["b"]["seed"] == mans["c"]["seed"], "8b same seed")
|
| 133 |
+
check(mans["b"]["answer_tokens"] == mans["c"]["answer_tokens"],
|
| 134 |
+
"8c same answer-token count")
|
| 135 |
+
|
| 136 |
+
print(f"\n{'ALL PASS' if not fails else str(len(fails)) + ' FAILURE(S)'}")
|
| 137 |
+
return 1 if fails else 0
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
if __name__ == "__main__":
|
| 141 |
+
sys.exit(main())
|