Source code for sklearn_genetic.space.space

import numpy as np
from scipy import stats
import random

from .space_parameters import (
    IntegerDistributions,
    ContinuousDistributions,
    CategoricalDistributions,
)
from .base import BaseDimension


[docs]class Integer(BaseDimension): """class for hyperparameters search space of integer values""" def __init__( self, lower: int = None, upper: int = None, distribution: str = "uniform", random_state=None, ): """ Parameters ---------- lower : int, default=None Lower bound of the possible values of the hyperparameter. upper : int, default=None Upper bound of the possible values of the hyperparameter. distribution : str, default="uniform" Distribution to sample initial population and mutation values, currently only supports 'uniform'. random_state : int or None, RandomState instance, default=None Pseudo random number generator state used for random dimension sampling. """ if not isinstance(lower, int): raise ValueError("lower bound must be an integer") if not isinstance(upper, int): raise ValueError("upper bound must be an integer") if lower > upper: raise ValueError("The upper bound can not be smaller that the lower bound") if distribution not in IntegerDistributions.list(): raise ValueError( f"distribution must be one of {IntegerDistributions.list()}, got {distribution} instead" ) self.lower = lower self.upper = upper self.distribution = distribution self.random_state = random_state self.rng = None if not self.random_state else np.random.default_rng(self.random_state) if self.distribution == IntegerDistributions.uniform.value: self.rvs = stats.randint.rvs
[docs] def sample(self): """Sample a random value from the assigned distribution""" return self.rvs(self.lower, self.upper + 1, random_state=self.rng)
[docs]class Continuous(BaseDimension): """class for hyperparameters search space of real values""" def __init__( self, lower: float = None, upper: float = None, distribution: str = "uniform", random_state=None, ): """ Parameters ---------- lower : int, default=None Lower bound of the possible values of the hyperparameter. upper : int, default=None Upper bound of the possible values of the hyperparameter. distribution : {'uniform', 'log-uniform'}, default='uniform' Distribution to sample initial population and mutation values. random_state : int or None, RandomState instance, default=None Pseudo random number generator state used for random dimension sampling. """ if not isinstance(lower, (int, float)): raise ValueError("lower bound must be an integer or float") if not isinstance(upper, (int, float)): raise ValueError("upper bound must be an integer or float") if lower > upper: raise ValueError("The upper bound can not be smaller that the lower bound") if distribution not in ContinuousDistributions.list(): raise ValueError( f"distribution must be one of {ContinuousDistributions.list()}, got {distribution} instead" ) self.lower = lower self.upper = upper self.distribution = distribution self.shifted_upper = self.upper self.random_state = random_state self.rng = None if not self.random_state else np.random.default_rng(self.random_state) if self.distribution == ContinuousDistributions.uniform.value: self.rvs = stats.uniform.rvs self.shifted_upper = self.upper - self.lower elif self.distribution == ContinuousDistributions.log_uniform.value: self.rvs = stats.loguniform.rvs
[docs] def sample(self): """Sample a random value from the assigned distribution""" return self.rvs(self.lower, self.shifted_upper, random_state=self.rng)
[docs]class Categorical(BaseDimension): """class for hyperparameters search space of categorical values""" def __init__( self, choices: list = None, priors: list = None, distribution: str = "choice", random_state=None, ): """ Parameters ---------- choices: list, default=None List with all the possible values of the hyperparameter. priors: int, default=None List with the probability of sampling each element of the "choices", if not set gives equals probability. distribution: str, default='choice' Distribution to sample initial population and mutation values, currently only supports "choice". random_state : int or None, RandomState instance, default=None Pseudo random number generator state used for random dimension sampling. """ if not choices or not isinstance(choices, list): raise ValueError("choices must be a non empty list") if priors is None: self.priors = priors elif sum(priors) != 1: raise ValueError( f"The sum of the probabilities in the priors must be one, got {sum(priors)} instead" ) elif not len(priors) == len(choices): raise ValueError("priors and choices must have same size") else: self.priors = priors if distribution not in CategoricalDistributions.list(): raise ValueError( f"distribution must be one of {CategoricalDistributions.list()}, got {distribution} instead" ) self.choices = choices self.distribution = distribution self.random_state = random_state random.seed(random_state) self.rng = None if not self.random_state else np.random.default_rng(self.random_state) if self.distribution == CategoricalDistributions.choice.value: self.rvs = self.rng.choice if self.rng else random.choice
[docs] def sample(self): """Sample a random value from the assigned distribution""" return self.rvs(self.choices)
def check_space(param_grid: dict = None): """ Parameters ---------- param_grid: dict, default=None Dictionary with the for {"hyperparameter_name": :obj:`~sklearn_genetic.space`} Returns ------- Raises a Value Error if the dictionary does not have valid space instances """ if not param_grid: raise ValueError(f"param_grid can not be empty") # Make sure that each of the param_grid values are defined using one of the available Space objects for key, value in param_grid.items(): if not isinstance(value, BaseDimension): raise ValueError( f"{key} must be a valid instance of Integer, Categorical or Continuous classes" )
[docs]class Space(object): """Search space for all the models hyperparameters""" def __init__(self, param_grid: dict = None): """ Parameters ---------- param_grid: dict, default=None Grid with the parameters to tune, expects keys a valid name of hyperparameter based on the estimator selected and as values one of :class:`~sklearn_genetic.space.Integer` , :class:`~sklearn_genetic.space.Categorical` :class:`~sklearn_genetic.space.Continuous` classes """ check_space(param_grid) self.param_grid = param_grid @property def dimensions(self): """ Returns ------- The number of hyperparameters defined in the param_grid """ return len(self.param_grid) @property def parameters(self): """ Returns ------- A list with all the names of the hyperparametes in the param_Grid """ return list(self.param_grid.keys()) def __len__(self): return self.dimensions def __getitem__(self, index): return self.param_grid[index]