summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYurenHao0426 <Blackhao0426@gmail.com>2026-07-22 14:17:04 -0500
committerYurenHao0426 <Blackhao0426@gmail.com>2026-07-22 14:33:50 -0500
commit41048d9d9c399ded6f5c473b578f59e6ac375ae9 (patch)
tree9bcfa1e540957500b9bb82e2c4e4537fe802c25f
parentd761f655301e3535368a87e9d4ffc26ab1ab8510 (diff)
protocol: freeze reciprocal KP baseline funnel
-rw-r--r--BASELINES.md6
-rw-r--r--KP_BASELINE.md78
-rw-r--r--NOVELTY.md7
-rw-r--r--ROADMAP.md9
-rwxr-xr-xexperiments/analyze_kp_short.py126
-rwxr-xr-xexperiments/kp_short_development.py36
6 files changed, 262 insertions, 0 deletions
diff --git a/BASELINES.md b/BASELINES.md
index 52fd12c..14c969c 100644
--- a/BASELINES.md
+++ b/BASELINES.md
@@ -32,6 +32,12 @@ The audited implementations and completed results are in `RESULTS.md`:
stochastic update vanishes sample-by-sample at Q=W; the extra feedback
prediction convolution is included in MAC accounting. It is also an
inherited learned-alignment control;
+- modified Kolen--Pollack (`kp`) is the reciprocal local-plasticity baseline of
+ Akrout et al. Forward and feedback synapses independently recompute the same
+ correlation from their own local activity pair and use matched optimizer and
+ decay dynamics. The feedback update never reads W or its update. A separate
+ reciprocal correlation is charged in the MAC audit; `KP_BASELINE.md` freezes
+ its staged ResNet gate before any KP task endpoint;
- direct node perturbation uses a causal target on every hidden update and no
learned vectorizer;
- the no-traffic, `P=0` SDIL backbone is learned direct feedback by node
diff --git a/KP_BASELINE.md b/KP_BASELINE.md
new file mode 100644
index 0000000..25696f7
--- /dev/null
+++ b/KP_BASELINE.md
@@ -0,0 +1,78 @@
+# Modified Kolen--Pollack baseline protocol
+
+## Attribution and boundary
+
+This is the reciprocal local-plasticity mechanism of Akrout et al., *Deep
+Learning without Weight Transport* (NeurIPS 2019), not an SDIL contribution.
+Their equations 16--18 give forward and feedback synapses equal adjustments
+from reciprocal local activity pairs and equal decay, so initially distinct
+weights converge without transmitting either weight. The public author
+repository is frozen for reference at revision
+`688f47addd2131684da1b7829b20365f585eee66`:
+
+<https://github.com/makrout/Deep-Learning-without-Weight-Transport>
+
+The public Python implementation is a fully connected pedagogical version and
+explicitly transposes a precomputed update. The implementation here instead
+recomputes each reciprocal convolutional correlation from its own cached
+parent activity and child teaching field. It follows the actual ResNet
+residual DAG, including local ReLU and BatchNorm Jacobians and parameter-free
+shortcut adjoints. The reciprocal update never reads a forward weight or a
+forward update tensor.
+
+KP is introduced because the frozen residual-response mirror full run tests a
+different, intermittent probe-response mechanism. KP is not a cadence or rate
+rescue of that branch. No KP task endpoint was generated before this protocol
+and its executable analyzer were committed.
+
+## KP-0: mechanics gate
+
+The convolutional smoke suite requires all of:
+
+- independently recomputed forward and reciprocal local directions agree to
+ absolute error below `1e-14` under the storage/sign convention;
+- changing every W and Q value after local activities are fixed changes the
+ reciprocal update by exactly zero;
+- exact `Q=W`, `R=-W_out^T` remains exact after two momentum-plus-decay steps;
+- the public KP task step leaves all parameters graph-free;
+- every pre-existing convolutional local-gradient and feedback check remains
+ green.
+
+The implemented errors are all exactly zero in float64.
+
+## KP-1: frozen 20-epoch useful-scale gate
+
+Run exactly one seed-0 ResNet-20 development record on the frozen 45k/5k
+CIFAR-10 split: batch 128, standard augmentation, hidden/output LR 0.1,
+epoch-100/150 step drops (therefore constant during this screen), no warmup,
+momentum 0.9, weight decay `1e-4`, BatchNorm, feedback scale 1, and a 32-example
+alignment audit. There is no LR, decay, initialization, or feedback-scale grid.
+
+The already frozen matched BP trajectory has 81.02% validation accuracy at
+epoch 20 and `1.09487808e14` linearly scaled training MACs. KP-1 passes only if:
+
+- the record, every epoch loss, and every tracking diagnostic are finite;
+- validation accuracy is at least 70% and within 15 points of BP epoch 20;
+- final early-third teaching alignment is at least 0.50;
+- final mean feedback/forward cosine is at least 0.80 and its epoch-11--20
+ mean is at least 0.70;
+- feedback learning uses zero task-loss queries;
+- total MACs, including a separate correlation at every reciprocal synapse,
+ are at most `1.40x` matched BP.
+
+Failure closes KP without an extra learning-rate/decay screen. Passing opens
+one full development run.
+
+## KP-2: conditional full baseline
+
+Copy KP-1 exactly for 200 epochs. KP-2 passes only if it is finite, reaches
+88% validation accuracy, retains early alignment 0.80, reaches final mean
+feedback/forward cosine 0.95, keeps the epoch-151--200 mean cosine at least
+0.95, uses zero task-loss queries, and costs at most `1.40x` the frozen full BP
+MACs. It never authorizes test access.
+
+KP remains an inherited baseline even if both gates pass, so it cannot raise
+the reviewer score. A KP-2 pass only opens a separately frozen
+raw-versus-norm-matched-raw-versus-innovation mixed-traffic experiment. The
+somato-dendritic innovation must be load-bearing there to affect the SDIL
+paper assessment.
diff --git a/NOVELTY.md b/NOVELTY.md
index 1dae12b..1ad1a5c 100644
--- a/NOVELTY.md
+++ b/NOVELTY.md
@@ -76,6 +76,13 @@ this attribution. Its zero-noise fixed point and any resulting scale belong to
local predictive weight estimation; they become relevant to SDIL only as the
feedback substrate on which innovation is separately ablated.
+The modified Kolen--Pollack reciprocal-plasticity path is likewise inherited
+from Akrout et al. Equal local activity products and matched decay in reciprocal
+synapses are not SDIL. Even if the audited implementation reaches BP-level
+accuracy, it only supplies a stable feedback substrate. The candidate novelty
+still requires neutral somato-dendritic subtraction to outperform raw and
+norm-matched raw apical activity under the same mixed-traffic intervention.
+
## Candidate SDIL contribution
The candidate contribution is the combination
diff --git a/ROADMAP.md b/ROADMAP.md
index 58ae77e..ddb674d 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -447,6 +447,15 @@ two accuracy margins by only 0.08 and 0.14 points; the LR-0.03 run reaches
validation run opens at the selected settings. The result remains baseline
evidence and the reviewer score stays 5/10.
+**Modified Kolen--Pollack baseline protocol: mechanics passed, task gate
+frozen.** The separate local reciprocal correlations, post-observation W/Q
+independence, and exact symmetric-limit momentum updates all pass at zero
+numerical error. `KP_BASELINE.md` freezes one 20-epoch full-development screen
+with no LR/decay grid and requires both validation accuracy and training-period
+feedback tracking. No KP task endpoint had been generated when this protocol
+was committed. KP is inherited Akrout et al. machinery and cannot raise the
+paper score; only a later load-bearing innovation ablation can do that.
+
Prepare convolutional local-update primitives and ResNet-20/32/56 protocols early. Queue frozen
runs opportunistically on authorized idle GPUs. Because BurstCCN already reports CIFAR-10 and
ImageNet scaling, dataset scale alone is not novel. The oral-level target is a memorable joint
diff --git a/experiments/analyze_kp_short.py b/experiments/analyze_kp_short.py
new file mode 100755
index 0000000..76d86e6
--- /dev/null
+++ b/experiments/analyze_kp_short.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python3
+"""Audit and gate the frozen KP-1 ResNet-20 development record."""
+import argparse
+import json
+import math
+import os
+
+
+SPLIT_HASH = "8328b206a97c420e49e54e3eca4abe3274c4756b084355784ea3fb8059e4515b"
+BP_EPOCH20_ACCURACY = 0.8102
+BP_EPOCH20_MACS = 109_487_808_000_000
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--input", default="results/kp_short/kp.json")
+ parser.add_argument(
+ "--bp", default="results/oral_a_dev/bp_reference_primary.json")
+ parser.add_argument("--out", default="results/kp_short_gate.json")
+ args = parser.parse_args()
+ with open(args.input) as handle:
+ record = json.load(handle)
+ run_args = record["args"]
+ expected = {
+ "mode": "kp", "depth": 20, "width": 16, "seed": 0,
+ "loader_seed": 0, "batch_size": 128, "epochs": 20,
+ "train_limit": 0, "val_examples": 5000, "split_seed": 2027,
+ "eval_split": "validation", "eval_every": 0,
+ "augment_train": 1, "lr": 0.1, "output_lr": 0.1,
+ "lr_schedule": "step", "lr_milestones": "100,150",
+ "lr_gamma": 0.1, "warmup_epochs": 0, "momentum": 0.9,
+ "weight_decay": 1e-4, "normalization": "batchnorm",
+ "a_scale": 1.0, "alignment_probe": 32,
+ }
+ for key, value in expected.items():
+ if run_args.get(key) != value:
+ raise ValueError(f"KP-1 {key} drift")
+ if record["provenance"]["git_tracked_dirty"]:
+ raise ValueError("tracked-dirty KP-1 result")
+ if record["split"]["validation_index_sha256"] != SPLIT_HASH:
+ raise ValueError("KP-1 split drift")
+ protocol = record["evaluation_protocol"]
+ if protocol["test_evaluations"] or protocol["test_used_for_selection"]:
+ raise ValueError("KP-1 touched test")
+ if record.get("calibration_metric_space") != (
+ "reciprocal_local_activity_products"):
+ raise ValueError("KP-1 metric-space drift")
+
+ with open(args.bp) as handle:
+ bp = json.load(handle)
+ bp_epoch20 = [row for row in bp["epochs"] if row["epoch"] == 20]
+ if (len(bp_epoch20) != 1
+ or float(bp_epoch20[0]["eval_accuracy"]) != BP_EPOCH20_ACCURACY):
+ raise ValueError("matched BP epoch-20 endpoint drift")
+ if int(bp["work"]["total_macs_estimate"]) // 10 != BP_EPOCH20_MACS:
+ raise ValueError("matched BP epoch-20 MAC reference drift")
+
+ epoch_tracking = [row.get("feedback_tracking") for row in record["epochs"]]
+ if len(epoch_tracking) != 20 or any(value is None for value in epoch_tracking):
+ raise ValueError("KP-1 tracking trajectory is incomplete")
+ trajectory_values = []
+ for row, tracking in zip(record["epochs"], epoch_tracking):
+ trajectory_values.extend([
+ float(row["train_loss"]),
+ float(tracking["mean_feedback_forward_cosine"]),
+ float(tracking["mean_feedback_forward_relative_error"]),
+ float(tracking["min_feedback_forward_cosine"]),
+ float(tracking["max_feedback_forward_relative_error"]),
+ ])
+ diagnostics = record["diagnostics"]
+ accuracy = float(record["final"]["accuracy"])
+ loss = float(record["final"]["loss"])
+ early = float(diagnostics["early_third_mean"])
+ final_cosine = float(diagnostics["mean_feedback_forward_cosine"])
+ late_cosine = sum(float(value["mean_feedback_forward_cosine"])
+ for value in epoch_tracking[10:]) / 10
+ total_macs = int(record["work"]["total_macs_estimate"])
+ queries = int(record["work"]["logical_batch_loss_queries"])
+ finite = (bool(record["final"]["finite"])
+ and all(math.isfinite(value) for value in
+ trajectory_values + [accuracy, loss, early, final_cosine]))
+ checks = {
+ "record_and_trajectory_finite": finite,
+ "accuracy_at_least_0.70": accuracy >= 0.70,
+ "within_15_points_of_bp_epoch20": (
+ accuracy >= BP_EPOCH20_ACCURACY - 0.15),
+ "early_alignment_at_least_0.50": early >= 0.50,
+ "final_feedback_cosine_at_least_0.80": final_cosine >= 0.80,
+ "epoch11_to20_feedback_cosine_at_least_0.70": late_cosine >= 0.70,
+ "zero_task_loss_queries": queries == 0,
+ "macs_at_most_1.40x_bp": total_macs <= 1.40 * BP_EPOCH20_MACS,
+ }
+ status = "passed" if all(checks.values()) else "failed"
+ metrics = {
+ "accuracy": accuracy, "loss": loss,
+ "bp_epoch20_accuracy": BP_EPOCH20_ACCURACY,
+ "early_third_alignment": early,
+ "final_mean_feedback_forward_cosine": final_cosine,
+ "epoch11_to20_mean_feedback_forward_cosine": late_cosine,
+ "final_mean_feedback_forward_relative_error": float(
+ diagnostics["mean_feedback_forward_relative_error"]),
+ "total_macs": total_macs, "bp_epoch20_total_macs": BP_EPOCH20_MACS,
+ "mac_ratio_to_bp": total_macs / BP_EPOCH20_MACS,
+ "logical_batch_loss_queries": queries,
+ "peak_memory_allocated_bytes": int(
+ record["hardware"]["peak_memory_allocated_bytes"]),
+ "wall_s": float(record["timing"]["total_timed_wall_s"]),
+ "source_commit": record["provenance"]["git_commit"],
+ }
+ output = {
+ "protocol": "kolen_pollack_short_v1", "status": status,
+ "checks": checks, "metrics": metrics,
+ "full_validation_opened": status == "passed",
+ "confirmation_test_seeds_touched": False,
+ "review_score_before": 5, "review_score_after": 5,
+ "score_change_rule": "inherited KP baseline cannot raise score",
+ }
+ os.makedirs(os.path.dirname(os.path.abspath(args.out)), exist_ok=True)
+ with open(args.out, "w") as handle:
+ json.dump(output, handle, indent=2, sort_keys=True)
+ handle.write("\n")
+ print(json.dumps(output, indent=2))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/experiments/kp_short_development.py b/experiments/kp_short_development.py
new file mode 100755
index 0000000..deefb59
--- /dev/null
+++ b/experiments/kp_short_development.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+"""Run the single frozen KP-1 ResNet-20 development record."""
+import argparse
+import os
+import subprocess
+import sys
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--device", default="cuda")
+ parser.add_argument("--dry_run", action="store_true")
+ args = parser.parse_args()
+ command = [
+ sys.executable, "experiments/conv_run.py", "--mode", "kp",
+ "--device", args.device, "--depth", "20", "--width", "16",
+ "--seed", "0", "--loader_seed", "0", "--batch_size", "128",
+ "--epochs", "20", "--train_limit", "0",
+ "--val_examples", "5000", "--split_seed", "2027",
+ "--eval_split", "validation", "--eval_every", "0",
+ "--augment_train", "1", "--lr", "0.1", "--output_lr", "0.1",
+ "--lr_schedule", "step", "--lr_milestones", "100,150",
+ "--lr_gamma", "0.1", "--warmup_epochs", "0",
+ "--momentum", "0.9", "--weight_decay", "1e-4",
+ "--normalization", "batchnorm", "--a_scale", "1",
+ "--alignment_probe", "32",
+ "--out", "results/kp_short/kp.json",
+ ]
+ os.makedirs("results/kp_short", exist_ok=True)
+ print(" ".join(command), flush=True)
+ if not args.dry_run:
+ subprocess.run(command, check=True)
+
+
+if __name__ == "__main__":
+ main()