diff --git a/crates/ml/src/trainers/dqn/config.rs b/crates/ml/src/trainers/dqn/config.rs index d938b9503..b9e5ae9e5 100644 --- a/crates/ml/src/trainers/dqn/config.rs +++ b/crates/ml/src/trainers/dqn/config.rs @@ -931,6 +931,13 @@ pub struct DQNHyperparameters { /// Prevents memorization of fixed simulation parameters. pub enable_domain_randomization: bool, + /// Periodic shrink-and-perturb interval (0=disabled, 20=every 20 epochs) + pub shrink_perturb_interval: usize, + /// Shrink-and-perturb alpha (0.85 = keep 85%, reinit 15%) + pub shrink_perturb_alpha: f64, + /// Shrink-and-perturb noise scale (Xavier) + pub shrink_perturb_sigma: f64, + // Wave 16 Portfolio Features /// Enable action masking (filters invalid actions based on position limits) pub enable_action_masking: bool, @@ -1413,6 +1420,10 @@ impl DQNHyperparameters { // Generalization: domain randomization (per-epoch jitter on sim params) enable_domain_randomization: true, // Default: enabled (prevents memorization of fixed sim params) + // Generalization: periodic shrink-and-perturb (kills memorized weights) + shrink_perturb_interval: 20, // Every 20 epochs (0=disabled) + shrink_perturb_alpha: 0.85, // Keep 85% of weights, reinit 15% + shrink_perturb_sigma: 0.01, // Xavier noise scale // Wave 16 Portfolio Features (default: ALL ENABLED) enable_action_masking: true, // Default: action masking enabled diff --git a/crates/ml/src/trainers/dqn/trainer/training_loop.rs b/crates/ml/src/trainers/dqn/trainer/training_loop.rs index 99846daec..5b9c72403 100644 --- a/crates/ml/src/trainers/dqn/trainer/training_loop.rs +++ b/crates/ml/src/trainers/dqn/trainer/training_loop.rs @@ -262,6 +262,19 @@ impl DQNTrainer { )); } + // ── Periodic shrink-and-perturb: kill memorized weights ── + let sp_interval = self.hyperparams.shrink_perturb_interval; + if sp_interval > 0 && epoch > 0 && epoch % sp_interval == 0 { + if let Some(ref mut fused) = self.fused_ctx { + let alpha = self.hyperparams.shrink_perturb_alpha as f32; + let sigma = self.hyperparams.shrink_perturb_sigma as f32; + match fused.shrink_and_perturb(alpha, sigma) { + Ok(()) => info!(epoch, alpha, sigma, "Periodic shrink-and-perturb applied"), + Err(e) => warn!(epoch, "Shrink-and-perturb failed (non-fatal): {e}"), + } + } + } + // ── Phase 3: Batched training from replay buffer ── let phase3_start = std::time::Instant::now(); let train_step_count = self.run_training_steps(training_data).await?;