Buckets:
| import argparse | |
| import gc | |
| import json | |
| import time | |
| import opharm | |
| import numpy as np | |
| import torch | |
| from opharm.bench.smoke import smoke_prompts | |
| from opharm.bench.tools import TOOLS | |
| from opharm.chat import load_tokenizer, pins, render, snapshot_dir | |
| from opharm.models import load_model | |
| from opharm.paths import RESULTS | |
| from opharm.run.decision import action_logodds, opener_ids | |
| from opharm.run.generate import end_ids, pad_id | |
| GEN = 32 | |
| def free(): | |
| gc.collect() | |
| if torch.backends.mps.is_available(): | |
| torch.mps.empty_cache() | |
| def mps_generate(key, prompts, stop, solver): | |
| model = load_model(key, solver=solver) | |
| conts, secs = [], [] | |
| with torch.inference_mode(): | |
| for ids in prompts: | |
| x = torch.tensor([ids], device=model.device) | |
| g = model.generate(x, attention_mask=torch.ones_like(x), max_new_tokens=GEN, do_sample=False, | |
| eos_token_id=stop[0], pad_token_id=stop[1]) | |
| conts.append(g[0, len(ids):].tolist()) | |
| t = time.perf_counter() | |
| model(input_ids=x, use_cache=False, logits_to_keep=1).logits.float().cpu() | |
| secs.append(time.perf_counter() - t) | |
| del model | |
| free() | |
| return conts, secs | |
| def torch_scores(key, device, dtype, prompts, conts, solver=None): | |
| model = load_model(key, device=device, dtype=dtype, solver=solver) | |
| out, secs = [], [] | |
| with torch.inference_mode(): | |
| for ids, cont in zip(prompts, conts): | |
| x = torch.tensor([ids + cont], device=model.device) | |
| t = time.perf_counter() | |
| out.append(model(input_ids=x, use_cache=False, logits_to_keep=len(cont) + 1).logits[0].float().cpu()) | |
| secs.append(time.perf_counter() - t) | |
| del model | |
| free() | |
| return out, secs | |
| def mlx_scores(key, prompts, conts): | |
| import mlx.core as mx | |
| from mlx_lm import load | |
| model, _ = load(str(snapshot_dir(key))) | |
| out, secs = [], [] | |
| for ids, cont in zip(prompts, conts): | |
| t = time.perf_counter() | |
| z = model(mx.array([ids + cont]))[0, len(ids) - 1:].astype(mx.float32) | |
| mx.eval(z) | |
| secs.append(time.perf_counter() - t) | |
| out.append(torch.from_numpy(np.array(z))) | |
| del model | |
| gc.collect() | |
| mx.clear_cache() | |
| return out, secs | |
| def first_divergence(rows, cont): | |
| pred = rows[:len(cont)].argmax(-1).tolist() | |
| return next((i for i, (a, b) in enumerate(zip(pred, cont)) if a != b), None) | |
| def compare(ref, test, conts, opener): | |
| lr = torch.stack([r[0] for r in ref]) | |
| lt = torch.stack([r[0] for r in test]) | |
| diff = (lr - lt).abs().max(-1).values | |
| pr, pt = lr.log_softmax(-1), lt.log_softmax(-1) | |
| div = [first_divergence(r, c) for r, c in zip(test, conts)] | |
| return { | |
| "n": len(diff), | |
| "top1_agree": int((lr.argmax(-1) == lt.argmax(-1)).sum()), | |
| "max_abs_logit_diff": {"max": diff.max().item(), "median": diff.median().item()}, | |
| "m_abs_diff_max": (action_logodds(lr, opener) - action_logodds(lt, opener)).abs().max().item(), | |
| "kl_max": (pr.exp() * (pr - pt)).sum(-1).max().item(), | |
| "greedy_identical_to_mps": sum(d is None for d in div), | |
| "greedy_first_divergence": div, | |
| } | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("model") | |
| ap.add_argument("--n", type=int, default=20) | |
| ap.add_argument("--ref-dtype", default="float32", choices=["float32", "bfloat16"]) | |
| ap.add_argument("--skip-mlx", action="store_true") | |
| ap.add_argument("--solver", default="loop", choices=["default", "loop"]) | |
| args = ap.parse_args() | |
| tok = load_tokenizer(args.model) | |
| opener, stop = opener_ids(tok), (sorted(end_ids(tok)), pad_id(tok)) | |
| prompts = [render(tok, s, u, TOOLS).ids for s, u in smoke_prompts()[:args.n]] | |
| conts, mps_secs = mps_generate(args.model, prompts, stop, args.solver) | |
| mps_scores = torch_scores(args.model, "mps", torch.bfloat16, prompts, conts, args.solver) | |
| ref_name = f"cpu_{args.ref_dtype}" | |
| scores = {ref_name: torch_scores(args.model, "cpu", getattr(torch, args.ref_dtype), prompts, conts), | |
| "mps_bfloat16": mps_scores} | |
| if not args.skip_mlx: | |
| scores["mlx_bfloat16"] = mlx_scores(args.model, prompts, conts) | |
| comparisons = {f"{k}_vs_{ref_name}": compare(scores[ref_name][0], v[0], conts, opener) | |
| for k, v in scores.items() if k != ref_name} | |
| comparisons[f"{ref_name}_greedy_vs_mps"] = { | |
| "greedy_identical_to_mps": sum(first_divergence(r, c) is None for r, c in zip(scores[ref_name][0], conts)), | |
| "greedy_first_divergence": [first_divergence(r, c) for r, c in zip(scores[ref_name][0], conts)], | |
| } | |
| report = { | |
| "model": args.model, "revision": pins()[args.model]["revision"], "solver": args.solver, "n": len(prompts), | |
| "prompt_tokens": {"min": min(map(len, prompts)), "max": max(map(len, prompts))}, | |
| "median_seconds_prefill_mps": float(np.median(mps_secs[1:])), | |
| "median_seconds_scoring_pass": {k: float(np.median(v[1][1:])) for k, v in scores.items()}, | |
| "m_reference": action_logodds(torch.stack([r[0] for r in scores[ref_name][0]]), opener).tolist(), | |
| "comparisons": comparisons, | |
| } | |
| RESULTS.mkdir(exist_ok=True) | |
| (RESULTS / f"numerics_{args.model}_{args.solver}.json").write_text(json.dumps(report, indent=1)) | |
| for k, v in comparisons.items(): | |
| print(k, {kk: vv for kk, vv in v.items() if kk != "greedy_first_divergence"}) | |
| print("prompt_tokens", report["prompt_tokens"], "prefill_mps", round(report["median_seconds_prefill_mps"], 3), | |
| "scoring", {k: round(v, 3) for k, v in report["median_seconds_scoring_pass"].items()}) | |
| if __name__ == "__main__": | |
| main() | |
Xet Storage Details
- Size:
- 5.69 kB
- Xet hash:
- e2230cfb6897f64ee0c83a9f49c85fc52dd6e6d5c232f40b1e1f2fe1b1434d38
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.