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
321 lines
9.7 KiB
Rust
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);
|