Implement grid search
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@ authors = ["Manuel Raimann <raimannma@outlook.de"]
|
|||||||
description = "A Rust library for optimization algorithms."
|
description = "A Rust library for optimization algorithms."
|
||||||
repository = "https://github.com/raimannma/rust-optimizer"
|
repository = "https://github.com/raimannma/rust-optimizer"
|
||||||
documentation = "https://docs.rs/optimizer"
|
documentation = "https://docs.rs/optimizer"
|
||||||
keywords = ["optimization", "algorithms", "rust"]
|
keywords = ["optimization", "hyperparameter", "tpe", "grid-search", "bayesian"]
|
||||||
categories = ["algorithm", "science", "data-structures"]
|
categories = ["algorithm", "science", "data-structures"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# optimizer
|
# optimizer
|
||||||
|
|
||||||
A Rust library for black-box optimization using Tree-Parzen Estimator (TPE).
|
A Rust library for black-box optimization with multiple sampling strategies.
|
||||||
|
|
||||||
[](https://docs.rs/optimizer)
|
[](https://docs.rs/optimizer)
|
||||||
[](https://crates.io/crates/optimizer)
|
[](https://crates.io/crates/optimizer)
|
||||||
@@ -9,6 +9,10 @@ A Rust library for black-box optimization using Tree-Parzen Estimator (TPE).
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Optuna-like API for hyperparameter optimization
|
- Optuna-like API for hyperparameter optimization
|
||||||
|
- Multiple sampling strategies:
|
||||||
|
- **Random Search** - Simple random sampling for baseline comparisons
|
||||||
|
- **TPE (Tree-Parzen Estimator)** - Bayesian optimization for efficient search
|
||||||
|
- **Grid Search** - Exhaustive search over a specified parameter grid
|
||||||
- Float, integer, and categorical parameter types
|
- Float, integer, and categorical parameter types
|
||||||
- Log-scale and stepped parameter sampling
|
- Log-scale and stepped parameter sampling
|
||||||
- Sync and async optimization with parallel trial evaluation
|
- Sync and async optimization with parallel trial evaluation
|
||||||
@@ -16,9 +20,10 @@ A Rust library for black-box optimization using Tree-Parzen Estimator (TPE).
|
|||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use optimizer::{Direction, Study, TpeSampler};
|
use optimizer::{Direction, Study};
|
||||||
|
use optimizer::sampler::tpe::TpeSampler;
|
||||||
|
|
||||||
let sampler = TpeSampler::builder().seed(42).build();
|
let sampler = TpeSampler::builder().seed(42).build().unwrap();
|
||||||
let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
|
let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
|
||||||
|
|
||||||
study
|
study
|
||||||
@@ -32,6 +37,50 @@ let best = study.best_trial().unwrap();
|
|||||||
println!("Best value: {} at x={:?}", best.value, best.params);
|
println!("Best value: {} at x={:?}", best.value, best.params);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Samplers
|
||||||
|
|
||||||
|
### Random Search
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use optimizer::{Direction, Study};
|
||||||
|
use optimizer::sampler::random::RandomSampler;
|
||||||
|
|
||||||
|
let study: Study<f64> = Study::with_sampler(
|
||||||
|
Direction::Minimize,
|
||||||
|
RandomSampler::with_seed(42),
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### TPE (Tree-Parzen Estimator)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use optimizer::{Direction, Study};
|
||||||
|
use optimizer::sampler::tpe::TpeSampler;
|
||||||
|
|
||||||
|
let sampler = TpeSampler::builder()
|
||||||
|
.gamma(0.15) // Quantile for good/bad split
|
||||||
|
.n_startup_trials(20) // Random trials before TPE kicks in
|
||||||
|
.n_ei_candidates(32) // Candidates to evaluate
|
||||||
|
.seed(42)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Grid Search
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use optimizer::{Direction, Study};
|
||||||
|
use optimizer::sampler::grid::GridSearchSampler;
|
||||||
|
|
||||||
|
let sampler = GridSearchSampler::builder()
|
||||||
|
.n_points_per_param(10) // Number of points per parameter dimension
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
|
||||||
|
```
|
||||||
|
|
||||||
## Feature Flags
|
## Feature Flags
|
||||||
|
|
||||||
- `async` - Enable async optimization methods (requires tokio)
|
- `async` - Enable async optimization methods (requires tokio)
|
||||||
|
|||||||
+38
-4
@@ -9,10 +9,16 @@
|
|||||||
#![deny(clippy::pedantic)]
|
#![deny(clippy::pedantic)]
|
||||||
#![deny(clippy::std_instead_of_core)]
|
#![deny(clippy::std_instead_of_core)]
|
||||||
|
|
||||||
//! A Tree-Parzen Estimator (TPE) library for black-box optimization.
|
//! A black-box optimization library with multiple sampling strategies.
|
||||||
//!
|
//!
|
||||||
//! This library provides an Optuna-like API for hyperparameter optimization
|
//! This library provides an Optuna-like API for hyperparameter optimization
|
||||||
//! using the Tree-Parzen Estimator algorithm. It supports:
|
//! with support for multiple sampling algorithms:
|
||||||
|
//!
|
||||||
|
//! - **Random Search** - Simple random sampling for baseline comparisons
|
||||||
|
//! - **TPE (Tree-Parzen Estimator)** - Bayesian optimization for efficient search
|
||||||
|
//! - **Grid Search** - Exhaustive search over a specified parameter grid
|
||||||
|
//!
|
||||||
|
//! Additional features include:
|
||||||
//!
|
//!
|
||||||
//! - Float, integer, and categorical parameter types
|
//! - Float, integer, and categorical parameter types
|
||||||
//! - Log-scale and stepped parameter sampling
|
//! - Log-scale and stepped parameter sampling
|
||||||
@@ -91,9 +97,22 @@
|
|||||||
//! .unwrap();
|
//! .unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # Configuring TPE
|
//! # Available Samplers
|
||||||
//!
|
//!
|
||||||
//! The [`sampler::tpe::TpeSampler`] can be configured using the builder pattern:
|
//! ## Random Search
|
||||||
|
//!
|
||||||
|
//! The simplest sampling strategy, useful for baselines:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use optimizer::sampler::random::RandomSampler;
|
||||||
|
//! use optimizer::{Direction, Study};
|
||||||
|
//!
|
||||||
|
//! let study: Study<f64> = Study::with_sampler(Direction::Minimize, RandomSampler::with_seed(42));
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## TPE (Tree-Parzen Estimator)
|
||||||
|
//!
|
||||||
|
//! Bayesian optimization that learns from previous trials:
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use optimizer::sampler::tpe::TpeSampler;
|
//! use optimizer::sampler::tpe::TpeSampler;
|
||||||
@@ -107,6 +126,21 @@
|
|||||||
//! .unwrap();
|
//! .unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! ## Grid Search
|
||||||
|
//!
|
||||||
|
//! Exhaustive search over a discretized parameter space:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use optimizer::sampler::grid::GridSearchSampler;
|
||||||
|
//! use optimizer::{Direction, Study};
|
||||||
|
//!
|
||||||
|
//! let sampler = GridSearchSampler::builder()
|
||||||
|
//! .n_points_per_param(10) // Points per parameter dimension
|
||||||
|
//! .build();
|
||||||
|
//!
|
||||||
|
//! let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
//! # Async and Parallel Optimization
|
//! # Async and Parallel Optimization
|
||||||
//!
|
//!
|
||||||
//! With the `async` feature enabled, you can run trials asynchronously:
|
//! With the `async` feature enabled, you can run trials asynchronously:
|
||||||
|
|||||||
+1157
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
|||||||
//! Sampler trait and implementations for parameter sampling.
|
//! Sampler trait and implementations for parameter sampling.
|
||||||
|
|
||||||
|
pub mod grid;
|
||||||
pub mod random;
|
pub mod random;
|
||||||
pub mod tpe;
|
pub mod tpe;
|
||||||
|
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ fn test_random_sampler_uniform_float_distribution() {
|
|||||||
fn test_random_sampler_uniform_int_distribution() {
|
fn test_random_sampler_uniform_int_distribution() {
|
||||||
let study: Study<f64> = Study::with_sampler(Direction::Minimize, RandomSampler::with_seed(123));
|
let study: Study<f64> = Study::with_sampler(Direction::Minimize, RandomSampler::with_seed(123));
|
||||||
|
|
||||||
let n_samples = 1000;
|
let n_samples = 5000;
|
||||||
let mut counts = [0u32; 10]; // counts for values 1-10
|
let mut counts = [0u32; 10]; // counts for values 1-10
|
||||||
|
|
||||||
study
|
study
|
||||||
@@ -165,11 +165,13 @@ fn test_random_sampler_uniform_int_distribution() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Each value should appear roughly n_samples / 10 times
|
// Each value should appear roughly n_samples / 10 times
|
||||||
|
// With 5000 samples, expected ~500 per bucket, std dev ~21
|
||||||
|
// 20% tolerance allows for ~4.5 std devs which is very safe
|
||||||
let expected = n_samples as f64 / 10.0;
|
let expected = n_samples as f64 / 10.0;
|
||||||
for (i, &count) in counts.iter().enumerate() {
|
for (i, &count) in counts.iter().enumerate() {
|
||||||
let diff = (count as f64 - expected).abs() / expected;
|
let diff = (count as f64 - expected).abs() / expected;
|
||||||
assert!(
|
assert!(
|
||||||
diff < 0.3,
|
diff < 0.2,
|
||||||
"value {} appeared {} times, expected ~{}, diff = {:.1}%",
|
"value {} appeared {} times, expected ~{}, diff = {:.1}%",
|
||||||
i + 1,
|
i + 1,
|
||||||
count,
|
count,
|
||||||
@@ -183,7 +185,7 @@ fn test_random_sampler_uniform_int_distribution() {
|
|||||||
fn test_random_sampler_uniform_categorical_distribution() {
|
fn test_random_sampler_uniform_categorical_distribution() {
|
||||||
let study: Study<f64> = Study::with_sampler(Direction::Minimize, RandomSampler::with_seed(456));
|
let study: Study<f64> = Study::with_sampler(Direction::Minimize, RandomSampler::with_seed(456));
|
||||||
|
|
||||||
let n_samples = 1000;
|
let n_samples = 2000;
|
||||||
let mut counts = [0u32; 4];
|
let mut counts = [0u32; 4];
|
||||||
let choices = ["a", "b", "c", "d"];
|
let choices = ["a", "b", "c", "d"];
|
||||||
|
|
||||||
@@ -197,11 +199,13 @@ fn test_random_sampler_uniform_categorical_distribution() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Each category should appear roughly n_samples / 4 times
|
// Each category should appear roughly n_samples / 4 times
|
||||||
|
// With 2000 samples, expected ~500 per bucket, std dev ~19
|
||||||
|
// 15% tolerance allows for ~4 std devs which is very safe
|
||||||
let expected = n_samples as f64 / 4.0;
|
let expected = n_samples as f64 / 4.0;
|
||||||
for (i, &count) in counts.iter().enumerate() {
|
for (i, &count) in counts.iter().enumerate() {
|
||||||
let diff = (count as f64 - expected).abs() / expected;
|
let diff = (count as f64 - expected).abs() / expected;
|
||||||
assert!(
|
assert!(
|
||||||
diff < 0.25,
|
diff < 0.15,
|
||||||
"category {} appeared {} times, expected ~{}, diff = {:.1}%",
|
"category {} appeared {} times, expected ~{}, diff = {:.1}%",
|
||||||
i,
|
i,
|
||||||
count,
|
count,
|
||||||
|
|||||||
Reference in New Issue
Block a user