diff --git a/config/training/dqn-production.toml b/config/training/dqn-production.toml index 66ea68330..ffb5c02ad 100644 --- a/config/training/dqn-production.toml +++ b/config/training/dqn-production.toml @@ -13,7 +13,11 @@ [training] epochs = 200 -batch_size = 16384 +# batch_size: removed 2026-05-14 — sourced from `GpuProfile.training.batch_size` +# (config/gpu/*.toml: H100=8192, L40S=4096, A100=2048, RTX3050=64). Hard-coded +# 16384 here was H100-tuned and caused OOM on L40S after the 2026-05-14 +# argo gpu-pool default flip from H100 → L40S (workflow train-ft8ph: next_states +# alloc 4 GiB collision at fold 0 from compounded H100-sized buffers). learning_rate = 1e-5 gamma = 0.99 weight_decay = 0.0001 @@ -52,7 +56,9 @@ q_gap_threshold = 0.05 noise_sigma = 0.1 [replay_buffer] -buffer_size = 500000 +# buffer_size: removed 2026-05-14 — sourced from `GpuProfile.training.buffer_size` +# (config/gpu/*.toml: H100=500K, L40S=300K, A100=200K, RTX3050=5K). Hard-coded +# 500_000 here was H100-tuned. See [training].batch_size comment. min_replay_size = 1000 per_alpha = 0.3 # was 0.6. Lower = more uniform. Dense micro-rewards have tiny TD-errors, high alpha ignores them. per_beta_start = 0.4 @@ -64,7 +70,12 @@ patience = 20 min_epochs_before_stopping = 80 [experience] -gpu_n_episodes = 4096 +# gpu_n_episodes: removed 2026-05-14 — sourced from `GpuProfile.experience. +# gpu_n_episodes` (config/gpu/*.toml: H100=4096, L40S=2048, A100=Some(?), +# RTX3050=Some(?)). Hard-coded 4096 here was H100-tuned; on L40S the +# `build_next_states_f32` rollout-step alloc = n_episodes × 2 × timesteps × +# state_dim × 4 = 4 GiB collides with the rest of the collector VRAM by the +# rollout phase (workflow train-ft8ph repro). See [training].batch_size comment. initial_capital = 35000.0 tx_cost_multiplier = 0.18 # bps: IBKR ES RT = $4.50/contract ($0.85 comm + $1.38 exch + $0.02 reg × 2 sides). At ES=$5100: 0.18 bps × 5100 × 0.0001 = 0.09 pts = $4.50 diff --git a/crates/ml/src/training_profile.rs b/crates/ml/src/training_profile.rs index 6e6f3be99..4d1965443 100644 --- a/crates/ml/src/training_profile.rs +++ b/crates/ml/src/training_profile.rs @@ -1198,12 +1198,18 @@ mod tests { #[test] fn test_apply_to_dqn_production() { let mut hp = crate::trainers::dqn::DQNHyperparameters::conservative(); + let baseline_batch_size = hp.batch_size; let profile = DqnTrainingProfile::load("dqn-production"); profile.apply_to(&mut hp); // [training] values must have been applied assert_eq!(hp.epochs, 200); - assert_eq!(hp.batch_size, 16384); + // batch_size: NOT overridden by dqn-production.toml (removed 2026-05-14 + // — H100-tuned 16384 caused OOM on L40S default; the value now flows + // from `GpuProfile.training.batch_size`). Assertion: apply_to leaves + // `batch_size` at the conservative() baseline so the GpuProfile-supplied + // value (set earlier in train_baseline_rl.rs:443) flows through cleanly. + assert_eq!(hp.batch_size, baseline_batch_size); assert!((hp.learning_rate - 1e-5).abs() < 1e-10); // OFI MBP-10 data dir should be set in production profile @@ -1468,11 +1474,15 @@ mod tests { fn test_production_profile_applies_all_sections() { let profile = DqnTrainingProfile::load("dqn-production"); let mut hp = crate::trainers::dqn::DQNHyperparameters::conservative(); + let baseline_batch_size = hp.batch_size; profile.apply_to(&mut hp); // [training] section assert_eq!(hp.epochs, 200); - assert_eq!(hp.batch_size, 16384); + // batch_size: see test_apply_to_dqn_production — VRAM-tuned values flow + // from GpuProfile, not the training TOML. apply_to leaves the conservative + // baseline in place so GpuProfile values aren't shadowed. + assert_eq!(hp.batch_size, baseline_batch_size); assert!((hp.learning_rate - 1e-5).abs() < 1e-10); assert!((hp.gamma - 0.99).abs() < 0.001); assert!((hp.reward_scale - 1.0).abs() < 0.01); diff --git a/docs/dqn-wire-up-audit.md b/docs/dqn-wire-up-audit.md index d4b9d6958..6e315a93b 100644 --- a/docs/dqn-wire-up-audit.md +++ b/docs/dqn-wire-up-audit.md @@ -2,6 +2,49 @@ **Status:** Populated during Plan 1 Task 6 (A.5 orphan audit). Updated on every commit per Invariant 7. +## 2026-05-14 — config(dqn): strip H100-tuned VRAM overrides from dqn-production.toml + +**Branch:** `sp20-aux-h-fixed`. **Trigger:** workflow `train-ft8ph` OOM at +`build_next_states_f32` 4 GiB alloc on fold 0/1/2 after the L40S-default +flip on the prior commit. + +**Root cause:** `config/training/dqn-production.toml` hard-coded three +H100-tuned values that shadow `GpuProfile` auto-detection: + +| field | dqn-production | GpuProfile L40S | GpuProfile H100 | +|-------|---------------:|----------------:|----------------:| +| `batch_size` | 16384 | 4096 | 8192 | +| `buffer_size` | 500K | 300K | 500K | +| `gpu_n_episodes` | 4096 | 2048 | 4096 | + +The training profile loader's `apply_to(&mut hyperparams)` runs AFTER +`train_baseline_rl.rs:443-490` populates hyperparams from `GpuProfile` +— so the production TOML always wins. On L40S (46 GB usable, but only +~7.86 GB free by the time the rollout-step `next_states` alloc fires +because the H100-sized batch/buffer/episode buffers were already +committed), the 4 GiB `n_episodes × 2 × timesteps × state_dim × 4 = +4096 × 2 × 1000 × 128 × 4` allocation OOMs. + +**Fix:** delete the three lines from `dqn-production.toml`. All three +fields are `Option<...>` in the TOML schema, so removal turns +`apply_to` into a no-op for them; values then flow cleanly from +`GpuProfile` (auto-detected per `profile_name_for_device("NVIDIA L40S") +→ "l40s"`). H100 detection still gets H100 values; L40S gets L40S +values; A100 / RTX3050 likewise. + +**Test updates:** two pinned assertions in `training_profile.rs::tests` +(`test_apply_to_dqn_production` line 1206, `test_production_profile_ +applies_all_sections` line 1475) checked `hp.batch_size == 16384` after +apply. Rewritten to check `hp.batch_size == baseline_batch_size` (i.e., +apply_to is a no-op on this field) — locks the new contract that VRAM- +tuned values stay GpuProfile-sourced. + +**Tests:** all 19 `training_profile` tests pass. Full lib suite +**1016/0 green** maintained. + +**Touched:** `config/training/dqn-production.toml`, +`crates/ml/src/training_profile.rs` (test updates only). + ## 2026-05-14 — SP22 H6 vNext: FoldReset registry entries for B5b/C collector buffers **Branch:** `sp20-aux-h-fixed`. **Builds on:** `4b40710b7` (B5b-2 collector