# Two-Phase Hyperopt: Search Quality over Speed ## Goal Split the 31D PSO search into two sequential phases: first optimize learning dynamics with a fixed small network, then optimize architecture with the best dynamics locked in. Better search quality from lower-dimensional spaces, with speed as a bonus. ## Problem The current 31D search mixes learning dynamics (lr, gamma, tau) with architecture (hidden_dim, num_atoms). A bad lr makes ALL network sizes look bad — PSO wastes evaluations on redundant lr×hidden_dim combinations. Halving the dimensionality at each phase dramatically improves convergence. ## Architecture ### Phase 1: Learning Dynamics (fast, ~30s/trial) **Fixed**: `hidden_dim_base=128`, `num_atoms=11`, `branch_hidden_dim=64` **Search** (~15D): learning_rate, gamma, tau, batch_size, buffer_size, per_alpha, per_beta_start, entropy_coefficient, cql_alpha, weight_decay, noisy_sigma_init, n_steps, kelly_fractional, dsr_eta, iqn_lambda **Config**: 50 trials × 30 epochs at ~173ms/epoch = 5s training + 30s data/backtest = ~35s/trial. Total: ~30 min. **Output**: Best learning dynamics config (JSON). ### Phase 2: Architecture (production speed, ~2 min/trial) **Fixed**: Best lr, gamma, tau, PER params from Phase 1 **Search** (~5D): hidden_dim_base [128-512], num_atoms [11-51], branch_hidden_dim [64-256], dueling_hidden_dim [128-512], v_range [10-50] **Config**: 20 trials × 50 epochs at production speed. Total: ~40 min. **Output**: Best full config (JSON) for production training. ## Implementation ### CLI changes to `hyperopt_baseline_rl` Add `--phase` argument: ``` --phase fast # Phase 1: fix architecture, search dynamics (default) --phase full # Phase 2: fix dynamics from --hyperopt-params, search architecture --phase single # Current behavior: search everything in one pass ``` Add `--fix-hidden-dim ` to override hidden_dim_base for Phase 1. Add `--fix-num-atoms ` to override num_atoms for Phase 1. ### Hyperopt adapter changes In `DQNParams::continuous_bounds()`: - If phase=fast: fix hidden_dim_base, num_atoms, branch_hidden_dim to constants. Their bounds become `(value, value)` — single points in the search space. - If phase=full: fix learning dynamics from the Phase 1 JSON. Load best params, set their bounds to `(best, best)`. This requires NO changes to the PSO algorithm — fixed params are just single-point bounds. The optimizer trivially converges on them and focuses search on the free dimensions. ### Config changes Add to `config/training/dqn-hyperopt.toml`: ```toml [phase_fast] hidden_dim_base = 128 num_atoms = 11 branch_hidden_dim = 64 [phase_full] # Loaded from --hyperopt-params JSON (Phase 1 output) ``` ### Workflow ```bash # Phase 1: find best learning dynamics (~30 min) cargo run --example hyperopt_baseline_rl -- \ --model dqn --phase fast --trials 50 --epochs 30 \ --data-dir data/futures-baseline --symbol ES.FUT \ --output phase1_results.json # Phase 2: find best architecture with locked dynamics (~40 min) cargo run --example hyperopt_baseline_rl -- \ --model dqn --phase full --trials 20 --epochs 50 \ --hyperopt-params phase1_results.json \ --data-dir data/futures-baseline --symbol ES.FUT \ --output phase2_results.json # Production training with best config cargo run --example train_baseline_rl -- \ --model dqn --epochs 100 \ --hyperopt-params phase2_results.json \ --data-dir data/futures-baseline --symbol ES.FUT ``` ## Non-Goals - Automatic phase chaining (user runs Phase 1, reviews, then Phase 2) - Multi-objective optimization (Sharpe + stability) - Architecture search beyond hidden dims (layer count, residual connections)