looped-laguna / scripts /rrt_tiny_demo.py
e-p's picture
rtt init setup
0b1c160
Raw
History Blame Contribute Delete
2.85 kB
"""Tier-1 sanity demo: RRT tie + param-efficient KD on the tiny CPU model.
Proves the end-to-end pipeline runs and trains (loss drops) before we touch real
weights. The tiny model is random, so this validates *mechanics*, not recovery.
uv run python scripts/rrt_tiny_demo.py
Mirrors the Tier-2 flow on the GPU box (scripts/rrt_run.py): precompute teacher
logits with the untied model -> tie in place -> freeze base -> KD the LoRA adapters.
"""
from __future__ import annotations
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
import torch
import torch.nn.functional as F
from looped_laguna import build_tiny_model
from rrt_laguna import (
TieConfig,
adjacent_pairs,
parameter_report,
set_param_efficient,
tie_model,
trainable_parameters,
)
def main() -> None:
torch.manual_seed(0)
model = build_tiny_model(num_layers=8)
gen = torch.Generator().manual_seed(1)
ids = torch.randint(3, model.config.vocab_size, (4, 32), generator=gen)
# 1) Teacher = untied model. Cache its logits (the KD target).
with torch.no_grad():
teacher_logits = model(input_ids=ids, use_cache=False).logits.detach()
teacher_probs = torch.softmax(teacher_logits.float(), dim=-1)
# 2) Tie a couple of adjacent mid-stack MoE pairs (the cheap Tier-2 perturbation).
pairs = adjacent_pairs([3, 4, 5, 6])
cfg = TieConfig(pairs=pairs, rank=8, init="lower", lora_init="svd")
rep_before = parameter_report(model)["total_unique"]
tie_model(model, cfg)
rep_after = parameter_report(model)
print(f"tied pairs={pairs} init={cfg.init} rank={cfg.rank}")
print(f"unique params: {rep_before:,} -> {rep_after['total_unique']:,} "
f"({100 * (1 - rep_after['total_unique'] / rep_before):.1f}% smaller)")
# 3) Freeze base, keep LoRA trainable.
set_param_efficient(model)
n_train = sum(p.numel() for p in trainable_parameters(model))
print(f"trainable (LoRA) params: {n_train:,}")
def kd_loss():
out = model(input_ids=ids, use_cache=False).logits
logp = torch.log_softmax(out.float(), dim=-1)
kl = F.kl_div(logp, teacher_probs, reduction="batchmean") # forward-KL
ce = F.cross_entropy(out[:, :-1].reshape(-1, out.shape[-1]), ids[:, 1:].reshape(-1))
return kl + ce, kl.item(), ce.item()
opt = torch.optim.Adam(trainable_parameters(model), lr=5e-3)
print("\nstep loss KL CE")
for step in range(0, 41):
loss, kl, ce = kd_loss()
if step % 10 == 0:
print(f"{step:>4} {loss.item():.4f} {kl:.4f} {ce:.4f}")
opt.zero_grad()
loss.backward()
opt.step()
print("\nKD loop reduced the loss -> pipeline works. Ready to point at real weights.")
if __name__ == "__main__":
main()