RegalInvader's picture
Release: Marco-Nano-Instruct 1-, 2- and 3-bit GGUF for phones
e5235fb verified
Raw History Blame Contribute Delete
2.4 kB
#!/usr/bin/env python3
"""Quick KL check of packed students against the teacher on one GPU (PyTorch), for deciding on a pod whether a run
paid off before downloading it. Mirrors the llama.cpp harness: the first N held-out eval sequences of 4,096 tokens,
plain causal attention, KL(teacher || student) over the full vocabulary, scored on the second half of each sequence.
python scripts/pod_kl.py --packed runs/A/packed/tok_N --packed runs/B/packed/tok_N [--n 40]
Absolute values differ slightly from llama.cpp's (tokenisation of the decoded text, kernels); compare runs measured
here with each other.
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
import numpy as np
import torch
REPO = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "src"))
from lowbit.model.student import load_packed_student # noqa: E402
TEACHER = "/path/to/data/models/Marco-Nano-Instruct"
EVAL = "/path/to/data/shards/eval/tokens.u32"
CTX = 4096
@torch.no_grad()
def logp(model, ids: torch.Tensor) -> torch.Tensor:
return torch.log_softmax(model(ids).logits[0, CTX // 2:].float(), -1)
def main() -> None:
p = argparse.ArgumentParser()
p.add_argument("--packed", action="append", required=True)
p.add_argument("--n", type=int, default=40)
a = p.parse_args()
from transformers import AutoModelForCausalLM
toks = np.memmap(EVAL, dtype=np.uint32, mode="r")
seqs = [torch.from_numpy(toks[i * CTX:(i + 1) * CTX].astype(np.int64))[None].cuda() for i in range(a.n)]
teacher = AutoModelForCausalLM.from_pretrained(TEACHER, dtype=torch.bfloat16).cuda().eval()
ref = [logp(teacher, s).cpu() for s in seqs] # ~0.6 GB per sequence on the CPU side
del teacher
torch.cuda.empty_cache()
out = {}
for d in a.packed:
m = load_packed_student(d, device="cuda")
kl = n = 0.0
same = 0
for s, lt in zip(seqs, ref):
ls = logp(m, s)
lt = lt.cuda()
kl += (lt.exp() * (lt - ls)).sum(-1).sum().item()
same += (lt.argmax(-1) == ls.argmax(-1)).sum().item()
n += lt.shape[0]
out[d] = {"kl": round(kl / n, 5), "same_top1_pct": round(100 * same / n, 2), "tokens": int(n)}
print(json.dumps({d: out[d]}), flush=True)
del m
torch.cuda.empty_cache()
if __name__ == "__main__":
main()