hypershap 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hypershap/__init__.py +18 -0
- hypershap/games/__init__.py +18 -0
- hypershap/games/ablation.py +101 -0
- hypershap/games/abstract.py +161 -0
- hypershap/games/optimizerbias.py +115 -0
- hypershap/games/tunability.py +183 -0
- hypershap/hypershap.py +409 -0
- hypershap/surrogate_model.py +235 -0
- hypershap/task.py +265 -0
- hypershap/utils.py +126 -0
- hypershap-0.0.1.dist-info/METADATA +168 -0
- hypershap-0.0.1.dist-info/RECORD +14 -0
- hypershap-0.0.1.dist-info/WHEEL +4 -0
- hypershap-0.0.1.dist-info/licenses/LICENSE +28 -0
hypershap/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""HyperSHAP: Shapley values and interactions for explaining hyperparameter optimization.
|
|
2
|
+
|
|
3
|
+
HyperSHAP is a package for explaining hyperparameter optimization, i.e., the effect of specific hyperparameter values,
|
|
4
|
+
the tunability of machine learning algorithms, or biases in the optimizer. The package is based on the well established
|
|
5
|
+
Shapley value and its generalization to interactions.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from .hypershap import HyperSHAP
|
|
11
|
+
from .task import ExplanationTask
|
|
12
|
+
from .utils import ConfigSpaceSearcher
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"ConfigSpaceSearcher",
|
|
16
|
+
"ExplanationTask",
|
|
17
|
+
"HyperSHAP",
|
|
18
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""The games module contains all the game-theoretic explanation games of HyperSHAP."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .ablation import AblationGame
|
|
6
|
+
from .abstract import AbstractHPIGame
|
|
7
|
+
from .optimizerbias import OptimizerBiasGame
|
|
8
|
+
from .tunability import MistunabilityGame, SearchBasedGame, SensitivityGame, TunabilityGame
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"AblationGame",
|
|
12
|
+
"AbstractHPIGame",
|
|
13
|
+
"MistunabilityGame",
|
|
14
|
+
"OptimizerBiasGame",
|
|
15
|
+
"SearchBasedGame",
|
|
16
|
+
"SensitivityGame",
|
|
17
|
+
"TunabilityGame",
|
|
18
|
+
]
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Module for ablation-based Hyperparameter Importance analysis.
|
|
2
|
+
|
|
3
|
+
This module provides the `AblationGame` class for analyzing hyperparameter
|
|
4
|
+
interactions through ablation-based experiments. It leverages a surrogate model
|
|
5
|
+
to estimate the impact of individual hyperparameters by systematically combining
|
|
6
|
+
baseline and optimized configuration values. The `AblationGame` allows for
|
|
7
|
+
understanding which hyperparameters contribute most to performance gains and
|
|
8
|
+
identifying potential areas for further tuning.
|
|
9
|
+
|
|
10
|
+
The module is designed to work in conjunction with the `hypershap.games.abstract`
|
|
11
|
+
module and relies on the `hypershap.task` module for defining the
|
|
12
|
+
hyperparameter search space and surrogate model.
|
|
13
|
+
|
|
14
|
+
Key components include:
|
|
15
|
+
|
|
16
|
+
* **`AblationGame`**: A game class that evaluates the performance of
|
|
17
|
+
configurations generated by blending baseline and optimized hyperparameter
|
|
18
|
+
values, providing insights into individual hyperparameter contributions.
|
|
19
|
+
|
|
20
|
+
This module facilitates a deeper understanding of hyperparameter dependencies
|
|
21
|
+
and guides optimization efforts by revealing the impact of each hyperparameter
|
|
22
|
+
in a systematic and interpretable manner.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import numpy as np
|
|
28
|
+
|
|
29
|
+
from hypershap.games.abstract import AbstractHPIGame
|
|
30
|
+
from hypershap.task import AblationExplanationTask
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class AblationGame(AbstractHPIGame):
|
|
34
|
+
"""The ablation game generates local explanations for hyperparameter configurations.
|
|
35
|
+
|
|
36
|
+
It does so by evaluating all potential ablation paths, switching from a baseline configuration
|
|
37
|
+
to an optimized configuration value by value. It leverages a surrogate model to estimate
|
|
38
|
+
performance based on blended configurations.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
explanation_task: AblationExplanationTask,
|
|
44
|
+
n_workers: int | None = None,
|
|
45
|
+
verbose: bool | None = None,
|
|
46
|
+
) -> None:
|
|
47
|
+
"""Initialize an AblationGame.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
explanation_task (AblationExplanationTask): An instance of `AblationExplanationTask`
|
|
51
|
+
providing access to the baseline configuration, configuration of interest,
|
|
52
|
+
and the surrogate model for evaluation.
|
|
53
|
+
n_workers (int | None): The number of worker threads to use for parallel
|
|
54
|
+
evaluation of coalitions. Defaults to None, which disables parallelization.
|
|
55
|
+
Using more workers can significantly speed up the computation of Shapley values.
|
|
56
|
+
The maximum number of workers is capped by the number of coalitions.
|
|
57
|
+
verbose (bool | None): A boolean indicating whether to print verbose messages
|
|
58
|
+
during computation. Defaults to None. When set to True, the method prints
|
|
59
|
+
debugging information and progress updates.
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
super().__init__(explanation_task, n_workers=n_workers, verbose=verbose)
|
|
63
|
+
|
|
64
|
+
def evaluate_single_coalition(self, coalition: np.ndarray) -> float:
|
|
65
|
+
"""Evaluate a single coalition (combination of baseline and optimized hyperparameters).
|
|
66
|
+
|
|
67
|
+
This method blends the baseline and optimized configurations based on the provided coalition,
|
|
68
|
+
then uses the surrogate model to estimate the performance of the blended configuration.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
coalition (np.ndarray): A Boolean array indicating which hyperparameters should be taken from the
|
|
72
|
+
baseline configuration (False) and which from the configuration of interest (True).
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
float: The performance score of the blended configuration as estimated by the surrogate model.
|
|
76
|
+
|
|
77
|
+
"""
|
|
78
|
+
baseline_cfg = self._get_explanation_task().baseline_config.get_array()
|
|
79
|
+
cfg_of_interest = self._get_explanation_task().config_of_interest.get_array()
|
|
80
|
+
blend = np.where(coalition == 0, baseline_cfg, cfg_of_interest)
|
|
81
|
+
res = self._get_explanation_task().surrogate_model.evaluate(blend)
|
|
82
|
+
|
|
83
|
+
# validate that we do not get a list of floats by accident
|
|
84
|
+
if isinstance(res, list):
|
|
85
|
+
raise TypeError
|
|
86
|
+
|
|
87
|
+
return res
|
|
88
|
+
|
|
89
|
+
def _get_explanation_task(self) -> AblationExplanationTask:
|
|
90
|
+
"""Retrieve the explanation task associated with this ablation game.
|
|
91
|
+
|
|
92
|
+
This method simply returns the `explanation_task` attribute, which provides
|
|
93
|
+
access to the baseline configuration, configuration of interest, and surrogate model.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
AblationExplanationTask: The explanation task associated with this ablation game.
|
|
97
|
+
|
|
98
|
+
"""
|
|
99
|
+
if isinstance(self.explanation_task, AblationExplanationTask):
|
|
100
|
+
return self.explanation_task
|
|
101
|
+
raise ValueError
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""The abstract module defines the abstract base class `AbstractHPIGame` for analyzing HPI.
|
|
2
|
+
|
|
3
|
+
The `AbstractHPIGame` class provides a foundational structure for constructing
|
|
4
|
+
and analyzing games that represent interactions between hyperparameters,
|
|
5
|
+
enabling a deeper understanding of their impact on optimization performance.
|
|
6
|
+
It handles initialization, normalization, and provides a framework for
|
|
7
|
+
parallel evaluation of coalitions. Subclasses must implement the
|
|
8
|
+
`evaluate_single_coalition` method to define the game-specific evaluation logic.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from abc import abstractmethod
|
|
14
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
from shapiq import Game
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from collections.abc import Iterable
|
|
22
|
+
|
|
23
|
+
from hypershap.task import ExplanationTask
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AbstractHPIGame(Game):
|
|
27
|
+
"""Abstract base class for Hyperparameter Importance Games (HPIGames).
|
|
28
|
+
|
|
29
|
+
Represents a game-theoretic framework for analyzing the importance of
|
|
30
|
+
hyperparameters for HPO. It leverages the `shapiq` library to compute
|
|
31
|
+
Shapley values and analyze coalition behavior.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
explanation_task: The `ExplanationTask` containing information about
|
|
35
|
+
the configuration space and surrogate model.
|
|
36
|
+
n_workers: The number of worker threads to use for parallel evaluation
|
|
37
|
+
of coalitions. Defaults to 1. Using more workers can significantly
|
|
38
|
+
speed up the computation of Shapley values.
|
|
39
|
+
verbose: A boolean indicating whether to print verbose messages during
|
|
40
|
+
computation. Defaults to False.
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
explanation_task: ExplanationTask,
|
|
47
|
+
n_workers: int | None = None,
|
|
48
|
+
verbose: bool | None = None,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Initialize the Hyperparameter Interaction Game (HPIGame).
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
explanation_task: The `ExplanationTask` containing information about
|
|
54
|
+
the configuration space and surrogate model. This task defines the
|
|
55
|
+
hyperparameter search space and the model used to estimate performance.
|
|
56
|
+
n_workers: The number of worker threads to use for parallel evaluation
|
|
57
|
+
of coalitions. Defaults to None meaning no parallelization. Using more workers can significantly
|
|
58
|
+
speed up the computation of Shapley values. The maximum number of workers is capped by the number of coalitions.
|
|
59
|
+
verbose: A boolean indicating whether to print verbose messages during
|
|
60
|
+
computation. Defaults to None. When set to True, the method prints
|
|
61
|
+
debugging information and progress updates.
|
|
62
|
+
|
|
63
|
+
"""
|
|
64
|
+
self.explanation_task = explanation_task
|
|
65
|
+
self.n_workers = n_workers if n_workers is not None else 1
|
|
66
|
+
self.verbose = verbose if verbose is not None else False
|
|
67
|
+
|
|
68
|
+
# determine the value of the empty coalition so that we can normalize wrt to that performance
|
|
69
|
+
normalization_value = self.evaluate_single_coalition(
|
|
70
|
+
np.array([False] * explanation_task.get_num_hyperparameters()),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
super().__init__(
|
|
74
|
+
n_players=explanation_task.get_num_hyperparameters(),
|
|
75
|
+
normalize=True,
|
|
76
|
+
normalization_value=normalization_value,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def _process_chunk(self, chunk: np.ndarray) -> list:
|
|
80
|
+
"""Process a chunk of coalitions, evaluating each coalition using the `evaluate_single_coalition` method.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
chunk: A NumPy array representing a subset of coalitions.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
A list of floats, where each float is the value of a coalition
|
|
87
|
+
in the input chunk.
|
|
88
|
+
|
|
89
|
+
"""
|
|
90
|
+
return [self.evaluate_single_coalition(c) for c in chunk]
|
|
91
|
+
|
|
92
|
+
def value_function(self, coalitions: np.ndarray) -> np.ndarray:
|
|
93
|
+
"""Calculate the value of a list of coalitions.
|
|
94
|
+
|
|
95
|
+
This method handles both single-worker and multi-worker scenarios for efficient computation.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
coalitions: A NumPy array representing a list of coalitions. Each
|
|
99
|
+
coalition is a Boolean array indicating which hyperparameters
|
|
100
|
+
are included in that coalition.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
A NumPy array containing the values of the input coalitions.
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
if self.n_workers == 1:
|
|
107
|
+
value_list = []
|
|
108
|
+
for coalition in coalitions:
|
|
109
|
+
value_list += [self.evaluate_single_coalition(coalition)]
|
|
110
|
+
else:
|
|
111
|
+
m = len(coalitions)
|
|
112
|
+
num_workers = min(self.n_workers, m)
|
|
113
|
+
base_size = m // num_workers
|
|
114
|
+
remainder = m % num_workers
|
|
115
|
+
|
|
116
|
+
chunk_indices: Iterable[tuple[int, int]] = []
|
|
117
|
+
start = 0
|
|
118
|
+
for i in range(num_workers):
|
|
119
|
+
size = base_size + (1 if i < remainder else 0)
|
|
120
|
+
chunk_indices.append((start, start + size))
|
|
121
|
+
start += size
|
|
122
|
+
|
|
123
|
+
chunks = [coalitions[start:end] for start, end in chunk_indices]
|
|
124
|
+
|
|
125
|
+
with ThreadPoolExecutor(max_workers=self.n_workers) as executor:
|
|
126
|
+
partial_results = list(executor.map(self._process_chunk, chunks))
|
|
127
|
+
|
|
128
|
+
value_list = [val for sublist in partial_results for val in sublist]
|
|
129
|
+
return np.array(value_list)
|
|
130
|
+
|
|
131
|
+
@abstractmethod
|
|
132
|
+
def evaluate_single_coalition(self, coalition: np.ndarray) -> float:
|
|
133
|
+
"""Evaluate the value of a single coalition.
|
|
134
|
+
|
|
135
|
+
This method *must* be implemented by subclasses.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
coalition: A boolean array representing a coalition of hyperparameters.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
The value of the coalition (a float).
|
|
142
|
+
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
def get_num_hyperparameters(self) -> int:
|
|
146
|
+
"""Return the number of hyperparameters being considered.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
The number of hyperparameters (an integer).
|
|
150
|
+
|
|
151
|
+
"""
|
|
152
|
+
return self.explanation_task.get_num_hyperparameters()
|
|
153
|
+
|
|
154
|
+
def get_hyperparameter_names(self) -> list[str]:
|
|
155
|
+
"""Return a list of the names of the hyperparameters.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
A list of strings, where each string is the name of a hyperparameter.
|
|
159
|
+
|
|
160
|
+
"""
|
|
161
|
+
return self.explanation_task.get_hyperparameter_names()
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""optimizerbias: Quantifying Bias in Hyperparameter Optimizers.
|
|
2
|
+
|
|
3
|
+
This module provides tools for measuring and explaining bias in hyperparameter optimizers using cooperative game
|
|
4
|
+
theory frameworks. It implements an explanation game specifically designed to assess how hyperparameter coalitions
|
|
5
|
+
influence the performance of specific HPO algorithms compared to diverse alternatives.
|
|
6
|
+
|
|
7
|
+
Key Features:
|
|
8
|
+
- Provides a mechanism to measure optimizer biases through mathematical games
|
|
9
|
+
- Enables identification of hyperparameters contributing most significantly to biased outcomes
|
|
10
|
+
|
|
11
|
+
Classes:
|
|
12
|
+
OptimizerBiasGame: An explanation game class that quantifies bias in optimization algorithms by comparing
|
|
13
|
+
performance against an ensemble of diverse optimizers.
|
|
14
|
+
|
|
15
|
+
Usage Notes:
|
|
16
|
+
This module requires integration with the HyperSHAP library and appropriate input data structures. It is designed
|
|
17
|
+
to help researchers understand and mitigate biases in machine learning optimization processes.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from typing import TYPE_CHECKING
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
import numpy as np
|
|
26
|
+
|
|
27
|
+
from hypershap.games import AbstractHPIGame
|
|
28
|
+
from hypershap.task import OptimizerBiasExplanationTask
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class OptimizerBiasGame(AbstractHPIGame):
|
|
32
|
+
"""An explanation game to measure bias in hyperparameter optimizers.
|
|
33
|
+
|
|
34
|
+
This class extends the `AbstractHPIGame` base class and is specifically designed
|
|
35
|
+
to quantify how much an optimizer is biased toward tuning certain hyperparameters.
|
|
36
|
+
To this end a set of diverse optimizers is used as a reference.
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
explanation_task (OptimizerBiasExplanationTask): The task that defines the game.
|
|
40
|
+
This includes the configuration sapce, the surrogate model and a baseline configuration, as well as the
|
|
41
|
+
optimizer of interest and the ensemble of diverse optimizers.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
Methods:
|
|
45
|
+
evaluate_single_coalition: Computes the marginal contribution of a coalition (subset of features)
|
|
46
|
+
by comparing the optimizer of interest against an optimizer ensemble. This method is called internally
|
|
47
|
+
during the game's main evaluation process.
|
|
48
|
+
|
|
49
|
+
Note:
|
|
50
|
+
The game evaluates coalitions based on the difference between the outcome using the primary optimizer
|
|
51
|
+
and the maximum outcome achieved by any optimizer in the provided ensemble.
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
explanation_task: OptimizerBiasExplanationTask,
|
|
58
|
+
n_workers: int | None = None,
|
|
59
|
+
verbose: bool | None = None,
|
|
60
|
+
) -> None:
|
|
61
|
+
"""Initialize an instance of `OptimizerBiasGame`.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
explanation_task (OptimizerBiasExplanationTask): The task that contains all necessary
|
|
65
|
+
information for defining the game. This includes the configuration space, the surrogate model, the
|
|
66
|
+
optimizer of interest, and the ensemble of diverse optimizers.
|
|
67
|
+
n_workers: The number of worker threads to use for parallel evaluation
|
|
68
|
+
of coalitions. Defaults to None meaning no parallelization. Using more workers can significantly
|
|
69
|
+
speed up the computation of Shapley values. The maximum number of workers is capped by the number of coalitions.
|
|
70
|
+
verbose: A boolean indicating whether to print verbose messages during
|
|
71
|
+
computation. Defaults to None. When set to True, the method prints
|
|
72
|
+
debugging information and progress updates.
|
|
73
|
+
|
|
74
|
+
Example:
|
|
75
|
+
>>> from hypershap.task import OptimizerBiasExplanationTask
|
|
76
|
+
>>> # Create an explanation task instance with specific parameters
|
|
77
|
+
>>> expl_task = OptimizerBiasExplanationTask(...)
|
|
78
|
+
>>> game = OptimizerBiasGame(expl_task)
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
super().__init__(explanation_task, n_workers=n_workers, verbose=verbose)
|
|
82
|
+
|
|
83
|
+
def _get_explanation_task(self) -> OptimizerBiasExplanationTask:
|
|
84
|
+
if isinstance(self.explanation_task, OptimizerBiasExplanationTask):
|
|
85
|
+
return self.explanation_task
|
|
86
|
+
raise ValueError
|
|
87
|
+
|
|
88
|
+
def evaluate_single_coalition(self, coalition: np.ndarray) -> float:
|
|
89
|
+
"""Evaluate a single coalition by comparing against an optimizer ensemble.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
coalition (np.ndarray): A binary array indicating which hyperparameters are included in the coalition.
|
|
93
|
+
The array has shape (n_hyperparameters,) where True means hyperparameters is included and False
|
|
94
|
+
otherwise.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
float: The marginal contribution of the coalition. It is computed as the difference between
|
|
98
|
+
the outcome when using the optimizer of interest with the given coalition, versus the best outcome
|
|
99
|
+
achievable by any optimizer in the ensemble (including the one of interest).
|
|
100
|
+
|
|
101
|
+
Note:
|
|
102
|
+
This method overrides a base class abstract method and must be implemented to provide specific game logic.
|
|
103
|
+
The value returned here is used for computing Shapley values or other cooperative game theory measures.
|
|
104
|
+
|
|
105
|
+
Example:
|
|
106
|
+
>>> coalition = np.array([1, 0, 1]) # Hyperparameters 0 and 2 are included
|
|
107
|
+
>>> marginal_contribution = game.evaluate_single_coalition(coalition)
|
|
108
|
+
>>> print(marginal_contribution) # Outputs the difference in outcomes for this coalition.
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
optimizer_res = self._get_explanation_task().optimizer_of_interest.search(coalition)
|
|
112
|
+
optimizer_ensemble_res = [
|
|
113
|
+
optimizer.search(coalition) for optimizer in self._get_explanation_task().optimizer_ensemble
|
|
114
|
+
]
|
|
115
|
+
return optimizer_res - max([*optimizer_ensemble_res, optimizer_res])
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Hyperparameter tunability games for analyzing the impact of tuning hyperparameters on performance.
|
|
2
|
+
|
|
3
|
+
This module provides a suite of game-theoretic tools for analyzing the tunability
|
|
4
|
+
of hyperparameters within a surrogate model of a black-box optimization
|
|
5
|
+
process. It defines classes that implement search-based games, allowing
|
|
6
|
+
exploration of scenarios involving coalitions of hyperparameters and assessment
|
|
7
|
+
of their impact on optimization performance. The module leverages the
|
|
8
|
+
`ExplanationTask` from `hypershap.task` to represent the hyperparameter
|
|
9
|
+
search space and surrogate model, and provides flexible configuration options
|
|
10
|
+
through the `ConfigSpaceSearcher` interface.
|
|
11
|
+
|
|
12
|
+
The core functionality revolves around defining games like `TunabilityGame`,
|
|
13
|
+
`SensitivityGame`, and `MistunabilityGame`, each representing a different
|
|
14
|
+
aspect of hyperparameter behavior under coalition-based constraints. These
|
|
15
|
+
games are built upon search strategies and allow for in-depth understanding of
|
|
16
|
+
hyperparameter dependencies and potential for optimization gains.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import logging
|
|
22
|
+
from typing import TYPE_CHECKING
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
import numpy as np
|
|
26
|
+
|
|
27
|
+
from hypershap.task import (
|
|
28
|
+
BaselineExplanationTask,
|
|
29
|
+
MistunabilityExplanationTask,
|
|
30
|
+
SensitivityExplanationTask,
|
|
31
|
+
TunabilityExplanationTask,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
from hypershap.games.abstract import AbstractHPIGame
|
|
35
|
+
from hypershap.utils import ConfigSpaceSearcher, RandomConfigSpaceSearcher
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class SearchBasedGame(AbstractHPIGame):
|
|
41
|
+
"""Base class for games that rely on searching the configuration space."""
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
explanation_task: BaselineExplanationTask,
|
|
46
|
+
cs_searcher: ConfigSpaceSearcher,
|
|
47
|
+
n_workers: int | None = None,
|
|
48
|
+
verbose: bool | None = None,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Initialize the search-based game.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
explanation_task: The explanation task containing the configuration
|
|
54
|
+
space and surrogate model.
|
|
55
|
+
cs_searcher: The configuration space searcher. If None, a
|
|
56
|
+
RandomConfigSpaceSearcher is used by default.
|
|
57
|
+
n_workers: The number of worker threads to use for parallel evaluation
|
|
58
|
+
of coalitions. Defaults to None meaning no parallelization. Using more workers can significantly
|
|
59
|
+
speed up the computation of Shapley values. The maximum number of workers is capped by the number of coalitions.
|
|
60
|
+
verbose: A boolean indicating whether to print verbose messages during
|
|
61
|
+
computation. Defaults to None. When set to True, the method prints
|
|
62
|
+
debugging information and progress updates.
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
self.cs_searcher = cs_searcher
|
|
66
|
+
super().__init__(explanation_task, n_workers=n_workers, verbose=verbose)
|
|
67
|
+
|
|
68
|
+
def evaluate_single_coalition(self, coalition: np.ndarray) -> float:
|
|
69
|
+
"""Evaluate the value of a single coalition using the configuration space searcher.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
coalition: A boolean array indicating which hyperparameters are
|
|
73
|
+
constrained by the coalition.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
The value of the coalition based on the search results.
|
|
77
|
+
|
|
78
|
+
"""
|
|
79
|
+
return self.cs_searcher.search(coalition)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class TunabilityGame(SearchBasedGame):
|
|
83
|
+
"""Game representing the tunability of hyperparameters."""
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
explanation_task: TunabilityExplanationTask,
|
|
88
|
+
cs_searcher: ConfigSpaceSearcher | None = None,
|
|
89
|
+
n_workers: int | None = None,
|
|
90
|
+
verbose: bool | None = None,
|
|
91
|
+
) -> None:
|
|
92
|
+
"""Initialize the tunability game.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
explanation_task: The explanation task containing the configuration
|
|
96
|
+
space and surrogate model.
|
|
97
|
+
cs_searcher: The configuration space searcher. If None, a
|
|
98
|
+
RandomConfigSpaceSearcher is used by default.
|
|
99
|
+
n_workers: The number of worker threads to use for parallel evaluation
|
|
100
|
+
of coalitions. Defaults to None meaning no parallelization. Using more workers can significantly
|
|
101
|
+
speed up the computation of Shapley values. The maximum number of workers is capped by the number of coalitions.
|
|
102
|
+
verbose: A boolean indicating whether to print verbose messages during
|
|
103
|
+
computation. Defaults to None. When set to True, the method prints
|
|
104
|
+
debugging information and progress updates.
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
# set cs searcher if not given by default to a random config space searcher.
|
|
108
|
+
if cs_searcher is None:
|
|
109
|
+
cs_searcher = RandomConfigSpaceSearcher(explanation_task, mode="max")
|
|
110
|
+
elif cs_searcher.mode != "max": # ensure that cs_searcher is maximizing
|
|
111
|
+
logger.warning("WARN: Tunability game set mode of given ConfigSpaceSearcher to maximize.")
|
|
112
|
+
cs_searcher.mode = "max"
|
|
113
|
+
super().__init__(explanation_task, cs_searcher, n_workers=n_workers, verbose=verbose)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class SensitivityGame(SearchBasedGame):
|
|
117
|
+
"""Game representing the sensitivity of hyperparameters."""
|
|
118
|
+
|
|
119
|
+
def __init__(
|
|
120
|
+
self,
|
|
121
|
+
explanation_task: SensitivityExplanationTask,
|
|
122
|
+
cs_searcher: ConfigSpaceSearcher | None = None,
|
|
123
|
+
n_workers: int | None = None,
|
|
124
|
+
verbose: bool | None = None,
|
|
125
|
+
) -> None:
|
|
126
|
+
"""Initialize the sensitivity game.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
explanation_task: The explanation task containing the configuration
|
|
130
|
+
space and surrogate model.
|
|
131
|
+
cs_searcher: The configuration space searcher. If None, a
|
|
132
|
+
RandomConfigSpaceSearcher is used by default.
|
|
133
|
+
n_workers: The number of worker threads to use for parallel evaluation
|
|
134
|
+
of coalitions. Defaults to None meaning no parallelization. Using more workers can significantly
|
|
135
|
+
speed up the computation of Shapley values. The maximum number of workers is capped by the number of coalitions.
|
|
136
|
+
verbose: A boolean indicating whether to print verbose messages during
|
|
137
|
+
computation. Defaults to None. When set to True, the method prints
|
|
138
|
+
debugging information and progress updates.
|
|
139
|
+
|
|
140
|
+
"""
|
|
141
|
+
# set cs searcher if not given by default to a random config space searcher.
|
|
142
|
+
if cs_searcher is None:
|
|
143
|
+
cs_searcher = RandomConfigSpaceSearcher(explanation_task, mode="var")
|
|
144
|
+
elif cs_searcher.mode != "var": # ensure that cs_searcher is maximizing
|
|
145
|
+
logger.warning("WARN: Sensitivity game set mode of given ConfigSpaceSearcher to variance.")
|
|
146
|
+
cs_searcher.mode = "var"
|
|
147
|
+
|
|
148
|
+
super().__init__(explanation_task, cs_searcher, n_workers=n_workers, verbose=verbose)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class MistunabilityGame(SearchBasedGame):
|
|
152
|
+
"""Game representing the mistunability of hyperparameters."""
|
|
153
|
+
|
|
154
|
+
def __init__(
|
|
155
|
+
self,
|
|
156
|
+
explanation_task: MistunabilityExplanationTask,
|
|
157
|
+
cs_searcher: ConfigSpaceSearcher | None = None,
|
|
158
|
+
n_workers: int | None = None,
|
|
159
|
+
verbose: bool | None = None,
|
|
160
|
+
) -> None:
|
|
161
|
+
"""Initialize the mistunability game.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
explanation_task: The explanation task containing the configuration
|
|
165
|
+
space and surrogate model.
|
|
166
|
+
cs_searcher: The configuration space searcher. If None, a
|
|
167
|
+
RandomConfigSpaceSearcher is used by default.
|
|
168
|
+
n_workers: The number of worker threads to use for parallel evaluation
|
|
169
|
+
of coalitions. Defaults to None meaning no parallelization. Using more workers can significantly
|
|
170
|
+
speed up the computation of Shapley values. The maximum number of workers is capped by the number of coalitions.
|
|
171
|
+
verbose: A boolean indicating whether to print verbose messages during
|
|
172
|
+
computation. Defaults to None. When set to True, the method prints
|
|
173
|
+
debugging information and progress updates.
|
|
174
|
+
|
|
175
|
+
"""
|
|
176
|
+
# set cs searcher if not given by default to a random config space searcher.
|
|
177
|
+
if cs_searcher is None:
|
|
178
|
+
cs_searcher = RandomConfigSpaceSearcher(explanation_task, mode="min")
|
|
179
|
+
elif cs_searcher.mode != "min": # ensure that cs_searcher is maximizing
|
|
180
|
+
logger.warning("WARN: Mistunability game set mode of given ConfigSpaceSearcher to minimize.")
|
|
181
|
+
cs_searcher.mode = "min"
|
|
182
|
+
|
|
183
|
+
super().__init__(explanation_task, cs_searcher, n_workers=n_workers, verbose=verbose)
|