summaryrefslogtreecommitdiff
path: root/external/dualprop_patches/0014-experiment-freeze-plain-CNN-P2-registry.patch
diff options
context:
space:
mode:
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.patch228
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
+