Download scripts/launch.py from quinnlue/mae-cross-objective: direct link, hf CLI and curl.
- Browser
- Download file 4.03 kB
-
https://huggingface.co/quinnlue/mae-cross-objective/resolve/main/scripts/launch.py
- Command line
-
hf download hf://quinnlue/mae-cross-objective/scripts/launch.py
-
curl -L -o launch.py https://huggingface.co/quinnlue/mae-cross-objective/resolve/main/scripts/launch.py
4.03 kB
| """GPU-queue launcher for eatmap pretraining jobs. | |
| One process per GPU pulling from a shared job list, because every campaign here | |
| is embarrassingly parallel across mixtures and the box has 4 devices. Jobs are | |
| skipped if their output directory already carries a finished run.json, so a | |
| crashed launcher can be re-run without redoing work -- which matters on an | |
| instance whose filesystem does not survive a recycle. | |
| Usage: | |
| python launch.py jobs.json [--gpus 0,1,2,3] [--dry-run] | |
| jobs.json is a list of {name, configs[], sets[]} records. | |
| """ | |
| import argparse | |
| import json | |
| import queue | |
| import subprocess | |
| import threading | |
| import time | |
| from pathlib import Path | |
| REPO = "/workspace/code/eat-map-regmix" | |
| RUNS = Path("/workspace/runs") | |
| LOGS = Path("/workspace/logs") | |
| def finished(out: Path) -> bool: | |
| status = out / "status.json" | |
| if not status.exists(): | |
| return False | |
| try: | |
| # runner.py writes {"completed": true, "final_step": ...} on a clean exit. | |
| return bool(json.loads(status.read_text()).get("completed")) | |
| except Exception: | |
| return False | |
| def worker(gpu: int, jobs: queue.Queue, results: list, lock: threading.Lock, dry: bool): | |
| while True: | |
| try: | |
| job = jobs.get_nowait() | |
| except queue.Empty: | |
| return | |
| name = job["name"] | |
| out = RUNS / name | |
| if finished(out): | |
| with lock: | |
| print(f"[gpu{gpu}] skip {name} (already finished)") | |
| results.append((name, "skipped", 0.0)) | |
| jobs.task_done() | |
| continue | |
| cmd = ["python", "-m", "eatmap.cli.pretrain", "--output-dir", str(out)] | |
| for c in job["configs"]: | |
| cmd += ["--config", c] | |
| for s in job.get("sets", []): | |
| cmd += ["--set", s] | |
| # Job names carry a campaign prefix ("mae-lr/p16-lr0.003"), so the log | |
| # path is nested and its parent has to exist before the open() below. | |
| log = LOGS / f"{name}.log" | |
| log.parent.mkdir(parents=True, exist_ok=True) | |
| with lock: | |
| print(f"[gpu{gpu}] start {name}") | |
| if dry: | |
| with lock: | |
| print(" " + " ".join(cmd)) | |
| jobs.task_done() | |
| continue | |
| t0 = time.time() | |
| with open(log, "w") as fh: | |
| proc = subprocess.run( | |
| cmd, cwd=REPO, stdout=fh, stderr=subprocess.STDOUT, | |
| env={**__import__("os").environ, "CUDA_VISIBLE_DEVICES": str(gpu), | |
| "PYTHONPATH": REPO}, | |
| ) | |
| dt = time.time() - t0 | |
| state = "ok" if proc.returncode == 0 else f"FAIL({proc.returncode})" | |
| with lock: | |
| print(f"[gpu{gpu}] {state} {name} in {dt/60:.1f} min -> {log}") | |
| results.append((name, state, dt)) | |
| jobs.task_done() | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("jobs") | |
| ap.add_argument("--gpus", default="0,1,2,3") | |
| ap.add_argument("--dry-run", action="store_true") | |
| args = ap.parse_args() | |
| spec = json.loads(Path(args.jobs).read_text()) | |
| jobs: queue.Queue = queue.Queue() | |
| for job in spec: | |
| jobs.put(job) | |
| gpus = [int(g) for g in args.gpus.split(",")] | |
| print(f"{len(spec)} jobs over {len(gpus)} gpus") | |
| results: list = [] | |
| lock = threading.Lock() | |
| threads = [threading.Thread(target=worker, args=(g, jobs, results, lock, args.dry_run)) | |
| for g in gpus] | |
| t0 = time.time() | |
| for t in threads: | |
| t.start() | |
| for t in threads: | |
| t.join() | |
| ok = [r for r in results if r[1] == "ok"] | |
| bad = [r for r in results if r[1].startswith("FAIL")] | |
| print(f"\nwall {(time.time()-t0)/60:.1f} min | ok {len(ok)} | skipped " | |
| f"{len([r for r in results if r[1]=='skipped'])} | failed {len(bad)}") | |
| for name, state, _ in bad: | |
| print(f" {state} {name} -> /workspace/logs/{name}.log") | |
| if ok: | |
| mean = sum(r[2] for r in ok) / len(ok) | |
| print(f"mean run time {mean/60:.1f} min") | |
| if __name__ == "__main__": | |
| main() | |