Download code/train_m3.py from GermannM/kenga-prophet-m5: direct link, hf CLI and curl.
- Browser
- Download file 20.9 kB
-
https://huggingface.co/GermannM/kenga-prophet-m5/resolve/main/code/train_m3.py
- Command line
-
hf download hf://GermannM/kenga-prophet-m5/code/train_m3.py
-
curl -L -o train_m3.py https://huggingface.co/GermannM/kenga-prophet-m5/resolve/main/code/train_m3.py
20.9 kB
| """ | |
| Mid-Prophet M3: real transformer-style decoder (numpy-only). | |
| Configurable: width (D), depth (L blocks), context (K), heads (H). | |
| Same training procedure as M3.0 regardless of size, so capacity is | |
| the only axis changed. | |
| Architecture: | |
| - vocabulary: 28-token (M3.0/M3.1) OR 64-token learned codec (M3.2) | |
| - K-token context window | |
| - learned embedding (V -> D) + positional embedding (K -> D) | |
| - L decoder blocks: causal multi-head attention (H heads, head_dim=D//H) | |
| with real QKV projections + Wo, | |
| tanh FFN (D -> 2D -> D), residuals | |
| - softmax projection (D -> V) | |
| - proper backprop through attention + FFN + embeddings | |
| Train: mini-batch Adam over the full Kenga corpus. Held out: 9 | |
| kenga_seed_*.kenga programs (next-token accuracy). | |
| M3.2 ("representation expansion"): set M3_CODEC=1 to use the 64-token | |
| codec (tools/codec_bpe.py, minds/kenga_bpe.pkl). Architecture, procedure, | |
| dataset and eval are identical to M3.1; only the vocabulary changes. | |
| Run: | |
| /c/Python314/python tools/train_m3.py | |
| """ | |
| import os | |
| import numpy as np | |
| KEYWORDS = {'fn','return','let','if','else','while','for','i64','println'} | |
| TWO_CHAR = {'->','==','<=','>=','!=','&&','||','<<','>>','&','|','^','~'} | |
| TOKENS = [ | |
| 'fn', 'return', 'let', 'if', 'else', 'while', | |
| 'for', 'i64', ':', ',', ';', '{', | |
| '}', '(', ')', '->', '+', '-', | |
| '*', '/', '=', '==', '<', '<=', | |
| '>', 'println','ID', 'NUM', | |
| ] | |
| V = len(TOKENS) | |
| VOCAB = {t: i for i, t in enumerate(TOKENS)} | |
| def tokenize(src): | |
| out = [] | |
| i = 0 | |
| n = len(src) | |
| while i < n: | |
| c = src[i] | |
| if c in ' \t\n\r': | |
| i += 1; continue | |
| if c == '/' and i+1 < n and src[i+1] == '/': | |
| while i < n and src[i] != '\n': i += 1 | |
| continue | |
| two = src[i:i+2] | |
| if two in TWO_CHAR: | |
| out.append(VOCAB.get(two, VOCAB['ID'])); i += 2; continue | |
| if c in (':', ',', ';', '{', '}', '(', ')', '+', '-', '*', '/', '=', '<', '>'): | |
| if c == '-' and i+1 < n and src[i+1] == '>': | |
| out.append(VOCAB['->']); i += 2; continue | |
| out.append(VOCAB[c]); i += 1; continue | |
| if c.isdigit(): | |
| j = i | |
| while j < n and src[j].isdigit(): j += 1 | |
| out.append(VOCAB['NUM']); i = j; continue | |
| if c.isalpha() or c == '_': | |
| j = i | |
| while j < n and (src[j].isalnum() or src[j] == '_'): | |
| j += 1 | |
| word = src[i:j] | |
| out.append(VOCAB[word] if word in KEYWORDS else VOCAB['ID']) | |
| i = j; continue | |
| i += 1 | |
| return out | |
| def make_codec(): | |
| """Load the codec (minds/kenga_bpe.pkl or kenga_digits.pkl). Returns a | |
| Codec-like object exposing tokens, token_to_id, encode_word.""" | |
| import pickle | |
| path = os.environ.get('M3_CODEC_FILE', 'minds/kenga_bpe.pkl') | |
| with open(path, 'rb') as f: | |
| data = pickle.load(f) | |
| tokens = data['tokens'] | |
| merges = data['merges'] | |
| id_to_token = tokens | |
| token_to_id = {t: i for i, t in enumerate(tokens)} | |
| merge_set = set(a + b for a, b in merges) | |
| def encode_word(w): | |
| if w in KEYWORDS: | |
| return [token_to_id[w]] | |
| spellable = all(ch in token_to_id for ch in w) | |
| if not spellable: | |
| return [token_to_id['ID']] | |
| toks = list(w) | |
| changed = True | |
| while changed: | |
| changed = False | |
| i = 0 | |
| while i < len(toks) - 1: | |
| merged = toks[i] + toks[i+1] | |
| if merged in merge_set: | |
| toks[i:i+2] = [merged] | |
| changed = True | |
| i += 1 | |
| return [token_to_id[t] for t in toks] | |
| return { | |
| 'tokens': tokens, | |
| 'id_to_token': id_to_token, | |
| 'token_to_id': token_to_id, | |
| 'encode_word': encode_word, | |
| } | |
| def make_codec_tokenize(codec): | |
| """Tokenizer that uses the codec for identifiers, syntax as usual.""" | |
| token_to_id = codec['token_to_id'] | |
| encode_word = codec['encode_word'] | |
| def tokenize_codec(src): | |
| out = [] | |
| i = 0 | |
| n = len(src) | |
| while i < n: | |
| c = src[i] | |
| if c in ' \t\n\r': | |
| i += 1; continue | |
| if c == '/' and i+1 < n and src[i+1] == '/': | |
| while i < n and src[i] != '\n': i += 1 | |
| continue | |
| two = src[i:i+2] | |
| if two in TWO_CHAR: | |
| out.append(token_to_id.get(two, token_to_id['ID'])); i += 2; continue | |
| if c in (':', ',', ';', '{', '}', '(', ')', '+', '-', '*', '/', '=', '<', '>'): | |
| if c == '-' and i+1 < n and src[i+1] == '>': | |
| out.append(token_to_id['->']); i += 2; continue | |
| out.append(token_to_id[c]); i += 1; continue | |
| if c.isdigit(): | |
| j = i | |
| while j < n and src[j].isdigit(): j += 1 | |
| if 'NUM' in token_to_id: | |
| out.append(token_to_id['NUM']); i = j; continue | |
| # digit codec: emit each digit as its own token | |
| for d in src[i:j]: | |
| out.append(token_to_id.get(d, token_to_id['ID'])) | |
| i = j; continue | |
| if c.isalpha() or c == '_': | |
| j = i | |
| while j < n and (src[j].isalnum() or src[j] == '_'): | |
| j += 1 | |
| word = src[i:j] | |
| out.extend(encode_word(word)) | |
| i = j; continue | |
| i += 1 | |
| return out | |
| return tokenize_codec | |
| def collect_corpus(): | |
| if os.environ.get('M3_ONLY_FACTORY', '0') == '1': | |
| return [] | |
| parts = [] | |
| SKIP_BIG = { | |
| 'bc_src_c.kenga','more.kenga','lower_kv.kenga','lower_c.kenga', | |
| 'rt_prophet.kenga','native_ml.kenga','rt_vm.kenga','rt_tensor.kenga', | |
| 'rt_kval_tape.kenga','rt_kval_mem.kenga', | |
| } | |
| include_big = os.environ.get('M3_INCLUDE_BIG', '0') == '1' | |
| # Phase II M5.2: file-level holdout of REAL code (whole files, not rows) | |
| import hashlib | |
| real_split = os.environ.get('M3_REAL_SPLIT', '') | |
| holdout_frac = float(real_split) if real_split else 0.0 | |
| for root in ('kenga', 'examples'): | |
| for r, ds, fs in os.walk(root): | |
| for f in fs: | |
| if not f.endswith('.kenga'): continue | |
| if not include_big and f in SKIP_BIG: continue | |
| if f.startswith('mid_prophet') or f.startswith('pico_birth'): continue | |
| p = os.path.join(r, f) | |
| if holdout_frac > 0: | |
| h = int(hashlib.md5(p.replace('\\\\', '/').encode()).hexdigest(), 16) % 10000 | |
| side = 'held' if h < holdout_frac * 10000 else 'train' | |
| parts.append((side, p)) | |
| continue | |
| if 'kenga_seed_' in p: | |
| if os.environ.get('M3_INCLUDE_SEEDS') == '1': | |
| repeat = int(os.environ.get('M3_SEED_REPEAT', '1')) | |
| for _ in range(max(1, repeat)): | |
| parts.append(('train', p)) | |
| else: | |
| parts.append(('held', p)) | |
| continue | |
| try: | |
| data = open(p, encoding='utf-8', errors='replace').read() | |
| parts.append(('train', p)) | |
| except Exception: | |
| pass | |
| return parts | |
| class Block: | |
| """One transformer decoder block: self-attention + tanh FFN, both residual.""" | |
| def __init__(self, D, FF, rng): | |
| self.Wq = rng.randn(D, D) * 0.05 | |
| self.Wk = rng.randn(D, D) * 0.05 | |
| self.Wv = rng.randn(D, D) * 0.05 | |
| self.Wo = rng.randn(D, D) * 0.05 | |
| self.W1 = rng.randn(D, FF) * 0.04 | |
| self.b1 = np.zeros(FF) | |
| self.W2 = rng.randn(FF, D) * 0.04 | |
| self.b2 = np.zeros(D) | |
| def params(self): | |
| return ['Wq', 'Wk', 'Wv', 'Wo', 'W1', 'b1', 'W2', 'b2'] | |
| class M3: | |
| def __init__(self, V, K, D, H, L, rng): | |
| self.V, self.K, self.D, self.H, self.L = V, K, D, H, L | |
| self.HEAD = D // H | |
| self.FF = D * 2 | |
| self.E_tok = rng.randn(V, D) * 0.10 | |
| self.E_pos = rng.randn(K, D) * 0.10 | |
| self.blocks = [Block(D, self.FF, rng) for _ in range(L)] | |
| self.Wout = rng.randn(D, V) * 0.05 | |
| self.bout = None | |
| self.mask = np.triu(np.ones((K, K), dtype=bool), k=1) | |
| def n_params(self): | |
| n = self.V * self.D + self.K * self.D | |
| n += sum(4 * self.D * self.D + 2 * self.D * self.FF + self.FF + self.D | |
| for _ in self.blocks) | |
| n += self.D * self.V + self.V | |
| return n | |
| def params_map(self): | |
| """Flatten all params into {name: array} for the optimizer.""" | |
| p = {} | |
| p['E_tok'] = self.E_tok | |
| p['E_pos'] = self.E_pos | |
| for li, blk in enumerate(self.blocks): | |
| for name in blk.params(): | |
| p[f'{li}:{name}'] = getattr(blk, name) | |
| p['Wout'] = self.Wout | |
| p['bout'] = self.bout | |
| return p | |
| def forward(self, x): | |
| """x: (B, K) token ids -> logits (B, V). Returns logits + caches.""" | |
| B = x.shape[0] | |
| K, D, H, HEAD = self.K, self.D, self.H, self.HEAD | |
| X = self.E_tok[x] + self.E_pos[np.arange(K)] # (B, K, D) | |
| cur = X | |
| caches = [] # per-block: (X_in, Q, K_, V_, q, k, v, scores, attn, ctx, attn_out, h1, act, h2, Y) | |
| for blk in self.blocks: | |
| Q = cur @ blk.Wq | |
| K_ = cur @ blk.Wk | |
| V_ = cur @ blk.Wv | |
| q = Q.reshape(B, K, H, HEAD).transpose(0, 2, 1, 3) | |
| k = K_.reshape(B, K, H, HEAD).transpose(0, 2, 1, 3) | |
| v = V_.reshape(B, K, H, HEAD).transpose(0, 2, 1, 3) | |
| scores = q @ k.transpose(0, 1, 3, 2) / np.sqrt(HEAD) | |
| scores = scores + np.where(self.mask, -1e9, 0.0) | |
| attn = np.exp(scores - scores.max(axis=-1, keepdims=True)) | |
| attn = attn / attn.sum(axis=-1, keepdims=True) | |
| ctx = attn @ v | |
| ctx = ctx.transpose(0, 2, 1, 3).reshape(B, K, D) | |
| attn_out = cur + ctx @ blk.Wo | |
| h1 = attn_out @ blk.W1 + blk.b1 | |
| act = np.tanh(h1) | |
| h2 = act @ blk.W2 + blk.b2 | |
| Y = attn_out + h2 | |
| caches.append((cur, Q, K_, V_, q, k, v, scores, attn, ctx, attn_out, h1, act, h2, Y)) | |
| cur = Y | |
| logits = cur @ self.Wout + self.bout | |
| return logits, (x, X, caches, cur) | |
| class AdamOpt: | |
| def __init__(self, params, lr=0.005, b1=0.9, b2=0.999, eps=1e-8): | |
| self.lr = lr | |
| self.b1, self.b2, self.eps = b1, b2, eps | |
| self.m = {k: np.zeros_like(v) for k, v in params.items()} | |
| self.v = {k: np.zeros_like(v) for k, v in params.items()} | |
| self.t = 0 | |
| self.params = params | |
| def step(self, grads, max_norm=None): | |
| self.t += 1 | |
| if max_norm: | |
| # global-norm gradient clipping (prevents loss explosions) | |
| total = 0.0 | |
| for g in grads.values(): | |
| total += float((g * g).sum()) | |
| total = total ** 0.5 | |
| if total > max_norm: | |
| scale = max_norm / (total + 1e-12) | |
| grads = {k: g * scale for k, g in grads.items()} | |
| for k, g in grads.items(): | |
| self.m[k] = self.b1 * self.m[k] + (1 - self.b1) * g | |
| self.v[k] = self.b2 * self.v[k] + (1 - self.b2) * (g * g) | |
| m_hat = self.m[k] / (1 - self.b1 ** self.t) | |
| v_hat = self.v[k] / (1 - self.b2 ** self.t) | |
| self.params[k] -= self.lr * m_hat / (np.sqrt(v_hat) + self.eps) | |
| def block_backward(blk, cache, dY, m, B, K, D, H, HEAD): | |
| """Backward through one block. dY: gradient on block output (B,K,D). | |
| Returns (grads_prefix, dXin) where grads_prefix keys are block-local | |
| ('Wq','Wk',...) and dXin is gradient on block input.""" | |
| cur, Q, K_, V_, q, k, v, scores, attn, ctx, attn_out, h1, act, h2, Y = cache | |
| g = {} | |
| dattn_out = dY.copy() | |
| dh2 = dY.copy() | |
| g['W2'] = act.reshape(-1, D * 2).T @ dh2.reshape(-1, D) | |
| g['b2'] = dh2.reshape(-1, D).sum(axis=0) | |
| dact = dh2 @ blk.W2.T | |
| dh1 = dact * (1 - act ** 2) | |
| g['W1'] = attn_out.reshape(-1, D).T @ dh1.reshape(-1, D * 2) | |
| g['b1'] = dh1.reshape(-1, D * 2).sum(axis=0) | |
| dattn_out = dattn_out + dh1 @ blk.W1.T | |
| dctx = dattn_out @ blk.Wo.T | |
| dX = dattn_out.copy() # residual 1 | |
| g['Wo'] = ctx.reshape(B * K, D).T @ dattn_out.reshape(B * K, D) | |
| dctx = dctx.reshape(B, K, H, HEAD).transpose(0, 2, 1, 3) | |
| dv = attn.transpose(0, 1, 3, 2) @ dctx | |
| dattn = dctx @ v.transpose(0, 1, 3, 2) | |
| dscores = attn * (dattn - (dattn * attn).sum(axis=-1, keepdims=True)) | |
| dscores = np.where(m.mask, 0.0, dscores) | |
| dscores = dscores / np.sqrt(HEAD) | |
| dq = dscores @ k | |
| dk = dscores.transpose(0, 1, 3, 2) @ q | |
| dq = dq.transpose(0, 2, 1, 3).reshape(B, K, D) | |
| dk = dk.transpose(0, 2, 1, 3).reshape(B, K, D) | |
| dv = dv.transpose(0, 2, 1, 3).reshape(B, K, D) | |
| g['Wq'] = cur.reshape(B * K, D).T @ dq.reshape(B * K, D) | |
| g['Wk'] = cur.reshape(B * K, D).T @ dk.reshape(B * K, D) | |
| g['Wv'] = cur.reshape(B * K, D).T @ dv.reshape(B * K, D) | |
| dX = dX + dq @ blk.Wq.T + dk @ blk.Wk.T + dv @ blk.Wv.T | |
| return g, dX | |
| def backward(m, cache, targets): | |
| """Proper backprop through all blocks + embeddings. | |
| targets: (B, K) - for each window position j, the next token arr[s+j+1]. | |
| Logits (B, K, V) predict each position's next token (causal LM objective). | |
| """ | |
| x, X, caches, Y = cache | |
| B, K, D, H, HEAD = x.shape[0], m.K, m.D, m.H, m.HEAD | |
| V = m.V | |
| grads = {} | |
| logits = Y @ m.Wout + m.bout | |
| probs = np.exp(logits - logits.max(axis=-1, keepdims=True)) | |
| probs = probs / probs.sum(axis=-1, keepdims=True) | |
| g = probs.copy() | |
| g[np.arange(B)[:, None], np.arange(K)[None, :], targets] -= 1.0 | |
| g = g / (B * K) | |
| grads['Wout'] = Y.reshape(B * K, D).T @ g.reshape(B * K, V) | |
| grads['bout'] = g.reshape(B * K, V).sum(axis=0) | |
| dY = g @ m.Wout.T | |
| # Backward through blocks in reverse | |
| for li in range(m.L - 1, -1, -1): | |
| blk = m.blocks[li] | |
| g_blk, dY = block_backward(blk, caches[li], dY, m, B, K, D, H, HEAD) | |
| for name, arr in g_blk.items(): | |
| grads[f'{li}:{name}'] = arr | |
| # embeddings: X = E_tok[x] + E_pos[pos] | |
| grads['E_tok'] = np.zeros_like(m.E_tok) | |
| np.add.at(grads['E_tok'], x, dY) | |
| grads['E_pos'] = dY.sum(axis=0) | |
| return grads | |
| def main(): | |
| parts = collect_corpus() | |
| train_files = [p for k, p in parts if k == 'train'] | |
| held_files = [p for k, p in parts if k == 'held'] | |
| print(f'corpus: {len(train_files)} train, {len(held_files)} held-out') | |
| # ---- vocabulary (M3_CODEC=1 switches to the 64-token learned codec) ---- | |
| global V, VOCAB, TOKENS, tokenize | |
| use_codec = os.environ.get('M3_CODEC', '0') == '1' | |
| if use_codec: | |
| codec = make_codec() | |
| TOKENS = codec['tokens'] | |
| V = len(TOKENS) | |
| VOCAB = codec['token_to_id'] | |
| tokenize = make_codec_tokenize(codec) | |
| is_digits = 'NUM' not in VOCAB | |
| print(f'vocab: codec (len={V}, digits={is_digits})') | |
| vocab_path = 'minds/mid_prophet_m37_vocab.txt' if is_digits else 'minds/mid_prophet_m32_vocab.txt' | |
| else: | |
| V = len(TOKENS) | |
| print(f'vocab: 28-token (len={V})') | |
| vocab_path = 'minds/mid_prophet_m3_vocab.txt' | |
| big = [] | |
| for p in train_files: | |
| try: | |
| src = open(p, encoding='utf-8', errors='replace').read() | |
| big.extend(tokenize(src)) | |
| except Exception: | |
| pass | |
| # Corpus Factory (Phase II): verified synthetic programs. Primaries and | |
| # semantic-equivalence variants only; mutants are NOT LM training text. | |
| factory_path = os.environ.get('M3_FACTORY') | |
| n_factory = 0 | |
| if factory_path: | |
| import json | |
| with open(factory_path, encoding='utf-8') as f: | |
| for line in f: | |
| line = line.strip() | |
| if not line: | |
| continue | |
| rec = json.loads(line) | |
| big.extend(tokenize(rec['src'])) | |
| for v in rec.get('variants', []): | |
| big.extend(tokenize(v['src'])) | |
| n_factory += 1 | |
| print(f'factory corpus: {factory_path} ({n_factory} programs)') | |
| print(f'total train tokens: {len(big)}') | |
| os.makedirs('minds', exist_ok=True) | |
| with open(vocab_path, 'w') as f: | |
| f.write(f'# vocab = {V}\n') | |
| for t, idx in VOCAB.items(): | |
| f.write(f'{idx}\t{t}\n') | |
| # ---- config (capacity is the only thing that changes between runs) ---- | |
| K = int(os.environ.get('M3_K', 64)) | |
| D = int(os.environ.get('M3_D', 48)) | |
| H = int(os.environ.get('M3_H', 6)) | |
| L = int(os.environ.get('M3_L', 2)) | |
| assert D % H == 0 | |
| BATCH = int(os.environ.get('M3_BATCH', 128)) | |
| STEPS = int(os.environ.get('M3_STEPS', 2000)) | |
| EVAL_EVERY = int(os.environ.get('M3_EVAL_EVERY', 400)) | |
| LR = float(os.environ.get('M3_LR', 0.005)) | |
| TAG = os.environ.get('M3_TAG', 'm31') | |
| # --------------------------------------------------------------------- | |
| rng = np.random.RandomState(11) | |
| m = M3(V, K, D, H, L, rng) | |
| m.bout = np.log((np.bincount(np.array(big, dtype=np.int32), minlength=V) + 1.0) / len(big)) | |
| print(f'arch: K={K} D={D} H={H} L={L} V={V} params~{m.n_params()}') | |
| arr = np.array(big, dtype=np.int32) | |
| n = len(arr) | |
| params = m.params_map() | |
| opt = AdamOpt(params, lr=LR) | |
| for step in range(STEPS): | |
| starts = rng.randint(0, n - K, size=BATCH) | |
| xs = np.stack([arr[s:s + K] for s in starts]) | |
| targets = np.stack([arr[s + 1:s + K + 1] for s in starts]) | |
| logits, cache = m.forward(xs) | |
| grads = backward(m, cache, targets) | |
| opt.step(grads, max_norm=float(os.environ.get('M3_CLIP', '0') or 0) or None) | |
| if step % EVAL_EVERY == 0 or step == STEPS - 1: | |
| preds = logits.argmax(axis=-1) | |
| acc = (preds == targets).mean() * 100 | |
| print(f' step {step:>4d}: batch-train-acc = {acc:.2f}%') | |
| # Save weights | |
| SCALE = 1000 | |
| def dump(name, arr_): | |
| flat = arr_.reshape(-1) | |
| return f'[{name}] shape={list(arr_.shape)} ' + ','.join(str(int(round(float(x) * SCALE))) for x in flat) + '\n' | |
| w_path = f'minds/mid_prophet_{TAG}_w.txt' | |
| with open(w_path, 'w') as f: | |
| f.write(f'vocab={V} k={K} d={D} h={H} head={m.HEAD} layers={L} scale={SCALE} arch=transformer\n') | |
| for name, arr_ in params.items(): | |
| f.write(dump(name, arr_)) | |
| # Held-out | |
| print('\n=== held-out ===') | |
| held_docs = [] | |
| for p in held_files: | |
| try: | |
| held_docs.append((os.path.basename(p), | |
| open(p, encoding='utf-8', errors='replace').read())) | |
| except Exception: | |
| pass | |
| factory_holdout = os.environ.get('M3_FACTORY_HOLDOUT') | |
| if factory_holdout: | |
| import json | |
| parts_h = [] | |
| with open(factory_holdout, encoding='utf-8') as f: | |
| for line in f: | |
| line = line.strip() | |
| if not line: | |
| continue | |
| rec = json.loads(line) | |
| parts_h.append(rec['src']) | |
| # short programs (<K tokens): concatenate into one stream so that | |
| # sliding-window evaluation still has full windows | |
| held_docs.append(('factory_test', '\n'.join(parts_h))) | |
| print(f'factory holdout: {factory_holdout} ({len(parts_h)} programs, combined stream)') | |
| val_total = 0 | |
| correct_total = 0 | |
| for name, text in held_docs: | |
| tok = tokenize(text) | |
| if len(tok) < K + 1: continue | |
| arr_h = np.array(tok, dtype=np.int32) | |
| # non-overlapping windows + chunked forwards (memory-safe for big K/D/L) | |
| idx_all = np.arange(K, len(arr_h), K) | |
| c_sum = 0 | |
| t_sum = 0 | |
| for s0 in range(0, len(idx_all), 128): | |
| chunk = idx_all[s0:s0 + 128] | |
| wins = np.stack([arr_h[chunk - K + j] for j in range(K)], axis=1) | |
| logits, _ = m.forward(wins) | |
| preds = logits.argmax(axis=-1) | |
| targets = np.stack([arr_h[chunk - K + 1 + j] for j in range(K)], axis=1) | |
| c_sum += int((preds == targets).sum()) | |
| t_sum += int(preds.size) | |
| val_total += t_sum | |
| correct_total += c_sum | |
| print(f' held {name}: {c_sum}/{t_sum} = {c_sum*100/max(1,t_sum):.2f}%') | |
| if val_total: | |
| print(f'\noverall: {correct_total}/{val_total} = {correct_total*100/val_total:.2f}%') | |
| else: | |
| print('\nno held-out files (all seeds in training)') | |
| with open(f'minds/mid_prophet_{TAG}_meta.txt', 'w') as f: | |
| f.write(f'V={V}\nK={K}\nD={D}\nH={H}\nHEAD={m.HEAD}\nL={L}\n') | |
| f.write(f'train_tokens={n}\nsteps={STEPS}\nLR={LR}\nbatch={BATCH}\n') | |
| if __name__ == '__main__': | |
| main() |