|
| 1 | +"""Scaled-up text sampling — d=384, n_blocks=6, longer training. |
| 2 | +
|
| 3 | +At d=128 / 4 blocks / 2500 steps, even dense produces gibberish, so the |
| 4 | +"is FibGen output usable?" question couldn't be answered. This script |
| 5 | +trains at GPT-2-tiny-class parameters (d=384, n_blocks=6) for enough |
| 6 | +steps to push dense into "barely-coherent Shakespeare" territory, then |
| 7 | +compares FibGen and composed at that scale. |
| 8 | +
|
| 9 | +Wall-time budget (rough CPU estimates): |
| 10 | + dense_crt d=384 6blk 6000 steps: ~20 min |
| 11 | + fibgen_K32_cross d=384 6blk 6000 steps: ~50 min |
| 12 | + composed d=384 6blk 6000 steps: ~80 min |
| 13 | +Total: ~2.5 hours. |
| 14 | +
|
| 15 | +Prints best-val checkpoints + generated text for each arch. |
| 16 | +""" |
| 17 | + |
| 18 | +import argparse |
| 19 | +import sys |
| 20 | +import time |
| 21 | +from pathlib import Path |
| 22 | + |
| 23 | +import torch |
| 24 | +import torch.nn.functional as F |
| 25 | + |
| 26 | +sys.path.insert(0, str(Path(__file__).parent)) |
| 27 | +from corpus import make_dataset |
| 28 | +from models import make_model |
| 29 | +from models_fibgen import FibGenLM, FibGenTransformerless |
| 30 | +from train_distractor_mix import build_distractor_stream |
| 31 | +from lazy_data import fib_positions_in_window, get_fib_strided_batch |
| 32 | +from sample_text import evaluate, train, generate_text |
| 33 | + |
| 34 | + |
| 35 | +def main(): |
| 36 | + parser = argparse.ArgumentParser() |
| 37 | + parser.add_argument("--steps", type=int, default=6000) |
| 38 | + parser.add_argument("--batch-size", type=int, default=32) |
| 39 | + parser.add_argument("--seq-len", type=int, default=128) |
| 40 | + parser.add_argument("--d-model", type=int, default=384) |
| 41 | + parser.add_argument("--n-blocks", type=int, default=6) |
| 42 | + parser.add_argument("--lr", type=float, default=3e-4) |
| 43 | + parser.add_argument("--seed", type=int, default=42) |
| 44 | + parser.add_argument("--distractor-frac", type=float, default=0.20) |
| 45 | + parser.add_argument("--prompt", type=str, |
| 46 | + default="ROMEO:\nWhat light through") |
| 47 | + parser.add_argument("--n-new", type=int, default=600) |
| 48 | + parser.add_argument("--temperature", type=float, default=0.8) |
| 49 | + parser.add_argument("--top-k", type=int, default=10) |
| 50 | + parser.add_argument("--out", type=str, default="results_samples_scaled.txt") |
| 51 | + parser.add_argument("--archs", type=str, |
| 52 | + default="dense_crt,fibgen_K32_cross,composed_transformerless") |
| 53 | + args = parser.parse_args() |
| 54 | + |
| 55 | + chars, stoi, itos, encoded = make_dataset(seq_len=args.seq_len, |
| 56 | + source="tinyshakespeare") |
| 57 | + vocab_size = len(chars) |
| 58 | + train_split, val_split = build_distractor_stream( |
| 59 | + encoded, args.distractor_frac, args.seq_len, args.seed, |
| 60 | + ) |
| 61 | + fib_positions = fib_positions_in_window(args.seq_len) |
| 62 | + |
| 63 | + arch_factories = { |
| 64 | + "dense_crt": lambda: make_model( |
| 65 | + "crt_only", vocab_size=vocab_size, seq_len=args.seq_len, |
| 66 | + d_model=args.d_model, n_blocks=args.n_blocks, |
| 67 | + ), |
| 68 | + "fibgen_K32_cross": lambda: FibGenLM( |
| 69 | + vocab_size=vocab_size, d_model=args.d_model, |
| 70 | + n_blocks=args.n_blocks, seq_len=args.seq_len, K=32, mode="cross", |
| 71 | + ), |
| 72 | + "composed_transformerless": lambda: FibGenTransformerless( |
| 73 | + vocab_size=vocab_size, d_model=args.d_model, n_blocks=args.n_blocks, |
| 74 | + seq_len=args.seq_len, K=32, mode="cross", n_specialists=5, |
| 75 | + ), |
| 76 | + } |
| 77 | + |
| 78 | + selected_archs = [a.strip() for a in args.archs.split(",")] |
| 79 | + |
| 80 | + space_id = stoi.get(" ", 0) |
| 81 | + prompt_ids = torch.tensor( |
| 82 | + [[stoi.get(c, space_id) for c in args.prompt]], dtype=torch.long, |
| 83 | + ) |
| 84 | + |
| 85 | + print(f"Scaled-up sampling: d={args.d_model}, n_blocks={args.n_blocks}, " |
| 86 | + f"steps={args.steps}", flush=True) |
| 87 | + print(f"Archs: {selected_archs}", flush=True) |
| 88 | + |
| 89 | + samples = {} |
| 90 | + meta = {} |
| 91 | + for name in selected_archs: |
| 92 | + if name not in arch_factories: |
| 93 | + print(f" skipping unknown arch: {name}", flush=True) |
| 94 | + continue |
| 95 | + t_arch = time.time() |
| 96 | + model = arch_factories[name]() |
| 97 | + model, best_val, best_step = train(name, model, train_split, val_split, |
| 98 | + args, fib_positions) |
| 99 | + wall = time.time() - t_arch |
| 100 | + meta[name] = {"best_val": best_val, "best_step": best_step, |
| 101 | + "n_params": sum(p.numel() for p in model.parameters()), |
| 102 | + "wall_seconds": wall} |
| 103 | + out_ids = generate_text(model, prompt_ids, args.n_new, args.seq_len, |
| 104 | + itos, temperature=args.temperature, |
| 105 | + top_k=args.top_k) |
| 106 | + text = "".join(itos[int(i)] for i in out_ids[0].tolist()) |
| 107 | + samples[name] = text |
| 108 | + print(f"\n{'=' * 70}") |
| 109 | + print(f"SAMPLE from {name} best_val={best_val:.4f} @ step {best_step} " |
| 110 | + f"wall={wall:.0f}s") |
| 111 | + print('=' * 70) |
| 112 | + print(text) |
| 113 | + print('=' * 70, flush=True) |
| 114 | + |
| 115 | + # Save partial result after each arch so we have results even if a later one crashes. |
| 116 | + out_path = Path(__file__).parent / args.out |
| 117 | + with open(out_path, "w") as f: |
| 118 | + f.write(f"# Scaled-up samples (d={args.d_model}, n_blocks={args.n_blocks}, " |
| 119 | + f"steps={args.steps}, temperature={args.temperature}, " |
| 120 | + f"top_k={args.top_k})\n") |
| 121 | + f.write(f"# Prompt: {args.prompt!r}\n\n") |
| 122 | + for n, s in samples.items(): |
| 123 | + m = meta[n] |
| 124 | + f.write(f"\n{'=' * 70}\n{n} best_val={m['best_val']:.4f} " |
| 125 | + f"@ step {m['best_step']} params={m['n_params']:,} " |
| 126 | + f"wall={m['wall_seconds']:.0f}s\n" |
| 127 | + f"{'=' * 70}\n{s}\n") |
| 128 | + |
| 129 | + print(f"\nWrote {out_path}") |
| 130 | + |
| 131 | + |
| 132 | +if __name__ == "__main__": |
| 133 | + main() |
0 commit comments