diff options
| author | YurenHao0426 <Blackhao0426@gmail.com> | 2026-07-30 16:34:30 -0500 |
|---|---|---|
| committer | YurenHao0426 <Blackhao0426@gmail.com> | 2026-07-30 16:34:30 -0500 |
| commit | 716205eba862076c1d0fbbff016601272e4614f2 (patch) | |
| tree | dc8c31dcfefa49d53c98d305fe35419da76e58c9 /external/dualprop_patches/0011-experiment-register-plain-CNN-P1-selector-grid.patch | |
| parent | 906d17e896329e4d2dd8d7f728e4d5a234afe0e4 (diff) | |
experiment: package portable A6000 crossover
Diffstat (limited to 'external/dualprop_patches/0011-experiment-register-plain-CNN-P1-selector-grid.patch')
| -rw-r--r-- | external/dualprop_patches/0011-experiment-register-plain-CNN-P1-selector-grid.patch | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/external/dualprop_patches/0011-experiment-register-plain-CNN-P1-selector-grid.patch b/external/dualprop_patches/0011-experiment-register-plain-CNN-P1-selector-grid.patch new file mode 100644 index 0000000..4f79f09 --- /dev/null +++ b/external/dualprop_patches/0011-experiment-register-plain-CNN-P1-selector-grid.patch @@ -0,0 +1,247 @@ +From bc3b656233c8731e7b8182087cf74a0079baadbd Mon Sep 17 00:00:00 2001 +From: YurenHao0426 <Blackhao0426@gmail.com> +Date: Mon, 27 Jul 2026 13:28:00 -0500 +Subject: [PATCH 11/19] experiment: register plain CNN P1 selector grid + +--- + crossover_grid.py | 228 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 228 insertions(+) + create mode 100644 crossover_grid.py + +diff --git a/crossover_grid.py b/crossover_grid.py +new file mode 100644 +index 0000000..dae3316 +--- /dev/null ++++ b/crossover_grid.py +@@ -0,0 +1,228 @@ ++#!/usr/bin/env python3 ++"""Immutable command registry for the plain-CNN crossover stages.""" ++import argparse ++import glob ++import hashlib ++import json ++import os ++import subprocess ++import sys ++import time ++ ++ ++ROOT = os.path.dirname(os.path.abspath(__file__)) ++MAIN_ROOT = "/home/yurenh2/sdil" ++PROTOCOL = os.path.join(MAIN_ROOT, "PLAIN_CNN_CROSSOVER.md") ++ ++ ++def output(*args): ++ return subprocess.run( ++ args, cwd=ROOT, check=True, capture_output=True, ++ text=True).stdout.strip() ++ ++ ++def sha256(path): ++ digest = hashlib.sha256() ++ with open(path, "rb") as handle: ++ for chunk in iter(lambda: handle.read(1024 * 1024), b""): ++ digest.update(chunk) ++ return digest.hexdigest() ++ ++ ++def source_report(): ++ commit = output("git", "rev-parse", "HEAD") ++ dirty = output( ++ "git", "status", "--porcelain", "--untracked-files=no") ++ if dirty: ++ raise RuntimeError("crossover runs require clean tracked author source") ++ main_commit = subprocess.run( ++ ["git", "rev-parse", "HEAD"], cwd=MAIN_ROOT, check=True, ++ capture_output=True, text=True).stdout.strip() ++ main_dirty = subprocess.run( ++ ["git", "status", "--porcelain", "--untracked-files=no"], ++ cwd=MAIN_ROOT, check=True, capture_output=True, ++ text=True).stdout.strip() ++ if main_dirty: ++ raise RuntimeError("crossover runs require clean tracked main source") ++ return { ++ "author_crossover_commit": commit, ++ "main_commit": main_commit, ++ "protocol_path": PROTOCOL, ++ "protocol_sha256": sha256(PROTOCOL), ++ "python": sys.executable, ++ "cuda_visible_devices": os.environ.get("CUDA_VISIBLE_DEVICES"), ++ } ++ ++ ++def rate_tag(rate): ++ return f"{rate:g}".replace(".", "p") ++ ++ ++def p1_jobs(): ++ # Expensive jobs are deliberately interleaved across modulo shards. ++ specifications = [ ++ ("ep", 0.003), ++ ("dualprop", 0.025), ++ ("ep", 0.01), ++ ("ff", 0.03), ++ ("ep", 0.03), ++ ("pepita", 0.01), ++ ("fa", 0.003), ++ ("bp", 0.025), ++ ("fa", 0.01), ++ ("dfa", 0.003), ++ ("fa", 0.03), ++ ("dfa", 0.01), ++ ("clean_kp", 0.003), ++ ("dfa", 0.03), ++ ("clean_kp", 0.01), ++ ("sdil", 0.003), ++ ("clean_kp", 0.03), ++ ("sdil", 0.01), ++ ("sdil", 0.03), ++ ] ++ return [p1_command(method, rate) for method, rate in specifications] ++ ++ ++def p1_command(method, rate): ++ cli_method = { ++ "dualprop": "dualprop-lagr-ff", ++ "clean_kp": "clean-kp", ++ }.get(method, method) ++ runner = "train_ff.py" if method == "ff" else "train.py" ++ name = f"plain-p1-{method}-minicnn-lr{rate_tag(rate)}" ++ command = [ ++ sys.executable, runner, ++ "--model", "miniCNN", ++ "--dataset", "cifar10", ++ "--num-epochs", "10", ++ "--batch-size", "100", ++ "--learning-rate", str(rate), ++ "--learning-rate-final", str(rate), ++ "--warmup-learning-rate", str(rate), ++ "--warmup-epochs", "0", ++ "--decay-epochs", "10", ++ "--momentum", "0.9", ++ "--weight-decay", "5e-4", ++ "--dtype", "float32", ++ "--param-dtype", "float32", ++ "--percent-train", "90", ++ "--percent-val", "10", ++ "--seeds", "0", ++ "--feedback-seed", "1729", ++ "--gradient-diagnostics", "none", ++ "--spectral-diagnostics", "none", ++ "--test-policy", "none", ++ "--early-stop-policy", "none", ++ "--learning-algorithm", cli_method, ++ "--experiment-name", name, ++ ] ++ if method == "pepita": ++ command.extend([ ++ "--optimizer-schedule", "pepita", ++ "--pepita-projection-scale", "0.05", ++ ]) ++ else: ++ command.extend(["--optimizer-schedule", "author"]) ++ if method == "ff": ++ command.extend([ ++ "--ff-threshold", "2.0", ++ "--ff-score-from-layer", "1", ++ ]) ++ if method == "ep": ++ command.extend([ ++ "--ep-beta", "0.5", ++ "--ep-dt", "0.5", ++ "--ep-free-steps", "20", ++ "--ep-nudge-steps", "4", ++ ]) ++ if method == "dualprop": ++ command.extend([ ++ "--loss", "sce", ++ "--alpha", "0.0", ++ "--beta", "0.1", ++ "--inference-sequence", "fwK", ++ "--inference-passes-nudged", "16", ++ ]) ++ if method == "sdil": ++ command.extend([ ++ "--sdil-traffic-ratio", "4", ++ "--sdil-traffic-seed", "4000", ++ "--sdil-calibration-examples", "64", ++ ]) ++ return { ++ "stage": "p1", ++ "method": method, ++ "architecture": "minicnn", ++ "rate": rate, ++ "experiment_name": name, ++ "command": command, ++ } ++ ++ ++def completed_histogram(experiment_name): ++ paths = glob.glob(os.path.join( ++ ROOT, "runs", experiment_name, "*", "hist.npy")) ++ if not paths: ++ return None ++ if len(paths) != 1: ++ raise RuntimeError( ++ f"expected one result for {experiment_name}, found {len(paths)}") ++ return paths[0] ++ ++ ++def run_job(job, source, dry_run): ++ result = completed_histogram(job["experiment_name"]) ++ if result is not None: ++ print(f"preserving {job['experiment_name']} -> {result}", flush=True) ++ return ++ print("RUN", " ".join(job["command"]), flush=True) ++ if dry_run: ++ return ++ started = time.time() ++ subprocess.run(job["command"], cwd=ROOT, check=True) ++ result = completed_histogram(job["experiment_name"]) ++ if result is None: ++ raise RuntimeError(f"job produced no histogram: {job['experiment_name']}") ++ manifest = { ++ **job, ++ "source": source, ++ "histogram": result, ++ "histogram_sha256": sha256(result), ++ "driver_wall_seconds": time.time() - started, ++ "completed_unix_time": time.time(), ++ } ++ manifest_path = os.path.join( ++ os.path.dirname(result), "crossover_manifest.json") ++ with open(manifest_path, "w") as handle: ++ json.dump(manifest, handle, indent=2, sort_keys=True) ++ handle.write("\n") ++ print(f"DONE {job['experiment_name']} -> {manifest_path}", flush=True) ++ ++ ++def main(): ++ parser = argparse.ArgumentParser() ++ parser.add_argument("--stage", choices=("p1",), default="p1") ++ parser.add_argument("--shard-index", type=int, default=0) ++ parser.add_argument("--num-shards", type=int, default=1) ++ parser.add_argument("--method") ++ parser.add_argument("--dry-run", action="store_true") ++ args = parser.parse_args() ++ if not 0 <= args.shard_index < args.num_shards: ++ raise ValueError("invalid shard") ++ source = source_report() ++ jobs = p1_jobs() ++ if args.method: ++ jobs = [job for job in jobs if job["method"] == args.method] ++ jobs = [ ++ job for index, job in enumerate(jobs) ++ if index % args.num_shards == args.shard_index ++ ] ++ if not jobs: ++ raise ValueError("no jobs selected") ++ for job in jobs: ++ run_job(job, source, args.dry_run) ++ ++ ++if __name__ == "__main__": ++ main() +-- +2.54.0 + |
