From 23589cb960b7637d0b989c46d4e6eb913d60ae9a Mon Sep 17 00:00:00 2001 From: wassname Date: Sat, 30 May 2026 23:46:33 +0000 Subject: [PATCH] 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> --- src/projected_grpo/train.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/projected_grpo/train.py b/src/projected_grpo/train.py index 51e1b4b..2c822c9 100644 --- a/src/projected_grpo/train.py +++ b/src/projected_grpo/train.py @@ -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