Params

Base class Params used to define hyper-parameters.

Params class

class letstune.Params

Base class used to define hyper-parameters.

Example for neural network training digits recognition:

>>> class DigitsTrainingParams(Params):
...     layer_number: int
...     learning_rate: float
...
>>> params = DigitsTrainingParams(layer_number=2, learning_rate=0.1)
>>> params
DigitsTrainingParams(layer_number=2, learning_rate=0.1)

Random params generation

You can assign random generators to fields and use them to get random DigitsTrainingParams:

>>> from letstune import rand
>>> import numpy as np
...
>>> class DigitsTrainingParams(Params):
...     layer_number: int = rand.oneof([1, 2, 3])
...     learning_rate: float = rand.uniform(0.01, 0.1)
...
>>> rng = np.random.default_rng(42)
>>> DigitsTrainingParams.get_random_params(rng)  
DigitsTrainingParams(layer_number=1, learning_rate=...)

Params can have defined parameter sampling in get_random_params() method:

>>> class DigitsTrainingParams(Params):
...     layer_number: int
...     learning_rate: float
...
...     @classmethod
...     def get_random_params(cls, rng):
...         return DigitsTrainingParams(
...             layer_number=rng.integers(1, 4),
...             learning_rate=rng.exponential(0.1),
...         )
>>> rng = np.random.default_rng(42)
>>> DigitsTrainingParams.get_random_params(rng)  
DigitsTrainingParams(layer_number=1, learning_rate=...)

Nested params

You can compose Params classes to express more sophisticated experiments.

>>> class NeuralNetworkParams(Params):
...     layer_number: int = rand.oneof([1, 2, 3])
...     channels: int = rand.oneof([64, 128, 256])
...
>>> class DigitsTrainingParams(Params):
...     neural_network: NeuralNetworkParams
...     learning_rate: float = rand.uniform(0.01, 0.1)
>>> rng = np.random.default_rng(42)
>>> DigitsTrainingParams.get_random_params(rng)  
DigitsTrainingParams(neural_network=NeuralNetworkParams(layer_number=1, channels=256), learning_rate=...)

Union types

You can have different parameters for various kinds of your model. This way letstune can help you decide, which model family is the best for your problem.

>>> class LassoParams(Params):
...     alpha: float = rand.uniform(0.01, 0.1)
...
>>> class RandomForestParams(Params):
...     min_samples_split: int = rand.ints(1, 5)
...     max_features: str = rand.oneof(["sqrt", "log2"])
...
>>> class ExperimentParams(Params):
...     model_params: LassoParams | RandomForestParams
...     pca_components: int = rand.oneof([16, 32, 64])
...
>>> rng = np.random.default_rng(48)
>>> ExperimentParams.get_random_params(rng)  
ExperimentParams(model_params=LassoParams(alpha=...), pca_components=32)
>>> ExperimentParams.get_random_params(rng)  
ExperimentParams(model_params=RandomForestParams(min_samples_split=3, max_features='sqrt'), pca_components=64)

Notice, that get_random_params() returns various classes of model_params.

create_model(**kwargs: Any) Any

Returns a model of a type declared in model_cls.

The model is created with arguments collected from the params. Additionally, it passes arguments given directly to the create_model() method.

Notice, that arguments passed to create_model() have precedence over the arguments from params.

final classmethod from_json(json: Any) SelfParams

Creates params instance from JSON, which was produced by to_json() method.

>>> class NeuralNetworkParams(Params):
...     layer_number: int
...     channels: int
...
>>> class DigitsTrainingParams(Params):
...     neural_network: NeuralNetworkParams
...     learning_rate: float
>>> j = {
...     'neural_network': {
...         'layer_number': 5,
...         'channels': 256
...     },
...     'learning_rate': 0.1
... }
>>> params = DigitsTrainingParams.from_json(j)
>>> params
DigitsTrainingParams(neural_network=NeuralNetworkParams(layer_number=5, channels=256), learning_rate=0.1)

Warning

from_json() function is not secure. If you do not trust the input data, please verify schema of the input JSON before calling from_json().

classmethod get_random_params(rng: Generator) SelfParams

Get random instance of the params.

Default random generator

The default implementation chooses a random generator for each field:

  • If random generator is manually passed, it is used.

  • Otherwise, a random generator is deduced from the declared field type.

  • If there is no default random generator for the given type, then letstune.params.NoDefaultRandomGeneratorError is raised.

Random generator from a type

If a given type has get_random_params method, then it is used.

bool is assigned True or False, each with 50% probability.

If a given type is a union, such as MyParams1 | MyParams2 | MyParams3, then it is assigned:

rand.oneof([MyParams1, MyParams2, MyParams3])
final to_dict() dict[str, Any]

Converts given params to a dict.

Notice, this is not a recursive operation.

>>> class NeuralNetworkParams(Params):
...     layer_number: int
...     channels: int
...
>>> class DigitsTrainingParams(Params):
...     neural_network: NeuralNetworkParams
...     learning_rate: float
...
>>> params = DigitsTrainingParams(
...     neural_network=NeuralNetworkParams(
...         layer_number=5,
...         channels=256
...     ),
...     learning_rate=0.1,
... )
...
>>> params.to_dict()
{'neural_network': NeuralNetworkParams(layer_number=5, channels=256), 'learning_rate': 0.1}
final to_json(*, add_union_type: bool = False) dict[str, Any]

Converts given params to JSON.

>>> class NeuralNetworkParams(Params):
...     layer_number: int
...     channels: int
...
>>> class DigitsTrainingParams(Params):
...     neural_network: NeuralNetworkParams
...     learning_rate: float
...
>>> params = DigitsTrainingParams(
...     neural_network=NeuralNetworkParams(
...         layer_number=5,
...         channels=256
...     ),
...     learning_rate=0.1,
... )
...
>>> params.to_json()
{'neural_network': {'layer_number': 5, 'channels': 256}, 'learning_rate': 0.1}

In case of union types, it wraps object in a dict with exactly one key - the name of the used type.

>>> class LassoParams(Params):
...     alpha: float
...
>>> class RandomForestParams(Params):
...     min_samples_split: int
...     max_features: str
...
>>> class ExperimentParams(Params):
...     model_params: LassoParams | RandomForestParams
...     pca_components: int
...
>>> params = ExperimentParams(
...     model_params=LassoParams(alpha=0.7),
...     pca_components=4
... )
>>> params.to_json()
{'model_params': {'LassoParams': {'alpha': 0.7}}, 'pca_components': 4}
>>> params = ExperimentParams(
...     model_params=RandomForestParams(
...         min_samples_split=4,
...         max_features="log2",
...     ),
...     pca_components=4
... )
>>> import json
>>> print(json.dumps(params.to_json(), indent=4))
{
    "model_params": {
        "RandomForestParams": {
            "min_samples_split": 4,
            "max_features": "log2"
        }
    },
    "pca_components": 4
}

If add_union_type is True, then the type of the object is added th the dict:

>>> print(json.dumps(params.to_json(add_union_type=True), indent=4))
{
    "model_params": {
        "RandomForestParams": {
            "min_samples_split": 4,
            "max_features": "log2"
        },
        "type": "RandomForestParams"
    },
    "pca_components": 4
}

Exceptions

exception letstune.params.NoDefaultRandomGeneratorError(qualname: str, field_name: str | None = None)

Bases: Exception

Raised when Params cannot derive default implementation of get_random_params for the given class.