File size: 2,399 Bytes
e5235fb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#!/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()