Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix several bugs & improve usability of HTMLVisualizer #58

Merged
merged 22 commits into from
Mar 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion openbox/optimizer/generic_smbo.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,10 @@ def __init__(
else:
raise ValueError('Invalid advisor type!')

self.visualizer = build_visualizer(visualization, self, auto_open_html=auto_open_html)
self.visualizer = build_visualizer(
option=visualization, history=self.get_history(),
logging_dir=self.output_dir, optimizer=self, advisor=None, auto_open_html=auto_open_html,
)
self.visualizer.setup()

def run(self) -> History:
Expand Down
2 changes: 1 addition & 1 deletion openbox/utils/feature_importance/get_importance.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def get_shap_importance(X: np.ndarray, Y: np.ndarray, **kwargs):
assert Y.ndim == 1 and Y.shape[0] == X.shape[0]

# Fit a LightGBMRegressor with observations
lgbr = LGBMRegressor(n_jobs=1, random_state=1, **kwargs)
lgbr = LGBMRegressor(n_jobs=1, random_state=1, min_child_samples=3, **kwargs)
lgbr.fit(X, Y)
explainer = shap.TreeExplainer(lgbr)
shap_values = explainer.shap_values(X)
Expand Down
25 changes: 14 additions & 11 deletions openbox/utils/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import os
import copy
import json

from tqdm import trange
from datetime import datetime
from functools import partial
from typing import List, Tuple, Union, Optional
Expand Down Expand Up @@ -740,7 +738,7 @@ def compute_hypervolume(
objectives = self.get_objectives(transform='none', warn_invalid_value=False)
HV = Hypervolume(ref_point=ref_point)
hv_list = []
for i in trange(len(self)):
for i in range(len(self)):
mask = feasible_mask[:i + 1]
objs = objectives[:i + 1]
pareto_front = get_pareto_front(objs[mask], lexsort=False)
Expand Down Expand Up @@ -845,7 +843,6 @@ def get_importance(self, method='fanova', return_dict=False):
if method == 'fanova':
importance_func = partial(get_fanova_importance, config_space=config_space)
elif method == 'shap':
# todo: try different hyperparameter in lgb
importance_func = get_shap_importance
if any([isinstance(hp, (CategoricalHyperparameter, OrdinalHyperparameter))
for hp in config_space.get_hyperparameters()]):
Expand Down Expand Up @@ -1000,31 +997,37 @@ def plot_hypervolumes(self, optimal_hypervolume=None, ref_point=None, logy=False
ax = plot_curve(x=x, y=y, xlabel=xlabel, ylabel=ylabel, ax=ax, **kwargs)
return ax

def visualize_html(self, open_html=True, show_importance=False, verify_surrogate=False, optimizer= None, **kwargs):
def visualize_html(self, logging_dir='logs/', open_html=True, show_importance=False, verify_surrogate=False,
task_info=None, optimizer=None, advisor=None, **kwargs):
"""
Visualize the history using OpenBox's HTML visualization.

Parameters
----------
logging_dir : str, default='logs/'
The directory to save the visualization.
open_html: bool, default=True
If True, the visualization will be opened in the browser automatically.
show_importance: bool, default=False
If True, the importance of each hyperparameter will be calculated and shown.
Note that additional packages are required to calculate the importance. (run `pip install shap lightgbm`)
verify_surrogate: bool, default=False
If True, the surrogate model will be verified and shown. This may take some time.
optimizer: Optimizer
The optimizer is required to obtain related information.
task_info : dict, optional
Task information for visualizer to use.
optimizer : Optimizer, optional
Optimizer to extract task_info from.
advisor : Advisor, optional
Advisor to extract task_info from.
kwargs: dict
Other keyword arguments passed to `build_visualizer` in `openbox.visualization`.
"""
from openbox.visualization import build_visualizer, HTMLVisualizer
# todo: user-friendly interface
if optimizer is None:
raise ValueError('Please provide optimizer for html visualization.')

option = 'advanced' if (show_importance or verify_surrogate) else 'basic'
visualizer = build_visualizer(option, optimizer=optimizer, **kwargs) # type: HTMLVisualizer
visualizer = build_visualizer(
option=option, history=self, logging_dir=logging_dir,
task_info=task_info, optimizer=optimizer, advisor=advisor, **kwargs) # type: HTMLVisualizer
if visualizer.history is not self:
visualizer.history = self
visualizer.meta_data['task_id'] = self.task_id
Expand Down
90 changes: 74 additions & 16 deletions openbox/visualization/base_visualizer.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
import abc
from typing import Union


def build_visualizer(option: Union[str, bool], optimizer, **kwargs):
from openbox import History


def build_visualizer(
option: Union[str, bool],
history: History,
*,
logging_dir='logs/',
task_info=None,
optimizer=None,
advisor=None,
**kwargs,
):
"""
Build visualizer for optimizer.

Parameters
----------
option : ['none', 'basic', 'advanced']
Visualizer option.

optimizer : Optimizer
Optimizer to visualize.

history : History
History to visualize.
logging_dir : str, default='logs/'
The directory to save the visualization.
task_info : dict, optional
Task information for visualizer to use.
optimizer : Optimizer, optional
Optimizer to extract task_info from.
advisor : Advisor, optional
Advisor to extract task_info from.
kwargs : dict
Other arguments for visualizer.
For HTMLVisualizer, available arguments are:
Expand All @@ -32,20 +48,19 @@ def build_visualizer(option: Union[str, bool], optimizer, **kwargs):
if option == 'none':
visualizer = NullVisualizer()
elif option in ['basic', 'advanced']:
advisor = optimizer.config_advisor
if task_info is None:
task_info = dict()
_task_info = extract_task_info(optimizer=optimizer, advisor=advisor)
_task_info.update(task_info)

from openbox.visualization.html_visualizer import HTMLVisualizer
visualizer = HTMLVisualizer(
logging_dir=optimizer.output_dir,
history=optimizer.get_history(),
logging_dir=logging_dir,
history=history,
task_info=_task_info,
auto_open_html=kwargs.get('auto_open_html', False),
advanced_analysis=(option == 'advanced'),
advanced_analysis_options=kwargs.get('advanced_analysis_options'),
advisor_type=optimizer.advisor_type,
surrogate_type=advisor.surrogate_type if hasattr(advisor, 'surrogate_type') else None,
max_iterations=optimizer.max_iterations,
time_limit_per_trial=optimizer.time_limit_per_trial,
surrogate_model=advisor.surrogate_model if hasattr(advisor, 'surrogate_model') else None,
constraint_models=advisor.constraint_models if hasattr(advisor, 'constraint_models') else None,
)
else:
raise ValueError('Unknown visualizer option: %s' % option)
Expand All @@ -66,6 +81,49 @@ def _parse_option(option: Union[str, bool]):
return option


def extract_task_info(*, optimizer=None, advisor=None):
"""
Extract task information from optimizer or advisor.

Parameters
----------
optimizer : Optimizer, optional
Optimizer to extract task_info from.

advisor : Advisor, optional
Advisor to extract task_info from.

Returns
-------
task_info : dict
Task information for visualizer to use.
"""
if optimizer is None and advisor is None:
return dict()

if advisor is None:
advisor = optimizer.config_advisor if hasattr(optimizer, 'config_advisor') else None

task_info = dict()

if optimizer is not None:
task_info.update({
'advisor_type': optimizer.advisor_type,
'max_iterations': optimizer.max_iterations,
'time_limit_per_trial': optimizer.time_limit_per_trial,
})
if advisor is not None:
task_info.update({
# todo: if model is altered, this will not be updated
'surrogate_type': advisor.surrogate_type if hasattr(advisor, 'surrogate_type') else None,
'constraint_surrogate_type': advisor.constraint_surrogate_type if hasattr(
advisor, 'constraint_surrogate_type') else None,
'transfer_learning_history': advisor.transfer_learning_history if hasattr(
advisor, 'transfer_learning_history') else None,
})
return task_info


class BaseVisualizer(object, metaclass=abc.ABCMeta):
def __init__(self, *args, **kwargs):
pass
Expand Down
Empty file.
Loading