Download scripts/run_elf_remaining.py from jyliuAI/diffusion_baseline: direct link, hf CLI and curl.
- Browser
- Download file 16 kB
-
https://huggingface.co/jyliuAI/diffusion_baseline/resolve/main/scripts/run_elf_remaining.py
- Command line
-
hf download hf://jyliuAI/diffusion_baseline/scripts/run_elf_remaining.py
-
curl -L -o run_elf_remaining.py https://huggingface.co/jyliuAI/diffusion_baseline/resolve/main/scripts/run_elf_remaining.py
16 kB
| #!/usr/bin/env python3 | |
| """Resume the ELF quality matrix on independent single-GPU workers (no timing).""" | |
| from __future__ import annotations | |
| import argparse | |
| from concurrent.futures import ThreadPoolExecutor, wait | |
| from dataclasses import asdict, dataclass | |
| from datetime import datetime, timezone | |
| import json | |
| import os | |
| from pathlib import Path | |
| import queue | |
| import signal | |
| import subprocess | |
| import sys | |
| import threading | |
| import time | |
| from elf_common import ROOT, SOURCE_COMMIT, default_sampling, manifest, read_jsonl, sha256, write_json | |
| from inspect_elf_progress import inspect_run, OWT_MAIN_STEPS | |
| EOS_POLICY = "released_ids_or_raw_plus_eos_then_source_cap_v2" | |
| TASKS = ("owt", "owt-prefix", "wmt14", "xsum") | |
| class Cell: | |
| task: str | |
| split: str | |
| steps: int | |
| count: int | |
| input_path: str | None = None | |
| input_sha256: str | None = None | |
| def key(self): | |
| return f"{self.task}/{self.split}/steps_{self.steps}/seed_42/compile_0" | |
| def build_matrix(root, tasks): | |
| cells, blocked = [], [] | |
| for task in tasks: | |
| if task == "owt": | |
| cells.extend(Cell(task, "unconditional", s, 1024) for s in OWT_MAIN_STEPS) | |
| continue | |
| splits = (("c64-text-t5", OWT_MAIN_STEPS, 1024),) if task == "owt-prefix" else ( | |
| ("validation", (1, 2, 4, 8, 16, 32, 64), 3000 if task == "wmt14" else 11332), | |
| ("test", (32, 64), 3003 if task == "wmt14" else 11334), | |
| ) | |
| for split, steps, expected_count in splits: | |
| path = root / "data/elf" / ("owt-prefix.jsonl" if task == "owt-prefix" else f"{task}-{split}.jsonl") | |
| try: | |
| meta = json.loads(path.with_suffix(".manifest.json").read_text()) | |
| digest = sha256(path) | |
| rows = read_jsonl(path) | |
| if (meta["task"] != task or meta["sha256"] != digest or meta["count"] != expected_count | |
| or len(rows) != expected_count or len({r["id"] for r in rows}) != expected_count): | |
| raise ValueError("input task/count/hash/IDs mismatch") | |
| if task == "owt-prefix": | |
| if meta.get("protocol") != "c64_text_t5_v1": | |
| raise ValueError("prefix protocol mismatch") | |
| source = root / "data/conditional/owt-c64/prompts.jsonl" | |
| if meta.get("source_sha256") != sha256(source): | |
| raise ValueError("prefix source changed; prepare prefix again") | |
| elif meta.get("split") != split: | |
| raise ValueError("input split mismatch") | |
| except (OSError, KeyError, TypeError, ValueError) as error: | |
| blocked.append({"task": task, "split": split, "status": "blocked_input", "reason": str(error)}) | |
| continue | |
| cells.extend(Cell(task, split, s, expected_count, str(path), digest) for s in steps) | |
| return sorted(cells, key=lambda c: (c.steps, c.task, c.split)), blocked | |
| def matches_request(request, cell, asset_hash, assets): | |
| checkpoint_task = "owt" if cell.task == "owt-prefix" else cell.task | |
| record = assets[checkpoint_task] | |
| expected = { | |
| "task": cell.task, "steps": cell.steps, "seed": 42, "compile": False, "batch_size": 8, | |
| "source_commit": SOURCE_COMMIT, "assets_manifest_sha256": asset_hash, | |
| "checkpoint_task": checkpoint_task, "checkpoint_selection": "ema_params1", | |
| "checkpoint_sha256": record["sha256"][record["checkpoint"]], | |
| "sampling": default_sampling(cell.task, cell.steps), | |
| "model_canvas_t5_tokens": 1088 if cell.task == "xsum" else 128 if cell.task == "wmt14" else 1024, | |
| "protocol": "c64_text_t5_v1" if cell.task == "owt-prefix" else "elf_native_v1", | |
| } | |
| if any(request.get(key) != value for key, value in expected.items()): | |
| return False | |
| if cell.task in ("wmt14", "xsum") and request.get("condition_tokenization_policy") != EOS_POLICY: | |
| return False | |
| return (request.get("input") or {}).get("sha256") == cell.input_sha256 | |
| def reusable_result(root, cell, asset_hash, assets): | |
| candidates, rejected = [], [] | |
| for path in sorted((root / "results").glob(f"elf*/quality/{cell.key}")): | |
| try: | |
| request = json.loads((path / "request.json").read_text()) | |
| if not matches_request(request, cell, asset_hash, assets): | |
| continue | |
| row = inspect_run(path) | |
| if row["status"] not in ("quality_valid", "quality_invalid", "needs_evaluation"): | |
| rejected.append({"run": str(path), "reason": row.get("reason", row["status"])}) | |
| continue | |
| gen = json.loads((path / "generation.json").read_text()) | |
| keys = ("sampling", "input", "assets_manifest_sha256", "checkpoint_sha256", "source_commit", | |
| "condition_tokenization_policy", "batch_size", "protocol") | |
| if any(gen.get(k) != request.get(k) for k in keys): | |
| raise ValueError("request/generation provenance differs") | |
| diversity = cell.task == "owt-prefix" | |
| total = cell.count + (min(256, cell.count) * 4 if diversity else 0) | |
| if gen["prompt_count"] != cell.count or gen["sample_count"] != total or bool(gen.get("diversity")) != diversity: | |
| continue # e.g. 1000-row sanity is not a 1024-row primary run. | |
| if row["status"] == "quality_invalid": | |
| reason = row.get("reason") or "" | |
| if not reason.startswith("At least one completion has fewer than two GPT-2 tokens;"): | |
| rejected.append({"run": str(path), "reason": "unrecognized invalid-quality reason"}) | |
| continue | |
| candidates.append(row) | |
| except (OSError, ValueError, KeyError, TypeError) as error: | |
| rejected.append({"run": str(path), "reason": str(error)}) | |
| # A valid score and a recorded invalid score for one exact cell conflict; | |
| # never silently choose the better run. | |
| completed = [r for r in candidates if r["status"] != "needs_evaluation"] | |
| if len({r["status"] for r in completed}) > 1: | |
| raise ValueError(f"Conflicting completed results for {cell.key}; inspect runs manually") | |
| return (completed or candidates or [None])[0], rejected | |
| def fresh_path(output_root, cell): | |
| for attempt in range(10000): | |
| base = output_root if attempt == 0 else output_root.with_name(output_root.name + f"-retry{attempt}") | |
| path = base / "quality" / cell.key | |
| if not path.exists(): | |
| return path | |
| raise RuntimeError("Too many existing retry directories") | |
| def commands(root, python, cell, path, generate): | |
| result = [] | |
| if generate: | |
| cmd = [python, "-B", str(root / "scripts/run_elf.py"), "generate", "--root", str(root), | |
| "--task", cell.task, "--steps", str(cell.steps), "--seed", "42", "--batch-size", "8", | |
| "--num-samples", str(cell.count), "--output", str(path)] | |
| if cell.input_path: | |
| cmd += ["--input", cell.input_path] | |
| if cell.task == "owt-prefix": | |
| cmd += ["--diversity"] | |
| result.append(("generate", cmd)) | |
| result.append(("evaluate", [python, "-B", str(root / "scripts/evaluate_elf.py"), "--root", str(root), | |
| "--run", str(path), "--batch-size", "8"])) | |
| return result | |
| def main(): | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--root", type=Path, default=ROOT) | |
| parser.add_argument("--output-root", type=Path) | |
| parser.add_argument("--tasks", nargs="+", choices=TASKS, default=list(TASKS)) | |
| parser.add_argument("--gpus", nargs="+", default=["0", "1"]) | |
| parser.add_argument("--plan", action="store_true", help="Read-only preview; no GPU queries, writes or inference") | |
| args = parser.parse_args() | |
| root = args.root.resolve() | |
| output_root = (args.output_root or root / "results/elf-gpu01").resolve() | |
| if output_root.parent != root / "results" or not output_root.name.startswith("elf"): | |
| parser.error("output-root must be an elf* directory directly under ROOT/results") | |
| if len(set(args.tasks)) != len(args.tasks): | |
| parser.error("duplicate tasks") | |
| assets = manifest(root)["assets"] | |
| asset_hash = sha256(root / "data/elf/assets.json") | |
| cells, blocked = build_matrix(root, args.tasks) | |
| jobs, reports = [], [] | |
| for cell in cells: | |
| existing, rejected = reusable_result(root, cell, asset_hash, assets) | |
| action = ("skip" if existing["status"] != "needs_evaluation" else "evaluate") if existing else "generate+evaluate" | |
| path = Path(existing["run"]) if existing else fresh_path(output_root, cell) | |
| row = {"cell": asdict(cell), "action": action, "run": str(path), "rejected_candidates": rejected, | |
| "status": existing["status"] if action == "skip" else "pending"} | |
| reports.append(row) | |
| print(f"{action:18} {cell.key} -> {path}", flush=True) | |
| if action != "skip": | |
| jobs.append((cell, row)) | |
| for row in blocked: | |
| print(f"BLOCKED {row['task']}/{row['split']}: {row['reason']}", flush=True) | |
| print(f"Plan: {len(cells)} cells, {len(jobs)} queued, {len(blocked)} blocked input groups; no timing.", flush=True) | |
| if args.plan: | |
| return 2 if blocked else 0 | |
| if sys.platform != "linux": | |
| parser.error("GPU execution is server-only; use --plan for a read-only preview") | |
| if not jobs: | |
| print("Nothing to run. Existing invalid-quality results remain invalid, not successful PPL points.") | |
| return 2 if blocked else 0 | |
| import fcntl | |
| from contextlib import ExitStack | |
| with ExitStack() as stack: | |
| gpu_records = [] | |
| lock_dir = root / "results/elf-queue-locks" | |
| lock_dir.mkdir(parents=True, exist_ok=True) | |
| for gpu in args.gpus: | |
| line = subprocess.check_output(["nvidia-smi", "-i", gpu, "--query-gpu=uuid,name", "--format=csv,noheader"], text=True).strip() | |
| uuid, name = [s.strip() for s in line.split(",", 1)] | |
| if not uuid.startswith("GPU-") or any(r["uuid"] == uuid for r in gpu_records): | |
| raise ValueError("GPU selections must resolve to distinct physical GPUs") | |
| handle = stack.enter_context((lock_dir / f"{uuid}.lock").open("a")) | |
| fcntl.flock(handle, fcntl.LOCK_EX | fcntl.LOCK_NB) | |
| gpu_records.append({"physical_selection": gpu, "uuid": uuid, "name": name}) | |
| stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%S") + f"-{os.getpid()}" | |
| log_dir = output_root / "logs" / stamp | |
| log_dir.mkdir(parents=True) | |
| report_path = log_dir / "queue.json" | |
| report = {"schema": "elf-quality-queue-v1", "gpus": gpu_records, "runs": reports, "blocked": blocked, | |
| "assets_manifest_sha256": asset_hash, "timing": False} | |
| mutex, stop = threading.Lock(), threading.Event() | |
| processes = {} | |
| work = queue.Queue() | |
| for job in jobs: | |
| work.put(job) | |
| def publish(): | |
| write_json(report_path, report) | |
| def worker(gpu): | |
| env = dict(os.environ, CUDA_VISIBLE_DEVICES=gpu["uuid"], PYTHONDONTWRITEBYTECODE="1", | |
| HF_HUB_OFFLINE="1", TRANSFORMERS_OFFLINE="1", WANDB_MODE="disabled") | |
| while not stop.is_set(): | |
| try: | |
| cell, row = work.get_nowait() | |
| except queue.Empty: | |
| break | |
| path = Path(row["run"]) | |
| with mutex: | |
| row.update(status="running", gpu=gpu["uuid"]) | |
| publish() | |
| print(f"START GPU {gpu['physical_selection']} {cell.key}", flush=True) | |
| try: | |
| for stage, command in commands(root, sys.executable, cell, path, row["action"] == "generate+evaluate"): | |
| if stop.is_set(): | |
| raise RuntimeError("queue interrupted") | |
| log = log_dir / (cell.key.replace("/", "__") + f"__{stage}.log") | |
| with log.open("w") as stream: | |
| stream.write(json.dumps(command) + "\n") | |
| stream.flush() | |
| with mutex: | |
| if stop.is_set(): | |
| raise RuntimeError("queue interrupted") | |
| proc = subprocess.Popen(command, cwd=root, env=env, stdout=stream, | |
| stderr=subprocess.STDOUT, start_new_session=True) | |
| processes[gpu["uuid"]] = proc | |
| code = proc.wait() | |
| with mutex: | |
| processes.pop(gpu["uuid"], None) | |
| with mutex: | |
| row[stage + "_log"] = str(log) | |
| if code != 0: | |
| checked, _ = reusable_result(root, cell, asset_hash, assets) | |
| if stage == "evaluate" and checked and checked["run"] == str(path) and checked["status"] == "quality_invalid": | |
| break | |
| raise RuntimeError(f"{stage} exit={code}; see {log}") | |
| checked, _ = reusable_result(root, cell, asset_hash, assets) | |
| if not checked or checked["run"] != str(path) or checked["status"] not in ("quality_valid", "quality_invalid"): | |
| raise RuntimeError("finished process did not publish a verified quality result") | |
| with mutex: | |
| row.update(status=checked["status"], reason=checked.get("reason")) | |
| except Exception as error: | |
| with mutex: | |
| row.update(status="failed", reason=str(error)) | |
| finally: | |
| with mutex: | |
| publish() | |
| print(f"DONE GPU {gpu['physical_selection']} {cell.key}: {row['status']}", flush=True) | |
| work.task_done() | |
| publish() | |
| print(f"Logs and live queue report: {log_dir}", flush=True) | |
| pool = ThreadPoolExecutor(max_workers=len(gpu_records)) | |
| futures = [pool.submit(worker, gpu) for gpu in gpu_records] | |
| interrupted = False | |
| try: | |
| pending = futures | |
| while pending: | |
| _, pending = wait(pending, timeout=30) | |
| if pending: | |
| with mutex: | |
| running = [r['cell'] for r in reports if r['status'] == 'running'] | |
| print(f"RUNNING {running}; remaining queued={work.qsize()}", flush=True) | |
| for future in futures: | |
| future.result() | |
| except KeyboardInterrupt: | |
| interrupted = True | |
| stop.set() | |
| with mutex: | |
| active = list(processes.values()) | |
| for proc in active: | |
| try: | |
| os.killpg(proc.pid, signal.SIGTERM) | |
| except ProcessLookupError: | |
| pass | |
| for proc in active: | |
| try: | |
| proc.wait(timeout=15) | |
| except subprocess.TimeoutExpired: | |
| try: | |
| os.killpg(proc.pid, signal.SIGKILL) | |
| except ProcessLookupError: | |
| pass | |
| finally: | |
| pool.shutdown(wait=True) | |
| with mutex: | |
| publish() | |
| counts = {s: sum(r["status"] == s for r in reports) for s in sorted({r["status"] for r in reports})} | |
| print(f"Final statuses: {counts}; blocked input groups={len(blocked)}; report={report_path}", flush=True) | |
| return 130 if interrupted else 2 if blocked or any(r["status"] in ("failed", "pending", "running") for r in reports) else 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |