fix(cuda): enable GPU experience collection for regime-conditional DQN
The GPU experience collector only supported DQNAgentType::Standard, but enable_regime_qnetwork defaults to true, creating RegimeConditional agents. This silently fell back to CPU with a debug! message invisible at INFO log level. - Add primary_head() accessor to RegimeConditionalDQN (returns trending head) - Extract weights from primary head for both collector init and weight sync - Upgrade debug! to warn! for missing GPU collection prerequisites - Add warn! to PPO for silent GPU skip when raw market data not uploaded Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -235,6 +235,14 @@ impl RegimeConditionalDQN {
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a reference to the primary (trending) DQN head.
|
||||
///
|
||||
/// Used by GPU experience collector for weight extraction — the trending head
|
||||
/// is the most common regime and provides representative network weights.
|
||||
pub fn primary_head(&self) -> &DQN {
|
||||
&self.trending_head
|
||||
}
|
||||
|
||||
/// Forward pass through regime-specific Q-network head
|
||||
///
|
||||
/// # Arguments
|
||||
|
||||
@@ -1412,9 +1412,16 @@ impl DQNTrainer {
|
||||
use crate::cuda_pipeline::gpu_experience_collector::GpuExperienceCollector;
|
||||
let stream = cuda_dev.cuda_stream();
|
||||
|
||||
// Read-lock agent to check for dueling networks
|
||||
// Read-lock agent to extract dueling network weights for GPU collector.
|
||||
// Works with both Standard and RegimeConditional agents — for regime-conditional,
|
||||
// we use the primary (trending) head's weights since the GPU kernel runs a single
|
||||
// Q-network for fast experience collection.
|
||||
let agent = self.agent.read().await;
|
||||
let init_result = if let DQNAgentType::Standard(ref dqn) = &*agent {
|
||||
let dqn_ref: Option<&crate::dqn::DQN> = match &*agent {
|
||||
DQNAgentType::Standard(ref dqn) => Some(dqn),
|
||||
DQNAgentType::RegimeConditional(ref regime_dqn) => Some(regime_dqn.primary_head()),
|
||||
};
|
||||
let init_result = if let Some(dqn) = dqn_ref {
|
||||
match (
|
||||
dqn.dueling_q_network.as_ref(),
|
||||
dqn.dueling_target_network.as_ref(),
|
||||
@@ -1432,12 +1439,12 @@ impl DQNTrainer {
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
debug!("GPU experience collector skipped: requires dueling networks + curiosity module");
|
||||
warn!("GPU experience collector skipped: requires dueling networks + curiosity module (have dueling_q={}, dueling_target={}, curiosity={})",
|
||||
dqn.dueling_q_network.is_some(), dqn.dueling_target_network.is_some(), self.curiosity_module.is_some());
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug!("GPU experience collector skipped: regime-conditional DQN not supported");
|
||||
None
|
||||
};
|
||||
drop(agent);
|
||||
@@ -2254,7 +2261,11 @@ impl DQNTrainer {
|
||||
#[cfg(feature = "cuda")]
|
||||
if let Some(ref mut collector) = self.gpu_experience_collector {
|
||||
let agent = self.agent.read().await;
|
||||
if let DQNAgentType::Standard(ref dqn) = &*agent {
|
||||
let dqn_ref: Option<&crate::dqn::DQN> = match &*agent {
|
||||
DQNAgentType::Standard(ref dqn) => Some(dqn),
|
||||
DQNAgentType::RegimeConditional(ref regime_dqn) => Some(regime_dqn.primary_head()),
|
||||
};
|
||||
if let Some(dqn) = dqn_ref {
|
||||
if let Some(ref online) = dqn.dueling_q_network {
|
||||
if let Err(e) = collector.sync_online_weights(online.vars()) {
|
||||
warn!("GPU online weight sync failed: {}", e);
|
||||
|
||||
@@ -502,6 +502,18 @@ impl PpoTrainer {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Log why GPU collection is not available so it doesn't silently fall back to CPU
|
||||
#[cfg(feature = "cuda")]
|
||||
{
|
||||
if gpu_ppo_collector.is_none() {
|
||||
warn!("PPO GPU experience collection skipped: collector not initialized");
|
||||
} else if self.features_raw_cuda.is_none() || self.targets_raw_cuda.is_none() {
|
||||
warn!("PPO GPU experience collection skipped: raw market data not uploaded (call set_raw_market_data() before train())");
|
||||
} else {
|
||||
// All conditions met but pattern match still failed — shouldn't happen
|
||||
warn!("PPO GPU experience collection skipped: unexpected state");
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user