diff options
| author | YurenHao0426 <Blackhao0426@gmail.com> | 2026-07-22 17:02:45 -0500 |
|---|---|---|
| committer | YurenHao0426 <Blackhao0426@gmail.com> | 2026-07-22 17:02:45 -0500 |
| commit | 8b28522305612d6300f485e14046ae38d49af426 (patch) | |
| tree | 8d6d656783cf4f248cdde1c748d5f282008ebb40 | |
| parent | eeb50b079dc2831947b09b174877fe454eaf387c (diff) | |
protocol: freeze dynamic projection training gate
| -rw-r--r-- | DYNAMIC_INNOVATION.md | 90 | ||||
| -rw-r--r-- | THEORY.md | 26 | ||||
| -rwxr-xr-x | experiments/analyze_kp_dynamic_projection.py | 172 | ||||
| -rwxr-xr-x | experiments/kp_dynamic_projection_development.py | 44 |
4 files changed, 332 insertions, 0 deletions
diff --git a/DYNAMIC_INNOVATION.md b/DYNAMIC_INNOVATION.md new file mode 100644 index 0000000..19e39da --- /dev/null +++ b/DYNAMIC_INNOVATION.md @@ -0,0 +1,90 @@ +# Dynamic neutral-projection development protocol + +## Claim boundary + +This is a new post-S0 development branch. It does not reinterpret or rescue +MT-1: MT-1 remains failed, MT-2/MT-3 remain sealed, and the reviewer score +remains 5/10. S0 ruled out extending a fixed one-sided predictor margin. The +new question is whether a fast *local and instruction-off* projection can keep +the multiplicative neutral-residual mode inside its two-sided stability window +as somatic statistics change. + +The slow predictor is still fitted exactly once on the original 64-example +neutral calibration prefix and is frozen during task learning. Before each +task update, every cell receives a paired instruction-off observation of its +current soma `h` and ordinary apical traffic `a0`. From the neutral residual + +```text +e0 = a0 - (P h + b), +``` + +it forms the current-batch local affine projection + +```text +q = Cov(e0, h) / Var(h), +e_stable = (e0 - mean(e0)) - q (h - mean(h)). +``` + +The plasticity signal is `r = s + e_stable`. The coefficient fit sees zero +task-instruction observations; it reads no label, loss, forward weight, +feedback weight, or downstream state, and it does not modify the slow +predictor. For the diagonal affine traffic used by frozen MT-1, the controller +nulls the current empirical residual-coupling coefficient. With weight decay, +the scalar homogeneous mode is therefore placed near `k=-lambda`, inside both +Jury boundaries, instead of at a fixed negative coefficient whose product with +an evolving covariance can cross the lower boundary. + +This controller requires one paired neutral apical microphase per task batch. +Its elementwise work and observation count must be charged separately in any +later endpoint comparison. It uses zero task-loss perturbation queries and no +reverse-mode differentiation. + +## D0: mechanics (completed before task-data evaluation) + +The implementation must prove on synthetic float64 tensors that: + +- an inaccurate frozen slow predictor leaves a nonzero affine neutral mode; +- fast projection reduces the post-projection neutral/traffic RMS ratio and + residual-soma slope below `1e-14` for diagonal affine traffic; +- projected innovation equals clean instruction below `1e-14`; +- the projection consumes zero instruction observations and leaves every slow + predictor parameter bitwise unchanged; +- forward and reciprocal correlations remain independently computed; and +- the extra elementwise work is exposed explicitly. + +These checks passed at clean revision `eeb50b0`. + +## D1: frozen training-prefix gate + +There is exactly one candidate and no hyperparameter grid. Keep the MT-1 +ResNet-20 architecture, CIFAR-10 45k training split, seed-0 initialization, +four-to-one traffic intervention, augmentation, batch size 128, learning rate +0.1, momentum 0.9, decay `1e-4`, and reciprocal KP path unchanged. Use a +zero-margin closed-form slow predictor, freeze it, and apply the fast neutral +projection on every one of the first 352 shuffled task minibatches. Restore +the task-loader state after calibration. Do not evaluate validation or test +examples. + +The candidate passes only if all conditions below hold: + +- every recorded loss, signal diagnostic, parameter, optimizer state, + BatchNorm statistic, and projection diagnostic remains finite for 352 steps; +- maximum task minibatch loss is at most 10 and final-32 mean loss is at most + 2.5; +- the used-signal/instruction RMS ratio stays within `1e-4` of one; +- the maximum post-projection neutral/traffic RMS ratio is at most `1e-5`; +- the maximum absolute post-projection residual-soma slope is at most `1e-5`; +- every projection uses the current task batch's 72--128 local neutral + observations and exactly zero instruction observations; +- maximum absolute forward and reciprocal-feedback weight is at most 10; +- maximum absolute forward and reciprocal momentum is at most 50; +- the 64-observation slow fit has zero stability margin, the slow predictor is + frozen during task training, initial traffic calibration is within `1e-5` + of ratio 4, and task-loader state restoration is exact; and +- validation and test evaluation counts are both zero. + +A failure closes this paired-neutral projection branch without changing a +threshold or adding a gain/clipping hyperparameter. A pass supplies no task +accuracy claim and cannot change the reviewer score. It only authorizes a new, +separately committed validation protocol with an explicit cost boundary. + @@ -571,6 +571,32 @@ transient loss and parameter growth. This is consistent with the two-sided condition above: sign control is necessary but does not bound the evolving gain `d c` or nonnormal deep-network transients. +The post-S0 dynamic controller instead separates a slow neutral predictor from +a fast instruction-off projection. On the current minibatch, let its neutral +residual be `e0=a0-P h-b`. Each cell forms + +```text +q_t = Cov_t(e0, h) / Var_t(h), +e_perp = (e0 - mean_t(e0)) - q_t (h - mean_t(h)). +``` + +Only `s+e_perp` enters plasticity. The fit for `q_t` never observes `s`, so it +cannot regress away a task instruction merely because that instruction is +correlated with soma. For the diagonal affine traffic intervention, +`e0=D h+c` and hence `e_perp=0` in exact arithmetic at every task state. The +effective homogeneous coefficient is then `k=-lambda`, which satisfies both +Jury bounds for the frozen positive learning rate, momentum below one, and +small positive decay. Unlike a fixed negative margin, nulling `D` is invariant +to changes in the nonnegative input-covariance eigenvalue `c`. + +This is an empirical local certificate for the intervention being tested, not +a global stability theorem for a nonlinear ResNet. Finite-batch regression, +roundoff, cross-layer nonnormality, and traffic outside the diagonal affine +family remain possible failure modes. The separately frozen D1 training-prefix +gate in `DYNAMIC_INNOVATION.md` therefore requires both the measured local +certificate and bounded loss, parameter, optimizer, and BatchNorm trajectories +before any validation endpoint can open. + ### Intermittent feedback tracking can hide behind a final cosine An idealized mirror event every `k` task updates uses diff --git a/experiments/analyze_kp_dynamic_projection.py b/experiments/analyze_kp_dynamic_projection.py new file mode 100755 index 0000000..f7cef08 --- /dev/null +++ b/experiments/analyze_kp_dynamic_projection.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 +"""Audit the frozen D1 dynamic neutral-projection training prefix.""" +import argparse +import json +import math +import os +import statistics + + +SPLIT_HASH = "8328b206a97c420e49e54e3eca4abe3274c4756b084355784ea3fb8059e4515b" + + +def numeric_leaves(value): + if isinstance(value, bool) or value is None: + return + if isinstance(value, (int, float)): + yield float(value) + elif isinstance(value, dict): + for child in value.values(): + yield from numeric_leaves(child) + elif isinstance(value, (list, tuple)): + for child in value: + yield from numeric_leaves(child) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--input", default="results/kp_dynamic_projection/dynamic.json") + parser.add_argument( + "--out", default="results/kp_dynamic_projection_gate.json") + args = parser.parse_args() + with open(args.input) as handle: + record = json.load(handle) + expected = { + "protocol": "kp_dynamic_neutral_projection_diagnosis_v1", + "scope": "training_only_no_validation_or_test_evaluation", + "rule": "innovation", + "predictor_mode": "closed_form", + "predictor_every": 0, + "stability_margin": 0.0, + "neutral_projection": True, + "max_steps": 352, + "validation_evaluations": 0, + "test_evaluations": 0, + } + for key, value in expected.items(): + if record.get(key) != value: + raise ValueError(f"D1 {key} drift") + if record["provenance"]["git_tracked_dirty"]: + raise ValueError("tracked-dirty D1 record") + if record["split"]["validation_index_sha256"] != SPLIT_HASH: + raise ValueError("D1 split drift") + trajectory = record["trajectory"] + if len(trajectory) != 352: + raise ValueError("D1 trajectory is incomplete") + + losses = [float(row["batch_loss"]) for row in trajectory] + signal_ratios = [ + float(row["teaching_rms"]) + / max(float(row["instruction_rms"]), 1e-30) + for row in trajectory] + reports = [row["neutral_projection"] for row in trajectory] + if any(report is None for report in reports): + raise ValueError("D1 projection report is missing") + state_finite = all( + value["all_finite"] + for row in trajectory + for value in row["parameter_state"].values()) + all_finite = all(math.isfinite(value) + for value in numeric_leaves(record)) + + def state_max(group): + return max(float(row["parameter_state"][group][ + "max_abs_over_finite_tensors"]) for row in trajectory) + + forward_weight_max = state_max("forward_weight") + feedback_weight_max = state_max("feedback_weight") + forward_momentum_max = state_max("forward_momentum") + feedback_momentum_max = state_max("feedback_momentum") + fit = record["predictor_warmup"]["closed_form_fit"] + ratio_errors = [abs(float(value) - 4.0) for value in + record["traffic_calibration"][ + "realized_traffic_instruction_rms_ratio"]] + maximum_post_ratio = max(float(report[ + "post_projection_traffic_rms_ratio"]) for report in reports) + maximum_post_slope = max(float(report[ + "max_absolute_post_projection_soma_slope"]) for report in reports) + maximum_pre_ratio = max(float(report[ + "pre_projection_traffic_rms_ratio"]) for report in reports) + maximum_correction_slope = max(float(report[ + "max_absolute_correction_slope"]) for report in reports) + observation_counts = [int(report["observations"]) for report in reports] + instruction_observations = [ + int(report["instruction_observations"]) for report in reports] + + checks = { + "record_trajectory_and_state_finite": ( + all_finite and state_finite + and record["first_any_nonfinite_step"] is None + and record["first_training_failure_step"] is None), + "maximum_batch_loss_at_most_10": max(losses) <= 10.0, + "final_32_mean_loss_at_most_2p5": ( + statistics.mean(losses[-32:]) <= 2.5), + "used_instruction_rms_ratio_within_1e_minus_4": ( + max(abs(value - 1.0) for value in signal_ratios) <= 1e-4), + "post_projection_traffic_ratio_at_most_1e_minus_5": ( + maximum_post_ratio <= 1e-5), + "post_projection_soma_slope_at_most_1e_minus_5": ( + maximum_post_slope <= 1e-5), + "paired_local_neutral_observations_only": ( + min(observation_counts) >= 72 + and max(observation_counts) <= 128 + and max(instruction_observations) == 0), + "forward_and_feedback_weight_max_at_most_10": ( + forward_weight_max <= 10.0 and feedback_weight_max <= 10.0), + "forward_and_feedback_momentum_max_at_most_50": ( + forward_momentum_max <= 50.0 + and feedback_momentum_max <= 50.0), + "zero_margin_64_observation_slow_fit": ( + int(fit["observations"]) == 64 + and float(fit["stability_margin"]) == 0.0), + "slow_predictor_frozen_during_task": all( + row["predictor_updated"] is False for row in trajectory), + "traffic_ratio_calibrated": max(ratio_errors) <= 1e-5, + "task_loader_state_restored": ( + record["predictor_warmup"][ + "task_loader_state_restored"] is True), + "no_held_out_evaluations": ( + record["validation_evaluations"] == 0 + and record["test_evaluations"] == 0), + } + passed = all(checks.values()) + output = { + "protocol": "kp_dynamic_neutral_projection_training_prefix_v1", + "status": "passed" if passed else "failed", + "checks": checks, + "metrics": { + "maximum_batch_loss": max(losses), + "final_32_mean_loss": statistics.mean(losses[-32:]), + "maximum_used_instruction_rms_ratio_error": max( + abs(value - 1.0) for value in signal_ratios), + "maximum_pre_projection_traffic_rms_ratio": maximum_pre_ratio, + "maximum_post_projection_traffic_rms_ratio": maximum_post_ratio, + "maximum_post_projection_soma_slope": maximum_post_slope, + "maximum_correction_slope": maximum_correction_slope, + "minimum_projection_observations": min(observation_counts), + "maximum_projection_observations": max(observation_counts), + "maximum_forward_weight": forward_weight_max, + "maximum_feedback_weight": feedback_weight_max, + "maximum_forward_momentum": forward_momentum_max, + "maximum_feedback_momentum": feedback_momentum_max, + "maximum_initial_traffic_ratio_error": max(ratio_errors), + }, + "source_commit": record["provenance"]["git_commit"], + "validation_evaluations": 0, + "test_evaluations": 0, + "review_score_before": 5, + "review_score_after": 5, + "score_change_rule": ( + "training-only stability evidence cannot change the paper 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_dynamic_projection_development.py b/experiments/kp_dynamic_projection_development.py new file mode 100755 index 0000000..1a6be90 --- /dev/null +++ b/experiments/kp_dynamic_projection_development.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Run the sole frozen D1 dynamic neutral-projection candidate.""" +import argparse +import json +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") + parser.add_argument( + "--s0_gate", default="results/kp_innovation_stability_gate.json") + parser.add_argument( + "--out", default="results/kp_dynamic_projection/dynamic.json") + args = parser.parse_args() + with open(args.s0_gate) as handle: + gate = json.load(handle) + if (gate.get("protocol") != "kp_stability_margin_training_prefix_v1" + or gate.get("status") != "failed_no_eligible_margin"): + raise ValueError("D1 requires the audited failed S0 gate") + command = [ + sys.executable, + "experiments/diagnose_kp_traffic_nonfinite.py", + "--rule", "innovation", + "--predictor_mode", "closed_form", + "--predictor_every", "0", + "--stability_margin", "0", + "--neutral_projection", + "--device", args.device, + "--max_steps", "352", + "--out", args.out, + ] + print(" ".join(command), flush=True) + if not args.dry_run: + os.makedirs(os.path.dirname(os.path.abspath(args.out)), exist_ok=True) + subprocess.run(command, check=True) + + +if __name__ == "__main__": + main() + |
