ncm-modular / src /ncm_modular /eval_all.py
Red-fuchs's picture
ncm-modular: initial release (code + results, no weights)
b5731a6 verified
Raw History Blame Contribute Delete
4.61 kB
"""Test-set metrics + latency for NCM generator, AR baseline, intent classifier."""
import os, sys, json, time, argparse
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from paths import WORK
os.environ.setdefault("MFMT", "C")
import numpy as np, mlx.core as mx
from transformers import AutoTokenizer
from gen_mlx import *
from ar_mlx import *
from massive_eval import score
ap = argparse.ArgumentParser()
ap.add_argument("--ncm", default=WORK+"/ncm_pilot"); ap.add_argument("--ar", default=WORK+"/ar_pilot"); ap.add_argument("--cls", default=WORK+"/cls_pilot")
ap.add_argument("--split", default="test"); ap.add_argument("--out", default=WORK+"/massive_de/results.json"); ap.add_argument("--P", type=int, default=40)
ap.add_argument("--nreq", type=int, default=200)
a = ap.parse_args()
EOS, PAD = 151645, 151643
tok = AutoTokenizer.from_pretrained(WORK+"/tok/Qwen3-0.6B"); fc = FrozenCodec()
D = [json.loads(l) for l in open(f"{WORK}/massive_de/{a.split}.jsonl")]
def pad(ids, n): ids = ids[:n]; return ids + [PAD] * (n - len(ids))
PT = np.array([pad(tok.encode(d["prefix"])[:a.P - 1] + [EOS], a.P) for d in D], np.int32)
def cut(r):
r = list(r); r = r[:r.index(EOS)] if EOS in r else r
return tok.decode([t for t in r if t != PAD], skip_special_tokens=True)
def lat(fn, bs, n):
ts = []
rng = np.random.RandomState(0)
for i in range(n + 3):
idx = rng.randint(0, len(D), bs); t = time.perf_counter(); fn(mx.array(PT[idx])); ts.append((time.perf_counter() - t) * 1000)
ts = np.array(ts[3:]); return dict(p50=float(np.percentile(ts, 50)), p95=float(np.percentile(ts, 95)), mean=float(ts.mean()))
res = {}
# ---- NCM
cfg = json.load(open(f"{a.ncm}/ckpt.json"))["args"]
master = Gen(fc, cfg["P"], cfg["D"], cfg["H"], cfg["nl"], PAD, drop=0.0); master.load_weights(f"{a.ncm}/best.safetensors")
g = Gen(fc, cfg["P"], cfg["D"], cfg["H"], cfg["nl"], PAD, drop=0.0).set_compute(mx.float16)
from mlx.utils import tree_map
g.update(tree_map(lambda t: t.astype(mx.float16), master.parameters())); g.eval(); mx.eval(g.parameters())
def ncm_gen(p): t, _ = generate(g, fc, p); mx.eval(t); return t
preds = []
for i in range(0, len(D), 64): preds += [cut(r) for r in ncm_gen(mx.array(PT[i:i + 64])).tolist()]
r = score(preds, D); r["NFE"] = g.K; r["params_M"] = count_params(master) / 1e6
r["lat_b1_ms"] = lat(ncm_gen, 1, a.nreq); r["lat_b16_ms"] = lat(ncm_gen, 16, max(a.nreq // 5, 20)); res["ncm"] = r
res["ncm_codec_ceiling_exact"] = None
json.dump({"preds_ncm": preds[:50]}, open(a.out.replace(".json", "_samples_ncm.json"), "w"), ensure_ascii=False, indent=1)
print("NCM", r, flush=True)
# ---- AR
if os.path.exists(f"{a.ar}/model.safetensors"):
voc = json.load(open(f"{a.ar}/vocab.json")); ck = json.load(open(f"{a.ar}/ckpt.json")); inv_v = {i + 3: t for i, t in enumerate(voc["vocab"])}
m = ARGen(fc.tok, ck["V"], ck["args"]["P"], ck["args"]["T"], pad_id=PAD, drop=0.0); m.load_weights(f"{a.ar}/best.safetensors")
m.set_dtype(mx.float16); m.eval(); mx.eval(m.parameters())
nfe = []
def ar_gen(p): o, n = m.generate(p, 1); mx.eval(o); nfe.append(n); return o
def dec(row):
row = row[:row.index(1)] if 1 in row else row
return tok.decode([inv_v[c] for c in row if c in inv_v], skip_special_tokens=True)
preds = []
for i in range(0, len(D), 64): preds += [dec(r) for r in ar_gen(mx.array(PT[i:i + 64])).tolist()]
r = score(preds, D); r["NFE_mean_b64"] = float(np.mean(nfe)); r["params_M"] = count_params(m) / 1e6
nfe.clear(); r["lat_b1_ms"] = lat(ar_gen, 1, a.nreq); r["NFE_mean_b1"] = float(np.mean(nfe)); nfe.clear()
r["lat_b16_ms"] = lat(ar_gen, 16, max(a.nreq // 5, 20)); r["NFE_mean_b16"] = float(np.mean(nfe)); res["ar"] = r
json.dump({"preds_ar": preds[:50]}, open(a.out.replace(".json", "_samples_ar.json"), "w"), ensure_ascii=False, indent=1)
print("AR", r, flush=True)
# ---- classifier
if os.path.exists(f"{a.cls}/model.safetensors"):
ints = json.load(open(f"{a.cls}/vocab.json"))["ints"]
m = IntentCls(fc.tok, len(ints), a.P, pad_id=PAD, drop=0.0); m.load_weights(f"{a.cls}/best.safetensors"); m.set_dtype(mx.float16); m.eval()
def cl(p): o = mx.argmax(m(p), -1); mx.eval(o); return o
hit = 0
for i in range(0, len(D), 256): hit += sum(ints[j] == d["intent"] for j, d in zip(cl(mx.array(PT[i:i + 256])).tolist(), D[i:i + 256]))
res["cls"] = dict(intent_acc=hit / len(D), params_M=count_params(m) / 1e6, NFE=1, lat_b1_ms=lat(cl, 1, a.nreq), lat_b16_ms=lat(cl, 16, max(a.nreq // 5, 20)))
print("CLS", res["cls"], flush=True)
json.dump(res, open(a.out, "w"), indent=1)