From 1961857c2267ec8b74c808a1feafdee94de674ce Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Tue, 21 Apr 2026 09:57:04 +0200 Subject: [PATCH] =?UTF-8?q?gem(G6+G10):=20measurement=20says=20redundant?= =?UTF-8?q?=20=E2=80=94=20unwire=20(V7=20methodology=20applied)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Empirical data from commit 61ab27ff3 across 14 captured epochs of the TD-propagation smoke test: HEALTH_DIAG[N]: ... gems [g6_branch_indep=X g10_temporal=Y g12_predictive=Z] G10 temporal_consistency: 0.000000 to 0.000002 (essentially zero) G6 branch_independence: 0.000102 to 0.000273 (~1e-4) G12 predictive_coding: 0.07 to 9657.83 (real signal) Verdict by V7-gem methodology — when an existing mechanism already covers the signal, wiring backward adds gradient noise without value: G10: spectral_norm (already wired on all 12 weight tensors) enforces a global Lipschitz constraint that subsumes per-pair Lipschitz on similar-state pairs. Cosine similarity between consecutive h_s2 samples is almost never > 0.95 anyway. Penalty value is below floating-point noise. G6: NoisyNets injects different parameter noise per layer; the 4 advantage branches are already 99.99% diverse. Penalty value is 4 orders of magnitude smaller than G12's working signal. G12: KEEP — backward already wired in commit e72885e8b, demonstrably reduced Best Sharpe variance from [13, 22] to [17.99, 19.56] (~6× tighter). Real gem. Changes: - Removed compute_branch_independence + compute_temporal_consistency calls from submit_aux_ops (no per-step kernel launches for G6/G10) - Removed g6/g10 columns from HEALTH_DIAG (kept g12) - Added comment block in submit_aux_ops documenting WHY they were measured-then-removed (V7 methodology trail for future-readers) What stays for now (deletable in a follow-up cleanup): - branch_independence_penalty kernel + branch_indep_kernel field + branch_indep_penalty_buf alloc - temporal_consistency_penalty kernel + temporal_consistency_kernel field + temporal_per_sample_buf + temporal_penalty_buf allocs - branch_indep_loss_value() / temporal_loss_value() readback methods - read_gem_losses() pass-through (now returns (g6=0, g10=0, g12=value)) - compute_branch_independence + compute_temporal_consistency Rust fns Keeping these as scaffolding means: if future evidence (different env, different scale, different model) shows the underlying signal IS material in some regime, re-wiring is just adding the call back to submit_aux_ops. The measurement infrastructure stays in place. Files touched: crates/ml/src/trainers/dqn/fused_training.rs (-12 / +14) crates/ml/src/trainers/dqn/trainer/training_loop.rs (-13 / +9) Verified: cargo check passes. Smoke test should now match the G12-only baseline variance of [17.99, 19.56] (next session can verify). Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/ml/src/trainers/dqn/fused_training.rs | 25 +++++++++++-------- .../src/trainers/dqn/trainer/training_loop.rs | 16 ++++++------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/crates/ml/src/trainers/dqn/fused_training.rs b/crates/ml/src/trainers/dqn/fused_training.rs index 98e5c48c2..321ccca6d 100644 --- a/crates/ml/src/trainers/dqn/fused_training.rs +++ b/crates/ml/src/trainers/dqn/fused_training.rs @@ -1479,17 +1479,20 @@ impl FusedTrainingCtx { self.trainer.compute_predictive_coding_loss(self.batch_size) .map_err(|e| anyhow::anyhow!("Predictive coding (G12): {e}"))?; - // G6 + G10 forward-only (measurement). Both kernels are now graph-safe - // (G6: single-block direct overwrite; G10: per-sample → c51_loss_reduce). - // No backward yet — wired only to log the penalty magnitudes via - // HEALTH_DIAG. Decision on backward integration depends on whether - // the measured penalty is meaningfully larger than what existing - // mechanisms (spectral_norm for G10, NoisyNets/ensemble-KL for G6) - // already enforce. V7-gem methodology: measure before commit. - self.trainer.compute_branch_independence(self.batch_size) - .map_err(|e| anyhow::anyhow!("Branch independence (G6 measure): {e}"))?; - self.trainer.compute_temporal_consistency(self.batch_size) - .map_err(|e| anyhow::anyhow!("Temporal consistency (G10 measure): {e}"))?; + // G6 + G10 measurement (commit 61ab27ff3) found both penalties are + // dominated by existing mechanisms: + // G10 temporal_consistency: 0.000000–0.000002 across 14 epochs. + // Cosine similarity between consecutive h_s2 samples almost never + // exceeds the 0.95 threshold; spectral_norm already enforces a + // global Lipschitz constraint that subsumes per-pair Lipschitz. + // G6 branch_independence: ~1e-4 (orders of magnitude smaller than G12). + // NoisyNets injects different parameter noise per layer → the 4 + // advantage branches are already ~99.99% diverse without explicit + // cosine-similarity penalty. + // + // Per V7-gem methodology: when the existing mechanisms cover the signal, + // wiring backward adds gradient noise without value. Both calls removed + // here; the kernels and Rust glue are deleted in a follow-up cleanup. // Ensemble diversity: heads 1..K-1 forward + KL gradient + trunk backward. // Runs inside aux_child graph — gradients land in grad_buf before Adam. diff --git a/crates/ml/src/trainers/dqn/trainer/training_loop.rs b/crates/ml/src/trainers/dqn/trainer/training_loop.rs index 8e9c4f277..0ff8e8167 100644 --- a/crates/ml/src/trainers/dqn/trainer/training_loop.rs +++ b/crates/ml/src/trainers/dqn/trainer/training_loop.rs @@ -2148,16 +2148,16 @@ impl DQNTrainer { // diag = pure monitoring signals (not in reward): sharpe EMA (formerly w_dsr // reward input) and action entropy normalized to [0,1] (formerly // position_entropy_weight reward). See Phase 1 inventory better-form taxonomy. - // gems = G6/G10/G12 gem-penalty values (sync DtoH from GPU; once per epoch). - // V7 methodology: log first to measure whether the gem is doing real work - // before deciding to wire its backward gradient. - let (g6_loss, g10_loss, g12_loss) = if let Some(ref fused) = self.fused_ctx { - fused.read_gem_losses() + // gems = G12 predictive_coding loss (active in training). G6 + G10 + // were measured (commit 61ab27ff3) and found redundant with existing + // mechanisms (NoisyNets, spectral_norm); their readbacks were removed. + let g12_loss = if let Some(ref fused) = self.fused_ctx { + fused.read_gem_losses().2 // (g6, g10, g12) — keep g12 only } else { - (0.0, 0.0, 0.0) + 0.0 }; tracing::info!( - "HEALTH_DIAG[{}]: health={:.2} components [q_gap={:.2} q_var={:.2} atoms={:.2} grad_stable={:.2} ens_agree={:.2} grad_cos={:.2} spectral={:.2}] effective [cql_alpha={:.4} iqn_budget={:.2} cql_budget={:.2} c51_budget={:.2} tau={:.5} sarsa_tau={:.2} gamma={:.3} cf_ratio={:.2}] novels [distill={} barrier={:.3} plasticity={} ib={:.3} ensemble_collapse={:.2} contrarian={} meta_q_pred={:.2}] diag [sharpe_ema={:.3} action_entropy={:.2}] gems [g6_branch_indep={:.6} g10_temporal={:.6} g12_predictive={:.6}]", + "HEALTH_DIAG[{}]: health={:.2} components [q_gap={:.2} q_var={:.2} atoms={:.2} grad_stable={:.2} ens_agree={:.2} grad_cos={:.2} spectral={:.2}] effective [cql_alpha={:.4} iqn_budget={:.2} cql_budget={:.2} c51_budget={:.2} tau={:.5} sarsa_tau={:.2} gamma={:.3} cf_ratio={:.2}] novels [distill={} barrier={:.3} plasticity={} ib={:.3} ensemble_collapse={:.2} contrarian={} meta_q_pred={:.2}] diag [sharpe_ema={:.3} action_entropy={:.2}] gems [g12_predictive={:.4}]", epoch, health_value, self.learning_health.components.q_gap_norm, @@ -2184,8 +2184,6 @@ impl DQNTrainer { self.last_meta_q_pred.unwrap_or(0.0), self.training_sharpe_ema, self.last_action_entropy.unwrap_or(1.0), - g6_loss, - g10_loss, g12_loss, );