Skip to content

Commit e57a718

Browse files
authored
Merge pull request #232 from automl/development
Release 1.1.5
2 parents 1fd49f6 + 23d3a6c commit e57a718

21 files changed

Lines changed: 273 additions & 139 deletions

cave/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.1.4"
1+
__version__ = "1.1.5"

cave/analyzer/bohb_learning_curves.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,10 @@ def _plot(self, result_object, learning_curves, hyperparameter_names, reset_time
116116
data['colors_iteration'].append(c_id[0])
117117

118118
# Tooltips
119-
tooltips=[(key, '@' + key) for key in data.keys() if not key in ['times', 'duration', 'colors',
120-
'colors_performance', 'colors_iteration']]
119+
tooltips = [(key, '@' + key) for key in data.keys() if not key in ['times', 'duration', 'colors',
120+
'colors_performance', 'colors_iteration']]
121121
tooltips.insert(4, ('duration (sec)', '@duration'))
122+
tooltips.insert(5, ('Configuration', ' '))
122123
hover = HoverTool(tooltips=tooltips)
123124

124125
# Create sources
@@ -154,14 +155,15 @@ def _plot(self, result_object, learning_curves, hyperparameter_names, reset_time
154155
p = figure(plot_height=500, plot_width=600,
155156
y_axis_type=y_axis_type,
156157
tools=[hover, 'save', 'pan', 'wheel_zoom', 'box_zoom', 'reset'],
157-
x_axis_label='Time', y_axis_label='Quality',
158+
x_axis_label='Time', y_axis_label='Cost',
158159
x_range=Range1d(x_min, x_max, bounds='auto'),
159160
y_range=Range1d(y_min, y_max, bounds='auto'),
160161
)
161162

162163
# Plot per HB_iteration, each config individually
163164
HB_iterations = sorted(set(data['HB_iteration']))
164-
HB_handles = []
165+
max_label_len = max([len(l) for l in HB_iterations])
166+
HB_handles, HB_labels = [], []
165167
self.logger.debug("Assuming config_info to be either \"model_based_pick=True\" or \"model_based_pick=False\"")
166168
for it in HB_iterations:
167169
line_handles = []
@@ -197,12 +199,15 @@ def _plot(self, result_object, learning_curves, hyperparameter_names, reset_time
197199
size=20,
198200
))
199201
HB_handles.append(line_handles)
202+
HB_labels.append('warmstart data' if l in [-1, '-1'] else '{number:0{width}d}'.format(width=max_label_len,
203+
number=int(it)))
200204

201-
iteration_labels = sorted(['warmstart data' if l in [-1, '-1'] else '{:02d}'.format(int(l)) for l in HB_iterations])
202-
self.logger.debug("iteration_labels: %s", str(iteration_labels))
203-
self.logger.debug("HB_iterations: %s", str(HB_iterations))
205+
# Sort all lists according to label
206+
HB_iterations, HB_handles, HB_labels = zip(*sorted(zip(HB_iterations, HB_handles, HB_labels), key=lambda tup: tup[2]))
207+
HB_iterations, HB_handles, HB_labels = list(HB_iterations), list(HB_handles), list(HB_labels)
208+
self.logger.debug("HB_iterations to labels: %s", str(list(zip(HB_iterations, HB_labels))))
204209

205-
checkbox, select_all, select_none = get_checkbox(HB_handles, iteration_labels)
210+
checkbox, select_all, select_none = get_checkbox(HB_handles, HB_labels)
206211

207212
callback_color = CustomJS(args=dict(source_multiline=source_multiline,
208213
source_scatter=source_scatter,
@@ -226,12 +231,17 @@ def _plot(self, result_object, learning_curves, hyperparameter_names, reset_time
226231
}}
227232
source.change.emit();
228233
""".format(min_perf, max_perf, min_iter, max_iter))
229-
select_color = Select(title="Select colors", value="performance", options = ["performance", "iteration"],
230-
callback=callback_color)
231234

235+
select_color = Select(title="Select colors",
236+
value="performance",
237+
options=["performance", "iteration"],
238+
callback=callback_color)
232239

233-
# Put it all together
234-
layout = column(p, row(widgetbox(select_all, select_none, width=100), widgetbox(checkbox, width=500),
240+
# Put it all together in a layout (width of checkbox-field sizes with number of elements
241+
width_of_checkbox = 650 if len(HB_labels) > 100 else 500 if len(HB_labels) > 70 else 400
242+
layout = row(p, column(widgetbox(checkbox, width=width_of_checkbox),
243+
row(widgetbox(select_all, width=50),
244+
widgetbox(select_none, width=50)),
235245
widgetbox(select_color, width=200)))
236246
return layout
237247

cave/analyzer/cave_fanova.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def parse_pairwise(p):
9595
# Get plot-paths
9696
self.single_plots = {p : os.path.join(self.output_dir, "fanova", p + '.png') for p in single_imp.keys()}
9797
# Right now no way to access paths of the plots -> file issue
98-
self.pairwise_plots = {" & ".join(parse_pairwise(k)) : os.path.join(self.output_dir, 'fanova', '_'.join(parse_pairwise(k)) + '.png') for p in pairwise_imp.keys()}
98+
self.pairwise_plots = {" & ".join(parse_pairwise(p)) : os.path.join(self.output_dir, 'fanova', '_'.join(parse_pairwise(p)) + '.png') for p in pairwise_imp.keys()}
9999
self.pairwise_plots = {p : path for p, path in self.pairwise_plots.items() if os.path.exists(path)}
100100

101101
def get_table(self):

cave/analyzer/compare_default_incumbent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def __init__(self, default, incumbent):
2121
self.logger = logging.getLogger(self.__module__ + '.' + self.__class__.__name__)
2222

2323
# Remove unused parameters
24-
keys = [k for k in default.keys() if default[k] or incumbent[k]]
24+
keys = [k for k in default.configuration_space.get_hyperparameter_names() if default[k] or incumbent[k]]
2525
default = [default[k] if default[k] is not None else "inactive" for k in keys]
2626
incumbent = [incumbent[k] if incumbent[k] is not None else "inactive" for k in keys]
2727
zipped = list(zip(keys, default, incumbent))

cave/analyzer/cost_over_time.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,6 @@ def plot(self):
334334
# Wrap renderers in nested lists for checkbox-code
335335
checkbox, select_all, select_none = get_checkbox(renderers, [l[0] for l in legend_it])
336336

337-
338337
# Tilt tick labels and configure axis labels
339338
p.xaxis.major_label_orientation = 3/4
340339

cave/analyzer/overview_table.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,30 @@
1111
from cave.utils.bokeh_routines import array_to_bokeh_table
1212

1313
class OverviewTable(BaseAnalyzer):
14-
def __init__(self, runs, output_dir):
14+
def __init__(self, runs, bohb_parallel, output_dir):
1515
""" Create overview-table.
1616
1717
Parameters
1818
----------
1919
runs: List[ConfiguratorRun]
2020
list with all runs
21+
bohb_parallel: False or int
22+
number of parallel bohb-runs if present
2123
output_dir: str
2224
output-directory for CAVE
2325
"""
2426
self.logger = logging.getLogger(self.__module__ + '.' + self.__class__.__name__)
2527
self.output_dir = output_dir
2628
self.runs = runs
29+
self.bohb_parallel = bohb_parallel
2730

2831
self.html_table_general, self.html_table_specific = self.run()
2932

3033
def run(self):
3134
""" Generate tables. """
3235
scenario = self.runs[0].scenario
3336

34-
general_dict = self._general_dict(scenario)
37+
general_dict = self._general_dict(scenario, self.bohb_parallel)
3538
html_table_general = DataFrame(data=OrderedDict([('General', general_dict)]))
3639
html_table_general = html_table_general.reindex(list(general_dict.keys()))
3740
html_table_general = html_table_general.to_html(escape=False, header=False, justify='left')
@@ -44,7 +47,7 @@ def run(self):
4447

4548
return html_table_general, html_table_specific
4649

47-
def _general_dict(self, scenario):
50+
def _general_dict(self, scenario, bohb_parallel=False):
4851
""" Generate the meta-information that holds for all runs (scenario info etc) """
4952
# general stores information that holds for all runs, runspec holds information on a run-basis
5053
general = OrderedDict()
@@ -54,6 +57,10 @@ def _general_dict(self, scenario):
5457
#if num_conf_runs != 1:
5558
# overview['Number of configurator runs'] = num_conf_runs
5659

60+
self.logger.debug("bohb_parallel in overview: %s", bohb_parallel)
61+
if bohb_parallel:
62+
general['# aggregated parallel BOHB runs'] = bohb_parallel
63+
5764
# Scenario related
5865
general['# parameters'] = len(scenario.cs.get_hyperparameters())
5966
general['Deterministic target algorithm'] = scenario.deterministic

cave/analyzer/parallel_coordinates.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@ def __init__(self,
3939
param_imp: Union[None, Dict[str, float]],
4040
params: Union[int, List[str]],
4141
n_configs: int,
42+
pc_sort_by: str,
4243
output_dir: str,
4344
cs: ConfigurationSpace,
4445
runtime: bool=False,
4546
max_runs_epm: int=3000000,
4647
):
47-
""""This function prepares the data from a SMAC-related
48+
"""This function prepares the data from a SMAC-related
4849
format (using runhistories and parameters) to a more general format
4950
(using a dataframe). The resulting dataframe is passed to the
5051
parallel_coordinates-routine
@@ -69,6 +70,8 @@ def __init__(self,
6970
important ones
7071
n_configs: int
7172
number of configs to be plotted
73+
pc_sort_by: str
74+
defines the pimp-method by which to choose the plotted parameters
7275
max_runs_epm: int
7376
maximum number of runs to train the epm with. this should prevent MemoryErrors
7477
output_dir: str
@@ -86,13 +89,33 @@ def __init__(self,
8689
self.param_imp = param_imp
8790
self.cs = cs
8891

89-
# Sorting by importance, if possible (choose first executed parameter-importance
92+
# Sorting by importance, if possible (choose first executed parameter-importance)
9093
self.method, self.importance = "", {}
91-
for m, i in list(self.param_imp.items()):
92-
if i:
93-
self.method, self.importance = m, i
94+
if pc_sort_by == 'all':
95+
self.logger.debug("Sorting by average importance")
96+
self.method = 'average'
97+
for m, i in self.param_imp.items():
98+
if i:
99+
for p, imp in i.items():
100+
if p in self.importance:
101+
self.importance[p].append(imp)
102+
else:
103+
self.importance[p] = [imp]
104+
self.importance = {k : sum(v) / len(v) for k, v in self.importance.items()}
105+
elif pc_sort_by in self.param_imp:
106+
self.method, self.importance = pc_sort_by, self.param_imp[pc_sort_by]
107+
else:
108+
self.logger.debug("%s not evaluated.. choosing at random from: %s", pc_sort_by,
109+
str(list(self.param_imp.keys())))
110+
for m, i in self.param_imp.items():
111+
if i:
112+
self.method, self.importance = m, i
113+
break
114+
94115
self.hp_names = sorted([hp for hp in self.cs.get_hyperparameter_names()],
95-
key=lambda x: self.importance.get(x, 0))
116+
key=lambda x: self.importance.get(x, 0),
117+
reverse=True)
118+
self.logger.debug("Sorted hp's by method \'%s\': %s", self.method, str(self.hp_names))
96119

97120
# To be set
98121
self.plots = []
@@ -146,6 +169,7 @@ def get_plots(self):
146169
self.plots = [self.pcp.plot_n_configs(self.n_configs, self.params)]
147170
except ValueError as err:
148171
self.error = str(err)
172+
self.logger.debug("Paths to plot(s): %s", str(self.plots))
149173
return self.plots
150174

151175
def get_html(self, d=None, tooltip=None):

cave/analyzer/plot_ecdf.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def prepare_data(x_data):
7070
output_fns = []
7171

7272
for insts, name in [(train, 'train'), (test, 'test')]:
73-
if insts == [None]:
73+
if len(insts) <= 1:
7474
self.logger.debug("No %s instances, skipping cdf", name)
7575
continue
7676
data = [prepare_data(np.array([v for k, v in costs if k in insts])) for costs in [def_costs, inc_costs]]
@@ -82,6 +82,8 @@ def prepare_data(x_data):
8282
self.output_fns = output_fns
8383

8484
def get_html(self, d=None, tooltip=None):
85+
if not self.output_fns:
86+
return
8587
if d is not None and self.output_fns:
8688
d["figure"] = self.output_fns
8789
d["tooltip"] = tooltip

cave/analyzer/plot_scatter.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def __init__(self,
5656

5757
out_fns = []
5858
for insts, name in [(train, 'train'), (test, 'test')]:
59-
if insts == [None]:
59+
if len(insts) <= 1:
6060
self.logger.debug("No %s instances, skipping scatter", name)
6161
continue
6262
default = np.array([v for k, v in def_costs if k in insts])
@@ -65,9 +65,12 @@ def __init__(self,
6565
out_fn = out_fn_base + name + '.png'
6666
out_fns.append(plot_scatter_plot((default,), (incumbent,), labels, metric=metric,
6767
min_val=min_val, max_val=timeout, out_fn=out_fn))
68-
self.output_fns = out_fns
68+
self.output_fns = out_fns if len(out_fns) > 0 else None
6969

7070
def get_html(self, d=None, tooltip=None):
71+
if not self.output_fns:
72+
return
73+
7174
if d is not None and self.output_fns:
7275
d["figure"] = self.output_fns
7376
d["tooltip"] = tooltip

0 commit comments

Comments
 (0)