[![Build check](https://github.com/rmitsuboshi/miniboosts/actions/workflows/build.yaml/badge.svg)](https://github.com/rmitsuboshi/miniboosts/actions/workflows/build.yaml)
[Documentation][miniboosts]
*MiniBoosts* is a library for boosting algorithm researchers.
## What is **Boosting**?
Boosting is a repeated game between a *Booster* and a *Weak Learner*.
For each round of the game,
1. The *Booster* chooses a distribution over training examples,
2. Then the *Weak Learner* chooses a hypothesis (function)
whose accuracy w.r.t. the distribution is
slightly better than random guessing.
After sufficient rounds, the *Booster* outputs a hypothesis
that performs significantly better on training examples.
## How to use this library
Write the following in your `cargo.toml.`
```toml
[dependencies]
minibosts = { version = "0.3.5" }
```
All boosting algorithms are implemented without [Gurobi][gurobi].
but, if you have a [Gurobi][gurobi] license,
you can use the Gurobi version of the algorithms by setting:
```toml
[dependencies]
minibosts = { version = "0.3.5", features = ["gurobi"] }
```
> [!CAUTION]
> Since I am no longer a student, I cannot check whether the compilation succeeded with the `"gurobi"` flag.
Currently, following boosting algorithms are available:
|`BOOSTER` | `FEATURE FLAG` |
| :--- | :--- |
| [AdaBoost][adaboost]
by Freund and Schapire, 1997 | |
| [MadaBoost][madaboost]
by Domingo and Watanabe, 2000 | |
| [GBM][gbm] (Gradient Boosting Machine)
by Jerome H. Friedman, 2001 | |
| [LPBoost][lpboost]
by Demiriz, Bennett, and Shawe-Taylor, 2002 | `gurobi` |
| [SmoothBoost][smoothboost]
by Servedio, 2003 | |
| [AdaBoostV][adaboostv]
by Rätsch and Warmuth, 2005 | |
| [TotalBoost][totalboost]
by Warmuth, Liao, and Rätsch, 2006 | `gurobi` |
| [SoftBoost][softboost]
by Warmuth, Glocer, and Rätsch, 2007 | `gurobi` |
| [ERLPBoost][erlpboost]
by Warmuth and Glocer, and Vishwanathan, 2008 | `gurobi` |
| [CERLPBoost][cerlpboost] (Corrective ERLPBoost)
by Shalev-Shwartz and Singer, 2010 | `gurobi` |
| [MLPBoost][mlpboost]
by Mitsuboshi, Hatano, and Takimoto, 2022 | `gurobi` |
| [GraphSepBoost][graphsepboost] (Graph Separation Boosting)
by Alon, Gonen, Hazan, and Moran, 2023 | |
If you invent a new boosting algorithm,
you can introduce it by implementing `Booster` trait.
See `cargo doc -F gurobi --open` for details.
|`WEAK LEARNER` |
| :--- |
| [Decision Tree][decisiontree] |
| [Regression Tree][regressiontree] |
| [A worst-case weak learner for LPBoost][badbaselearner] |
| Gaussian Naive Bayes |
| Neural Network (Experimental) |
## Why *MiniBoosts*?
If you write a paper about boosting algorithms,
you need to compare your algorithm against others.
At this point, some issues arise.
- Some boosting algorithms,
such as [*LightGBM*][lightgbm] or [*XGBoost*][xgboost],
are implemented and available for free.
These are very easy to use in Python3 but hard to compare to other algorithms
since they are implemented in C++ internally.
Implementing your algorithm in Python3
makes the running time comparison unfair
(Python3 is significantly slow compared to C++).
However, implementing it in C++ is extremely hard (based on my experience).
- Most boosting algorithms are designed
for a decision-tree weak learner
even though the boosting protocol does not demand.
- There is no implementation for margin optimization boosting algorithms.
Margin optimization is a better goal than empirical risk minimization
in binary classification.
*MiniBoosts* is a crate to address the above issues.
This crate provides the followings.
- Two main traits, named `Booster` and `WeakLearner.`
- If you invent a new Boosting algorithm,
all you need is to implement `Booster.`
- If you invent a new Weak Learning algorithm,
all you need is to implement `WeakLearner.`
- Some famous boosting algorithms,
including [*AdaBoost*][adaboost],
[*LPBoost*][lpboost],
[*ERLPBoost*][erlpboost], etc.
- Some weak learners, including Decision-Tree, Regression-Tree, etc.
## *MiniBoosts* for reasearch
Sometimes, one wants to log each step of boosting procedure.
You can use `Logger` struct to output log to `.csv` file,
while printing the status like this:
![Research feature example](img/research-feature-example.png)
See [Research feature](#research-feature) section for detail.
## Examples
[Documentation][miniboosts]
Write the following to `Cargo.toml`.
```TOML
miniboosts = { version = "0.3.5" }
```
If you want to use `gurobi` features, enable the flag:
```TOML
miniboosts = { version = "0.3.5", features = ["gurobi"] }
```
Here is a sample code:
```rust
use miniboosts::prelude::*;
fn main() {
// Set file name
let file = "/path/to/input/data.csv";
// Read the CSV file
// The column named `class` corresponds to the labels (targets).
let sample = SampleReader::new()
.file(file)
.has_header(true)
.target_feature("class")
.read()
.unwrap();
// Set tolerance parameter as `0.01`.
let tol: f64 = 0.01;
// Initialize Booster
let mut booster = AdaBoost::init(&sample)
.tolerance(tol); // Set the tolerance parameter.
// Construct `DecisionTree` Weak Learner from `DecisionTreeBuilder`.
let weak_learner = DecisionTreeBuilder::new(&sample)
.max_depth(3) // Specify the max depth (default is 2)
.criterion(Criterion::Twoing) // Choose the split rule.
.build(); // Build `DecisionTree`.
// Run the boosting algorithm
// Each booster returns a combined hypothesis.
let f = booster.run(&weak_learner);
// Get the batch prediction for all examples in `data`.
let predictions = f.predict_all(&sample);
// You can predict the `i`th instance.
let i = 0_usize;
let prediction = f.predict(&sample, i);
// You can convert the hypothesis `f` to `String`.
let s = serde_json::to_string(&f);
}
```
If you use boosting for soft margin optimization,
initialize booster like this:
```rust
let n_sample = sample.shape().0; // Get the number of training examples
let nu = n_sample as f64 * 0.2; // Set the upper-bound of the number of outliers.
let lpboost = LPBoost::init(&sample)
.tolerance(tol)
.nu(nu); // Set a capping parameter.
```
Note that the capping parameter must satisfies `1 <= nu && nu <= n_sample`.
## Research feature
This crate can output a CSV file for such values in each step.
Here is an example:
```rust
use miniboosts::prelude::*;
use miniboosts::{
Logger,
LoggerBuilder,
SoftMarginObjective,
};
// Define a loss function
fn zero_one_loss(sample: &Sample, f: &H) -> f64
where H: Classifier
{
let n_sample = sample.shape().0 as f64;
let target = sample.target();
f.predict_all(sample)
.into_iter()
.zip(target.into_iter())
.map(|(fx, &y)| if fx != y as i64 { 1.0 } else { 0.0 })
.sum::()
/ n_sample
}
fn main() {
// Read the training data
let path = "/path/to/train/data.csv";
let train = SampleReader::new()
.file(path)
.has_header(true)
.target_feature("class")
.read()
.unwrap();
// Set some parameters used later.
let n_sample = train.shape().0 as f64;
let nu = 0.01 * n_sample;
// Read the test data
let path = "/path/to/test/data.csv";
let test = SampleReader::new()
.file(path)
.has_header(true)
.target_feature("class")
.read()
.unwrap();
let booster = LPBoost::init(&train)
.tolerance(0.01)
.nu(nu);
let weak_learner = DecisionTreeBuilder::new(&train)
.max_depth(2)
.criterion(Criterion::Entropy)
.build();
// Set the objective function.
// One can use your own function by implementing ObjectiveFunction trait.
let objective = SoftMarginObjective::new(nu);
let mut logger = LoggerBuilder::new()
.booster(booster)
.weak_learner(tree)
.train_sample(&train)
.test_sample(&test)
.objective_function(objective)
.loss_function(zero_one_loss)
.time_limit_as_secs(120) // Terminate after 120 seconds
.print_every(10) // Print log every 10 rounds.
.build();
// Each line of `lpboost.csv` contains the following four information:
// Objective value, Train loss, Test loss, Time per iteration
// The returned value `f` is the combined hypothesis.
let f = logger.run("logfile.csv")
.expect("Failed to logging");
}
```
## Others
- Currently, this crate mainly supports
boosting algorithms for binary classification.
- Some boosting algorithms use [Gurobi optimizer][gurobi],
so you must acquire a license to use this library.
If you have the license, you can use these boosting algorithms (boosters)
by specifying `features = ["gurobi"]` in `Cargo.toml`.
The compilation fails
if you try to use the gurobi feature without a Gurobi license.
- One can log your algorithm by implementing `Research` trait.
- Run `cargo doc -F gurobi --open` to see more information.
- `GraphSepBoost` only supports the aggregation rule
shown in Lemma 4.2 of their paper.
## Future work
- Boosters
- [AnyBoost][anyboost]
- [SparsiBoost][sparsiboost]
- [LogitBoost][logitboost]
- [AdaBoost.L][adaboostl]
- [Branching Program][branching]
- Weak Learners
- Bag of words
- TF-IDF
- [RBF-Net](https://link.springer.com/content/pdf/10.1023/A:1007618119488.pdf)
[adaboost]: https://www.sciencedirect.com/science/article/pii/S002200009791504X?via%3Dihub
[adaboostl]: https://link.springer.com/article/10.1023/A:1013912006537
[adaboostv]: http://jmlr.org/papers/v6/ratsch05a.html
[anyboost]: https://www.researchgate.net/publication/243689632_Functional_gradient_techniques_for_combining_hypotheses
[badbaselearner]: https://papers.nips.cc/paper_files/paper/2007/hash/cfbce4c1d7c425baf21d6b6f2babe6be-Abstract.html
[branching]: https://www.sciencedirect.com/science/article/pii/S0022000001917969
[cerlpboost]: https://link.springer.com/article/10.1007/s10994-010-5173-z
[decisiontree]: https://www.amazon.co.jp/-/en/Leo-Breiman/dp/0412048418
[erlpboost]: https://www.stat.purdue.edu/~vishy/papers/WarGloVis08.pdf
[gbm]: https://projecteuclid.org/journals/annals-of-statistics/volume-29/issue-5/Greedy-function-approximation-A-gradient-boostingmachine/10.1214/aos/1013203451.full
[graphsepboost]: https://theoretics.episciences.org/10757
[gurobi]: https://www.gurobi.com
[lightgbm]: https://github.com/microsoft/LightGBM
[logitboost]: https://projecteuclid.org/journals/annals-of-statistics/volume-28/issue-2/Additive-logistic-regression--a-statistical-view-of-boosting-With/10.1214/aos/1016218223.full
[lpboost]: https://link.springer.com/content/pdf/10.1023/A:1012470815092.pdf
[mlpboost]: https://arxiv.org/abs/2209.10831
[madaboost]: https://www.learningtheory.org/colt2000/papers/DomingoWatanabe.pdf
[regressiontree]: https://www.amazon.co.jp/-/en/Leo-Breiman/dp/0412048418
[smoothboost]: https://link.springer.com/chapter/10.1007/3-540-44581-1_31
[softboost]: https://proceedings.neurips.cc/paper/2007/file/cfbce4c1d7c425baf21d6b6f2babe6be-Paper.pdf
[sparsiboost]: http://proceedings.mlr.press/v97/mathiasen19a/mathiasen19a.pdf
[totalboost]: https://dl.acm.org/doi/10.1145/1143844.1143970
[xgboost]: https://github.com/dmlc/xgboost
[miniboosts]: https://docs.rs/miniboosts/latest/miniboosts/