nirdizati_light.hyperparameter_optimisation.common

  1from enum import Enum
  2from typing import Optional, Union
  3from predictive_model.predictive_model import PredictiveModel
  4import hyperopt
  5import numpy as np
  6from hyperopt import Trials, hp, fmin
  7from hyperopt.pyll import scope
  8
  9from nirdizati_light.predictive_model.common import ClassificationMethods, RegressionMethods
 10
 11
 12class HyperoptTarget(Enum):
 13    """
 14    Available targets for hyperparameter optimisation
 15    """
 16    AUC = 'auc'
 17    F1 = 'f1_score'
 18    MAE = 'mae'
 19    ACCURACY = 'accuracy'
 20
 21
 22def _get_space(model_type: Union[ClassificationMethods, RegressionMethods]) -> dict:
 23    if model_type is ClassificationMethods.RANDOM_FOREST.value:
 24        return {
 25            'n_estimators': hp.choice('n_estimators', np.arange(150, 1000, dtype=int)),
 26            'max_depth': scope.int(hp.quniform('max_depth', 4, 30, 1)),
 27            'max_features': hp.choice('max_features', ['sqrt', 'log2', None]),
 28            'criterion': hp.choice('criterion',['gini','entropy']),
 29            'warm_start': True
 30        }
 31    elif model_type is ClassificationMethods.DT.value:
 32        return {
 33            'max_depth': hp.choice('max_depth', range(1, 6)),
 34            'max_features': hp.choice('max_features', range(7, 50)),
 35            'criterion': hp.choice('criterion', ["gini", "entropy"]),
 36            'min_samples_split': hp.choice('min_samples_split', range(2, 10)),
 37            'min_samples_leaf': hp.choice('min_samples_leaf', range(1, 10)),
 38
 39        }
 40    elif model_type is ClassificationMethods.KNN.value:
 41        return {
 42            'n_neighbors': hp.choice('n_neighbors', np.arange(1, 20, dtype=int)),
 43            'weights': hp.choice('weights', ['uniform', 'distance']),
 44        }
 45
 46    elif model_type is ClassificationMethods.XGBOOST.value:
 47        return {
 48            'n_estimators': hp.choice('n_estimators', np.arange(150, 1000, dtype=int)),
 49            'max_depth': scope.int(hp.quniform('max_depth', 3, 30, 1)),
 50        }
 51
 52    elif model_type is ClassificationMethods.SGDCLASSIFIER.value:
 53        return {
 54            'loss': hp.choice('loss', ['hinge', 'log_loss', 'modified_huber', 'squared_hinge', 'perceptron', 'squared_error',
 55                                       'huber', 'epsilon_insensitive', 'squared_epsilon_insensitive']),
 56            'penalty': hp.choice('penalty', [None, 'l1', 'l2', 'elasticnet']),
 57            'alpha': hp.uniform('alpha', 0.0001, 0.5),
 58            'fit_intercept': hp.choice('fit_intercept', [True, False]),
 59            'tol': hp.uniform('tol', 1e-3, 0.5),
 60            'shuffle': hp.choice('shuffle', [True, False]),
 61            'eta0': hp.quniform('eta0', 0, 5, 1),
 62            # 'early_stopping': hp.choice('early_stopping', [True, False]), #needs to be false with partial_fit
 63            'validation_fraction': 0.1,
 64            'n_iter_no_change': scope.int(hp.quniform('n_iter_no_change', 1, 30, 5))
 65        }
 66    elif model_type is ClassificationMethods.SVM.value:
 67        return{
 68            'kernel' : hp.choice('kernel',['linear', 'poly', 'rbf', 'sigmoid', 'precomputed']),
 69            'C' : hp.uniform('C',0.1,1)
 70        }
 71    elif model_type is ClassificationMethods.PERCEPTRON.value:
 72        return {
 73            'penalty': hp.choice('penalty', [None, 'l1', 'l2', 'elasticnet']),
 74            'alpha': hp.uniform('alpha', 0.0001, 0.5),
 75            'fit_intercept': hp.choice('fit_intercept', [True, False]),
 76            'tol': hp.uniform('tol', 1e-3, 0.5),
 77            'shuffle': hp.choice('shuffle', [True, False]),
 78            'eta0': scope.int(hp.quniform('eta0', 4, 30, 1)),
 79            # 'early_stopping': hp.choice('early_stopping', [True, False]), #needs to be false with partial_fit
 80            'validation_fraction': 0.1,
 81            'n_iter_no_change': scope.int(hp.quniform('n_iter_no_change', 5, 30, 5))
 82        }
 83    elif model_type is ClassificationMethods.MLP.value:
 84        return {
 85            'hidden_layer_sizes': scope.int(hp.uniform('hidden_layer_sizes',10,100)),
 86            'alpha': hp.uniform('alpha', 0.0001, 0.5),
 87            'shuffle': hp.choice('shuffle', [True, False]),
 88#            'eta0': scope.int(hp.quniform('eta0', 4, 30, 1)),
 89            # 'early_stopping': hp.choice('early_stopping', [True, False]), #needs to be false with partial_fit
 90            'validation_fraction': 0.1,
 91            'n_iter_no_change': scope.int(hp.quniform('n_iter_no_change', 5, 30, 5))
 92        }
 93    elif model_type is ClassificationMethods.RANDOM_FOREST.value:
 94        return {
 95            'n_estimators': hp.choice('n_estimators', np.arange(150, 1000, dtype=int)),
 96            'max_depth': scope.int(hp.quniform('max_depth', 4, 30, 1)),
 97            'max_features': hp.choice('max_features', ['sqrt', 'log2', 'auto', None]),
 98            'warm_start': True
 99        }
100
101    elif model_type in [ClassificationMethods.LSTM.value, ClassificationMethods.CUSTOM_PYTORCH.value]:
102        return {
103            'max_num_epochs': 200,
104            'lr': hp.uniform('lr', 0.0001, 0.1),
105            'lstm_hidden_size': hp.choice('lstm_hidden_size', np.arange(50, 200, dtype=int)),
106            'lstm_num_layers': hp.choice('lstm_num_layers', np.arange(1, 3, dtype=int)),
107            'early_stop_patience': scope.int(hp.quniform('early_stop_patience', 5, 30, 5)),
108            'early_stop_min_delta': hp.uniform('early_stop_min_delta', 0.005, 0.05),
109            'batch_size': 128
110        }
111    elif model_type is RegressionMethods.RANDOM_FOREST.value:
112        return {
113            'n_estimators': hp.choice('n_estimators', np.arange(150, 1000, dtype=int)),
114            'max_depth': scope.int(hp.quniform('max_depth', 4, 30, 1)),
115            'max_features': hp.choice('max_features', ['sqrt', 'log2', None]),
116            'criterion': hp.choice('criterion', ['squared_error', 'friedman_mse', 'poisson', 'absolute_error']),
117            'warm_start': True
118        }
119    else:
120        raise Exception('Unsupported model_type')
121
122
123def retrieve_best_model(
124    predictive_models: list[PredictiveModel],
125    max_evaluations: int,
126    target: HyperoptTarget,
127    seed: Optional[int]=None
128):
129    """
130    Perform hyperparameter optimization on the given model.
131
132    Args:
133        predictive_models (list[PredictiveModel]): List of models to perform optimization on (each model must be of class PredictiveModel).
134        max_evaluations (int): Maximum number of hyperparameter configurations to try.
135        target (HyperoptTarget): Which target score to optimize for.
136        seed (Optional[int]): Optional seed value for reproducibility.
137
138    Returns:
139        tuple: A tuple containing the best candidates, the best model index, the best model, and the best hyperparameter configuration.
140    """
141
142    best_candidates = []
143    best_target_per_model = []
144
145    if type(predictive_models) is not list:
146        predictive_models = [predictive_models]
147
148    for predictive_model in predictive_models:
149        print(f'Running hyperparameter optimization on model {predictive_model.model_type}...')
150
151        space = _get_space(predictive_model.model_type)
152
153        # update hyperopt space with the user provided one, if available
154        if predictive_model.hyperopt_space is not None:
155            space.update(predictive_model.hyperopt_space)
156
157        trials = Trials()
158
159        fmin(
160            lambda x: predictive_model.train_and_evaluate_configuration(config=x, target=target),
161            space,
162            algo=hyperopt.tpe.suggest,
163            max_evals=max_evaluations,
164            trials=trials,rstate=np.random.default_rng(seed)
165        )
166        best_candidate = trials.best_trial['result']
167
168        best_candidates.append(best_candidate)
169        best_target_per_model.append(best_candidate['result'][target])
170
171    # Find the best performing model
172    best_model_idx = best_target_per_model.index(max(best_target_per_model))
173
174    return best_candidates, best_model_idx, best_candidates[best_model_idx]['model'], best_candidates[best_model_idx]['config']
class HyperoptTarget(enum.Enum):
13class HyperoptTarget(Enum):
14    """
15    Available targets for hyperparameter optimisation
16    """
17    AUC = 'auc'
18    F1 = 'f1_score'
19    MAE = 'mae'
20    ACCURACY = 'accuracy'

Available targets for hyperparameter optimisation

AUC = <HyperoptTarget.AUC: 'auc'>
F1 = <HyperoptTarget.F1: 'f1_score'>
MAE = <HyperoptTarget.MAE: 'mae'>
ACCURACY = <HyperoptTarget.ACCURACY: 'accuracy'>
Inherited Members
enum.Enum
name
value
def retrieve_best_model( predictive_models: list[predictive_model.predictive_model.PredictiveModel], max_evaluations: int, target: HyperoptTarget, seed: Optional[int] = None):
124def retrieve_best_model(
125    predictive_models: list[PredictiveModel],
126    max_evaluations: int,
127    target: HyperoptTarget,
128    seed: Optional[int]=None
129):
130    """
131    Perform hyperparameter optimization on the given model.
132
133    Args:
134        predictive_models (list[PredictiveModel]): List of models to perform optimization on (each model must be of class PredictiveModel).
135        max_evaluations (int): Maximum number of hyperparameter configurations to try.
136        target (HyperoptTarget): Which target score to optimize for.
137        seed (Optional[int]): Optional seed value for reproducibility.
138
139    Returns:
140        tuple: A tuple containing the best candidates, the best model index, the best model, and the best hyperparameter configuration.
141    """
142
143    best_candidates = []
144    best_target_per_model = []
145
146    if type(predictive_models) is not list:
147        predictive_models = [predictive_models]
148
149    for predictive_model in predictive_models:
150        print(f'Running hyperparameter optimization on model {predictive_model.model_type}...')
151
152        space = _get_space(predictive_model.model_type)
153
154        # update hyperopt space with the user provided one, if available
155        if predictive_model.hyperopt_space is not None:
156            space.update(predictive_model.hyperopt_space)
157
158        trials = Trials()
159
160        fmin(
161            lambda x: predictive_model.train_and_evaluate_configuration(config=x, target=target),
162            space,
163            algo=hyperopt.tpe.suggest,
164            max_evals=max_evaluations,
165            trials=trials,rstate=np.random.default_rng(seed)
166        )
167        best_candidate = trials.best_trial['result']
168
169        best_candidates.append(best_candidate)
170        best_target_per_model.append(best_candidate['result'][target])
171
172    # Find the best performing model
173    best_model_idx = best_target_per_model.index(max(best_target_per_model))
174
175    return best_candidates, best_model_idx, best_candidates[best_model_idx]['model'], best_candidates[best_model_idx]['config']

Perform hyperparameter optimization on the given model.

Arguments:
  • predictive_models (list[PredictiveModel]): List of models to perform optimization on (each model must be of class PredictiveModel).
  • max_evaluations (int): Maximum number of hyperparameter configurations to try.
  • target (HyperoptTarget): Which target score to optimize for.
  • seed (Optional[int]): Optional seed value for reproducibility.
Returns:

tuple: A tuple containing the best candidates, the best model index, the best model, and the best hyperparameter configuration.