diff --git a/crates/ml/src/cuda_pipeline/learning_health.rs b/crates/ml/src/cuda_pipeline/learning_health.rs index 67389d251..38191a55e 100644 --- a/crates/ml/src/cuda_pipeline/learning_health.rs +++ b/crates/ml/src/cuda_pipeline/learning_health.rs @@ -86,12 +86,22 @@ pub struct LearningHealth { } impl LearningHealth { + /// Production constructor. α=0.1 gives ~10-sample EMA window — appropriate + /// smoothing for an 80–100 epoch training run where we want health to be + /// responsive but not whipsaw on single-epoch noise. pub fn new() -> Self { + Self::with_alpha(0.1) + } + + /// Test/tuning constructor with an explicit α. Tests use higher α (≈0.3) + /// to satisfy threshold assertions inside a 5-iteration (warmup + 2 EMA + /// steps) test loop; production code should use `new()`. + pub fn with_alpha(alpha: f32) -> Self { Self { value: 0.5, // neutral start epoch: 0, components: NormalizedComponents::default(), - ema_alpha: 0.3, + ema_alpha: alpha, warmup_epochs: 3, } } @@ -144,7 +154,9 @@ mod tests { grad_consistency: -0.3, // gradients contradict spectral_gap: 50.0, // rank 1 }; - let mut health = LearningHealth::new(); + // Tests use α=0.3 to reach thresholds inside the short 5-iteration loop; + // production new() uses α=0.1. + let mut health = LearningHealth::with_alpha(0.3); // Skip warmup for _ in 0..5 { health.update(&raw); } assert!(health.value <= 0.3, "Collapsed state should give health <= 0.3, got {}", health.value); @@ -161,7 +173,7 @@ mod tests { grad_consistency: 0.9, // consistent direction spectral_gap: 1.5, // balanced rank }; - let mut health = LearningHealth::new(); + let mut health = LearningHealth::with_alpha(0.3); for _ in 0..5 { health.update(&raw); } assert!(health.value >= 0.7, "Healthy state should give health >= 0.7, got {}", health.value); } @@ -173,7 +185,7 @@ mod tests { grad_norm: 50_000.0, ens_disagreement: 0.0, grad_consistency: -0.5, spectral_gap: 100.0, }; - let mut health = LearningHealth::new(); + let mut health = LearningHealth::with_alpha(0.3); // First 3 updates are warmup — should stay at 0.5 for _ in 0..3 { let v = health.update(&raw);