Text Generation
GGUF
llama.cpp
pocketpal
on-device
Mixture of Experts
multilingual
quantization
distillation
1-bit
2-bit
3-bit
conversational
Instructions to use RegalInvader/Marco-Nano-Instruct-GGUF with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use RegalInvader/Marco-Nano-Instruct-GGUF with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0 # Run inference directly in the terminal: llama cli -hf RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0 # Run inference directly in the terminal: llama cli -hf RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0
Build from source code
git clone https://github.com/ggml-org/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-app # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama serve -hf RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0 # Run inference directly in the terminal: ./build/bin/llama cli -hf RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0
Use Docker
docker model run hf.co/RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0
- Llama new
- LM Studio
- Jan
- vLLM
How to use RegalInvader/Marco-Nano-Instruct-GGUF with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "RegalInvader/Marco-Nano-Instruct-GGUF" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "RegalInvader/Marco-Nano-Instruct-GGUF", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0
- Ollama
How to use RegalInvader/Marco-Nano-Instruct-GGUF with Ollama:
ollama run hf.co/RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0
- Unsloth Desktop
- Pi
How to use RegalInvader/Marco-Nano-Instruct-GGUF with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use RegalInvader/Marco-Nano-Instruct-GGUF with Docker Model Runner:
docker model run hf.co/RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0
- Lemonade
How to use RegalInvader/Marco-Nano-Instruct-GGUF with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0
Run and chat with the model
lemonade run user.Marco-Nano-Instruct-GGUF-TQ1_0
List all available models
lemonade list
- Hermes Agent
How to use RegalInvader/Marco-Nano-Instruct-GGUF with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use RegalInvader/Marco-Nano-Instruct-GGUF with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "RegalInvader/Marco-Nano-Instruct-GGUF:TQ1_0" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
Download code/scripts/fp8_check.py from RegalInvader/Marco-Nano-Instruct-GGUF: direct link, hf CLI and curl.
- Browser
- Download file 23.8 kB
-
https://huggingface.co/RegalInvader/Marco-Nano-Instruct-GGUF/resolve/main/code/scripts/fp8_check.py
- Command line
-
hf download hf://RegalInvader/Marco-Nano-Instruct-GGUF/code/scripts/fp8_check.py
-
curl -L -o fp8_check.py https://huggingface.co/RegalInvader/Marco-Nano-Instruct-GGUF/resolve/main/code/scripts/fp8_check.py
23.8 kB
| #!/usr/bin/env python3 | |
| """T1.4 FP8 acceptance check on the exact generation config (DECISIONS.md S-20). | |
| Spec §6 criteria: teacher-forced KL(bf16 || FP8) <= 0.02 nats/token, and GSM8K-250 accuracy within | |
| 1.5 points of bf16. "FP8" is the whole generation config in configs/gen/teacher_fp8.yaml — FP8 block | |
| weights AND FP8 KV cache, router bf16, Triton MoE and attention — run on gen's own kernels. The bf16 | |
| reference is configs/gen/teacher_bf16_ref.yaml (same kernels, bf16 weights and KV, part of the | |
| weights in pinned CPU memory). | |
| KL is exact over the full 151,936-token vocabulary: the capture step records the final hidden state | |
| at every position (the input of vLLM's compute_logits), and the kl step rebuilds both models' logits | |
| through the shared, unquantised bf16 LM head in strict fp32. Triton attention reads K/V from the | |
| (FP8) cache in prefill as in decode, so teacher-forced positions carry the same KV quantisation as | |
| generation. | |
| Subcommands: prep-gsm8k, prep-math, prep-tf, generate, seqs-from, capture, kl, score-gsm8k, | |
| analyze-trunc. Every vLLM step runs as its own process through the gen queue. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import hashlib | |
| import json | |
| import os | |
| import random | |
| import urllib.request | |
| from collections import Counter, defaultdict | |
| from pathlib import Path | |
| import numpy as np | |
| import yaml | |
| from lowbit.eval.gsm8k import MATH_SUFFIX, extract_answer, same_number # noqa: F401 (re-exported) | |
| from lowbit.gen.loops import loop_score # noqa: E402,F401 (re-exported for router_sampling_sweep.py) | |
| REPO = Path(__file__).resolve().parent.parent | |
| DATA = Path(os.environ.get("MT_DATA", "/path/to/data")) | |
| EOS_DOC = 151643 # <|endoftext|>, the document separator in packed text | |
| PROD_CFG = REPO / "configs" / "gen" / "teacher_fp8.yaml" # spec §6 sampling lives here | |
| PARQUET = "https://huggingface.co/datasets/{ds}/resolve/refs%2Fconvert%2Fparquet/{cfg}/{split}/0000.parquet" | |
| MARCO_AYA = {"en": "English", "zh": "Simplified Chinese", "ar": "Standard Arabic", "de": "German", "es": "Spanish", | |
| "fr": "French", "ko": "Korean", "ja": "Japanese", "pt": "Portuguese", "tr": "Turkish", | |
| "id": "Indonesian", "it": "Italian", "nl": "Dutch", "pl": "Polish", "ru": "Russian", | |
| "vi": "Vietnamese", "th": "Thai", "uk": "Ukrainian", "ms": "Standard Malay", "bn": "Bengali", | |
| "ur": "Urdu", "el": "Greek", "hu": "Hungarian", "ne": "Nepali"} # Aya lacks he, cs, kk, ro, az | |
| def fetch(url: str, dest: Path) -> str: | |
| dest.parent.mkdir(parents=True, exist_ok=True) | |
| if not dest.exists(): | |
| urllib.request.urlretrieve(url, dest) | |
| return hashlib.sha256(dest.read_bytes()).hexdigest() | |
| def write_jsonl(path: Path, rows) -> None: | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| with path.open("w") as f: | |
| for r in rows: | |
| f.write(json.dumps(r, ensure_ascii=False) + "\n") | |
| def read_jsonl(path) -> list[dict]: | |
| return [json.loads(l) for l in open(path) if l.strip()] | |
| def load_tokenizer(model: str): | |
| from transformers import AutoTokenizer | |
| return AutoTokenizer.from_pretrained(model) | |
| # ---------------------------------------------------------------- data prep (CPU) | |
| def prep_gsm8k(a) -> None: | |
| import pyarrow.parquet as pq | |
| dest = DATA / "evalsets/gsm8k/main_test.parquet" | |
| sha = fetch(PARQUET.format(ds="openai/gsm8k", cfg="main", split="test"), dest) | |
| rows = pq.read_table(dest).to_pylist() | |
| ids = sorted(random.Random(a.seed).sample(range(len(rows)), 250)) | |
| subset = {"dataset": "openai/gsm8k", "config": "main", "split": "test", "dataset_sha": a.dataset_sha, | |
| "parquet_sha256": sha, "seed": a.seed, "n": 250, "ids": ids} | |
| out = REPO / "configs/eval/subsets/gsm8k_250.json" | |
| out.parent.mkdir(parents=True, exist_ok=True) | |
| out.write_text(json.dumps(subset, indent=1) + "\n") | |
| write_jsonl(DATA / "fp8check/gsm8k_250.jsonl", | |
| [{"id": f"gsm8k-{i}", "text": rows[i]["question"] + MATH_SUFFIX, | |
| "gold": rows[i]["answer"].split("####")[-1].strip().replace(",", ""), "kind": "gsm8k"} | |
| for i in ids]) | |
| print(f"gsm8k: {len(rows)} test items, 250 sampled (seed {a.seed}) -> {out}") | |
| def prep_math(a) -> None: | |
| import pyarrow.parquet as pq | |
| subjects = ["algebra", "counting_and_probability", "geometry", "intermediate_algebra", "number_theory", | |
| "prealgebra", "precalculus"] | |
| pool = [] | |
| for s in subjects: | |
| dest = DATA / f"evalsets/hendrycks_math/{s}_train.parquet" | |
| fetch(PARQUET.format(ds="EleutherAI/hendrycks_math", cfg=s, split="train"), dest) | |
| pool += [dict(r, subject=s) for r in pq.read_table(dest).to_pylist() if r["level"] in ("Level 4", "Level 5")] | |
| pick = random.Random(a.seed).sample(pool, a.n) | |
| write_jsonl(DATA / "fp8check/math_long.jsonl", | |
| [{"id": f"math-{i}", "text": r["problem"] + MATH_SUFFIX, "kind": "math", "level": r["level"], | |
| "subject": r["subject"]} for i, r in enumerate(pick)]) | |
| print(f"math: {len(pool)} level-4/5 train problems, {a.n} sampled") | |
| def prep_tf(a) -> None: | |
| """Aya human-written prompt+completion text in Marco's languages, packed into fixed windows.""" | |
| import pyarrow.parquet as pq | |
| tok = load_tokenizer(a.model) | |
| t = pq.read_table(DATA / "bench/aya_train.parquet", columns=["inputs", "targets", "language"]).to_pylist() | |
| rng = random.Random(a.seed) | |
| by_lang = defaultdict(list) | |
| names = {v: k for k, v in MARCO_AYA.items()} | |
| for r in t: | |
| if r["language"] in names: | |
| by_lang[names[r["language"]]].append(r["inputs"].strip() + "\n\n" + r["targets"].strip()) | |
| per_lang = a.tokens // len(by_lang) | |
| docs = [] | |
| for lang, texts in sorted(by_lang.items()): | |
| rng.shuffle(texts) | |
| n = 0 | |
| for txt in texts: | |
| ids = tok(txt, add_special_tokens=False)["input_ids"] | |
| docs.append((lang, ids)) | |
| n += len(ids) + 1 | |
| if n >= per_lang: | |
| break | |
| rng.shuffle(docs) | |
| windows, cur, segs = [], [], [] | |
| for lang, ids in docs: | |
| ids = ids + [EOS_DOC] | |
| while ids: | |
| room = a.window - len(cur) | |
| segs.append([len(cur), lang]) | |
| cur += ids[:room] | |
| ids = ids[room:] | |
| if len(cur) == a.window: | |
| windows.append({"id": f"aya-{len(windows)}", "kind": "aya", "token_ids": cur, "segments": segs}) | |
| cur, segs = [], [] | |
| write_jsonl(DATA / "fp8check/tf_aya.jsonl", windows) | |
| langs = Counter() | |
| for w in windows: | |
| bounds = [s[0] for s in w["segments"]] + [a.window] | |
| for (start, lang), end in zip(w["segments"], bounds[1:]): | |
| langs[lang] += end - start | |
| print(f"prep-tf: {len(windows)} windows x {a.window} = {len(windows) * a.window:,} tokens; per language: {dict(langs)}") | |
| # ---------------------------------------------------------------- vLLM steps (GPU, via the gen queue) | |
| def build_llm(cfg_path: str, overrides: list[str], in_process: bool): | |
| cfg = yaml.safe_load(open(cfg_path)) | |
| os.environ.update({k: str(v) for k, v in cfg.get("env", {}).items()}) | |
| if in_process: # the capture hook must live in the process that runs the model | |
| os.environ["VLLM_ENABLE_V1_MULTIPROCESSING"] = "0" | |
| from vllm import LLM | |
| eng = dict(cfg["engine"]) | |
| for kv in overrides: | |
| k, v = kv.split("=", 1) | |
| eng[k] = yaml.safe_load(v) | |
| return LLM(model=cfg["model"], seed=0, **eng), cfg | |
| def spec_sampling(path: str | Path = PROD_CFG) -> dict: | |
| """Spec §6 sampling, always from the production generation config: an FP8-vs-bf16 comparison | |
| must sample both engines identically, and the bf16 reference config carries no sampling block.""" | |
| return yaml.safe_load(open(path))["sampling"] | |
| def generate(a) -> None: | |
| llm, _ = build_llm(a.engine, a.override, in_process=False) | |
| from vllm import SamplingParams | |
| from vllm.inputs import TokensPrompt | |
| tok = llm.get_tokenizer() | |
| rows = read_jsonl(a.prompts)[: a.limit or None] | |
| prompts = [TokensPrompt(prompt_token_ids=tok(tok.apply_chat_template([{"role": "user", "content": r["text"]}], | |
| add_generation_prompt=True, tokenize=False), | |
| add_special_tokens=False)["input_ids"]) for r in rows] | |
| if a.sampling == "greedy": | |
| sp = SamplingParams(temperature=0.0, max_tokens=a.max_tokens) | |
| else: | |
| s = spec_sampling(a.sampling_config) | |
| sp = SamplingParams(temperature=s["temperature"], top_p=s["top_p"], max_tokens=a.max_tokens, seed=0) | |
| outs = llm.generate(prompts, sp) | |
| write_jsonl(Path(a.out), [{"id": r.get("id", f"p{i}"), **{k: v for k, v in r.items() if k != "text"}, | |
| "prompt_token_ids": o.prompt_token_ids, | |
| "output_token_ids": list(o.outputs[0].token_ids), "text": o.outputs[0].text, | |
| "finish_reason": o.outputs[0].finish_reason} for i, (r, o) in enumerate(zip(rows, outs))]) | |
| n_tok = sum(len(o.outputs[0].token_ids) for o in outs) | |
| print(json.dumps({"n": len(outs), "out_tokens": n_tok, | |
| "truncated": sum(o.outputs[0].finish_reason == "length" for o in outs)})) | |
| def seqs_from(a) -> None: | |
| """Teacher-forcing sequences from generate outputs: chat prompt + response, response start marked.""" | |
| rows = read_jsonl(a.generated) | |
| write_jsonl(Path(a.out), [{"id": r["id"], "kind": r.get("kind", "gen"), | |
| "token_ids": r["prompt_token_ids"] + r["output_token_ids"], | |
| "response_start": len(r["prompt_token_ids"]), "finish_reason": r["finish_reason"]} | |
| for r in rows if r["output_token_ids"]]) | |
| def install_capture(store: dict): | |
| """Wrap the V2 runner's prompt-logprob step to keep each prefill row's final hidden state.""" | |
| from vllm.v1.worker.gpu.sample import prompt_logprob as pl | |
| orig = pl.PromptLogprobsWorker.compute_prompt_logprobs | |
| def wrapped(self, logits_fn, hidden_states, input_batch, all_token_ids, num_computed_tokens, prompt_lens): | |
| plens = prompt_lens[input_batch.idx_mapping_np] | |
| computed = input_batch.num_computed_prefill_tokens_np | |
| qsl = input_batch.query_start_loc_np | |
| for i, rid in enumerate(input_batch.req_ids): | |
| if computed[i] >= plens[i]: | |
| continue # decode row | |
| s, e = int(qsl[i]), int(qsl[i + 1]) | |
| store[rid].append((int(computed[i]), hidden_states[s:e].detach().to("cpu", copy=True))) | |
| return orig(self, logits_fn, hidden_states, input_batch, all_token_ids, num_computed_tokens, prompt_lens) | |
| pl.PromptLogprobsWorker.compute_prompt_logprobs = wrapped | |
| def capture(a) -> None: | |
| store: dict[str, list] = defaultdict(list) | |
| llm, _ = build_llm(a.engine, a.override, in_process=True) | |
| install_capture(store) # after construction: the warm-up runs carry no prompt logprobs | |
| import torch | |
| from vllm import SamplingParams | |
| from vllm.inputs import TokensPrompt | |
| seqs = [] | |
| for f in a.sequences: | |
| seqs += read_jsonl(f) | |
| seqs = seqs[: a.limit or None] | |
| sp = SamplingParams(temperature=0.0, max_tokens=1, prompt_logprobs=0) | |
| outs = llm.generate([TokensPrompt(prompt_token_ids=s["token_ids"]) for s in seqs], sp) | |
| out_dir = Path(a.out) | |
| out_dir.mkdir(parents=True, exist_ok=True) | |
| total = sum(len(s["token_ids"]) for s in seqs) | |
| hid = np.lib.format.open_memmap(out_dir / "hidden_bf16.npy", mode="w+", dtype=np.uint16, shape=(total, 1024)) | |
| vllm_lp = np.full(total, np.nan, dtype=np.float32) # vLLM's own logprob of token p given < p | |
| index, off = [], 0 | |
| for s, o in zip(seqs, outs): | |
| rid = o.request_id | |
| chunks = store.get(rid) or next((v for k, v in store.items() if k.startswith(rid + "-") or rid.startswith(k)), None) | |
| if chunks is None: | |
| raise RuntimeError(f"no captured hidden states for request {rid} ({s['id']}); ids seen: {list(store)[:5]}") | |
| chunks.sort(key=lambda c: c[0]) | |
| h = torch.cat([c[1] for c in chunks]) | |
| n = len(s["token_ids"]) | |
| starts = [c[0] for c in chunks] | |
| if h.shape[0] != n or starts[0] != 0: | |
| raise RuntimeError(f"{s['id']}: captured {h.shape[0]} rows from {starts}, expected {n}") | |
| hid[off:off + n] = h.to(torch.bfloat16).view(torch.int16).numpy().view(np.uint16) | |
| for p, d in enumerate(o.prompt_logprobs or []): | |
| if d is not None: | |
| vllm_lp[off + p] = d[s["token_ids"][p]].logprob | |
| index.append({k: v for k, v in s.items() if k != "token_ids"} | {"offset": off, "length": n}) | |
| off += n | |
| hid.flush() | |
| np.save(out_dir / "vllm_token_logprob.npy", vllm_lp) | |
| write_jsonl(out_dir / "index.jsonl", index) | |
| (out_dir / "meta.json").write_text(json.dumps({"engine": a.engine, "override": a.override, "positions": total, | |
| "sequences": len(seqs)}, indent=1)) | |
| print(json.dumps({"captured_positions": total, "sequences": len(seqs)})) | |
| def kl(a) -> None: | |
| import torch | |
| from safetensors import safe_open | |
| torch.backends.cuda.matmul.allow_tf32 = False | |
| torch.set_float32_matmul_precision("highest") | |
| emb = None | |
| for f in sorted(Path(a.model).glob("*.safetensors")): | |
| with safe_open(f, "pt") as st: | |
| if "model.embed_tokens.weight" in st.keys(): | |
| emb = st.get_tensor("model.embed_tokens.weight") | |
| dev = a.device | |
| E = emb.to(dev, torch.float32) # tied LM head, bf16 -> fp32 exactly | |
| ref_dir, test_dir = Path(a.ref), Path(a.test) | |
| ref_idx = {r["id"]: r for r in read_jsonl(ref_dir / "index.jsonl")} | |
| test_idx = {r["id"]: r for r in read_jsonl(test_dir / "index.jsonl")} | |
| seqs = {} | |
| for f in a.sequences: | |
| seqs.update({s["id"]: s for s in read_jsonl(f)}) | |
| H_ref = np.load(ref_dir / "hidden_bf16.npy", mmap_mode="r") | |
| H_test = np.load(test_dir / "hidden_bf16.npy", mmap_mode="r") | |
| lp_ref_vllm = np.load(ref_dir / "vllm_token_logprob.npy") | |
| cols = defaultdict(list) | |
| for sid, r in ref_idx.items(): | |
| if sid not in test_idx: # compare the positions both captures have (subset diagnostics) | |
| continue | |
| t, s = test_idx[sid], seqs[sid] | |
| n = r["length"] | |
| assert t["length"] == n and len(s["token_ids"]) == n, sid | |
| hb = torch.from_numpy(np.array(H_ref[r["offset"]:r["offset"] + n], copy=True).view(np.int16)).view(torch.bfloat16) | |
| hf = torch.from_numpy(np.array(H_test[t["offset"]:t["offset"] + n], copy=True).view(np.int16)).view(torch.bfloat16) | |
| lang = np.full(n, "", dtype=object) | |
| if "segments" in s: | |
| bounds = [x[0] for x in s["segments"]] + [n] | |
| for (st_, lg), en in zip(s["segments"], bounds[1:]): | |
| lang[st_:en] = lg | |
| toks = torch.tensor(s["token_ids"], device=dev) | |
| parts = defaultdict(list) | |
| for c in range(0, n, a.chunk): | |
| e = min(n, c + a.chunk) | |
| lb = torch.log_softmax(hb[c:e].to(dev, torch.float32) @ E.T, -1) | |
| lf = torch.log_softmax(hf[c:e].to(dev, torch.float32) @ E.T, -1) | |
| parts["kl"].append((lb.exp() * (lb - lf)).sum(-1).cpu()) | |
| parts["rkl"].append((lf.exp() * (lf - lb)).sum(-1).cpu()) | |
| parts["agree"].append((lb.argmax(-1) == lf.argmax(-1)).cpu()) | |
| nxt = torch.arange(c + 1, e + 1, device=dev).clamp(max=n - 1) | |
| parts["our_lp"].append(lb.gather(1, toks[nxt].unsqueeze(1)).squeeze(1).cpu()) # logprob of token p+1 | |
| our = torch.cat(parts["our_lp"]).numpy().astype(np.float64) | |
| our[-1] = np.nan | |
| vl = np.full(n, np.nan) | |
| vl[:-1] = lp_ref_vllm[r["offset"] + 1:r["offset"] + n] | |
| pos = np.arange(n) | |
| cols["kind"].append(np.full(n, s.get("kind", ""), dtype=object)) | |
| cols["lang"].append(lang) | |
| cols["pos"].append(pos) | |
| cols["is_resp"].append(pos >= s.get("response_start", n + 1)) | |
| cols["kl"].append(torch.cat(parts["kl"]).numpy().astype(np.float64)) | |
| cols["rkl"].append(torch.cat(parts["rkl"]).numpy().astype(np.float64)) | |
| cols["agree"].append(torch.cat(parts["agree"]).numpy()) | |
| cols["our_lp"].append(our) | |
| cols["vl"].append(vl) | |
| kinds, langs, pos, is_resp = (np.concatenate(cols[k]) for k in ("kind", "lang", "pos", "is_resp")) | |
| klv, rkl, agree, our_lp, vl = (np.concatenate(cols[k]) for k in ("kl", "rkl", "agree", "our_lp", "vl")) | |
| out_dir = Path(a.out) | |
| out_dir.mkdir(parents=True, exist_ok=True) | |
| np.savez_compressed(out_dir / "per_position.npz", kind=kinds.astype(str), lang=langs.astype(str), pos=pos, | |
| is_response=is_resp, kl=klv, rkl=rkl, top1_agree=agree) | |
| def stats(mask): | |
| m = mask & np.isfinite(klv) | |
| if not m.any(): | |
| return None | |
| return {"positions": int(m.sum()), "kl_mean": float(klv[m].mean()), "kl_p50": float(np.median(klv[m])), | |
| "kl_p99": float(np.quantile(klv[m], 0.99)), "kl_max": float(klv[m].max()), | |
| "reverse_kl_mean": float(rkl[m].mean()), "top1_agreement": float(agree[m].mean())} | |
| every = np.ones_like(klv, dtype=bool) | |
| summary = {"ref": str(ref_dir), "test": str(test_dir), "threshold_nats_per_token": 0.02, | |
| "all": stats(every), "by_kind": {k: stats(kinds == k) for k in sorted(set(kinds))}, | |
| "by_position_bucket": {f"{b * 512}-{b * 512 + 511}": stats((pos // 512) == b) | |
| for b in range(int(pos.max()) // 512 + 1)}, | |
| "by_language": {l: stats(langs == l) for l in sorted(set(langs)) if l}, | |
| "math_response_by_bucket": {f"{b * 512}-{b * 512 + 511}": stats((kinds == "math") & is_resp & ((pos // 512) == b)) | |
| for b in range(int(pos.max()) // 512 + 1)}} | |
| ok = np.isfinite(our_lp) & np.isfinite(vl) | |
| summary["lm_head_reconstruction"] = {"positions": int(ok.sum()), "max_abs_diff_vs_vllm_logprob": float(np.abs(our_lp[ok] - vl[ok]).max()), | |
| "mean_abs_diff": float(np.abs(our_lp[ok] - vl[ok]).mean())} | |
| (out_dir / "summary.json").write_text(json.dumps(summary, indent=1)) | |
| print(json.dumps({k: summary[k] for k in ("all", "by_kind", "lm_head_reconstruction")}, indent=1)) | |
| # ---------------------------------------------------------------- scoring and analysis (CPU) | |
| def score_gsm8k(a) -> None: | |
| gold = {r["id"]: r["gold"] for r in read_jsonl(DATA / "fp8check/gsm8k_250.jsonl")} | |
| runs = {} | |
| for spec in a.pred: | |
| name, path = spec.split("=", 1) | |
| runs[name] = {r["id"]: r for r in read_jsonl(path)} | |
| res = {} | |
| correct = {} | |
| for name, rows in runs.items(): | |
| c = {i: same_number(extract_answer(r["text"]), gold[i]) for i, r in rows.items()} | |
| correct[name] = c | |
| res[name] = {"n": len(c), "accuracy": 100 * sum(c.values()) / len(c), | |
| "truncated": sum(r["finish_reason"] == "length" for r in rows.values()), | |
| "mean_out_tokens": float(np.mean([len(r["output_token_ids"]) for r in rows.values()]))} | |
| if len(runs) == 2: | |
| (na, ca), (nb, cb) = correct.items() | |
| ids = sorted(set(ca) & set(cb)) | |
| res["paired"] = {"a": na, "b": nb, "diff_points": res[na]["accuracy"] - res[nb]["accuracy"], | |
| "both_right": sum(ca[i] and cb[i] for i in ids), "only_a": sum(ca[i] and not cb[i] for i in ids), | |
| "only_b": sum(cb[i] and not ca[i] for i in ids), "both_wrong": sum(not ca[i] and not cb[i] for i in ids), | |
| "identical_text": sum(runs[na][i]["text"] == runs[nb][i]["text"] for i in ids), | |
| "threshold_points": 1.5} | |
| res["paired"]["pass"] = abs(res["paired"]["diff_points"]) <= 1.5 | |
| Path(a.out).write_text(json.dumps(res, indent=1)) | |
| print(json.dumps(res, indent=1)) | |
| def analyze_trunc(a) -> None: | |
| rows = read_jsonl(a.generated) | |
| trunc = [r for r in rows if r["finish_reason"] == "length"] | |
| scores = [loop_score(r["output_token_ids"]) for r in trunc] | |
| loops = sum(s >= 0.5 for s in scores) | |
| by_lang = Counter(r.get("language", "") for r in trunc) | |
| tot_lang = Counter(r.get("language", "") for r in rows) | |
| out = {"n": len(rows), "truncated": len(trunc), "truncated_share": len(trunc) / max(1, len(rows)), | |
| "truncated_loops(score>=0.5)": loops, "truncated_non_loops": len(trunc) - loops, | |
| "loop_score_quartiles": [float(q) for q in np.quantile(scores, [0.25, 0.5, 0.75])] if scores else None, | |
| "truncated_by_language": {k: f"{by_lang[k]}/{tot_lang[k]}" for k in sorted(tot_lang)}, | |
| "output_tokens_total": sum(len(r["output_token_ids"]) for r in rows), | |
| "output_tokens_in_truncated": sum(len(r["output_token_ids"]) for r in trunc)} | |
| examples = [{"id": r["id"], "language": r.get("language"), "loop_score": round(s, 3), "tail": r["text"][-300:]} | |
| for r, s in sorted(zip(trunc, scores), key=lambda x: x[1])][:: max(1, len(trunc) // 8)] | |
| Path(a.out).write_text(json.dumps({"summary": out, "examples": examples}, indent=1, ensure_ascii=False)) | |
| print(json.dumps(out, indent=1)) | |
| def main() -> None: | |
| p = argparse.ArgumentParser() | |
| sub = p.add_subparsers(dest="op", required=True) | |
| s = sub.add_parser("prep-gsm8k"); s.add_argument("--seed", type=int, default=0); s.add_argument("--dataset-sha", default="740312add88f781978c0658806c59bc2815b9866") | |
| s = sub.add_parser("prep-math"); s.add_argument("--seed", type=int, default=0); s.add_argument("--n", type=int, default=200) | |
| s = sub.add_parser("prep-tf"); s.add_argument("--model", required=True); s.add_argument("--tokens", type=int, default=1_000_000) | |
| s.add_argument("--window", type=int, default=4000); s.add_argument("--seed", type=int, default=0) | |
| for name in ("generate", "capture"): | |
| s = sub.add_parser(name) | |
| s.add_argument("--engine", required=True) | |
| s.add_argument("--override", nargs="*", default=[], help="engine KEY=VAL (capacity knobs; diagnostic arms)") | |
| s.add_argument("--limit", type=int, default=0) | |
| s.add_argument("--out", required=True) | |
| if name == "generate": | |
| s.add_argument("--prompts", required=True) | |
| s.add_argument("--sampling", choices=["greedy", "spec"], required=True) | |
| s.add_argument("--max-tokens", type=int, required=True) | |
| s.add_argument("--sampling-config", default=str(PROD_CFG), help="config whose sampling block --sampling spec uses") | |
| else: | |
| s.add_argument("--sequences", nargs="+", required=True) | |
| s = sub.add_parser("seqs-from"); s.add_argument("--generated", required=True); s.add_argument("--out", required=True) | |
| s = sub.add_parser("kl"); s.add_argument("--ref", required=True); s.add_argument("--test", required=True) | |
| s.add_argument("--sequences", nargs="+", required=True); s.add_argument("--model", required=True) | |
| s.add_argument("--chunk", type=int, default=512); s.add_argument("--out", required=True); s.add_argument("--device", default="cuda") | |
| s = sub.add_parser("score-gsm8k"); s.add_argument("--pred", nargs="+", required=True, help="name=path"); s.add_argument("--out", required=True) | |
| s = sub.add_parser("analyze-trunc"); s.add_argument("--generated", required=True); s.add_argument("--out", required=True) | |
| a = p.parse_args() | |
| {"prep-gsm8k": prep_gsm8k, "prep-math": prep_math, "prep-tf": prep_tf, "generate": generate, | |
| "seqs-from": seqs_from, "capture": capture, "kl": kl, "score-gsm8k": score_gsm8k, | |
| "analyze-trunc": analyze_trunc}[a.op](a) | |
| if __name__ == "__main__": | |
| main() | |