Download groot1/evaluate.py from marcyano/Groot-1: direct link, hf CLI and curl.
- Browser
- Download file 5.05 kB
-
https://huggingface.co/marcyano/Groot-1/resolve/main/groot1/evaluate.py
- Command line
-
hf download hf://marcyano/Groot-1/groot1/evaluate.py
-
curl -L -o evaluate.py https://huggingface.co/marcyano/Groot-1/resolve/main/groot1/evaluate.py
5.05 kB
| """Evaluation of a Groot checkpoint on decision rows and public benchmarks. | |
| groot-eval --model runs/groot-1-s/final --rows data/test.jsonl [--max-rows 2000] [--out eval.json] | |
| groot-eval --model ... --bench typed_decisions # LocalLLaMA/typed-decisions test split | |
| Reports accuracy per type, NLL, Brier, ECE (with and without the stored | |
| temperatures), coverage/accuracy at confidence thresholds, and per-source | |
| breakdowns. Rows are the JSONL format of data.py. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| from collections import defaultdict | |
| from pathlib import Path | |
| import numpy as np | |
| import torch | |
| from .calibration import Record, evaluate_records, softmax | |
| from .config import QTYPES | |
| from .data import CONVERTERS, read_jsonl | |
| from .model import GrootDecisionModel | |
| from .schema import confidence_choice, confidence_noul, confidence_score | |
| from .serialize import Serializer | |
| from .train import evaluate | |
| def confidence_of(probabilities: list[float], qtype: str) -> float: | |
| if qtype == "choice": | |
| return confidence_choice(probabilities) | |
| if qtype == "score": | |
| return confidence_score(probabilities) | |
| return confidence_noul(probabilities[1]) | |
| def coverage_table(records: list[Record], temps: list[float], thresholds=(0.3, 0.5, 0.7, 0.9)) -> dict: | |
| """Accuracy of answers whose System One confidence passes each threshold.""" | |
| rows = [] | |
| for r in records: | |
| p = softmax(r.logits, temps[r.qtype]).tolist() | |
| c = confidence_of(p, QTYPES[r.qtype]) | |
| rows.append((c, int(np.argmax(p) == int(r.target.argmax())))) | |
| out = {} | |
| for th in thresholds: | |
| kept = [a for c, a in rows if c >= th] | |
| out[str(th)] = {"coverage": len(kept) / max(1, len(rows)), "accuracy": float(np.mean(kept)) if kept else None} | |
| return out | |
| def per_source(rows: list[dict], records: list[Record], temps: list[float]) -> dict: | |
| groups: dict[str, list[Record]] = defaultdict(list) | |
| for row, rec in zip(rows, records): | |
| groups[row.get("source", "?")].append(rec) | |
| return {src: evaluate_records(recs, temps).to_dict() for src, recs in groups.items()} | |
| def main(argv: list[str] | None = None) -> None: | |
| ap = argparse.ArgumentParser(prog="groot-eval") | |
| ap.add_argument("--model", required=True) | |
| ap.add_argument("--rows", default=None) | |
| ap.add_argument("--bench", default=None, help="name of a converter in data.py (uses its test split)") | |
| ap.add_argument("--max-rows", type=int, default=None) | |
| ap.add_argument("--batch-size", type=int, default=16) | |
| ap.add_argument("--device", default=None) | |
| ap.add_argument("--out", default=None) | |
| ap.add_argument("--set", action="append", default=[], help="config override key=value (e.g. state_retrieval=true)") | |
| ap.add_argument("--calibrate-on", default=None, help="fit per-type temperatures on this JSONL before evaluating") | |
| ap.add_argument("--save-calibration", action="store_true", help="write the fitted temperatures into the checkpoint") | |
| args = ap.parse_args(argv) | |
| from transformers import AutoTokenizer | |
| device = torch.device(args.device or ("cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu")) | |
| model = GrootDecisionModel.from_pretrained(args.model, device=device) | |
| tok = AutoTokenizer.from_pretrained(Path(args.model) / "tokenizer") | |
| if args.set: | |
| from .config import GrootConfig, parse_overrides | |
| model.cfg = GrootConfig.from_dict({**model.cfg.to_dict(), **parse_overrides(args.set)}) | |
| ser = Serializer(tok, model.cfg) | |
| if args.calibrate_on: | |
| from .calibration import fit_temperatures | |
| cal_rows = list(read_jsonl(args.calibrate_on)) | |
| cal_records, _ = evaluate(model, cal_rows, ser, device, args.batch_size, None) | |
| temps = fit_temperatures(cal_records, n_types=len(model.cfg.qtypes)) | |
| model.temperature.copy_(torch.tensor(temps, dtype=model.temperature.dtype, device=model.temperature.device)) | |
| print(json.dumps({"fitted_temperatures": temps, "on": args.calibrate_on, "rows": len(cal_rows)}), flush=True) | |
| if args.save_calibration: | |
| (Path(args.model) / "calibration.json").write_text(json.dumps({"temperature": temps}, indent=2) + "\n") | |
| if args.rows: | |
| rows = list(read_jsonl(args.rows, args.max_rows)) | |
| elif args.bench: | |
| rows = [r for r in CONVERTERS[args.bench](limit=args.max_rows) if r["split"] == "test"] | |
| else: | |
| raise SystemExit("pass --rows or --bench") | |
| records, raw = evaluate(model, rows, ser, device, args.batch_size, None) | |
| temps = model.temperature.tolist() | |
| report = { | |
| "model": args.model, | |
| "rows": len(rows), | |
| "raw": raw, | |
| "calibrated": evaluate_records(records, temps).to_dict(), | |
| "coverage": coverage_table(records, temps), | |
| "per_source": per_source(rows, records, temps), | |
| } | |
| text = json.dumps(report, indent=2) | |
| print(text) | |
| if args.out: | |
| Path(args.out).write_text(text + "\n") | |
| if __name__ == "__main__": | |
| main() | |