Download code/paya/evaluate.py from yeabwang/Paya: direct link, hf CLI and curl.
- Browser
- Download file 4.94 kB
-
https://huggingface.co/yeabwang/Paya/resolve/main/code/paya/evaluate.py
- Command line
-
hf download hf://yeabwang/Paya/code/paya/evaluate.py
-
curl -L -o evaluate.py https://huggingface.co/yeabwang/Paya/resolve/main/code/paya/evaluate.py
4.94 kB
| """Measure decision accuracy, NLL, Brier score and calibration error.""" | |
| import argparse | |
| import json | |
| from pathlib import Path | |
| import tempfile | |
| import numpy as np | |
| import torch | |
| import yaml | |
| from .data import ChoiceDataset, make_batch | |
| from .atomic import atomic_output | |
| class PredictionRecords: | |
| def __init__(self, path, count): | |
| self.path, self.count = Path(path), count | |
| def __iter__(self): | |
| with self.path.open(encoding="utf-8") as source: | |
| for line in source: | |
| yield json.loads(line) | |
| def __len__(self): | |
| return self.count | |
| def collect(model, dataset, batch_size=8, cache=None, output_path=None): | |
| model.eval() | |
| if output_path is None: | |
| directory=tempfile.TemporaryDirectory(prefix='paya-predictions-') | |
| records=collect(model,dataset,batch_size,cache,output_path=Path(directory.name)/'predictions.jsonl') | |
| records._temporary=directory | |
| return records | |
| if output_path is not None: | |
| count = 0 | |
| with atomic_output(output_path, "w") as output: | |
| for rows in dataset.batches(batch_size): | |
| result = model(make_batch(model, rows), cache=cache) | |
| for row, logits in zip(rows, result["logits"]): | |
| output.write(json.dumps({"logits": logits[:len(row["criteria"])].cpu().tolist(), | |
| "target": row["target"], "criteria": row["criteria"], | |
| **{k: row[k] for k in ("source_id", "group_id", "recording_id")}, | |
| **{k: row[k] for k in ("mapping_id", "datasetname") if k in row}}, allow_nan=False)+"\n") | |
| count += 1 | |
| return PredictionRecords(output_path, count) | |
| def metrics(records, temperature=1.0): | |
| if not np.isfinite(temperature) or temperature <= 0: | |
| raise ValueError("Need predictions and a finite positive temperature") | |
| count = 0 | |
| losses = briers = correct = 0.0 | |
| bin_count = np.zeros(15, dtype=np.int64) | |
| bin_correct = np.zeros(15) | |
| bin_confidence = np.zeros(15) | |
| for row in records: | |
| z = np.asarray(row["logits"], dtype=np.float64)/temperature | |
| if not np.isfinite(z).all(): | |
| raise ValueError("Nonfinite prediction logits") | |
| z -= z.max() | |
| p = np.exp(z); p /= p.sum() | |
| target = row["target"] | |
| losses += float(np.log(np.exp(z).sum())-z[target]) | |
| briers += float(((p - np.eye(len(p))[target])**2).sum()) | |
| winner = min(range(len(p)), key=lambda i: (-p[i], row["criteria"][i])) | |
| hit = float(winner == target) | |
| confidence = float(p[winner]) | |
| correct += hit | |
| count += 1 | |
| index = min(int(confidence*15),14) | |
| bin_count[index] += 1 | |
| bin_correct[index] += hit | |
| bin_confidence[index] += confidence | |
| if not count: | |
| raise ValueError("Need predictions and a finite positive temperature") | |
| reliability = [{"bin": i, "count": int(bin_count[i]), | |
| "accuracy": float(bin_correct[i]/bin_count[i]), | |
| "confidence": float(bin_confidence[i]/bin_count[i])} | |
| for i in range(15) if bin_count[i]] | |
| return {"count": count, "accuracy": correct/count, "nll": losses/count, | |
| "brier": briers/count, "ece_15": float(sum(r["count"]/count*abs(r["accuracy"]-r["confidence"]) | |
| for r in reliability)), | |
| "reliability": reliability} | |
| if __name__ == "__main__": | |
| from .checkpoint import load_checkpoint | |
| from .features import open_feature_cache | |
| from .model import PayaModel | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--config", type=Path, default=Path("config/paya.yaml")) | |
| parser.add_argument("--checkpoint", type=Path, required=True) | |
| parser.add_argument("--split", choices=("selection", "calibration", "test"), default="test") | |
| parser.add_argument("--output", type=Path, required=True) | |
| parser.add_argument("--device", default="cpu") | |
| args = parser.parse_args() | |
| config = yaml.safe_load(args.config.read_text(encoding="utf-8")) | |
| model = PayaModel.from_assets(config["assets"], config.get("preparation"), config.get("head"), args.device) | |
| load_checkpoint(args.checkpoint, model) | |
| dataset = ChoiceDataset(config["data"][args.split]) | |
| cache = open_feature_cache(config) | |
| records = collect(model, dataset, config.get("batch_size", 8), cache, | |
| output_path=args.output.with_suffix(".predictions.jsonl")) | |
| report = {"split": args.split, "calibrated": model.calibration is not None, | |
| "metrics": metrics(records, float(model.temperature)), "predictions_path": str(records.path)} | |
| args.output.parent.mkdir(parents=True, exist_ok=True) | |
| with atomic_output(args.output,"w") as output: | |
| json.dump(report,output,indent=2) | |
| print(json.dumps(report["metrics"], indent=2)) | |