-
Notifications
You must be signed in to change notification settings - Fork 3
Default and optional parameters #2
Description
Related to the goal of defining a set of common ML traits as discussed in #1, another question is how to define default and optional parameters in ML models.
For instance, let's take as an example the LogisticRegression estimator that takes as input following parameters (with their default values),
- regularization penalty:
penalty="l2" - Tolerance for stopping criteria:
tol=0.0001 - inverse of the regularization strenght:
C=1.0 - etc.
There are then multiple choices for initializing the model with some of these parameters,
1. Passing parameters explicitly + default method
// initialize the model with default parameters
let model = LogisticRegression::default();
// otherwise initialize all parameters explicitly
let model = LogisticRegression::new("l2", 0.001, 0.1);From a quick look, this pattern (or something similar) appears to be used in the rusty-machine crate (e.g. here). For models with 10+ parameters passing all of them explicitly seems hardly practical.
2. Builder pattern
One could also use the builder pattern and either construct a LogisticRegressionBuilder or maybe even use it directly with LogisticRegression,
use some_crate::linear_model::logistic_regression::Hyperparameters
// Other parameters are left at their default
let params = Hyperparameters::new()
.penalty("l1")
.tol(0.01);
// build a model with these parameters
let model = params.build();Is used by rustlearn as far as I can tell (e.g. here).
The advantage is that hyperparameters are already in a struct, which helps if you want to pass them between estimators or serialize them. The disadvantage is it requires to create a builder for each model. Also, I find that having multiple objects called Hyperparameters in the code base somewhat confusing (and it will definitely be an issue when searching the code for something).
3. Using struct update syntax
Possibly something around the struct update syntax, though I have not explored this topic too much.
struct LogisticRegression {
penalty: String,
tol: f64,
C: f64
}
impl LogisticRegression {
fn default() -> Self {
LogisticRegression {
penalty: "l2".to_string(),
tol: 0.001,
C: 1.0
}
}
// update one parameter, keep others as defaults
let model = LogisticRegression {tol: 0.1, .. LogisticRegression::default()};(Note: I have not tried to complile it to see if this actually works)
4. Other approaches
This topic was discussed at length in rust-lang/rfcs#323 and related RFCs, but I'm not sure what was accepted as of now or could be used in rust stable now (or in near future).
Comments would be very welcome. Please let me know if I missed something.