summaryrefslogtreecommitdiff
path: root/experiments/conv_local_smoke.py
diff options
context:
space:
mode:
Diffstat (limited to 'experiments/conv_local_smoke.py')
-rw-r--r--experiments/conv_local_smoke.py129
1 files changed, 127 insertions, 2 deletions
diff --git a/experiments/conv_local_smoke.py b/experiments/conv_local_smoke.py
index e8cf294..887674e 100644
--- a/experiments/conv_local_smoke.py
+++ b/experiments/conv_local_smoke.py
@@ -8,10 +8,12 @@ import torch
import torch.nn.functional as F
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-from sdil.conv import (CIFARHierarchicalFAResNet, CIFARKPResNet,
- CIFARLocalResNet, CIFARSDILResNet, ConvSDILConfig,
+from sdil.conv import (CIFARHierarchicalFAResNet, CIFARKPMixedTrafficResNet,
+ CIFARKPResNet, CIFARLocalResNet, CIFARSDILResNet,
+ ConvSDILConfig,
channel_subspace_apical_calibration,
conv_hierarchical_step, conv_kolen_pollack_step,
+ conv_kp_mixed_traffic_step,
conv_local_step,
hierarchical_mirror_observations,
hierarchical_parameter_subspace_calibration,
@@ -937,6 +939,128 @@ def kolen_pollack_checks():
}
+def kp_mixed_traffic_checks():
+ """Mixed traffic isolates subtraction from norm and preserves KP locality."""
+ torch.manual_seed(211)
+ common = dict(
+ depth=8, base_width=2, seed=67, dtype=torch.float64,
+ normalization="batchnorm", residual_scale=1.0,
+ traffic_seed=4000)
+ net = CIFARKPMixedTrafficResNet(**common)
+ x = torch.randn(5, 3, 32, 32, dtype=torch.float64)
+ y = torch.tensor([0, 2, 4, 6, 8])
+ forward = net.forward(
+ x, return_cache=True, training=True, update_stats=False)
+ output_error = (torch.softmax(forward["logits"], dim=1)
+ - F.one_hot(y, 10).to(torch.float64))
+ instruction = net.hierarchical_teaching(output_error, forward)
+
+ # Zero traffic and zero predictor collapse all three rules to clean KP.
+ zero_errors = []
+ for rule in ("raw", "matched", "innovation"):
+ components = net.mixed_apical_components(
+ instruction, forward["hiddens"], rule)
+ zero_errors.extend(float((left - right).abs().max()) for left, right in
+ zip(components["used"], instruction))
+ assert max(zero_errors) == 0.0
+
+ calibration = net.calibrate_traffic_gain(
+ instruction, forward["hiddens"], target_ratio=4.0)
+ ratio_error = max(abs(value - 4.0) for value in
+ calibration["realized_traffic_instruction_rms_ratio"])
+ assert ratio_error < 1e-12
+
+ # An exact per-unit predictor removes all predictable traffic.
+ for slope, gain, coefficient in zip(
+ net.P_traffic, net.traffic_gain, net.B_traffic):
+ slope.copy_(gain * coefficient)
+ exact = net.mixed_apical_components(
+ instruction, forward["hiddens"], "innovation")
+ exact_predictor_error = max(float((left - right).abs().max())
+ for left, right in zip(
+ exact["innovation"], instruction))
+ assert exact_predictor_error < 1e-14
+
+ for slope, bias in zip(net.P_traffic, net.P_traffic_bias):
+ slope.zero_()
+ bias.zero_()
+ components = net.mixed_apical_components(
+ instruction, forward["hiddens"], "matched")
+ norm_errors = []
+ direction_errors = []
+ for raw, innovation, matched in zip(
+ components["raw"], components["innovation"],
+ components["matched"]):
+ raw_flat = raw.flatten(1)
+ innovation_flat = innovation.flatten(1)
+ matched_flat = matched.flatten(1)
+ norm_errors.append(float((
+ (matched_flat.norm(dim=1) - innovation_flat.norm(dim=1)).abs()
+ / innovation_flat.norm(dim=1).clamp_min(1e-30)).max()))
+ direction_errors.append(float((F.cosine_similarity(
+ raw_flat, matched_flat, dim=1) - 1.0).abs().max()))
+ assert max(norm_errors) < 1e-12
+ assert max(direction_errors) < 1e-12
+
+ # All used signals retain equal independently recomputed local KP products.
+ correlation_errors = []
+ for rule in ("raw", "matched", "innovation"):
+ used = net.mixed_apical_components(
+ instruction, forward["hiddens"], rule)["used"]
+ forward_directions, _, _, output_weight, _ = (
+ net.local_ascent_directions(used, output_error, forward))
+ reciprocal, reciprocal_readout = net.reciprocal_feedback_directions(
+ used, output_error, forward)
+ correlation_errors.extend(float((left - right).abs().max())
+ for left, right in zip(
+ forward_directions[1:], reciprocal[1:]))
+ correlation_errors.append(float(
+ (reciprocal_readout + output_weight.t()).abs().max()))
+ assert max(correlation_errors) < 1e-14
+
+ # Predictor plasticity consumes only the supplied soma/traffic pair: once
+ # those are fixed, changing every forward/feedback weight has no effect.
+ left = CIFARKPMixedTrafficResNet(**common)
+ right = CIFARKPMixedTrafficResNet(**common)
+ for left_gain, right_gain, source in zip(
+ left.traffic_gain, right.traffic_gain, net.traffic_gain):
+ left_gain.copy_(source)
+ right_gain.copy_(source)
+ fixed_hiddens = [value.detach().clone() for value in forward["hiddens"]]
+ for value in right.W + right.Q + [right.W_out, right.R_out]:
+ value.add_(torch.randn_like(value))
+ left.predictor_step(fixed_hiddens, eta=0.1)
+ right.predictor_step(fixed_hiddens, eta=0.1)
+ predictor_independence_error = max(float((a - b).abs().max())
+ for a, b in zip(
+ left.P_traffic + left.P_traffic_bias,
+ right.P_traffic + right.P_traffic_bias))
+ assert predictor_independence_error == 0.0
+
+ net.traffic_rule = "innovation"
+ result = conv_kp_mixed_traffic_step(
+ net, x, y, ConvSDILConfig(
+ eta=1e-4, eta_output=1e-4, eta_P=0.1, momentum=0.0,
+ weight_decay=0.0, learn_A=False, learn_P=True),
+ step=0, rule="innovation", predictor_every=16)
+ assert math.isfinite(result["loss"]) and result["did_predictor_update"]
+ assert all(not value.requires_grad for value in
+ net.W + net.Q + net.P_traffic + net.P_traffic_bias
+ + [net.W_out, net.R_out, net.b_out])
+ assert net.mixed_elementwise_ops_per_example("matched") > (
+ net.mixed_elementwise_ops_per_example("raw"))
+ return {
+ "kp_traffic_zero_limit_error": max(zero_errors),
+ "kp_traffic_ratio_error": ratio_error,
+ "kp_traffic_exact_predictor_error": exact_predictor_error,
+ "kp_traffic_matched_norm_error": max(norm_errors),
+ "kp_traffic_matched_direction_error": max(direction_errors),
+ "kp_traffic_reciprocal_correlation_error": max(correlation_errors),
+ "kp_traffic_predictor_parameter_independence_error": (
+ predictor_independence_error),
+ }
+
+
def apical_learning_checks():
torch.manual_seed(11)
net = CIFARSDILResNet(depth=8, base_width=2, seed=6)
@@ -1046,6 +1170,7 @@ def main():
report.update(hierarchical_parameter_calibration_checks())
report.update(normalized_response_mirror_checks())
report.update(kolen_pollack_checks())
+ report.update(kp_mixed_traffic_checks())
report.update(apical_learning_checks())
print(report)
print("ALL CONVOLUTIONAL LOCAL-ELIGIBILITY CHECKS PASSED")