Passed
Push — master ( c241e4...b050e9 )
by Simon
01:57
created

GridOptimizer.__init__()   A

Complexity

Conditions 1

Size

Total Lines 21
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 21
rs 9.45
c 0
b 0
f 0
cc 1
nop 9

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
"""Grid optimizer."""
2
# copyright: hyperactive developers, MIT License (see LICENSE file)
3
4
from .._adapters._base_optuna_adapter import _BaseOptunaAdapter
5
6
7
class GridOptimizer(_BaseOptunaAdapter):
8
    """Grid search optimizer.
9
10
    Parameters
11
    ----------
12
    param_space : dict[str, tuple or list or optuna distributions]
13
        The search space to explore. Dictionary with parameter names
14
        as keys and either tuples/lists of (low, high) or
15
        optuna distribution objects as values.
16
    n_trials : int, default=100
17
        Number of optimization trials.
18
    initialize : dict[str, int], default=None
19
        The method to generate initial positions. A dictionary with
20
        the following key literals and the corresponding value type:
21
        {"grid": int, "vertices": int, "random": int, "warm_start": list[dict]}
22
    random_state : None, int, default=None
23
        If None, create a new random state. If int, create a new random state
24
        seeded with the value.
25
    early_stopping : int, default=None
26
        Number of trials after which to stop if no improvement.
27
    max_score : float, default=None
28
        Maximum score threshold. Stop optimization when reached.
29
    search_space : dict, default=None
30
        Explicit search space for grid search.
31
    experiment : BaseExperiment, optional
32
        The experiment to optimize parameters for.
33
        Optional, can be passed later via ``set_params``.
34
35
    Examples
36
    --------
37
    Basic usage of GridOptimizer with a scikit-learn experiment:
38
39
    >>> from hyperactive.experiment.integrations import SklearnCvExperiment
40
    >>> from hyperactive.opt.optuna import GridOptimizer
41
    >>> from sklearn.datasets import load_iris
42
    >>> from sklearn.svm import SVC
43
    >>> X, y = load_iris(return_X_y=True)
44
    >>> sklearn_exp = SklearnCvExperiment(estimator=SVC(), X=X, y=y)
45
    >>> param_space = {
46
    ...     "C": [0.01, 0.1, 1, 10],
47
    ...     "gamma": [0.0001, 0.01, 0.1, 1],
48
    ... }
49
    >>> optimizer = GridOptimizer(
50
    ...     param_space=param_space, n_trials=50, experiment=sklearn_exp
51
    ... )
52
    >>> best_params = optimizer.run()
53
    """
54
55
    _tags = {
56
        "info:name": "Grid Optimizer",
57
        "info:local_vs_global": "global",
58
        "info:explore_vs_exploit": "explore",
59
        "info:compute": "low",
60
        "python_dependencies": ["optuna"],
61
    }
62
63
    def __init__(
64
        self,
65
        param_space=None,
66
        n_trials=100,
67
        initialize=None,
68
        random_state=None,
69
        early_stopping=None,
70
        max_score=None,
71
        search_space=None,
72
        experiment=None,
73
    ):
74
        self.search_space = search_space
75
76
        super().__init__(
77
            param_space=param_space,
78
            n_trials=n_trials,
79
            initialize=initialize,
80
            random_state=random_state,
81
            early_stopping=early_stopping,
82
            max_score=max_score,
83
            experiment=experiment,
84
        )
85
86
    def _get_optimizer(self):
87
        """Get the Grid optimizer.
88
89
        Returns
90
        -------
91
        optimizer
92
            The Optuna GridOptimizer instance
93
        """
94
        import optuna
95
96
        # Convert param_space to Optuna search space format if needed
97
        search_space = self.search_space
98
        if search_space is None and self.param_space is not None:
99
            search_space = {}
100
            for key, space in self.param_space.items():
101
                if isinstance(space, list):
102
                    search_space[key] = space
103
                elif isinstance(space, (tuple,)) and len(space) == 2:
104
                    # Convert range to discrete list for grid search
105
                    low, high = space
106
                    if isinstance(low, int) and isinstance(high, int):
107
                        search_space[key] = list(range(low, high + 1))
108
                    else:
109
                        # Create a reasonable grid for continuous spaces
110
                        import numpy as np
111
112
                        search_space[key] = np.linspace(low, high, 10).tolist()
113
114
        return optuna.samplers.GridSampler(search_space)
115
116
    @classmethod
117
    def get_test_params(cls, parameter_set="default"):
118
        """Return testing parameter settings for the optimizer."""
119
        from sklearn.datasets import load_iris
120
        from sklearn.neighbors import KNeighborsClassifier
121
        from sklearn.svm import SVC
122
123
        from hyperactive.experiment.integrations import SklearnCvExperiment
124
125
        X, y = load_iris(return_X_y=True)
126
127
        # Test case 1: Basic continuous parameters (converted to discrete)
128
        svm_exp = SklearnCvExperiment(estimator=SVC(), X=X, y=y)
129
        param_space_1 = {
130
            "C": [0.01, 0.1, 1, 10],
131
            "gamma": [0.0001, 0.01, 0.1, 1],
132
        }
133
134
        # Test case 2: Mixed categorical and discrete parameters
135
        knn_exp = SklearnCvExperiment(estimator=KNeighborsClassifier(), X=X, y=y)
136
        param_space_2 = {
137
            "n_neighbors": [1, 3, 5, 7],  # Discrete integers
138
            "weights": ["uniform", "distance"],  # Categorical
139
            "metric": ["euclidean", "manhattan"],  # Categorical
140
            "p": [1, 2],  # Discrete for minkowski
141
        }
142
143
        # Test case 3: Small exhaustive grid (tests complete enumeration)
144
        param_space_3 = {
145
            "C": [0.1, 1],  # 2 values
146
            "kernel": ["rbf", "linear"],  # 2 values
147
        }
148
        # Total: 2 x 2 = 4 combinations, n_trials should cover all
149
150
        return [
151
            {
152
                "param_space": param_space_1,
153
                "n_trials": 10,
154
                "experiment": svm_exp,
155
            },
156
            {
157
                "param_space": param_space_2,
158
                "n_trials": 15,
159
                "experiment": knn_exp,
160
            },
161
            {
162
                "param_space": param_space_3,
163
                "n_trials": 4,  # Exact number for exhaustive search
164
                "experiment": svm_exp,
165
            },
166
        ]
167