import os import sys import subprocess import json from pathlib import Path from typing import Optional import torch import traceback from transformers import GPT2LMHeadModel, GPT2Tokenizer class Benchmark: def __init__(self, benchmark_config, model, tokenizer, checkpointing, output_dir, device=None): self.benchmark_config = benchmark_config self.model = model self.tokenizer = tokenizer self.checkpointing = checkpointing self.device = device if device is not None else torch.device("cuda" if torch.cuda.is_available() else "cpu") self.output_dir = Path(f"{benchmark_config.prefix if benchmark_config.prefix is not None else ''}{output_dir}") self.output_dir.mkdir(parents=True, exist_ok=True) self.lm_eval_dir = Path(__file__).resolve().parent / "lm-evaluation-harness" self.results_dir = self.output_dir / "results" self.summary_path = self.output_dir / "summary.json" self.checkpointing.load_model_states("recent") self._init_repo() def _init_repo(self): if not self.lm_eval_dir.exists(): print("Cloning lm-evaluation-harness...") subprocess.run([ "git", "clone", "https://github.com/EleutherAI/lm-evaluation-harness", str(self.lm_eval_dir) ], check=True) print("Installing lm-evaluation-harness...") subprocess.run( ["pip", "install", "-e", "."], cwd=str(self.lm_eval_dir), check=True ) else: print("lm-evaluation-harness already installed.") def run_benchmarks(self): sys.path.insert(0, str(self.lm_eval_dir)) from lm_eval.evaluator import simple_evaluate from .benchmark_wrapper import BenchmarkWrapper summary = self._load_existing_summary() status_report = {} model = self.model if self.benchmark_config.compile: print(f"Compiling model with mode: {self.benchmark_config.compile_mode}") model = torch.compile(model, mode=self.benchmark_config.compile_mode) wrapped_model = BenchmarkWrapper( model, self.tokenizer, device=self.device, batch_size=self.benchmark_config.batch_size, dtype=getattr(torch, self.benchmark_config.precision) ) new_results = {} for task in self.benchmark_config.tasks: try: if task in summary: print(f"Skipping {task}, already benchmarked.") status_report[task] = {"status": "cached", "result": summary[task]} continue print(f"Running benchmark for task: {task}") result = simple_evaluate( model=wrapped_model, tasks=[task], num_fewshot=self.benchmark_config.num_fewshot, batch_size=self.benchmark_config.batch_size, limit=self.benchmark_config.limit if hasattr(self.benchmark_config, "limit") else None, bootstrap_iters=self.benchmark_config.bootstrap_iters, device=self.device, log_samples=False ) self.results_dir.mkdir(parents=True, exist_ok=True) def make_json_safe(obj): if isinstance(obj, torch.device): return str(obj) elif isinstance(obj, dict): return {k: make_json_safe(v) for k, v in obj.items()} elif isinstance(obj, list): return [make_json_safe(v) for v in obj] elif isinstance(obj, tuple): return tuple(make_json_safe(v) for v in obj) else: return obj with open(self.results_dir / f"{task}.json", "w") as f: json.dump(make_json_safe(result), f, indent=2) task_result = result["results"].get(task, {}) summary[task] = task_result new_results[task] = task_result status_report[task] = {"status": "completed", "result": task_result} except Exception as e: print(f"⚠️ Warning: Benchmark for task '{task}' failed with error: {e}") traceback.print_exc() status_report[task] = {"status": "failed", "error": str(e)} self._save_summary(summary) self._print_status_report(status_report) def _load_existing_summary(self) -> dict: if self.summary_path.exists(): with open(self.summary_path, "r") as f: return json.load(f) return {} def _save_summary(self, summary: dict): with open(self.summary_path, "w") as f: json.dump(summary, f, indent=2) def _print_status_report(self, report): def is_metric_key(k): return any(key in k.lower() for key in ["acc", "f1", "loss", "perplexity", "exact", "score"]) def format_metric(k, v): clean_key = k.replace(",none", "") if "norm" in k: return None if isinstance(v, dict) and "value" in v: val = v["value"] stderr = v.get("stderr", None) if stderr is not None: return f"{clean_key}: {val:.5f} ± {stderr:.5f}" else: return f"{clean_key}: {val:.5f}" elif isinstance(v, (float, int)): return f"{clean_key}: {v:.5f}" return None print("\n=== Benchmark Summary Report ===") for task, info in report.items(): status = info["status"] if status in {"completed", "cached"}: label = "✅" if status == "completed" else "📄" metrics = info["result"] filtered_metrics = { k: v for k, v in metrics.items() if is_metric_key(k) } formatted = [ format_metric(k, v) for k, v in filtered_metrics.items() if format_metric(k, v) is not None ] metric_str = ", ".join(formatted) print(f"{label} {task}: {status} | {metric_str}") elif status == "failed": print(f"❌ {task}: {status} | Error: {info['error']}")