diff options
Diffstat (limited to 'external/dualprop_patches/0014-experiment-freeze-plain-CNN-P2-registry.patch')
| -rw-r--r-- | external/dualprop_patches/0014-experiment-freeze-plain-CNN-P2-registry.patch | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/external/dualprop_patches/0014-experiment-freeze-plain-CNN-P2-registry.patch b/external/dualprop_patches/0014-experiment-freeze-plain-CNN-P2-registry.patch new file mode 100644 index 0000000..f6ce895 --- /dev/null +++ b/external/dualprop_patches/0014-experiment-freeze-plain-CNN-P2-registry.patch @@ -0,0 +1,228 @@ +From 0645bf8369bf49ca4c23a9b2c19e4742f174addc Mon Sep 17 00:00:00 2001 +From: YurenHao0426 <Blackhao0426@gmail.com> +Date: Mon, 27 Jul 2026 13:49:15 -0500 +Subject: [PATCH 14/19] experiment: freeze plain CNN P2 registry + +--- + crossover_grid.py | 177 +++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 175 insertions(+), 2 deletions(-) + +diff --git a/crossover_grid.py b/crossover_grid.py +index 340e174..846a2f1 100644 +--- a/crossover_grid.py ++++ b/crossover_grid.py +@@ -13,6 +13,14 @@ 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") ++SELECTOR = os.path.join( ++ MAIN_ROOT, "results", "plain_cnn_p1_selector.json") ++SELECTOR_SHA256 = ( ++ "3365f4618e0941de7dd7183f69c1f1bdd596a94b60f58de9e4e60ed05c38918c") ++P2_METHODS = ( ++ "bp", "fa", "dfa", "pepita", "ff", "ep", "dualprop", "clean_kp", ++ "sdil") ++P2_ARCHITECTURES = ("minicnn", "vgglike", "vgg16") + + + def output(*args): +@@ -58,6 +66,18 @@ def rate_tag(rate): + return f"{rate:g}".replace(".", "p") + + ++def selector_report(): ++ if sha256(SELECTOR) != SELECTOR_SHA256: ++ raise RuntimeError("P1 selector artifact hash drift") ++ with open(SELECTOR, encoding="utf-8") as handle: ++ report = json.load(handle) ++ if report["gate"] != "pass" or report["num_audited_records"] != 19: ++ raise RuntimeError("P1 selector gate did not pass") ++ if set(report["selected"]) != set(P2_METHODS): ++ raise RuntimeError("P1 selector method registry drift") ++ return report ++ ++ + def p1_jobs(): + # Expensive jobs are deliberately interleaved across modulo shards. + specifications = [ +@@ -84,6 +104,159 @@ def p1_jobs(): + return [p1_command(method, rate) for method, rate in specifications] + + ++def p2_jobs(): ++ report = selector_report() ++ # Expensive largest-model cells start first. Modulo-2 assignment places ++ # VGG16 EP and DP on different physical GPUs. ++ schedule = [ ++ ("ep", "vgg16"), ++ ("dualprop", "vgg16"), ++ ("ff", "vgg16"), ++ ("pepita", "vgg16"), ++ ("sdil", "vgg16"), ++ ("clean_kp", "vgg16"), ++ ("fa", "vgg16"), ++ ("dfa", "vgg16"), ++ ("bp", "vgg16"), ++ ("ep", "vgglike"), ++ ("dualprop", "vgglike"), ++ ("ff", "vgglike"), ++ ("pepita", "vgglike"), ++ ("sdil", "vgglike"), ++ ("clean_kp", "vgglike"), ++ ("fa", "vgglike"), ++ ("dfa", "vgglike"), ++ ("bp", "vgglike"), ++ ("ep", "minicnn"), ++ ("dualprop", "minicnn"), ++ ("ff", "minicnn"), ++ ("pepita", "minicnn"), ++ ("sdil", "minicnn"), ++ ("clean_kp", "minicnn"), ++ ("fa", "minicnn"), ++ ("dfa", "minicnn"), ++ ("bp", "minicnn"), ++ ] ++ assert len(schedule) == len(P2_METHODS) * len(P2_ARCHITECTURES) ++ assert set(schedule) == { ++ (method, architecture) ++ for method in P2_METHODS for architecture in P2_ARCHITECTURES} ++ return [ ++ p2_command( ++ method, architecture, report["selected"][method]["rate"]) ++ for method, architecture in schedule ++ ] ++ ++ ++def p2_command(method, architecture, rate): ++ cli_method = { ++ "bp": "backprop", ++ "dualprop": "dualprop-lagr-ff", ++ "clean_kp": "clean-kp", ++ }.get(method, method) ++ model = { ++ "minicnn": "miniCNN", ++ "vgglike": "VGGlike", ++ "vgg16": "VGG16", ++ }[architecture] ++ runner = "train_ff.py" if method == "ff" else "train.py" ++ if method in ("bp", "fa", "dfa", "dualprop", "clean_kp", "sdil"): ++ epochs = 130 ++ final_rate = 2e-6 ++ warmup_rate = 0.001 ++ warmup_epochs = 10 ++ decay_epochs = 120 ++ optimizer_schedule = "author" ++ elif method == "pepita": ++ epochs = 100 ++ final_rate = warmup_rate = rate ++ warmup_epochs = 0 ++ decay_epochs = epochs ++ optimizer_schedule = "pepita" ++ elif method == "ff": ++ epochs = 40 ++ final_rate = warmup_rate = rate ++ warmup_epochs = 0 ++ decay_epochs = epochs ++ optimizer_schedule = "author" ++ elif method == "ep": ++ epochs = 100 ++ final_rate = warmup_rate = rate ++ warmup_epochs = 0 ++ decay_epochs = epochs ++ optimizer_schedule = "author" ++ else: ++ raise ValueError(method) ++ name = f"plain-p2-{method}-{architecture}" ++ command = [ ++ sys.executable, runner, ++ "--model", model, ++ "--dataset", "cifar10", ++ "--num-epochs", str(epochs), ++ "--batch-size", "100", ++ "--learning-rate", str(rate), ++ "--learning-rate-final", str(final_rate), ++ "--warmup-learning-rate", str(warmup_rate), ++ "--warmup-epochs", str(warmup_epochs), ++ "--decay-epochs", str(decay_epochs), ++ "--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, ++ "--optimizer-schedule", optimizer_schedule, ++ ] ++ if method == "pepita": ++ command.extend(["--pepita-projection-scale", "0.05"]) ++ 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": "p2", ++ "method": method, ++ "architecture": architecture, ++ "rate": rate, ++ "expected_epochs": epochs, ++ "experiment_name": name, ++ "selector_path": SELECTOR, ++ "selector_sha256": SELECTOR_SHA256, ++ "timeout_seconds": 48 * 60 * 60, ++ "command": command, ++ } ++ ++ + def p1_command(method, rate): + cli_method = { + "bp": "backprop", +@@ -203,7 +376,7 @@ def run_job(job, source, dry_run): + + def main(): + parser = argparse.ArgumentParser() +- parser.add_argument("--stage", choices=("p1",), default="p1") ++ parser.add_argument("--stage", choices=("p1", "p2"), default="p1") + parser.add_argument("--shard-index", type=int, default=0) + parser.add_argument("--num-shards", type=int, default=1) + parser.add_argument("--method") +@@ -212,7 +385,7 @@ def main(): + if not 0 <= args.shard_index < args.num_shards: + raise ValueError("invalid shard") + source = source_report() +- jobs = p1_jobs() ++ jobs = p1_jobs() if args.stage == "p1" else p2_jobs() + if args.method: + jobs = [job for job in jobs if job["method"] == args.method] + jobs = [ +-- +2.54.0 + |
