E13b/c: harden real-weight speciation — full symmetry group + emergent-divergence null
E13c (the symmetry defense): alignment now runs modulo the FULL function-preserving unit symmetry group of a ReLU MLP (per-unit positive rescaling via canonicalise_scale, composed with Re-Basin permutations; sanity gate recovers a permuted-and-rescaled copy exactly). Verdict: the full group removes the independent-init barrier (residual 0.001) and essentially none of the conflict barrier (0.502 -> 0.497) — the residual is functional, not a missed symmetry (answers arXiv:2606.23607). The cliff gains a hybrid-fitness readout: merged accuracy 0.97 -> 0.03 with conflict. Floor proposition drafted (paper/si-notes.md S1): endpoint invariance + max(eps_A, eps_B) >= mu(S)/2 for any merged model under any alignment group. E13b (emergent divergence): pre-registered second reading — with NO conflicting training signal (disjoint class specialists; rolled-input conventions), residual is 0.000 at every divergence to t_div=3200, and the merge RESCUES the forgetting specialists (parents 0.535/0.474 -> merged 0.955; a sustained Fisher-Muller rescue at zero barrier). Speciation in real weights requires functional conflict; it does not emerge from compatible specialisation on shared ancestry. LLM-scale over-specialisation (cf. 2607.11997) deferred to Phase-3 llm_speciation. 3-panel figure, READMEs, +2 tests (149 green), make mnist wired. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BkRLcc18rwT2Lysu6PbG7v
This commit is contained in:
parent
72d5e9e736
commit
ea051a5f92
15 changed files with 435 additions and 77 deletions
|
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from neural.rebasin import apply_perms, barrier, interpolate, weight_matching
|
||||
from neural.rebasin import apply_perms, barrier, canonicalise_scale, interpolate, weight_matching
|
||||
|
||||
|
||||
def _mlp(sizes, rng):
|
||||
|
|
@ -64,3 +64,44 @@ def test_apply_perms_preserves_function():
|
|||
perms = [rng.permutation(7), rng.permutation(7)]
|
||||
X = rng.standard_normal((16, 4))
|
||||
assert np.allclose(_forward(A, X), _forward(apply_perms(A, perms), X))
|
||||
|
||||
|
||||
def _rescale(params, scales_per_layer):
|
||||
# Apply the positive per-unit rescaling symmetry: unit i of hidden layer k scaled by c>0.
|
||||
out = [(W.copy(), b.copy()) for W, b in params]
|
||||
for k, scales in enumerate(scales_per_layer):
|
||||
W, b = out[k]
|
||||
out[k] = (W * scales[:, None], b * scales)
|
||||
Wn, bn = out[k + 1]
|
||||
out[k + 1] = (Wn / scales[None, :], bn)
|
||||
return out
|
||||
|
||||
|
||||
def test_canonicalise_scale_preserves_function_and_normalises():
|
||||
rng = np.random.default_rng(6)
|
||||
A = _mlp([5, 9, 9, 2], rng)
|
||||
C = canonicalise_scale(A)
|
||||
X = rng.standard_normal((24, 5))
|
||||
assert np.allclose(_forward(A, X), _forward(C, X), atol=1e-8) # function-preserving (ReLU homogeneity)
|
||||
for k in range(len(C) - 1): # every hidden unit's (W, b) is unit-norm
|
||||
W, b = C[k]
|
||||
assert np.allclose(np.sqrt((W ** 2).sum(axis=1) + b ** 2), 1.0)
|
||||
|
||||
|
||||
def test_weight_matching_recovers_permutation_and_rescaling():
|
||||
# A permuted AND positively-rescaled copy is functionally identical; permutation-only matching can
|
||||
# miss it, but canonicalise-then-match must realign it to functional identity — the full ReLU
|
||||
# symmetry group (the E13c referee-proofing gate).
|
||||
rng = np.random.default_rng(7)
|
||||
A = _mlp([6, 12, 12, 3], rng)
|
||||
B = apply_perms(_rescale(A, [np.exp(rng.uniform(-2, 2, 12)), np.exp(rng.uniform(-2, 2, 12))]),
|
||||
[rng.permutation(12), rng.permutation(12)])
|
||||
X = rng.standard_normal((32, 6))
|
||||
assert np.allclose(_forward(A, X), _forward(B, X), atol=1e-6) # symmetry-equivalent copy
|
||||
cA, cB = canonicalise_scale(A), canonicalise_scale(B)
|
||||
perms = weight_matching(cA, cB, np.random.default_rng(8))
|
||||
B_aligned = apply_perms(cB, perms)
|
||||
assert np.allclose(_forward(cA, X), _forward(B_aligned, X), atol=1e-5) # realigned exactly
|
||||
# and the aligned weights themselves coincide (canonical form is unique up to permutation)
|
||||
for (Wa, ba), (Wb, bb) in zip(cA, B_aligned):
|
||||
assert np.allclose(Wa, Wb, atol=1e-6) and np.allclose(ba, bb, atol=1e-6)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue