Skip to content

Commit cff1b90

Browse files
authored
Merge pull request #104 from automl/development
Release 1.0.6
2 parents b235a31 + 6313eb4 commit cff1b90

9 files changed

Lines changed: 273 additions & 155 deletions

File tree

pimp/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"""Version information."""
2-
__version__ = "1.0.5"
2+
__version__ = "1.0.6"

pimp/epm/base_epm.py

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
from pyrfr import regression
55

66
from smac.epm.base_epm import AbstractEPM
7+
from smac.configspace import (
8+
CategoricalHyperparameter,
9+
UniformFloatHyperparameter,
10+
UniformIntegerHyperparameter,
11+
Constant,
12+
)
713

814

915
class RandomForestWithInstances(AbstractEPM):
@@ -28,8 +34,11 @@ class RandomForestWithInstances(AbstractEPM):
2834
logger : logging.logger
2935
"""
3036

31-
def __init__(self, types: np.ndarray,
37+
def __init__(self,
38+
configspace,
39+
types: np.ndarray,
3240
bounds: np.ndarray,
41+
seed: int,
3342
num_trees: int = 10,
3443
do_bootstrapping: bool = True,
3544
n_points_per_tree: int = -1,
@@ -39,13 +48,14 @@ def __init__(self, types: np.ndarray,
3948
max_depth: int = 20,
4049
eps_purity: int = 1e-8,
4150
max_num_nodes: int = 2 ** 20,
42-
seed: int = 42,
4351
logged_y: bool = True,
4452
**kwargs):
4553
"""Constructor
4654
4755
Parameters
4856
----------
57+
configspace: ConfigurationSpace
58+
configspace to be passed to random forest (used to impute inactive parameter-values)
4959
types : np.ndarray (D)
5060
Specifies the number of categorical values of an input dimension where
5161
the i-th entry corresponds to the i-th input dimension. Let's say we
@@ -54,6 +64,8 @@ def __init__(self, types: np.ndarray,
5464
have to pass np.array([2, 0]). Note that we count starting from 0.
5565
bounds : np.ndarray (D, 2)
5666
Specifies the bounds for continuous features.
67+
seed : int
68+
The seed that is passed to the random_forest_run library.
5769
num_trees : int
5870
The number of trees in the random forest.
5971
do_bootstrapping : bool
@@ -74,17 +86,20 @@ def __init__(self, types: np.ndarray,
7486
different
7587
max_num_nodes : int
7688
The maxmimum total number of nodes in a tree
77-
seed : int
78-
The seed that is passed to the random_forest_run library.
7989
logged_y: bool
8090
Indicates if the y data is transformed (i.e. put on logscale) or not
8191
"""
8292
try:
83-
super().__init__(types, bounds, **kwargs)
93+
super().__init__(configspace=configspace, types=types, bounds=bounds, seed=seed, **kwargs)
8494
except TypeError:
85-
# To ensure backwards-compatibility with smac<0.9.0
86-
super().__init__(**kwargs)
87-
95+
try:
96+
# To ensure backwards-compatibility with smac==0.10.0
97+
super().__init__(types, bounds, **kwargs)
98+
except TypeError:
99+
# To ensure backwards-compatibility with smac<0.9.0
100+
super().__init__(**kwargs)
101+
102+
self.configspace = configspace
88103
self.types = types
89104
self.bounds = bounds
90105
self.rng = regression.default_random_engine(seed)
@@ -112,9 +127,33 @@ def __init__(self, types: np.ndarray,
112127
min_samples_leaf, max_depth, eps_purity, seed]
113128
self.seed = seed
114129

130+
self.impute_values = {}
131+
115132
self.logger = logging.getLogger(self.__module__ + "." +
116133
self.__class__.__name__)
117134

135+
def _impute_inactive(self, X: np.ndarray) -> np.ndarray:
136+
X = X.copy()
137+
for idx, hp in enumerate(self.configspace.get_hyperparameters()):
138+
if idx not in self.impute_values:
139+
parents = self.configspace.get_parents_of(hp.name)
140+
if len(parents) == 0:
141+
self.impute_values[idx] = None
142+
else:
143+
if isinstance(hp, CategoricalHyperparameter):
144+
self.impute_values[idx] = len(hp.choices)
145+
elif isinstance(hp, (UniformFloatHyperparameter, UniformIntegerHyperparameter)):
146+
self.impute_values[idx] = -1
147+
elif isinstance(hp, Constant):
148+
self.impute_values[idx] = 1
149+
else:
150+
raise ValueError
151+
152+
nonfinite_mask = ~np.isfinite(X[:, idx])
153+
X[nonfinite_mask, idx] = self.impute_values[idx]
154+
155+
return X
156+
118157
def _train(self, X: np.ndarray, y: np.ndarray, **kwargs):
119158
"""Trains the random forest on X and y.
120159
@@ -130,9 +169,8 @@ def _train(self, X: np.ndarray, y: np.ndarray, **kwargs):
130169
self
131170
"""
132171

133-
self.X = X
172+
self.X = self._impute_inactive(X)
134173
self.y = y.flatten()
135-
136174
if self.n_points_per_tree <= 0:
137175
self.rf_opts.num_data_points_per_tree = self.X.shape[0]
138176
else:
@@ -141,6 +179,7 @@ def _train(self, X: np.ndarray, y: np.ndarray, **kwargs):
141179
self.rf.options = self.rf_opts
142180
data = self.__init_data_container(self.X, self.y)
143181
self.rf.fit(data, rng=self.rng)
182+
144183
return self
145184

146185
def __init_data_container(self, X: np.ndarray, y: np.ndarray):
@@ -200,6 +239,9 @@ def _predict(self, X: np.ndarray):
200239
(self.types.shape[0], X.shape[1]))
201240

202241
means, vars_ = [], []
242+
243+
X = self._impute_inactive(X)
244+
203245
for row_X in X:
204246
mean, var = self.rf.predict_mean_var(row_X)
205247
means.append(mean)

pimp/epm/epar_x_rfwi.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
class EPARrfi(rfi):
1414

15-
def __init__(self, types, bounds,
15+
def __init__(self, configspace, types, bounds, seed,
1616
cutoff=0,
1717
threshold=0, **kwargs):
1818
"""
@@ -21,6 +21,8 @@ def __init__(self, types, bounds,
2121
2222
Parameters
2323
----------
24+
configspace: ConfigurationSpace
25+
configspace to be passed to random forest (used to impute inactive parameter-values)
2426
types: np.ndarray (D)
2527
Specifies the number of categorical values of an input dimension. Where
2628
the i-th entry corresponds to the i-th input dimension. Let say we have
@@ -29,6 +31,8 @@ def __init__(self, types, bounds,
2931
have to pass np.array([2, 0]). Note that we count starting from 0.
3032
bounds: np.ndarray (D)
3133
Specifies the bounds
34+
seed: int
35+
The seed that is passed to the random_forest_run library.
3236
instance_features: np.ndarray (I, K)
3337
Contains the K dimensional instance features
3438
of the I different instances
@@ -48,16 +52,13 @@ def __init__(self, types, bounds,
4852
4953
max_num_nodes: int
5054
51-
seed: int
52-
The seed that is passed to the random_forest_run library.
53-
5455
cutoff: int
5556
The cutoff used in the specified scenario
5657
5758
threshold:
5859
Maximal possible value
5960
"""
60-
super().__init__(types=types, bounds=bounds, **kwargs)
61+
super().__init__(configspace=configspace, types=types, bounds=bounds, seed=seed, **kwargs)
6162
np.seterr(divide='ignore', invalid='ignore')
6263
self.cutoff = cutoff
6364
self.threshold = threshold

pimp/epm/unlogged_epar_x_rfwi.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010

1111
class UnloggedEPARXrfi(EPARrfi, Urfi):
1212

13-
def __init__(self, types, bounds,
13+
def __init__(self, configspace, types, bounds, seed,
1414
cutoff=0,
1515
threshold=0, **kwargs):
1616
"""
1717
TODO
1818
"""
19-
Urfi.__init__(self, types=types, bounds=bounds, **kwargs)
20-
EPARrfi.__init__(self, types=types, bounds=bounds, cutoff=cutoff, threshold=threshold, **kwargs)
19+
Urfi.__init__(self, configspace=configspace, types=types, bounds=bounds, seed=seed, **kwargs)
20+
EPARrfi.__init__(self, configspace=configspace, types=types, bounds=bounds, seed=seed, cutoff=cutoff, threshold=threshold, **kwargs)
2121

2222
def predict(self, X):
2323
"""

pimp/epm/unlogged_rfwi.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212

1313
class Unloggedrfwi(rfi):
1414

15-
def __init__(self, types, bounds, **kwargs):
15+
def __init__(self, configspace, types, bounds, seed, **kwargs):
1616
"""
1717
Interface to the random forest that takes instance features
1818
into account.
1919
2020
Parameters
2121
----------
22+
configspace: ConfigurationSpace
23+
configspace to be passed to random forest (used to impute inactive parameter-values)
2224
types: np.ndarray (D)
2325
Specifies the number of categorical values of an input dimension. Where
2426
the i-th entry corresponds to the i-th input dimension. Let say we have
@@ -27,6 +29,9 @@ def __init__(self, types, bounds, **kwargs):
2729
have to pass np.array([2, 0]). Note that we count starting from 0.
2830
bounds: np.ndarray (D)
2931
Specifies the bounds
32+
seed: int
33+
The seed that is passed to the random_forest_run library.
34+
3035
instance_features: np.ndarray (I, K)
3136
Contains the K dimensional instance features
3237
of the I different instances
@@ -46,16 +51,13 @@ def __init__(self, types, bounds, **kwargs):
4651
4752
max_num_nodes: int
4853
49-
seed: int
50-
The seed that is passed to the random_forest_run library.
51-
5254
cutoff: int
5355
The cutoff used in the specified scenario
5456
5557
threshold:
5658
Maximal possible value
5759
"""
58-
super().__init__(types=types, bounds=bounds, **kwargs)
60+
super().__init__(configspace=configspace, types=types, bounds=bounds, seed=seed, **kwargs)
5961

6062
# With the usage of pyrfr 0.8.0 this method is obsolete.
6163
# def _predict(self, X):

pimp/evaluator/base_evaluator.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from collections import OrderedDict
44

55
from smac.epm.rf_with_instances import RandomForestWithInstances
6+
from ConfigSpace.hyperparameters import UniformFloatHyperparameter
67

78
from pimp.configspace import ConfigurationSpace
89
from pimp.utils import Scenario
@@ -102,6 +103,9 @@ def _refit_model(self, types, bounds, X, y):
102103
y:ndarray
103104
corresponding y vector
104105
"""
105-
self.model = RandomForestWithInstances(types, bounds, do_bootstrapping=True)
106+
# We need to fake config-space bypass imputation of inactive values in random forest implementation
107+
fake_cs = ConfigurationSpace(name="fake-cs-for-configurator-footprint")
108+
109+
self.model = RandomForestWithInstances(fake_cs, types, bounds, seed=12345, do_bootstrapping=True)
106110
self.model.rf_opts.compute_oob_error = True
107111
self.model.train(X, y)

0 commit comments

Comments
 (0)