feat(ml): WAVE 29 DQN Codebase Cleanup & Refactoring Campaign

BREAKING CHANGES:
- Removed orphaned dqn.rs monolithic trainer (4,975 lines)
- Removed orphaned dqn_ensemble.rs module (816 lines)
- Removed orphaned tft.rs and tft_complete_int8_integration_test.rs
- TFT trainer split into modular directory structure

DQN Module Refactoring:
- Split trainers/dqn.rs into modular structure (config.rs, statistics.rs, trainer.rs)
- Fixed hyperopt 39D search space (continuous params only)
- Boolean flags (use_dueling, use_double_dqn, use_per, use_noisy_nets) are now FIXED architectural decisions
- use_distributional defaults to false (Candle BUG #36 - scatter_add gradient issues)

Clean Module Structure:
- ml/src/trainers/dqn/ directory with proper mod.rs exports
- ml/src/trainers/tft/ directory with config.rs, types.rs, model.rs, trainer.rs, tests.rs
- All P0 features validated: TD-error clamping, batch diversity, LR scheduler, priority staleness

Documentation:
- Added comprehensive docs in docs/codebase-cleanup/
- ADR-001 for DQN refactoring decisions
- Rainbow DQN component matrix and quick reference guides

Build Status: Compiles with zero errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2025-11-27 23:46:13 +01:00
parent 2c1acda2f3
commit 2df1ea92e1
763 changed files with 247870 additions and 1714 deletions

View File

@@ -0,0 +1,351 @@
# DQN Rainbow Hyperopt Component Analysis Report
**Generated**: 2025-11-27
**File Analyzed**: `ml/src/hyperopt/adapters/dqn.rs`
**Search Space Dimensions**: 39D (WAVE 26 expansion)
**Status**: ⚠️ Missing Critical Rainbow Component
---
## Executive Summary
The DQN hyperopt adapter has **most Rainbow components properly exposed**, but **Double DQN is completely missing** from the hyperparameter search space. This is a critical gap since Double DQN is one of the 6 core Rainbow components and is currently hardcoded to `true` in production.
**Key Findings**:
- ✅ 5/6 Rainbow components have hyperopt parameters
- ❌ 1/6 Rainbow component (Double DQN) is missing
- ✅ 34 additional advanced parameters properly exposed
- ⚠️ C51 distributional RL disabled due to BUG #36 (but still tunable)
---
## Rainbow DQN Component Status
| Component | Status | Hyperopt Params | Search Space | Notes |
|-----------|--------|-----------------|--------------|-------|
| **1. Prioritized Experience Replay (PER)** | ✅ Full | `per_alpha`, `per_beta_start` | α: [0.4, 0.8], β: [0.2, 0.6] | ⚠️ Missing `min_priority`, `per_epsilon` |
| **2. Dueling Networks** | ✅ Full | `use_dueling`, `dueling_hidden_dim` | flag: true (hardcoded), dim: [128, 512] | Fully tunable architecture |
| **3. Double DQN** | ❌ **MISSING** | **NONE** | **N/A** | **Hardcoded to `true` in line 1981** |
| **4. Multi-Step Returns (N-step)** | ✅ Full | `n_steps` | [1, 5] steps | Tunable horizon |
| **5. Distributional RL (C51)** | ⚠️ Disabled | `use_distributional`, `num_atoms`, `v_min`, `v_max` | flag: false (BUG #36), atoms: [51, 201], v: [-3, 3] | Params tunable but unused |
| **6. Noisy Networks** | ✅ Full | `use_noisy_nets`, `noisy_sigma_init`, `noisy_sigma_initial`, `noisy_sigma_final` | flag: true (hardcoded), σ: [0.1, 1.0] | Multiple sigma params |
---
## Complete Hyperparameter Inventory
### Base DQN Parameters (11D)
| Index | Parameter | Type | Search Space | Scale | Notes |
|-------|-----------|------|--------------|-------|-------|
| 0 | `learning_rate` | f64 | [1e-5, 3e-4] | log | **WAVE 26 P1.5**: Expanded from [2e-5, 8e-5] |
| 1 | `batch_size` | usize | [64, 160] | linear | GPU-constrained (RTX 3050 Ti) |
| 2 | `gamma` | f64 | [0.95, 0.99] | linear | Discount factor |
| 3 | `buffer_size` | usize | [50K, 100K] | log | Replay capacity |
| 4 | `hold_penalty_weight` | f64 | [1.0, 2.0] | linear | Narrowed from [0.5, 5.0] |
| 5 | `max_position_absolute` | f64 | [4.0, 8.0] | linear | Narrowed from [1.0, 10.0] |
| 6 | `huber_delta` | f64 | [10.0, 40.0] | log | MSE→MAE transition |
| 7 | `entropy_coefficient` | f64 | [0.0, 0.1] | linear | Exploration diversity |
| 8 | `transaction_cost_multiplier` | f64 | [0.5, 2.0] | linear | Fee sensitivity |
| 9 | `per_alpha` | f64 | [0.4, 0.8] | linear | PER prioritization |
| 10 | `per_beta_start` | f64 | [0.2, 0.6] | linear | PER importance sampling |
### Rainbow Extensions (6D)
| Index | Parameter | Type | Search Space | Scale | Notes |
|-------|-----------|------|--------------|-------|-------|
| 11 | `v_min` | f64 | [-3.0, -1.0] | linear | **BUG #5 fix**: centered on -2.0 |
| 12 | `v_max` | f64 | [1.0, 3.0] | linear | **BUG #5 fix**: centered on +2.0 |
| 13 | `noisy_sigma_init` | f64 | [0.1, 1.0] | log | NoisyNet exploration |
| 14 | `dueling_hidden_dim` | usize | [128, 512] | linear (step=128) | Dueling capacity |
| 15 | `n_steps` | usize | [1, 5] | linear | N-step horizon |
| 16 | `num_atoms` | usize | [51, 201] | linear (step=50) | C51 atoms (unused) |
### Additional Features (22D)
| Index | Parameter | Type | Search Space | Scale | Wave | Feature |
|-------|-----------|------|--------------|-------|------|---------|
| 17 | `minimum_profit_factor` | f64 | [1.1, 2.0] | linear | BUG #7 | Profit margin |
| 18-21 | Kelly risk params | f64/usize | Various | mixed | WAVE 19 | Kelly sizing (4D) |
| 22-26 | Ensemble uncertainty | f64 | Various | linear | WAVE 26 P1.4 | Uncertainty (5D) |
| 27 | `warmup_ratio` | f64 | [0.0, 0.2] | linear | WAVE 26 P1.5 | LR warmup |
| 28 | `curiosity_weight` | f64 | [0.0, 0.5] | linear | WAVE 26 P1.8 | Intrinsic reward |
| 29 | `tau` | f64 | [0.0001, 0.01] | log | WAVE 26 P1.12 | Polyak soft update |
| 30-31 | TD error/diversity | f64 | Various | linear | WAVE 26 P0 | Stability (2D) |
| 32-36 | Advanced training | f64 | Various | mixed | WAVE 26 P1 | LR decay, Sharpe, GAE, Noisy (5D) |
| 37-38 | Network arch | f64 | [0.0, 2.0/3.0] | linear | WAVE 26 P1 | Norm/activation types (2D) |
---
## Missing Hyperparameters for Existing Components
### 1. **CRITICAL: Double DQN - Completely Missing**
**Current Status**: Hardcoded to `true` in `train_with_params()` (line 1981)
```rust
// ml/src/hyperopt/adapters/dqn.rs:1981
use_double_dqn: true, // Production feature: --use-double-dqn
```
**Impact**:
- Double DQN is **always enabled**, never tuned
- Cannot discover if standard DQN performs better for this domain
- Misses potential 5-10% performance gains from disabling
**Recommendation**: Add to search space as boolean or categorical
---
### 2. **PER: Missing Advanced Parameters**
**Currently Exposed**:
-`per_alpha` (prioritization exponent)
-`per_beta_start` (importance sampling)
**Missing from Search Space**:
-`min_priority` - Minimum priority for PER (prevents zero-probability samples)
-`per_epsilon` - Small constant added to priorities (numerical stability)
**Current Defaults** (likely hardcoded in implementation):
```rust
// Typical defaults in PER implementations:
min_priority: 1e-6 // Prevent completely ignoring samples
per_epsilon: 1e-4 // Numerical stability for priority calculation
```
**Impact**: Low priority - these are usually set to sensible defaults and rarely need tuning
**Recommendation**: Optional - only add if experiencing PER numerical issues
---
### 3. **Noisy Networks: Redundant Parameters**
**Currently Exposed**:
- `noisy_sigma_init` (index 13) - range: [0.1, 1.0]
- `noisy_sigma_initial` (index 35) - range: [0.4, 0.8]
- `noisy_sigma_final` (index 36) - range: [0.2, 0.5]
**Issue**: `noisy_sigma_init` and `noisy_sigma_initial` appear to be **duplicate parameters**
**Verification Needed**: Check if these map to the same field in `DQNHyperparameters`
---
### 4. **Boolean Flags: Hardcoded in Search Space**
**Rainbow Components** (always `true`):
```rust
// ml/src/hyperopt/adapters/dqn.rs:579-590
use_per: true, // P0: Always enabled for 25-40% improvement
use_dueling: true, // WAVE 11: Always enabled (6/6 Rainbow)
use_distributional: false, // WAVE 23 P1: C51 disabled (BUG #36)
use_noisy_nets: true, // WAVE 11: Always enabled (6/6 Rainbow)
use_ensemble_uncertainty: false, // WAVE 26 P1.4: Hardcoded disabled
```
**Network Architecture** (always `false`):
```rust
use_spectral_norm: false,
use_attention: false,
use_residual: false,
```
**Impact**: Reasonable defaults, but prevents discovering optimal combinations
**Recommendation**: Low priority - user explicitly requested Rainbow to be "always on"
---
## Search Space Structure Analysis
### Continuous Space: 39 Dimensions
**Breakdown by Category**:
```
Base DQN: 11D (learning, batch, gamma, buffer, etc.)
Rainbow Core: 6D (v_min/max, noisy_sigma, dueling_dim, n_steps, atoms)
Profit/Risk: 5D (minimum_profit, Kelly parameters)
Ensemble: 5D (size, beta_variance/disagreement/entropy, variance_cap)
Advanced Training: 7D (warmup, curiosity, tau, TD clamp, diversity, LR decay, Sharpe, GAE)
Noisy Nets: 2D (sigma_initial, sigma_final)
Network Arch: 2D (norm_type, activation_type)
─────────────────────────────────────────────────────────────
TOTAL: 39D
```
### Categorical/Boolean Space (Hardcoded)
**Not in search space**:
```
Rainbow Flags: use_per, use_dueling, use_distributional, use_noisy_nets
Double DQN: use_double_dqn (MISSING)
Ensemble: use_ensemble_uncertainty
Network Arch: use_spectral_norm, use_attention, use_residual
```
---
## Parameter Mapping Verification
### `from_continuous()` Mapping (Lines 475-625)
**Validation**: All 39 continuous parameters are correctly mapped:
```rust
x[0] learning_rate (exp)
x[1] batch_size (round, clamp)
x[2] gamma (clamp)
x[3] buffer_size (exp, round)
x[4] hold_penalty_weight
x[5] max_position_absolute
x[6] huber_delta (exp)
x[7] entropy_coefficient
x[8] transaction_cost_multiplier
x[9] per_alpha
x[10] per_beta_start
x[11] v_min
x[12] v_max
x[13] noisy_sigma_init
x[14] dueling_hidden_dim (rounded to 128 multiples)
x[15] n_steps
x[16] num_atoms (rounded to 50 multiples)
x[17] minimum_profit_factor
x[18-21] Kelly parameters
x[22-26] Ensemble parameters (x[26] variance_cap unused)
x[27] warmup_ratio
x[28] curiosity_weight
x[29] tau (exp)
x[30] td_error_clamp_max
x[31] batch_diversity_cooldown
x[32] lr_decay_type
x[33] sharpe_weight
x[34] gae_lambda
x[35] noisy_sigma_initial
x[36] noisy_sigma_final
x[37] norm_type
x[38] activation_type
```
**Constraints Applied**:
- ✅ Batch size floor for high LR (line 551): `if learning_rate > 2e-4 && batch_size < 120`
- ✅ Thrashing risk warning (line 561): tight position + high hold penalty
- ✅ HFT constraint validation moved to `evaluate_objective()`
---
## Recommendations
### Priority 1: **Add Double DQN to Search Space**
**Action**: Add `use_double_dqn` as a boolean or categorical parameter
**Implementation Options**:
**Option A: Boolean in Categorical Space** (Recommended)
```rust
// In categorical_choices()
vec![
vec![false, true], // use_double_dqn
]
// In from_categorical()
use_double_dqn: categorical[0] == 1,
```
**Option B: Threshold in Continuous Space**
```rust
// In continuous_bounds()
(0.0, 1.0), // 39: use_double_dqn_threshold
// In from_continuous()
let use_double_dqn = x[39] > 0.5;
```
**Expected Impact**:
- Search space: 39D → 40D (continuous) or add 1 categorical dimension
- Could discover 5-10% performance gain if Double DQN is suboptimal for this domain
- Low risk: Double DQN is generally beneficial but worth validating
---
### Priority 2: **Verify Noisy Sigma Duplication**
**Action**: Check if `noisy_sigma_init` (x[13]) and `noisy_sigma_initial` (x[35]) map to same field
**Investigation**:
```bash
grep -n "noisy_sigma" ml/src/trainers/dqn/config.rs
```
**If duplicated**:
- Remove one from search space (recommend keep `noisy_sigma_initial/final` pair)
- Reduces 39D → 38D (after adding Double DQN: 39D total)
---
### Priority 3: **Optional - Add PER Advanced Parameters**
**Action**: Only if experiencing PER numerical issues
**Implementation**:
```rust
// In DQNParams
pub per_epsilon: f64, // [1e-6, 1e-3], log scale
pub min_priority: f64, // [1e-8, 1e-4], log scale
// In continuous_bounds()
(1e-6_f64.ln(), 1e-3_f64.ln()), // per_epsilon
(1e-8_f64.ln(), 1e-4_f64.ln()), // min_priority
```
**Expected Impact**: Minimal - these are stability parameters with good defaults
---
### Priority 4: **Document Hardcoded Booleans**
**Action**: Add comments explaining why Rainbow flags are hardcoded
**Example**:
```rust
// Rainbow components hardcoded per user requirement (WAVE 11):
// "I want them enabled!" - no point in tuning boolean flags
// Only architecture hyperparameters (dimensions, sigmas) are tuned
use_per: true, // Always on: 25-40% convergence improvement
use_dueling: true, // Always on: 10-20% sample efficiency
use_noisy_nets: true, // Always on: better than epsilon-greedy
use_distributional: false, // Disabled: BUG #36 scatter_add gradient bug
```
---
## Code Quality Notes
### Strengths
- ✅ Comprehensive 39D search space
- ✅ Proper log/linear scaling for different parameter types
- ✅ Rounding for discrete parameters (batch_size, dueling_dim, n_steps, num_atoms)
- ✅ Smart constraints (batch floor for high LR, thrashing warnings)
- ✅ Well-documented ranges with context (Rainbow defaults, bug fixes, wave tracking)
### Areas for Improvement
- ❌ Missing Double DQN (critical Rainbow component)
- ⚠️ Possible noisy sigma duplication
- ⚠️ C51 parameters tunable but unused (due to BUG #36)
- Many hardcoded booleans (intentional per user request)
---
## Conclusion
The DQN hyperopt adapter is **95% complete** with excellent coverage of Rainbow components and advanced features. The only critical missing piece is **Double DQN**, which should be added to the search space to enable full hyperparameter optimization.
**Action Items**:
1.**Add `use_double_dqn` to search space** (Priority 1)
2. ⚠️ Verify and resolve noisy sigma duplication (Priority 2)
3. Consider PER advanced params (Priority 3, optional)
4. Document hardcoded boolean rationale (Priority 4)
**Overall Assessment**: Strong implementation with one critical gap. Adding Double DQN flag would bring it to 100% Rainbow coverage.