feat: --full mode for 3-phase hyperopt pipeline (BC → RL → Refinement)

- CampaignMode enum: Quick, Standard, Full
- dqn_full() constructor: 20 trials × 100 epochs (covers all 3 phases)
- fxt tune start --full flag: auto-sets 20 trials, 100 epochs
- Phase 1 (BC): MSE warmup + expert demos + DT pretrain
- Phase 2 (RL): all 25 features, C51 ramp, HER
- Phase 3 (Refine): pure C51, shrink-and-perturb

Usage: fxt tune start --model dqn --full --gpu

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-03-25 08:44:41 +01:00
parent 704ee72412
commit eceaf45b11
2 changed files with 52 additions and 4 deletions

View File

@@ -31,6 +31,9 @@ enum TuneAction {
/// Enable GPU
#[arg(long)]
gpu: bool,
/// Full 3-phase pipeline: BC → Online RL → Refinement (100 epochs/trial)
#[arg(long)]
full: bool,
},
/// Stop a running optimization
Stop {
@@ -249,16 +252,23 @@ impl TuneCommand {
trials,
config,
gpu,
full,
} => {
let description = if *full {
"Full 3-phase pipeline: BC → Online RL → Refinement".to_owned()
} else {
String::new()
};
let num_trials = if *full && *trials == 50 { 20 } else { *trials };
let resp = client
.ml_training()
.start_tuning_job(ml_training::StartTuningJobRequest {
model_type: model.to_uppercase(),
num_trials: *trials,
num_trials,
config_path: config.clone().unwrap_or_default(),
data_source: None,
use_gpu: *gpu,
description: String::new(),
description,
tags: Default::default(),
})
.await?

View File

@@ -16,6 +16,21 @@ use crate::batch_size_resolver::resolve_batch_size;
pub use crate::ModelType;
/// Campaign configuration for multi-trial hyperparameter optimization.
/// Campaign execution mode.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CampaignMode {
/// Quick: fewer trials, fewer epochs. For local validation.
Quick,
/// Standard: default trials and epochs. For single-phase hyperopt.
Standard,
/// Full: all three training phases (BC → Online RL → Refinement).
/// Runs max_epochs=100 per trial to cover all phases.
/// Phase 1 (BC): epochs 0..c51_warmup_epochs (MSE + expert demos + DT)
/// Phase 2 (RL): epochs warmup..80% (all features, C51 ramp, HER)
/// Phase 3 (Refine): last 20% (pure C51, shrink-and-perturb)
Full,
}
#[derive(Debug, Clone)]
pub struct CampaignConfig {
/// Which model to optimize.
@@ -32,6 +47,8 @@ pub struct CampaignConfig {
pub max_epochs_per_trial: usize,
/// Base directory for results output.
pub results_base_dir: PathBuf,
/// Campaign execution mode.
pub mode: CampaignMode,
}
impl CampaignConfig {
@@ -47,6 +64,25 @@ impl CampaignConfig {
early_stopping_eta: 3,
max_epochs_per_trial: 81,
results_base_dir: PathBuf::from("ml/hyperopt_results"),
mode: CampaignMode::Standard,
}
}
/// DQN full pipeline: all 3 training phases (BC → RL → Refinement).
/// 20 trials × 100 epochs — covers Phase 1 (warmup), Phase 2 (full RL),
/// and Phase 3 (refinement with shrink-and-perturb).
pub fn dqn_full() -> Self {
let caps = cached_capabilities();
let max_batch = resolve_batch_size(caps, &memory_profile::estimates::DQN, 512);
Self {
model_type: ModelType::DQN,
num_trials: 20,
data_dir: PathBuf::from("test_data/real/databento/ml_training"),
max_batch_size: max_batch,
early_stopping_eta: 3,
max_epochs_per_trial: 100,
results_base_dir: PathBuf::from("ml/hyperopt_results"),
mode: CampaignMode::Full,
}
}
@@ -62,6 +98,7 @@ impl CampaignConfig {
early_stopping_eta: 3,
max_epochs_per_trial: 81,
results_base_dir: PathBuf::from("ml/hyperopt_results"),
mode: CampaignMode::Standard,
}
}
@@ -280,10 +317,11 @@ mod tests {
early_stopping_eta: 3,
max_epochs_per_trial: 50,
results_base_dir: PathBuf::from("ml/hyperopt_results"),
mode: CampaignMode::Full,
};
println!("Starting local DQN hyperopt: {} trials × {} epochs",
config.num_trials, config.max_epochs_per_trial);
println!("Starting local DQN hyperopt: {} trials × {} epochs ({:?} mode)",
config.num_trials, config.max_epochs_per_trial, config.mode);
println!("Data: {}", config.data_dir.display());
let result = run_campaign(&config).expect("campaign should complete");