AbstractPhil commited on
Commit
57e06d7
·
verified ·
1 Parent(s): db8dbba

frame prototype v0.1: byte-state arm beats index twin (digits 0.275 vs 0.045, twin has only 200 keys for 999 states); RSA says the codebook kept C ancestry; entry channel real vs failing random control

Browse files
Files changed (1) hide show
  1. proto_frame/proto_phase0.py +215 -0
proto_frame/proto_phase0.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Frame prototype Phase 0 — instruments + full extraction (v0.1).
2
+
3
+ 1. T5 walk table of C (instrument-first: entry loss needs whole-walk
4
+ census; only the wordpiece walk existed).
5
+ 2. Boundary-context profiles per state (prev/next byte distributions
6
+ at attested sites) for the byte-structural codebook init.
7
+ 3. Extraction v2: ALL sites, no cap. Unmasked anchor readouts
8
+ (byte-anchored first subtoken + last subtoken, k per side) AND
9
+ masked-span readouts (bert: [MASK]*k, read first mask; t5:
10
+ <extra_id_0> replacing span, read sentinel in encoder) — the
11
+ reading-vs-guessing control. CUDA, <18GB.
12
+ """
13
+ import json
14
+ import sys
15
+
16
+ sys.path.insert(0, r"E:\mirel\geolip-bytelex")
17
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
18
+ import numpy as np
19
+ import torch
20
+ import transformers
21
+ from transformers import AutoTokenizer, AutoModel, T5EncoderModel
22
+ import transformers.utils.logging as hlog
23
+
24
+ hlog.set_verbosity_error()
25
+ CODEX = r"E:\mirel\data\bytelex\codex_v1.txt"
26
+ WORDS = r"E:\mirel\data\bytelex\words_of_C.json"
27
+ OUTDIR = r"E:\mirel\data\bytelex\proto_frame"
28
+ SEPS = set(b" \t\n\r.,;:!?\"'()[]{}")
29
+ L_BERT, L_T5 = 8, 6
30
+ DEV = "cuda"
31
+ BATCH = 256
32
+
33
+ states = json.load(open(WORDS, encoding="utf-8"))
34
+ sid_of = {s["text"]: i for i, s in enumerate(states)}
35
+ tkB = AutoTokenizer.from_pretrained("bert-base-uncased")
36
+ tkA = AutoTokenizer.from_pretrained("google/flan-t5-small")
37
+
38
+ # ---- 1. t5 walk table of C
39
+ t5_walk = []
40
+ for s in states:
41
+ ids = tkA(s["text"], add_special_tokens=False)["input_ids"]
42
+ toks = tkA.convert_ids_to_tokens(ids)
43
+ t5_walk.append({"text": s["text"], "count": s["count"],
44
+ "k": len(ids), "seg": "|".join(toks),
45
+ "ids": ids,
46
+ "whole": len(ids) == 1})
47
+ n_whole = sum(w["whole"] for w in t5_walk)
48
+ with open(rf"{OUTDIR}\t5_walk_of_C.json", "w", encoding="utf-8") as f:
49
+ json.dump(t5_walk, f, indent=0)
50
+ print(f"[P0] t5 walk of C: {n_whole}/999 whole", flush=True)
51
+
52
+ # ---- sites (ALL, no cap) + 2. boundary-context profiles
53
+ lines = open(CODEX, "rb").read().decode("ascii").split("\n")
54
+ sites = []
55
+ prev_ctx = np.zeros((999, 256))
56
+ next_ctx = np.zeros((999, 256))
57
+ for li, ln in enumerate(lines):
58
+ if not ln:
59
+ continue
60
+ raw = ln.encode("ascii") + b" "
61
+ lo = None
62
+ for j, ch in enumerate(raw):
63
+ if ch in SEPS:
64
+ if lo is not None:
65
+ sid = sid_of.get(ln[lo:j])
66
+ if sid is not None:
67
+ sites.append((li, lo, j, sid))
68
+ prev_ctx[sid, raw[lo - 1] if lo else 32] += 1
69
+ next_ctx[sid, ch] += 1
70
+ lo = None
71
+ elif lo is None:
72
+ lo = j
73
+ np.savez_compressed(rf"{OUTDIR}\ctx_profiles.npz",
74
+ prev_ctx=prev_ctx, next_ctx=next_ctx)
75
+ print(f"[P0] {len(sites)} sites (uncapped), ctx profiles saved",
76
+ flush=True)
77
+
78
+ mB = AutoModel.from_pretrained("bert-base-uncased").to(DEV).eval()
79
+ mT = T5EncoderModel.from_pretrained("google/flan-t5-small").to(DEV).eval()
80
+ embT = mT.get_input_embeddings()
81
+ SENT = tkA.convert_tokens_to_ids("<extra_id_0>")
82
+ MASK = tkB.mask_token_id
83
+
84
+ N = len(sites)
85
+ H_B = np.zeros((N, 2, 768), dtype=np.float16) # first,last
86
+ H_T = np.zeros((N, 2, 512), dtype=np.float32)
87
+ M_B = np.zeros((N, 768), dtype=np.float16) # masked readout
88
+ M_T = np.zeros((N, 512), dtype=np.float32)
89
+ E_T = np.zeros((N, 512), dtype=np.float32) # t5 input emb (first)
90
+ KK = np.zeros((N, 2), dtype=np.int16) # k_B, k_T
91
+
92
+ # ---- unmasked pass, batched by line
93
+ by_line = {}
94
+ for k, (li, lo, hi, sid) in enumerate(sites):
95
+ by_line.setdefault(li, []).append(k)
96
+ line_ids = sorted(by_line)
97
+ tokcacheB, tokcacheT = {}, {}
98
+ with torch.no_grad():
99
+ for bs in range(0, len(line_ids), BATCH):
100
+ chunk = line_ids[bs:bs + BATCH]
101
+ texts = [lines[li] for li in chunk]
102
+ eb = tkB(texts, return_offsets_mapping=True, padding=True,
103
+ return_tensors="pt")
104
+ et = tkA(texts, return_offsets_mapping=True, padding=True,
105
+ return_tensors="pt")
106
+ hb = mB(input_ids=eb["input_ids"].to(DEV),
107
+ attention_mask=eb["attention_mask"].to(DEV),
108
+ output_hidden_states=True).hidden_states[L_BERT].cpu()
109
+ ht = mT(input_ids=et["input_ids"].to(DEV),
110
+ attention_mask=et["attention_mask"].to(DEV),
111
+ output_hidden_states=True).hidden_states[L_T5].cpu()
112
+ em = embT(et["input_ids"].to(DEV)).cpu()
113
+ for r, li in enumerate(chunk):
114
+ offB = eb["offset_mapping"][r].tolist()
115
+ offT = et["offset_mapping"][r].tolist()
116
+ idsB = eb["input_ids"][r].tolist()
117
+ idsT = et["input_ids"][r].tolist()
118
+ tokcacheB[li] = (idsB, offB)
119
+ tokcacheT[li] = (idsT, offT)
120
+ for k in by_line[li]:
121
+ _, lo, hi, sid = sites[k]
122
+ ixB = [i for i, (s, t) in enumerate(offB)
123
+ if t > s and s < hi and t > lo]
124
+ ixT = [i for i, (s, t) in enumerate(offT)
125
+ if t > s and s < hi and t > lo]
126
+ if not ixB or not ixT:
127
+ KK[k] = (0, 0)
128
+ continue
129
+ H_B[k, 0] = hb[r, ixB[0]].numpy()
130
+ H_B[k, 1] = hb[r, ixB[-1]].numpy()
131
+ H_T[k, 0] = ht[r, ixT[0]].numpy()
132
+ H_T[k, 1] = ht[r, ixT[-1]].numpy()
133
+ E_T[k] = em[r, ixT[0]].numpy()
134
+ KK[k] = (len(ixB), len(ixT))
135
+ if (bs // BATCH) % 5 == 0:
136
+ print(f"[P0-unmasked] {bs}/{len(line_ids)} lines", flush=True)
137
+ print("[P0] unmasked pass done", flush=True)
138
+
139
+ # ---- masked pass: one sequence per SITE, id-spliced
140
+ def masked_batchB(ks):
141
+ seqs, poss = [], []
142
+ for k in ks:
143
+ li, lo, hi, sid = sites[k]
144
+ idsB, offB = tokcacheB[li]
145
+ ix = [i for i, (s, t) in enumerate(offB)
146
+ if t > s and s < hi and t > lo]
147
+ pre = [idsB[i] for i, (s, t) in enumerate(offB)
148
+ if t > s and t <= lo]
149
+ post = [idsB[i] for i, (s, t) in enumerate(offB)
150
+ if t > s and s >= hi]
151
+ seqs.append([tkB.cls_token_id] + pre + [MASK] * max(len(ix), 1)
152
+ + post + [tkB.sep_token_id])
153
+ poss.append(1 + len(pre))
154
+ return seqs, poss
155
+
156
+
157
+ def masked_batchT(ks):
158
+ seqs, poss = [], []
159
+ for k in ks:
160
+ li, lo, hi, sid = sites[k]
161
+ ln = lines[li]
162
+ src = ln[:lo] + "<extra_id_0>" + ln[hi:]
163
+ ids = tkA(src, add_special_tokens=False)["input_ids"]
164
+ try:
165
+ p = ids.index(SENT)
166
+ except ValueError:
167
+ p = 0
168
+ seqs.append(ids)
169
+ poss.append(p)
170
+ return seqs, poss
171
+
172
+
173
+ def run_masked(model, seqs, poss, layer, pad_id):
174
+ mx = max(len(s) for s in seqs)
175
+ ids = torch.full((len(seqs), mx), pad_id, dtype=torch.long)
176
+ att = torch.zeros((len(seqs), mx), dtype=torch.long)
177
+ for i, s in enumerate(seqs):
178
+ ids[i, :len(s)] = torch.tensor(s)
179
+ att[i, :len(s)] = 1
180
+ with torch.no_grad():
181
+ h = model(input_ids=ids.to(DEV), attention_mask=att.to(DEV),
182
+ output_hidden_states=True).hidden_states[layer].cpu()
183
+ return h[torch.arange(len(seqs)), torch.tensor(poss)]
184
+
185
+ order = [k for k in range(N) if KK[k, 0] > 0]
186
+ for bs in range(0, len(order), BATCH):
187
+ ks = order[bs:bs + BATCH]
188
+ sq, ps = masked_batchB(ks)
189
+ out = run_masked(mB, sq, ps, L_BERT, tkB.pad_token_id)
190
+ for j, k in enumerate(ks):
191
+ M_B[k] = out[j].numpy()
192
+ sq, ps = masked_batchT(ks)
193
+ out = run_masked(mT, sq, ps, L_T5, tkA.pad_token_id)
194
+ for j, k in enumerate(ks):
195
+ M_T[k] = out[j].numpy()
196
+ if (bs // BATCH) % 20 == 0:
197
+ print(f"[P0-masked] {bs}/{len(order)} sites", flush=True)
198
+
199
+ np.savez_compressed(
200
+ rf"{OUTDIR}\frame_dump_v2.npz",
201
+ H_B=H_B, H_T=H_T, M_B=M_B, M_T=M_T, E_T=E_T, KK=KK,
202
+ sid=np.array([s[3] for s in sites], dtype=np.int32),
203
+ line=np.array([s[0] for s in sites], dtype=np.int32),
204
+ lo=np.array([s[1] for s in sites], dtype=np.int32),
205
+ hi=np.array([s[2] for s in sites], dtype=np.int32))
206
+ meta = {"n_sites": N, "skipped": int(N - len(order)),
207
+ "layers": {"bert": L_BERT, "t5": L_T5},
208
+ "t5_whole": n_whole,
209
+ "env": {"transformers": transformers.__version__,
210
+ "torch": torch.__version__}}
211
+ with open(rf"{OUTDIR}\phase0_meta.json", "w", encoding="utf-8") as f:
212
+ json.dump(meta, f, indent=1)
213
+ print(f"[P0] COMPLETE: {len(order)}/{N} sites, "
214
+ f"vram peak {torch.cuda.max_memory_allocated()/2**30:.1f}GB",
215
+ flush=True)