Files
foxhunt/ml/benches/hyperopt_bench.rs
jgrusewski 6da9d262db feat(ml): MAMBA-2 P0 fixes + hyperparameter optimization (13 params)
CRITICAL P0 FIXES (Validated - Loss 0.87 → 0.07):
- Add sigmoid activation to inference and training (ml/src/mamba/mod.rs:798, 1538)
- Fix config.total_decay_steps (was hardcoded 10000) (ml/src/mamba/mod.rs:2271)
- Update d_state: 16→64, 32→64 (Mamba-2 spec) (ml/src/mamba/mod.rs:178, 730)

HYPERPARAMETER OPTIMIZATION:
- Implement 13-parameter Bayesian optimization with argmin
- Add async data loading with 3-batch prefetch (+20-30% speedup)
- Create hyperopt adapter: ml/src/hyperopt/adapters/mamba2.rs
- Add example: ml/examples/hyperopt_mamba2_demo.rs

VALIDATION:
- Local test: Loss 0.07 vs 0.87 (12× improvement)
- Val loss: 0.04-0.14 vs 1.2 (27× improvement)
- Accuracy: 12-30% vs 1-5% (3-6× improvement)
- All binaries rebuilt and uploaded to Runpod S3

DEPLOYMENT:
- RTX 4090 pod active (n0fq2ikt4uk0zy)
- Training: 10 trials × 50 epochs, batch_size=256
- Expected: 1.3 days, $10.41 cost

Fixes #P0-sigmoid #P0-decay-steps #hyperopt-mamba2
2025-10-28 14:11:18 +01:00

321 lines
9.7 KiB
Rust

//! Benchmark Tests for Hyperparameter Optimization
//!
//! These benchmarks measure the performance of key operations in the
//! hyperparameter optimization framework.
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use ml::hyperopt::{BestHyperparameters, HyperparameterSpace, OptimizationResult, TrialResult};
use ndarray::Array1;
// Mock denormalize function for benchmarking (since we can't access private functions)
fn denormalize_params_mock(
normalized: &Array1<f64>,
space: &HyperparameterSpace,
) -> (f64, usize, f64, f64) {
let lr_norm = normalized[0];
let batch_norm = normalized[1];
let dropout_norm = normalized[2];
let wd_norm = normalized[3];
// Learning rate (log scale)
let lr_log = space.learning_rate_log_min
+ lr_norm * (space.learning_rate_log_max - space.learning_rate_log_min);
let learning_rate = 10_f64.powf(lr_log);
// Batch size (integer, linear scale)
let batch_size = (space.batch_size_min as f64
+ batch_norm * (space.batch_size_max - space.batch_size_min) as f64)
.round() as usize;
// Dropout (linear scale)
let dropout = space.dropout_min + dropout_norm * (space.dropout_max - space.dropout_min);
// Weight decay (log scale)
let wd_log = space.weight_decay_log_min
+ wd_norm * (space.weight_decay_log_max - space.weight_decay_log_min);
let weight_decay = 10_f64.powf(wd_log);
(learning_rate, batch_size, dropout, weight_decay)
}
fn benchmark_param_conversion(c: &mut Criterion) {
let space = HyperparameterSpace::default();
let mut group = c.benchmark_group("param_conversion");
// Benchmark single conversion
group.bench_function("single_conversion", |b| {
let normalized = Array1::from_vec(vec![0.5, 0.5, 0.5, 0.5]);
b.iter(|| {
let result = denormalize_params_mock(black_box(&normalized), black_box(&space));
black_box(result);
});
});
// Benchmark batch conversions (simulating optimization)
for batch_size in [10, 50, 100, 500].iter() {
group.bench_with_input(
BenchmarkId::from_parameter(format!("batch_{}", batch_size)),
batch_size,
|b, &size| {
let normalized_batch: Vec<Array1<f64>> = (0..size)
.map(|i| {
let norm = i as f64 / size as f64;
Array1::from_vec(vec![norm, norm, norm, norm])
})
.collect();
b.iter(|| {
for normalized in &normalized_batch {
let result =
denormalize_params_mock(black_box(normalized), black_box(&space));
black_box(result);
}
});
},
);
}
group.finish();
}
fn benchmark_log_scale_computation(c: &mut Criterion) {
let mut group = c.benchmark_group("log_scale");
// Benchmark pow computation (expensive operation)
group.bench_function("pow_computation", |b| {
let log_value = -3.5;
b.iter(|| {
let result = 10_f64.powf(black_box(log_value));
black_box(result);
});
});
// Benchmark linear interpolation
group.bench_function("linear_interpolation", |b| {
let min = -5.0;
let max = -2.0;
let norm = 0.5;
b.iter(|| {
let result = black_box(min) + black_box(norm) * (black_box(max) - black_box(min));
black_box(result);
});
});
group.finish();
}
fn benchmark_batch_size_rounding(c: &mut Criterion) {
let mut group = c.benchmark_group("batch_rounding");
// Benchmark integer rounding
group.bench_function("round_to_integer", |b| {
let value = 127.8;
b.iter(|| {
let result = black_box(value).round() as usize;
black_box(result);
});
});
// Benchmark floor
group.bench_function("floor_to_integer", |b| {
let value = 127.8;
b.iter(|| {
let result = black_box(value).floor() as usize;
black_box(result);
});
});
// Benchmark ceil
group.bench_function("ceil_to_integer", |b| {
let value = 127.8;
b.iter(|| {
let result = black_box(value).ceil() as usize;
black_box(result);
});
});
group.finish();
}
fn benchmark_serialization(c: &mut Criterion) {
let mut group = c.benchmark_group("serialization");
let best_params = BestHyperparameters {
learning_rate: 0.001,
batch_size: 64,
dropout: 0.2,
weight_decay: 0.0001,
best_validation_loss: 12.5,
trials_used: 30,
};
// Benchmark JSON serialization
group.bench_function("json_serialize", |b| {
b.iter(|| {
let json = serde_json::to_string(black_box(&best_params)).unwrap();
black_box(json);
});
});
// Benchmark JSON deserialization
let json = serde_json::to_string(&best_params).unwrap();
group.bench_function("json_deserialize", |b| {
b.iter(|| {
let result: BestHyperparameters =
serde_json::from_str(black_box(&json)).unwrap();
black_box(result);
});
});
// Benchmark YAML serialization
group.bench_function("yaml_serialize", |b| {
b.iter(|| {
let yaml = serde_yaml::to_string(black_box(&best_params)).unwrap();
black_box(yaml);
});
});
// Benchmark YAML deserialization
let yaml = serde_yaml::to_string(&best_params).unwrap();
group.bench_function("yaml_deserialize", |b| {
b.iter(|| {
let result: BestHyperparameters = serde_yaml::from_str(black_box(&yaml)).unwrap();
black_box(result);
});
});
group.finish();
}
fn benchmark_optimization_result_creation(c: &mut Criterion) {
let mut group = c.benchmark_group("result_creation");
// Benchmark creating OptimizationResult
for trial_count in [10, 30, 50, 100].iter() {
group.bench_with_input(
BenchmarkId::from_parameter(format!("trials_{}", trial_count)),
trial_count,
|b, &count| {
b.iter(|| {
let best_params = BestHyperparameters {
learning_rate: 0.001,
batch_size: 64,
dropout: 0.2,
weight_decay: 0.0001,
best_validation_loss: 12.5,
trials_used: count,
};
let trial_history: Vec<TrialResult> = (0..count)
.map(|i| TrialResult {
trial_number: i + 1,
learning_rate: 0.001,
batch_size: 64,
dropout: 0.2,
weight_decay: 0.0001,
validation_loss: 15.0 - i as f64 * 0.05,
training_time_seconds: 18.0,
})
.collect();
let result = OptimizationResult {
best_params,
trial_history,
};
black_box(result);
});
},
);
}
group.finish();
}
fn benchmark_array_creation(c: &mut Criterion) {
let mut group = c.benchmark_group("array_ops");
// Benchmark Array1 creation
group.bench_function("array1_from_vec", |b| {
let values = vec![0.1, 0.2, 0.3, 0.4];
b.iter(|| {
let arr = Array1::from_vec(black_box(values.clone()));
black_box(arr);
});
});
// Benchmark Array1 indexing
group.bench_function("array1_indexing", |b| {
let arr = Array1::from_vec(vec![0.1, 0.2, 0.3, 0.4]);
b.iter(|| {
let val = black_box(&arr)[0];
black_box(val);
});
});
// Benchmark Array1 to owned
group.bench_function("array1_to_owned", |b| {
let arr = Array1::from_vec(vec![0.1, 0.2, 0.3, 0.4]);
let view = arr.view();
b.iter(|| {
let owned = black_box(&view).to_owned();
black_box(owned);
});
});
group.finish();
}
fn benchmark_hyperparameter_space_creation(c: &mut Criterion) {
let mut group = c.benchmark_group("space_creation");
// Benchmark default space creation
group.bench_function("default_space", |b| {
b.iter(|| {
let space = HyperparameterSpace::default();
black_box(space);
});
});
// Benchmark custom space creation
group.bench_function("custom_space", |b| {
b.iter(|| {
let space = HyperparameterSpace {
learning_rate_log_min: -4.0,
learning_rate_log_max: -1.0,
batch_size_min: 32,
batch_size_max: 128,
dropout_min: 0.1,
dropout_max: 0.3,
weight_decay_log_min: -5.0,
weight_decay_log_max: -3.0,
};
black_box(space);
});
});
// Benchmark space cloning
group.bench_function("clone_space", |b| {
let space = HyperparameterSpace::default();
b.iter(|| {
let cloned = black_box(&space).clone();
black_box(cloned);
});
});
group.finish();
}
criterion_group!(
benches,
benchmark_param_conversion,
benchmark_log_scale_computation,
benchmark_batch_size_rounding,
benchmark_serialization,
benchmark_optimization_result_creation,
benchmark_array_creation,
benchmark_hyperparameter_space_creation
);
criterion_main!(benches);