diag: log refreshed-basis overlap with prior basis per v_hack refresh

cin_t collapses from ~0.3 to ~0.04 exactly at the first refresh step on the
4B substrate route run. Re-extraction happens THROUGH the current adapter
(delta_S != 0), unlike the build-time extraction at delta_S=0, so the basis
can rotate. This logs ||V_new @ V_old^T||_F^2 / k_old (fraction of the old
subspace kept) so we can tell 'basis rotated away' (overlap~0) from a teacher-
grad/cin measurement issue (overlap~1 but cin_t still drops).

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
This commit is contained in:
wassname
2026-05-30 23:46:33 +00:00
co-authored by Claudypoo
parent b89e0e62f7
commit 23589cb960
+15
View File
@@ -1443,6 +1443,21 @@ def main(cfg: Config) -> int:
finally:
logger.enable("projected_grpo.extract_vhack_grad")
logger.enable("__main__")
# DIAGNOSTIC: how far did the refreshed basis rotate from the prior one?
# Rows are orthonormal, so ||V_new @ V_old^T||_F^2 / k_old = fraction of
# the OLD subspace still spanned by the NEW basis, in [0,1].
# ~1 -> refresh tracks a stable hack subspace (the design's premise)
# ~0 -> re-extraction at current weights landed near-orthogonal, so the
# live grad's overlap (cin_t) jumps discontinuously at the refresh.
shared = set(v_hack) & set(_post)
ovl = [((_post[n].float().to(device) @ v_hack[n].float().mT)).pow(2).sum().item()
/ v_hack[n].shape[0] for n in shared]
overlap = sum(ovl) / max(1, len(ovl))
logger.info(
f"refresh@step{step}: {len(_post)}mod/{sum(V.shape[0] for V in _post.values())}ax "
f"basis_overlap_with_prev={overlap:.3f} "
f"SHOULD: >~0.5 if refresh tracks a stable hack subspace; <~0.2 => "
f"re-extraction rotated the basis (cin_t jumps, refresh is harmful)")
v_hack.clear()
v_hack.update({n: V.to(device) for n, V in _post.items()})
opt.zero_grad(set_to_none=True) # extract leaves .grad populated