diff --git a/crates/ml/tests/dqn_early_stopping_termination_test.rs b/crates/ml/tests/dqn_early_stopping_termination_test.rs index 757b68514..bbbaa557b 100644 --- a/crates/ml/tests/dqn_early_stopping_termination_test.rs +++ b/crates/ml/tests/dqn_early_stopping_termination_test.rs @@ -138,14 +138,12 @@ async fn test_early_stopping_terminates_with_error() { ml::training_profile::DqnTrainingProfile::load("dqn-smoketest").apply_to(&mut hyperparams); // Force gradient collapse detection by setting threshold above typical grad norm. - // Threshold = 1e-8 × 1e9 = 10.0 (always above typical grad norms ~0.5-2.0). - // RegimeConditionalDQN tracks collapse across epoch-boundary log_diagnostics() - // and per-step check_gradient_collapse() with a shared counter. - // With patience=3, collapse triggers during epoch 2 training steps - // (epoch 1 boundary increments to 1, per-step checks in epoch 2 reach 3). + // Threshold = lr × multiplier. With v_range ±240 (C51), gradient norms can reach + // 100-10000 depending on epoch/data. Set threshold = 1e-8 × 1e12 = 10000.0 to + // guarantee the norm stays below threshold → collapse triggers reliably. hyperparams.learning_rate = 1e-8; hyperparams.epochs = 10; - hyperparams.gradient_collapse_multiplier = 1e9; + hyperparams.gradient_collapse_multiplier = 1e12; hyperparams.gradient_collapse_patience = 3; hyperparams.min_epochs_before_stopping = 1; hyperparams.early_stopping_enabled = true; @@ -212,13 +210,14 @@ async fn test_gradient_collapse_propagates_error() { let mut hyperparams = DQNHyperparameters::conservative(); ml::training_profile::DqnTrainingProfile::load("dqn-smoketest").apply_to(&mut hyperparams); - // Force gradient collapse detection: threshold = lr × multiplier = 5e-9 × 1e9 = 5.0 - // Typical grad norms are ~0.5-2.0, so threshold 5.0 is always above → collapse triggers. + // Force gradient collapse detection: threshold = lr × multiplier = 5e-9 × 1e12 = 5000.0 + // With v_range ±240 (C51), grad norms can reach 100-10000. Threshold must be above + // the maximum expected norm to guarantee collapse detection fires. // GPU PER requires buffer_size >= 1024. Warmup = 1024 × 0.2 = 204 steps. // max_training_steps_per_epoch=300 clears warmup in epoch 1 (300 > 204). hyperparams.learning_rate = 5e-9; hyperparams.epochs = 10; - hyperparams.gradient_collapse_multiplier = 1e9; + hyperparams.gradient_collapse_multiplier = 1e12; hyperparams.gradient_collapse_patience = 3; hyperparams.min_epochs_before_stopping = 1; hyperparams.early_stopping_enabled = true; diff --git a/crates/ml/tests/dqn_training_smoke_test.rs b/crates/ml/tests/dqn_training_smoke_test.rs index 68a83d860..49dc63e28 100644 --- a/crates/ml/tests/dqn_training_smoke_test.rs +++ b/crates/ml/tests/dqn_training_smoke_test.rs @@ -355,6 +355,10 @@ async fn test_dqn_training_smoke() -> Result<()> { dqn_config.use_iqn = false; dqn_config.epsilon_start = 0.3; dqn_config.epsilon_end = 0.01; + // Walk-forward uses raw price returns (~0.001), NOT the production reward + // function (scale 10.0). Override v_range to match this simpler reward scale. + dqn_config.v_min = -10.0; + dqn_config.v_max = 10.0; let mut strategy = DqnStrategy::new(dqn_config)?; let report = harness.validate(&mut strategy, &ts_data)?;