F-Labs/dzeta-agi-bucket / compare_baselines_sft.py
F-Labs's picture
download
raw
12.4 kB
import os
import sys
import time
import math
import random
import subprocess
from collections import defaultdict, Counter
import torch
import torch.nn as nn
from torch.nn import functional as F
torch.manual_seed(42)
random.seed(42)
CORPUS_PATH = "benchmarks/data/sft_dolly_1000.txt"
DZETA_MODEL_PATH = "benchmarks/models/dzeta_sft_v3.dzeta.bin"
DZETA_BIN = "./dzeta_inspect_model.exe"
PROMPTS = [
"User: Which is a species of fish? Tope or Rope Assistant:",
"User: Why can camels survive for long without water? Assistant:",
"User: When did Virgin Australia start operating? Assistant:",
"User: Alice's parents have three daughters: Amy, Jessy, and what's the name of the third daughter? Assistant:"
]
def load_corpus():
with open(CORPUS_PATH, "r", encoding="utf-8") as f:
text = f.read()
return text
def tokenize(text):
import re
return re.findall(r"\w+|[^\w\s]", text.lower())
print("Loading SFT Dolly corpus...", flush=True)
corpus_text = load_corpus()
tokens = tokenize(corpus_text)
print(f"Total tokens in corpus: {len(tokens)}", flush=True)
vocab = sorted(list(set(tokens)))
vocab_size = len(vocab)
print(f"Unique vocab size: {vocab_size}", flush=True)
w2i = {w: i for i, w in enumerate(vocab)}
i2w = {i: w for i, w in enumerate(vocab)}
# -------------------------------------------------------------
# 1. MARKOV CHAIN BASELINE
# -------------------------------------------------------------
class MarkovTrigram:
def __init__(self):
self.trigrams = defaultdict(Counter)
self.bigrams = defaultdict(Counter)
self.unigrams = Counter()
def train(self, tokens):
print("[Markov] Training trigram model on SFT...", flush=True)
t0 = time.time()
for i in range(len(tokens) - 2):
w1, w2, w3 = tokens[i], tokens[i+1], tokens[i+2]
self.trigrams[(w1, w2)][w3] += 1
self.bigrams[w1][w2] += 1
self.unigrams[w1] += 1
self.unigrams[tokens[-2]] += 1
self.unigrams[tokens[-1]] += 1
elapsed = time.time() - t0
print(f"[Markov] Trained in {elapsed:.4f}s", flush=True)
return elapsed
def generate(self, prompt, length=16, temperature=0.7):
prompt_tokens = tokenize(prompt)
curr = list(prompt_tokens)
for _ in range(length):
candidates = None
if len(curr) >= 2:
key = (curr[-2], curr[-1])
if key in self.trigrams:
candidates = self.trigrams[key]
if not candidates and len(curr) >= 1:
key = curr[-1]
if key in self.bigrams:
candidates = self.bigrams[key]
if not candidates:
candidates = self.unigrams
words, counts = zip(*candidates.items())
probs = torch.tensor(counts, dtype=torch.float)
if temperature > 0:
probs = probs ** (1.0 / temperature)
probs = probs / probs.sum()
next_word = words[torch.multinomial(probs, 1).item()]
else:
next_word = words[torch.argmax(probs).item()]
curr.append(next_word)
return " ".join(curr[len(prompt_tokens):])
# -------------------------------------------------------------
# 2. nanoGPT TRANSFORMER BASELINE
# -------------------------------------------------------------
class Head(nn.Module):
def __init__(self, head_size, n_embd, block_size):
super().__init__()
self.key = nn.Linear(n_embd, head_size, bias=False)
self.query = nn.Linear(n_embd, head_size, bias=False)
self.value = nn.Linear(n_embd, head_size, bias=False)
self.register_buffer('tril', torch.tril(torch.ones(block_size, block_size)))
def forward(self, x):
B, T, C = x.shape
k = self.key(x)
q = self.query(x)
wei = q @ k.transpose(-2, -1) * (C ** -0.5)
wei = wei.masked_fill(self.tril[:T, :T] == 0, float('-inf'))
wei = F.softmax(wei, dim=-1)
v = self.value(x)
out = wei @ v
return out
class MultiHeadAttention(nn.Module):
def __init__(self, num_heads, head_size, n_embd, block_size):
super().__init__()
self.heads = nn.ModuleList([Head(head_size, n_embd, block_size) for _ in range(num_heads)])
self.proj = nn.Linear(head_size * num_heads, n_embd)
def forward(self, x):
out = torch.cat([h(x) for h in self.heads], dim=-1)
out = self.proj(out)
return out
class FeedForward(nn.Module):
def __init__(self, n_embd):
super().__init__()
self.net = nn.Sequential(
nn.Linear(n_embd, 4 * n_embd),
nn.GELU(),
nn.Linear(4 * n_embd, n_embd),
)
def forward(self, x):
return self.net(x)
class Block(nn.Module):
def __init__(self, n_embd, n_head, block_size):
super().__init__()
head_size = n_embd // n_head
self.sa = MultiHeadAttention(n_head, head_size, n_embd, block_size)
self.ffwd = FeedForward(n_embd)
self.ln1 = nn.LayerNorm(n_embd)
self.ln2 = nn.LayerNorm(n_embd)
def forward(self, x):
x = x + self.sa(self.ln1(x))
x = x + self.ffwd(self.ln2(x))
return x
class NanoGPT(nn.Module):
def __init__(self, vocab_size, n_embd=128, block_size=64, n_layer=4, n_head=4):
super().__init__()
self.block_size = block_size
self.token_embedding_table = nn.Embedding(vocab_size, n_embd)
self.position_embedding_table = nn.Embedding(block_size, n_embd)
self.blocks = nn.Sequential(*[Block(n_embd, n_head, block_size) for _ in range(n_layer)])
self.ln_f = nn.LayerNorm(n_embd)
self.lm_head = nn.Linear(n_embd, vocab_size)
def forward(self, idx, targets=None):
B, T = idx.shape
tok_emb = self.token_embedding_table(idx)
pos_emb = self.position_embedding_table(torch.arange(T, device=idx.device))
x = tok_emb + pos_emb
x = self.blocks(x)
x = self.ln_f(x)
logits = self.lm_head(x)
if targets is None:
loss = None
else:
B, T, C = logits.shape
logits = logits.view(B*T, C)
targets = targets.view(B*T)
loss = F.cross_entropy(logits, targets)
return logits, loss
def generate(self, idx, max_new_tokens, temperature=0.8):
for _ in range(max_new_tokens):
idx_cond = idx[:, -self.block_size:]
logits, _ = self(idx_cond)
logits = logits[:, -1, :] / temperature
probs = F.softmax(logits, dim=-1)
idx_next = torch.multinomial(probs, num_samples=1)
idx = torch.cat((idx, idx_next), dim=1)
return idx
def train_nanogpt(tokens, vocab_size, epochs=2, batch_size=32, block_size=64):
print("[nanoGPT] Initializing model for SFT...", flush=True)
model = NanoGPT(vocab_size=vocab_size, n_embd=128, block_size=block_size, n_layer=4, n_head=4)
param_count = sum(p.numel() for p in model.parameters())
print(f"[nanoGPT] Total parameters: {param_count:,}", flush=True)
token_ids = torch.tensor([w2i[w] for w in tokens], dtype=torch.long)
n_samples = len(token_ids) - block_size - 1
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-2)
steps_per_epoch = n_samples // (batch_size * block_size)
total_steps = steps_per_epoch * epochs
print(f"[nanoGPT] Training {epochs} epochs ({total_steps} steps, batch={batch_size}, block={block_size})...", flush=True)
t0 = time.time()
model.train()
step = 0
final_loss = 0.0
for epoch in range(epochs):
perm = torch.randperm(n_samples)
for i in range(0, n_samples - batch_size, batch_size):
batch_indices = perm[i:i+batch_size]
x = torch.stack([token_ids[idx:idx+block_size] for idx in batch_indices])
y = torch.stack([token_ids[idx+1:idx+block_size+1] for idx in batch_indices])
logits, loss = model(x, y)
optimizer.zero_grad(set_to_none=True)
loss.backward()
optimizer.step()
step += 1
final_loss = loss.item()
if step % 25 == 0 or step == total_steps:
print(f"[nanoGPT] step {step}/{total_steps} (epoch {epoch+1}/{epochs}) loss={final_loss:.4f}", flush=True)
if step >= total_steps:
break
if step >= total_steps:
break
elapsed = time.time() - t0
print(f"[nanoGPT] Training completed in {elapsed:.2f}s, final loss={final_loss:.4f}", flush=True)
return model, elapsed, param_count, final_loss
# -------------------------------------------------------------
# 3. DZETA AGI BASELINE
# -------------------------------------------------------------
def run_dzeta(prompts):
print("[DZETA] Running dzeta_inspect_model on SFT prompts...", flush=True)
cmd = [DZETA_BIN, "--model", DZETA_MODEL_PATH]
for p in prompts:
cmd.extend(["--prompt", p])
res = subprocess.run(cmd, capture_output=True, text=True, check=True)
outputs = {}
current_prompt = None
for line in res.stdout.splitlines():
if line.startswith("prompt="):
current_prompt = line[len("prompt="):].strip()
elif line.startswith("prompt_output=") and current_prompt:
outputs[current_prompt] = line[len("prompt_output="):].strip()
current_prompt = None
return outputs
# -------------------------------------------------------------
# MAIN BENCHMARK RUN
# -------------------------------------------------------------
def main():
# 1. Markov
markov = MarkovTrigram()
markov_time = markov.train(tokens)
# 2. nanoGPT
nanogpt, gpt_time, gpt_params, gpt_final_loss = train_nanogpt(tokens, vocab_size, epochs=2, batch_size=32, block_size=64)
# 3. DZETA
dzeta_outputs = run_dzeta(PROMPTS)
print("\n" + "="*80)
print("EMPIRICAL SHOWDOWN: DZETA AGI vs MARKOV vs nanoGPT (SFT Dolly-15k 1000)")
print("="*80)
results = []
for p in PROMPTS:
# Markov generation
m_out = markov.generate(p, length=16, temperature=0.7)
# nanoGPT generation
p_tokens = tokenize(p)
p_ids = [w2i.get(w, 0) for w in p_tokens]
if not p_ids:
p_ids = [0]
inp = torch.tensor([p_ids], dtype=torch.long)
nanogpt.eval()
with torch.no_grad():
out_ids = nanogpt.generate(inp, max_new_tokens=16, temperature=0.8)[0].tolist()
gpt_words = [i2w.get(idx, "<unk>") for idx in out_ids[len(p_ids):]]
gpt_out = " ".join(gpt_words)
# DZETA generation
dz_out = dzeta_outputs.get(p, "N/A")
results.append({
"prompt": p,
"markov": m_out,
"nanogpt": gpt_out,
"dzeta": dz_out
})
# Print table
for r in results:
print(f"\n--- PROMPT: \"{r['prompt']}\" ---")
print(f"[Markov] : {r['markov']}")
print(f"[nanoGPT]: {r['nanogpt']}")
print(f"[DZETA] : {r['dzeta']}")
# Overlap metric (Attractor Collapse)
def compute_overlap(outputs):
import itertools
def to_words(text):
return set(tokenize(text))
sets = [to_words(o) for o in outputs]
scores = []
for s1, s2 in itertools.combinations(sets, 2):
if not s1 or not s2:
continue
inter = len(s1 & s2)
scores.append(inter / min(len(s1), len(s2)))
return sum(scores) / max(1, len(scores)) if scores else 0.0
m_overlap = compute_overlap([r["markov"] for r in results])
gpt_overlap = compute_overlap([r["nanogpt"] for r in results])
dz_overlap = compute_overlap([r["dzeta"] for r in results])
print("\n" + "="*80)
print("SUMMARY COMPARISON METRICS (SFT Instruction Following)")
print("="*80)
print(f"Markov : Train Time = {markov_time:.2f}s | Overlap = {m_overlap:.4f}")
print(f"nanoGPT : Train Time = {gpt_time:.2f}s | Params = {gpt_params:,} | Loss = {gpt_final_loss:.4f} | Overlap = {gpt_overlap:.4f}")
print(f"DZETA : Storage = 2048 dims (int16 compact) | Loss = ~3e-6 | Overlap = {dz_overlap:.4f}")
if __name__ == "__main__":
main()

Xet Storage Details

Size:
12.4 kB
·
Xet hash:
9a17ac0e3d5e7dc64122a25f6219a73c7e8051b3974fd7e47c407a2b4bfdc7a5

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.