GridSearchSk._check_param_grid()   C
last analyzed

Complexity

Conditions 9

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 21
rs 6.6666
c 0
b 0
f 0
cc 9
nop 2
1
"""Grid search optimizer."""
2
# copyright: hyperactive developers, MIT License (see LICENSE file)
3
4
from collections.abc import Sequence
5
6
import numpy as np
7
8
from sklearn.model_selection import ParameterGrid
9
10
from hyperactive.base import BaseOptimizer
11
12
13
class GridSearchSk(BaseOptimizer):
14
    """Grid search optimizer, with backend selection and sklearn style parameter grid.
15
16
    Parameters
17
    ----------
18
    param_grid : dict[str, list]
19
        The search space to explore. A dictionary with parameter
20
        names as keys and a numpy array as values.
21
    error_score : float, default=np.nan
22
        The score to assign if an error occurs during the evaluation of a parameter set.
23
    experiment : BaseExperiment, optional
24
        The experiment to optimize parameters for.
25
        Optional, can be passed later via ``set_params``.
26
27
    Example
28
    -------
29
    Grid search applied to scikit-learn parameter tuning:
30
31
    1. defining the experiment to optimize:
32
    >>> from hyperactive.experiment.integrations import SklearnCvExperiment
33
    >>> from sklearn.datasets import load_iris
34
    >>> from sklearn.svm import SVC
35
    >>>
36
    >>> X, y = load_iris(return_X_y=True)
37
    >>>
38
    >>> sklearn_exp = SklearnCvExperiment(
39
    ...     estimator=SVC(),
40
    ...     X=X,
41
    ...     y=y,
42
    ... )
43
44
    2. setting up the grid search optimizer:
45
    >>> from hyperactive.opt import GridSearchSk as GridSearch
46
    >>> param_grid = {
47
    ...     "C": [0.01, 0.1, 1, 10],
48
    ...     "gamma": [0.0001, 0.01, 0.1, 1, 10],
49
    ... }
50
    >>> grid_search = GridSearch(param_grid, experiment=sklearn_exp)
51
52
    3. running the grid search:
53
    >>> best_params = grid_search.run()
54
55
    Best parameters can also be accessed via the attributes:
56
    >>> best_params = grid_search.best_params_
57
    """
58
59
    def __init__(
60
        self,
61
        param_grid=None,
62
        error_score=np.nan,
63
        experiment=None,
64
    ):
65
        self.experiment = experiment
66
        self.param_grid = param_grid
67
        self.error_score = error_score
68
69
        super().__init__()
70
71
    def _check_param_grid(self, param_grid):
72
        """_check_param_grid from sklearn 1.0.2, before it was removed."""
73
        if hasattr(param_grid, "items"):
74
            param_grid = [param_grid]
75
76
        for p in param_grid:
77
            for name, v in p.items():
78
                if isinstance(v, np.ndarray) and v.ndim > 1:
79
                    raise ValueError("Parameter array should be one-dimensional.")
80
81
                if isinstance(v, str) or not isinstance(v, (np.ndarray, Sequence)):
82
                    raise ValueError(
83
                        f"Parameter grid for parameter ({name}) needs to"
84
                        f" be a list or numpy array, but got ({type(v)})."
85
                        " Single values need to be wrapped in a list"
86
                        " with one element."
87
                    )
88
89
                if len(v) == 0:
90
                    raise ValueError(
91
                        f"Parameter values for parameter ({name}) need "
92
                        "to be a non-empty sequence."
93
                    )
94
95
    def _run(self, experiment, param_grid, error_score):
96
        """Run the optimization search process."""
97
        self._check_param_grid(param_grid)
98
        candidate_params = list(ParameterGrid(param_grid))
99
100
        scores = []
101
        for candidate_param in candidate_params:
102
            try:
103
                score = experiment(**candidate_param)
104
            except Exception:  # noqa: B904
105
                # Catch all exceptions and assign error_score
106
                score = error_score
107
            scores.append(score)
108
109
        best_index = np.argmin(scores)
110
        best_params = candidate_params[best_index]
111
112
        self.best_index_ = best_index
113
        self.best_score_ = scores[best_index]
114
115
        return best_params
116
117
    @classmethod
118
    def get_test_params(cls, parameter_set="default"):
119
        """Return testing parameter settings for the skbase object.
120
121
        ``get_test_params`` is a unified interface point to store
122
        parameter settings for testing purposes. This function is also
123
        used in ``create_test_instance`` and ``create_test_instances_and_names``
124
        to construct test instances.
125
126
        ``get_test_params`` should return a single ``dict``, or a ``list`` of ``dict``.
127
128
        Each ``dict`` is a parameter configuration for testing,
129
        and can be used to construct an "interesting" test instance.
130
        A call to ``cls(**params)`` should
131
        be valid for all dictionaries ``params`` in the return of ``get_test_params``.
132
133
        The ``get_test_params`` need not return fixed lists of dictionaries,
134
        it can also return dynamic or stochastic parameter settings.
135
136
        Parameters
137
        ----------
138
        parameter_set : str, default="default"
139
            Name of the set of test parameters to return, for use in tests. If no
140
            special parameters are defined for a value, will return `"default"` set.
141
142
        Returns
143
        -------
144
        params : dict or list of dict, default = {}
145
            Parameters to create testing instances of the class
146
            Each dict are parameters to construct an "interesting" test instance, i.e.,
147
            `MyClass(**params)` or `MyClass(**params[i])` creates a valid test instance.
148
            `create_test_instance` uses the first (or only) dictionary in `params`
149
        """
150
        from hyperactive.experiment.integrations import SklearnCvExperiment
151
152
        sklearn_exp = SklearnCvExperiment.create_test_instance()
153
        param_grid = {
154
            "C": [0.01, 0.1, 1, 10],
155
            "gamma": [0.0001, 0.01, 0.1, 1, 10],
156
        }
157
        params_sklearn = {
158
            "experiment": sklearn_exp,
159
            "param_grid": param_grid,
160
        }
161
162
        from hyperactive.experiment.toy import Ackley
163
164
        ackley_exp = Ackley.create_test_instance()
165
        param_grid = {
166
            "x0": np.linspace(-5, 5, 10),
167
            "x1": np.linspace(-5, 5, 10),
168
        }
169
        params_ackley = {
170
            "experiment": ackley_exp,
171
            "param_grid": param_grid,
172
        }
173
        
174
        return [params_sklearn, params_ackley]
175