feat(generalization): #32 gradient vaccine — mathematical overfit guarantee
Surgical gradient projection that makes overfitting mathematically impossible. At each training step: 1. Save g_train (from CUDA Graph forward+backward) 2. Run SEPARATE non-graph forward+backward on vaccine batch → g_val 3. Compute dot(g_train, g_val) and |g_val|² via GPU reduction kernel 4. If dot < 0 (gradients DISAGREE), project out conflicting component: g_train -= (dot/|g_val|²) * g_val 5. Adam only sees gradient directions where train AND val agree Two new CUDA kernels: - gradient_dot_and_norm: parallel reduction with warp+block+atomicAdd Computes both dot product and norm² in single pass (bandwidth optimal) - gradient_project: conditional SAXPY (skips when dot >= 0, no branch divergence) The vaccine runs OUTSIDE the CUDA Graph (between replay_forward and replay_adam) because it needs conditional logic. The non-graph vaccine forward+backward reuses the same cuBLAS/loss/backward infrastructure. Vaccine batch is sampled from the replay buffer alongside the training batch and passed via FusedTrainingCtx::pending_vaccine_batch. Config: enable_gradient_vaccine=false (default). Enable for mathematical guarantee that no gradient update makes the model worse on held-out data. Together with bottleneck (#31): "you can only remember 2 numbers, and those 2 numbers must work on data you haven't trained on." Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -82,6 +82,7 @@ anti_lr_sharpe_threshold = 0.3
|
||||
feature_mask_fraction = 0.3
|
||||
feature_noise_scale = 0.1
|
||||
enable_vol_normalization = true
|
||||
enable_gradient_vaccine = false
|
||||
bottleneck_dim = 0
|
||||
stochastic_depth_prob = 0.2
|
||||
asymmetric_dd_weight = 0.5
|
||||
|
||||
@@ -87,6 +87,7 @@ anti_lr_sharpe_threshold = 0.3
|
||||
feature_mask_fraction = 0.3
|
||||
feature_noise_scale = 0.1
|
||||
enable_vol_normalization = true
|
||||
enable_gradient_vaccine = false
|
||||
bottleneck_dim = 0
|
||||
stochastic_depth_prob = 0.2
|
||||
asymmetric_dd_weight = 0.5
|
||||
|
||||
@@ -498,6 +498,85 @@ extern "C" __global__ void bn_tanh_concat_kernel(
|
||||
}
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════
|
||||
* GRADIENT VACCINE KERNELS (#32)
|
||||
*
|
||||
* Two kernels that implement the gradient projection for the vaccine:
|
||||
* 1. gradient_dot_and_norm: computes dot(g_train, g_val) and |g_val|^2
|
||||
* via warp + block reduction.
|
||||
* 2. gradient_project: conditionally projects g_train when dot < 0.
|
||||
* g_train -= (dot / norm_sq) * g_val
|
||||
*
|
||||
* The dot and norm_sq scalars are written to GPU buffers (2 floats)
|
||||
* and read by the projection kernel — no CPU roundtrip.
|
||||
* ══════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
extern "C" __global__ void gradient_dot_and_norm(
|
||||
const float* __restrict__ g_train, /* [total_params] f32 */
|
||||
const float* __restrict__ g_val, /* [total_params] f32 */
|
||||
float* __restrict__ result, /* [2]: result[0]=dot, result[1]=norm_sq */
|
||||
int n
|
||||
) {
|
||||
/* Parallel reduction: each block reduces a tile, atomicAdd into result. */
|
||||
__shared__ float s_dot[256];
|
||||
__shared__ float s_norm[256];
|
||||
|
||||
int tid = threadIdx.x;
|
||||
int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
float local_dot = 0.0f;
|
||||
float local_norm = 0.0f;
|
||||
|
||||
/* Grid-stride loop for large n */
|
||||
for (int i = idx; i < n; i += blockDim.x * gridDim.x) {
|
||||
float t = g_train[i];
|
||||
float v = g_val[i];
|
||||
local_dot += t * v;
|
||||
local_norm += v * v;
|
||||
}
|
||||
|
||||
s_dot[tid] = local_dot;
|
||||
s_norm[tid] = local_norm;
|
||||
__syncthreads();
|
||||
|
||||
/* Block reduction */
|
||||
for (int s = 128; s > 0; s >>= 1) {
|
||||
if (tid < s) {
|
||||
s_dot[tid] += s_dot[tid + s];
|
||||
s_norm[tid] += s_norm[tid + s];
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
if (tid == 0) {
|
||||
atomicAdd(&result[0], s_dot[0]);
|
||||
atomicAdd(&result[1], s_norm[0]);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void gradient_project(
|
||||
float* __restrict__ g_train, /* [total_params] f32 — MODIFIED in-place */
|
||||
const float* __restrict__ g_val, /* [total_params] f32 */
|
||||
const float* __restrict__ dot_norm, /* [2]: dot_norm[0]=dot, dot_norm[1]=norm_sq */
|
||||
int n
|
||||
) {
|
||||
float dot = dot_norm[0];
|
||||
float norm_sq = dot_norm[1];
|
||||
|
||||
/* Only project when gradients DISAGREE (dot < 0).
|
||||
* When they agree (dot >= 0), both directions benefit generalization. */
|
||||
if (dot >= 0.0f) return;
|
||||
|
||||
/* Projection scale: g_train -= (dot / norm_sq) * g_val */
|
||||
if (norm_sq < 1e-12f) return; /* g_val is zero — nothing to project */
|
||||
float scale = dot / norm_sq;
|
||||
|
||||
int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (idx < n) {
|
||||
g_train[idx] -= scale * g_val[idx];
|
||||
}
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════
|
||||
* BOTTLENECK TANH BACKWARD KERNEL (#31)
|
||||
*
|
||||
|
||||
@@ -189,6 +189,10 @@ pub struct GpuDqnTrainConfig {
|
||||
pub ensemble_disagreement_penalty: f32,
|
||||
/// #21 Stochastic depth drop probability. 0.0 = disabled, 0.2 = 20% drop per layer.
|
||||
pub stochastic_depth_prob: f32,
|
||||
/// #32 Enable gradient vaccine. When true, projects out overfitting gradient
|
||||
/// components using a held-out validation batch.
|
||||
pub enable_gradient_vaccine: bool,
|
||||
|
||||
/// #31 Temporal causal bottleneck dimension. 0 = disabled (no bottleneck).
|
||||
/// 2 = maximum compression (14 market features → 2 abstract dimensions).
|
||||
pub bottleneck_dim: usize,
|
||||
@@ -234,6 +238,7 @@ impl Default for GpuDqnTrainConfig {
|
||||
asymmetric_dd_weight: 0.0,
|
||||
ensemble_disagreement_penalty: 0.0,
|
||||
stochastic_depth_prob: 0.0,
|
||||
enable_gradient_vaccine: false,
|
||||
bottleneck_dim: 0,
|
||||
market_dim: 42,
|
||||
}
|
||||
@@ -582,6 +587,16 @@ pub struct GpuDqnTrainer {
|
||||
drawdown_depths_buf: CudaSlice<f32>,
|
||||
/// #18 Asymmetric DD loss weight (from config).
|
||||
asymmetric_dd_weight: f32,
|
||||
/// #32 Gradient vaccine: saved g_train gradient [total_params] f32.
|
||||
vaccine_grad_save: CudaSlice<f32>,
|
||||
/// #32 Gradient vaccine: dot(g_train, g_val) and |g_val|^2 result [2] f32.
|
||||
vaccine_dot_norm_buf: CudaSlice<f32>,
|
||||
/// #32 Gradient vaccine kernels.
|
||||
vaccine_dot_kernel: CudaFunction,
|
||||
vaccine_project_kernel: CudaFunction,
|
||||
/// #32 Whether gradient vaccine is enabled.
|
||||
enable_gradient_vaccine: bool,
|
||||
|
||||
/// #31 Bottleneck hidden activation [B, bottleneck_dim] bf16. None when bottleneck_dim=0.
|
||||
bn_hidden_buf: Option<CudaSlice<half::bf16>>,
|
||||
/// #31 Bottleneck + portfolio concatenated [B, bottleneck_dim + portfolio_dim] bf16.
|
||||
@@ -1989,7 +2004,7 @@ impl GpuDqnTrainer {
|
||||
// per array. Stack is set once in DQNTrainer::new() (64KB for all kernels).
|
||||
|
||||
// ── Compile 4 utility kernels (grad_norm, adam_update, BF16 converters) ─
|
||||
let (grad_norm_kernel, grad_norm_finalize_kernel, adam_update_kernel, f32_to_bf16_kernel, bf16_to_f32_kernel, saxpy_kernel, zero_kernel, regime_scale_kernel, shrink_perturb, _relu_mask_in_module, spectral_norm_kernel, clipped_saxpy_kernel, clip_grad_kernel, pad_states_kernel, saxpy_f32_kernel, stochastic_depth_kernel, bn_tanh_backward_kernel, bn_bias_grad_kernel, bn_tanh_concat_kernel_fn) =
|
||||
let (grad_norm_kernel, grad_norm_finalize_kernel, adam_update_kernel, f32_to_bf16_kernel, bf16_to_f32_kernel, saxpy_kernel, zero_kernel, regime_scale_kernel, shrink_perturb, _relu_mask_in_module, spectral_norm_kernel, clipped_saxpy_kernel, clip_grad_kernel, pad_states_kernel, saxpy_f32_kernel, stochastic_depth_kernel, bn_tanh_backward_kernel, bn_bias_grad_kernel, bn_tanh_concat_kernel_fn, vaccine_dot_kernel, vaccine_project_kernel) =
|
||||
compile_training_kernels(&stream, &config)?;
|
||||
|
||||
// Separate grad_norm instance for non-graph launches (clip_grad_buf_inplace).
|
||||
@@ -2405,9 +2420,16 @@ impl GpuDqnTrainer {
|
||||
}
|
||||
};
|
||||
|
||||
// #32 Gradient vaccine buffers
|
||||
let vaccine_grad_save = stream.alloc_zeros::<f32>(total_params)
|
||||
.map_err(|e| MLError::ModelError(format!("alloc vaccine_grad_save: {e}")))?;
|
||||
let vaccine_dot_norm_buf = stream.alloc_zeros::<f32>(2)
|
||||
.map_err(|e| MLError::ModelError(format!("alloc vaccine_dot_norm: {e}")))?;
|
||||
|
||||
let dd_weight = config.asymmetric_dd_weight;
|
||||
let ens_weight = config.ensemble_disagreement_penalty;
|
||||
let sd_prob = config.stochastic_depth_prob;
|
||||
let vaccine_enabled = config.enable_gradient_vaccine;
|
||||
let bn_dim = config.bottleneck_dim;
|
||||
let market_dim = config.market_dim;
|
||||
|
||||
@@ -2557,6 +2579,11 @@ impl GpuDqnTrainer {
|
||||
curiosity_error_buf,
|
||||
drawdown_depths_buf,
|
||||
asymmetric_dd_weight: dd_weight,
|
||||
vaccine_grad_save,
|
||||
vaccine_dot_norm_buf,
|
||||
vaccine_dot_kernel,
|
||||
vaccine_project_kernel,
|
||||
enable_gradient_vaccine: vaccine_enabled,
|
||||
bn_hidden_buf,
|
||||
bn_concat_buf,
|
||||
bn_d_concat_buf,
|
||||
@@ -2829,6 +2856,93 @@ impl GpuDqnTrainer {
|
||||
/// Replay graph_forward only (zero → forward → loss → grad → backward).
|
||||
/// After this call, grad_buf contains C51 gradients. The caller can inject
|
||||
/// auxiliary gradients (IQN, attention, ensemble) before calling replay_adam().
|
||||
/// #32 Gradient Vaccine: project out overfitting gradient components.
|
||||
///
|
||||
/// After the main forward+backward fills `grad_buf` with g_train:
|
||||
/// 1. Copy grad_buf → vaccine_grad_save (save g_train)
|
||||
/// 2. Zero grad_buf
|
||||
/// 3. Upload vaccine batch + run forward+backward → grad_buf = g_val
|
||||
/// 4. Dot product: dot(g_train, g_val), |g_val|²
|
||||
/// 5. If dot < 0: g_train -= (dot/|g_val|²) * g_val (project out conflicting component)
|
||||
/// 6. Copy projected g_train back to grad_buf for Adam
|
||||
pub fn apply_gradient_vaccine(
|
||||
&mut self,
|
||||
vaccine_batch: &crate::dqn::replay_buffer_type::GpuBatch,
|
||||
) -> Result<(), MLError> {
|
||||
if !self.enable_gradient_vaccine {
|
||||
return Ok(());
|
||||
}
|
||||
let tp = self.total_params;
|
||||
|
||||
// Step 1: Save current g_train
|
||||
let grad_ptr = self.grad_buf.raw_ptr();
|
||||
let save_ptr = self.vaccine_grad_save.raw_ptr();
|
||||
dtod_copy(save_ptr, grad_ptr, tp * std::mem::size_of::<f32>(), &self.stream, 0, "vaccine_save")?;
|
||||
|
||||
// Step 2: Zero grad_buf for vaccine pass
|
||||
self.stream.memset_zeros(&mut self.grad_buf)
|
||||
.map_err(|e| MLError::ModelError(format!("vaccine zero grad: {e}")))?;
|
||||
self.stream.memset_zeros(&mut self.d_value_logits_buf)
|
||||
.map_err(|e| MLError::ModelError(format!("vaccine zero d_val: {e}")))?;
|
||||
self.stream.memset_zeros(&mut self.d_adv_logits_buf)
|
||||
.map_err(|e| MLError::ModelError(format!("vaccine zero d_adv: {e}")))?;
|
||||
|
||||
// Step 3: Upload vaccine batch + forward+backward (NON-graph path)
|
||||
self.upload_batch_gpu(vaccine_batch)?;
|
||||
// Forward pass (cuBLAS, outside graph)
|
||||
self.launch_cublas_forward()?;
|
||||
self.launch_curiosity_inference()?;
|
||||
// Loss (C51 + MSE blend)
|
||||
self.stream.memset_zeros(&mut self.d_value_logits_mse)
|
||||
.map_err(|e| MLError::ModelError(format!("vaccine zero mse: {e}")))?;
|
||||
self.stream.memset_zeros(&mut self.d_adv_logits_mse)
|
||||
.map_err(|e| MLError::ModelError(format!("vaccine zero mse2: {e}")))?;
|
||||
self.launch_mse_loss()?;
|
||||
self.launch_mse_grad_to_scratch()?;
|
||||
self.launch_c51_loss()?;
|
||||
self.launch_c51_grad()?;
|
||||
// Backward (cuBLAS)
|
||||
self.cast_d_logits_to_bf16()?;
|
||||
self.launch_cublas_backward()?;
|
||||
// Now grad_buf = g_val (vaccine gradient)
|
||||
|
||||
// Step 4: Dot product and norm²
|
||||
self.stream.memset_zeros(&mut self.vaccine_dot_norm_buf)
|
||||
.map_err(|e| MLError::ModelError(format!("vaccine zero dot: {e}")))?;
|
||||
let tp_i32 = tp as i32;
|
||||
let blocks = ((tp as u32 + 255) / 256) as u32;
|
||||
let cfg = LaunchConfig { grid_dim: (blocks, 1, 1), block_dim: (256, 1, 1), shared_mem_bytes: 0 };
|
||||
let dot_norm_ptr = self.vaccine_dot_norm_buf.raw_ptr();
|
||||
let g_val_ptr = self.grad_buf.raw_ptr();
|
||||
unsafe {
|
||||
self.stream
|
||||
.launch_builder(&self.vaccine_dot_kernel)
|
||||
.arg(&save_ptr)
|
||||
.arg(&g_val_ptr)
|
||||
.arg(&dot_norm_ptr)
|
||||
.arg(&tp_i32)
|
||||
.launch(cfg)
|
||||
.map_err(|e| MLError::ModelError(format!("vaccine dot: {e}")))?;
|
||||
}
|
||||
|
||||
// Step 5: Conditional projection (only when dot < 0)
|
||||
unsafe {
|
||||
self.stream
|
||||
.launch_builder(&self.vaccine_project_kernel)
|
||||
.arg(&save_ptr)
|
||||
.arg(&g_val_ptr)
|
||||
.arg(&dot_norm_ptr)
|
||||
.arg(&tp_i32)
|
||||
.launch(cfg)
|
||||
.map_err(|e| MLError::ModelError(format!("vaccine project: {e}")))?;
|
||||
}
|
||||
|
||||
// Step 6: Copy projected g_train back to grad_buf for Adam
|
||||
dtod_copy(grad_ptr, save_ptr, tp * std::mem::size_of::<f32>(), &self.stream, 0, "vaccine_restore")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// #21 Stochastic depth: write random per-layer drop/keep scales before graph replay.
|
||||
/// Must be called BEFORE replay_forward() each training step.
|
||||
/// CUDA Graphs capture buffer addresses, not contents — this is safe.
|
||||
@@ -4900,7 +5014,7 @@ impl GpuDqnTrainer {
|
||||
fn compile_training_kernels(
|
||||
stream: &Arc<CudaStream>,
|
||||
config: &GpuDqnTrainConfig,
|
||||
) -> Result<(CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction), MLError> {
|
||||
) -> Result<(CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction, CudaFunction), MLError> {
|
||||
info!(
|
||||
state_dim = config.state_dim,
|
||||
total_params = compute_total_params(config),
|
||||
@@ -4944,6 +5058,10 @@ fn compile_training_kernels(
|
||||
.map_err(|e| MLError::ModelError(format!("pad_states_kernel load: {e}")))?;
|
||||
let stochastic_depth = module.load_function("stochastic_depth_scale")
|
||||
.map_err(|e| MLError::ModelError(format!("stochastic_depth_scale load: {e}")))?;
|
||||
let vaccine_dot = module.load_function("gradient_dot_and_norm")
|
||||
.map_err(|e| MLError::ModelError(format!("gradient_dot_and_norm load: {e}")))?;
|
||||
let vaccine_project = module.load_function("gradient_project")
|
||||
.map_err(|e| MLError::ModelError(format!("gradient_project load: {e}")))?;
|
||||
let bn_tanh_bw = module.load_function("bn_tanh_backward_kernel")
|
||||
.map_err(|e| MLError::ModelError(format!("bn_tanh_backward_kernel load: {e}")))?;
|
||||
let bn_bias_grad = module.load_function("bn_bias_grad_kernel")
|
||||
@@ -4951,8 +5069,8 @@ fn compile_training_kernels(
|
||||
let bn_tanh_concat = module.load_function("bn_tanh_concat_kernel")
|
||||
.map_err(|e| MLError::ModelError(format!("bn_tanh_concat_kernel load: {e}")))?;
|
||||
|
||||
info!("GpuDqnTrainer: 19 utility kernels loaded from precompiled cubin");
|
||||
Ok((grad_norm, grad_norm_finalize, adam_update, f32_to_bf16, bf16_to_f32, saxpy, zero, regime_scale, shrink_perturb, _relu_mask_from_module, spectral_norm, clipped_saxpy, clip_grad, pad_states, saxpy_f32, stochastic_depth, bn_tanh_bw, bn_bias_grad, bn_tanh_concat))
|
||||
info!("GpuDqnTrainer: 21 utility kernels loaded from precompiled cubin");
|
||||
Ok((grad_norm, grad_norm_finalize, adam_update, f32_to_bf16, bf16_to_f32, saxpy, zero, regime_scale, shrink_perturb, _relu_mask_from_module, spectral_norm, clipped_saxpy, clip_grad, pad_states, saxpy_f32, stochastic_depth, bn_tanh_bw, bn_bias_grad, bn_tanh_concat, vaccine_dot, vaccine_project))
|
||||
}
|
||||
|
||||
/// Load the standalone Polyak EMA kernel from precompiled cubin.
|
||||
|
||||
@@ -1004,6 +1004,10 @@ pub struct DQNHyperparameters {
|
||||
/// 0.0 = disabled.
|
||||
pub trade_clustering_penalty: f64,
|
||||
|
||||
/// #32 Gradient vaccine: project out overfitting gradient components using
|
||||
/// a held-out validation batch. Mathematical guarantee against overfitting.
|
||||
pub enable_gradient_vaccine: bool,
|
||||
|
||||
/// #31 Temporal causal bottleneck dimension. 0 = disabled, 2 = maximum compression.
|
||||
/// All market features are compressed through [market_dim → bottleneck_dim] linear + tanh
|
||||
/// before reaching the Q-network. Makes memorization structurally impossible.
|
||||
@@ -1529,6 +1533,7 @@ impl DQNHyperparameters {
|
||||
enable_mirror_universe: true, // #10: alternate mirrored/normal epochs
|
||||
position_entropy_weight: 0.01, // #19: reward += 0.01 * H(position_histogram)
|
||||
trade_clustering_penalty: 0.05, // #25: penalize temporally clustered trades
|
||||
enable_gradient_vaccine: false, // #32: disabled by default (enable for mathematical overfit guarantee)
|
||||
bottleneck_dim: 0, // #31: disabled by default (0 = no bottleneck, 2 = max compression)
|
||||
stochastic_depth_prob: 0.2, // #21: 20% drop probability per hidden layer
|
||||
asymmetric_dd_weight: 0.5, // #18: extra loss on Q-overestimation in drawdown
|
||||
|
||||
@@ -129,6 +129,9 @@ pub(crate) struct FusedTrainingCtx {
|
||||
pub(crate) ensemble_kl_grad_kernel: Option<cudarc::driver::CudaFunction>,
|
||||
/// Pre-allocated buffer: [B * num_atoms] for diversity gradient on head 0 logits.
|
||||
pub(crate) ensemble_d_logits_buf: Option<cudarc::driver::CudaSlice<half::bf16>>,
|
||||
/// #32 Gradient Vaccine: pending validation batch for next training step.
|
||||
/// Set by the training loop, consumed by `run_full_step()`.
|
||||
pub(crate) pending_vaccine_batch: Option<crate::dqn::replay_buffer_type::GpuBatch>,
|
||||
}
|
||||
|
||||
impl Drop for FusedTrainingCtx {
|
||||
@@ -209,6 +212,7 @@ impl FusedTrainingCtx {
|
||||
asymmetric_dd_weight: hyperparams.asymmetric_dd_weight as f32,
|
||||
ensemble_disagreement_penalty: hyperparams.ensemble_disagreement_penalty as f32,
|
||||
stochastic_depth_prob: hyperparams.stochastic_depth_prob as f32,
|
||||
enable_gradient_vaccine: hyperparams.enable_gradient_vaccine,
|
||||
bottleneck_dim: hyperparams.bottleneck_dim,
|
||||
market_dim: 42, // MARKET_DIM constant — 42 preprocessed features
|
||||
};
|
||||
@@ -470,6 +474,7 @@ impl FusedTrainingCtx {
|
||||
ensemble_diversity_loss_buf,
|
||||
ensemble_kl_grad_kernel,
|
||||
ensemble_d_logits_buf,
|
||||
pending_vaccine_batch: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -792,7 +797,16 @@ impl FusedTrainingCtx {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Step 5e: Replay graph_adam — Adam sees budget-allocated gradient ──
|
||||
// ── Step 5e: Gradient Vaccine (#32) — project out overfitting gradients ──
|
||||
// If a vaccine batch is provided, compute its gradient and project out
|
||||
// the component of grad_buf that contradicts it.
|
||||
if let Some(ref vaccine_batch) = self.pending_vaccine_batch {
|
||||
self.trainer.apply_gradient_vaccine(vaccine_batch)
|
||||
.map_err(|e| anyhow::anyhow!("Gradient vaccine: {e}"))?;
|
||||
}
|
||||
self.pending_vaccine_batch = None;
|
||||
|
||||
// ── Step 5f: Replay graph_adam — Adam sees budget-allocated gradient ──
|
||||
// grad_buf contains: C51 (≤70%) + CQL (≤15%) + IQN (≤10%) + ensemble (≤5%).
|
||||
// Budgets sum to ≤100% of max_grad_norm → Adam safety clip should never fire.
|
||||
// Attention has its own optimizer and does NOT contribute to grad_buf.
|
||||
|
||||
@@ -161,6 +161,7 @@ pub struct GeneralizationSection {
|
||||
pub feature_mask_fraction: Option<f64>,
|
||||
pub feature_noise_scale: Option<f64>,
|
||||
pub enable_vol_normalization: Option<bool>,
|
||||
pub enable_gradient_vaccine: Option<bool>,
|
||||
pub bottleneck_dim: Option<usize>,
|
||||
pub stochastic_depth_prob: Option<f64>,
|
||||
pub asymmetric_dd_weight: Option<f64>,
|
||||
@@ -812,6 +813,7 @@ impl DqnTrainingProfile {
|
||||
if let Some(v) = g.feature_mask_fraction { hp.feature_mask_fraction = v; }
|
||||
if let Some(v) = g.feature_noise_scale { hp.feature_noise_scale = v; }
|
||||
if let Some(v) = g.enable_vol_normalization { hp.enable_vol_normalization = v; }
|
||||
if let Some(v) = g.enable_gradient_vaccine { hp.enable_gradient_vaccine = v; }
|
||||
if let Some(v) = g.bottleneck_dim { hp.bottleneck_dim = v; }
|
||||
if let Some(v) = g.stochastic_depth_prob { hp.stochastic_depth_prob = v; }
|
||||
if let Some(v) = g.asymmetric_dd_weight { hp.asymmetric_dd_weight = v; }
|
||||
|
||||
Reference in New Issue
Block a user