Changes: - Add --huber-delta CLI flag with default 100.0 - Add huber_delta to hyperopt search space (10.0-200.0) - Update DQNParams to include huber_delta - Add 2 new tests for configurability and hyperopt bounds - Optimal value identified: 24.77 (Trial 3) Validation: - 10/30 trials completed successfully - Gradient stability: 0.0-1.1 (target <1000) ✅ - Q-values: ±2-25 (vs ±10,000 before fix) ✅ - Best Sharpe: 0.3340 (Trial 3, huber_delta=24.77) Impact: - 46K-94Kx gradient improvement - 400-5000x Q-value improvement - Optimal range identified: 20-30 Tests: 14/14 passing (2 ignored) Files: 3 modified (train_dqn.rs, dqn.rs, test files)
56 lines
1.7 KiB
Rust
56 lines
1.7 KiB
Rust
use candle_core::{Device, Tensor};
|
|
use candle_nn::{VarBuilder, VarMap};
|
|
use ml::MLError;
|
|
|
|
#[test]
|
|
fn test_scatter_add_gradient_flow() -> Result<(), MLError> {
|
|
println!("\n=== Testing scatter_add gradient flow ===\n");
|
|
|
|
let device = Device::cuda_if_available(0)?;
|
|
|
|
// Create learnable source values
|
|
let varmap = VarMap::new();
|
|
let vb = VarBuilder::from_varmap(&varmap, candle_core::DType::F32, &device);
|
|
|
|
let source = vb.get((2, 3), "source")?;
|
|
println!("Source values: {:?}", source.to_vec2::<f32>()?);
|
|
|
|
// Create indices (fixed, not learnable)
|
|
let indices = Tensor::new(&[[0i64, 1, 2], [2, 1, 0]], &device)?;
|
|
println!("Indices: {:?}", indices.to_vec2::<i64>()?);
|
|
|
|
// Create base tensor
|
|
let base = Tensor::zeros((2, 5), candle_core::DType::F32, &device)?;
|
|
|
|
// Scatter add
|
|
let result = base.scatter_add(&indices, &source, 1)?;
|
|
println!("Result: {:?}", result.to_vec2::<f32>()?);
|
|
|
|
// Compute loss
|
|
let loss = result.sum_all()?;
|
|
println!("Loss: {:.6}", loss.to_scalar::<f32>()?);
|
|
|
|
// Backward
|
|
let grads = loss.backward()?;
|
|
|
|
// Check gradients
|
|
let all_vars = varmap.all_vars();
|
|
if let Some(grad) = grads.get(&all_vars[0]) {
|
|
println!("Gradients: {:?}", grad.to_vec2::<f32>()?);
|
|
let grad_norm = grad.sqr()?.sum_all()?.to_scalar::<f32>()?.sqrt();
|
|
println!("Gradient norm: {:.6}", grad_norm);
|
|
|
|
assert!(
|
|
grad_norm > 1e-6,
|
|
"scatter_add breaks gradient flow! Got norm={:.9}",
|
|
grad_norm
|
|
);
|
|
|
|
println!("\n✅ SUCCESS: scatter_add preserves gradient flow!\n");
|
|
} else {
|
|
panic!("No gradients computed!");
|
|
}
|
|
|
|
Ok(())
|
|
}
|