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

Feature/symbolic explanations #101

Merged
merged 32 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
35eea9b
Draft for symbolic explanations plugin
Sep 8, 2023
aded257
Format
Sep 8, 2023
252ea68
Filter out categorical HPs for symbolic explanations
Sep 27, 2023
a510db8
Handling sympy timeouts
Sep 27, 2023
e7db918
Resolve merge conflict
Oct 25, 2023
b1a5a97
Resolve merge conflict
Oct 25, 2023
72df8cd
Merge branch 'development' of github.com:automl/DeepCAVE into feature…
Nov 9, 2023
6a47a7f
Solve merge conflict
Dec 18, 2023
60580c5
Improvements for symbolic explanations plugin
Dec 22, 2023
4f2636e
Add exit button and delete jobs on exit
Dec 22, 2023
803cfc3
Update author email
Jan 3, 2024
cd5b1c5
Add symb expl docs and handle long formulas
Jan 3, 2024
1da5915
Edit exit callbacks
Jan 3, 2024
d56b82f
Disable debug mode
Jan 3, 2024
1e75d3c
Update changelog
Jan 3, 2024
87b02bd
Remove signal runtime alarm from symbolic conversion
Jan 3, 2024
205f672
Update version number
Jan 3, 2024
95473b6
Update changelog
Jan 4, 2024
d8bb6a7
Reset inputs for symb expl when changing runs
Jan 5, 2024
77e2246
Edit symb expl input settings
Jan 5, 2024
7777934
Fix varying configspace index order in PDP and symbolic explanations
Jan 15, 2024
b3723aa
Allow longer expression
Jan 16, 2024
933a137
Add pdp plot to symbolic explanation output
Jan 23, 2024
b62462e
Add pdp plot to symbolic explanation output and add paper to docs
Jan 24, 2024
4252994
Solve merge conflict
Jan 24, 2024
e7b2e9c
Disable existing loggers again to suppress redis logs
Jan 24, 2024
ab0164a
Solve merge conflict
Jan 24, 2024
208ebfa
Revert incomplete fix of configspace index order in pdp and symb. expl.
Feb 9, 2024
04b660a
Resolve merge conflict
Feb 16, 2024
da987e8
Fixes from review
Feb 16, 2024
bc8bfd4
Fixes from review
Feb 16, 2024
d4101f6
Fixes from review
Feb 16, 2024
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
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
# Version 1.1.4
# Version 1.2

## Plugins
- Add symbolic explanations plugin (#46).

## Enhancements
- Fix lower bounds of dependency versions.
- Allow to load multi-objective SMAC3v2 and add example (#69)
- Do not disable existing loggers.
- Update author email.
- Add exit button which first deletes running jobs and then terminates DeepCave.
- Nicer handling of Keyboard Interrupt.
- Disable debug mode.

## Bug-Fixes
- Don't convert BOHB runs with status 'running' (consistent with SMAC).
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# are usually completed in github actions.

SHELL := /bin/bash
VERSION := 1.1.3
VERSION := 1.2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This says version 1.2, but in the Changelog the change is under Version 1.1.4

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edited to 1.2 in Changelog.


NAME := DeepCAVE
PACKAGE_NAME := deepcave
Expand Down Expand Up @@ -42,7 +42,7 @@ MYPY ?= mypy
PRECOMMIT ?= pre-commit
FLAKE8 ?= flake8

install:
install:
$(PIP) install -e .

install-dev:
Expand Down Expand Up @@ -115,7 +115,7 @@ build:
# This is done to prevent accidental publishing but provide the same conveniences
publish: clean build
read -p "Did you update the version number in Makefile and deepcave/__init__.py?"

$(PIP) install twine
$(PYTHON) -m twine upload --repository testpypi ${DIST}/*
@echo
Expand Down
4 changes: 2 additions & 2 deletions deepcave/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
name = "DeepCAVE"
package_name = "deepcave"
author = "R. Sass and E. Bergman and A. Biedenkapp and F. Hutter and M. Lindauer"
author_email = "sass@tnt.uni-hannover.de"
author_email = "s.segel@ai.uni-hannover.de"
description = "An interactive framework to visualize and analyze your AutoML process in real-time."
url = "automl.org"
project_urls = {
"Documentation": "https://automl.github.io/DeepCAVE/main",
"Source Code": "https://github.com/automl/deepcave",
}
copyright = f"Copyright {datetime.date.today().strftime('%Y')}, {author}"
version = "1.1.3"
version = "1.2"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the previous comment about inconsistent versions

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.


_exec_file = sys.argv[0]
_exec_files = ["server.py", "worker.py", "sphinx-build"]
Expand Down
7 changes: 4 additions & 3 deletions deepcave/cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import Any, List

import multiprocessing
import subprocess
from pathlib import Path
Expand Down Expand Up @@ -49,4 +47,7 @@ def execute(_) -> None:


def main() -> None:
app.run(execute)
try:
app.run(execute)
except KeyboardInterrupt:
exit("KeyboardInterrupt.")
6 changes: 5 additions & 1 deletion deepcave/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
class Config:
# General config
TITLE: str = "DeepCAVE"
DEBUG: bool = True
DEBUG: bool = False
# How often to refresh background activities (such as update the sidebar or process button for
# static plugins). Value in milliseconds.
REFRESH_RATE: int = 500
Expand Down Expand Up @@ -49,6 +49,9 @@ def PLUGINS(self) -> Dict[str, List["Plugin"]]:
from deepcave.plugins.budget.budget_correlation import BudgetCorrelation
from deepcave.plugins.hyperparameter.importances import Importances
from deepcave.plugins.hyperparameter.pdp import PartialDependencies
from deepcave.plugins.hyperparameter.symbolic_explanations import (
SymbolicExplanations,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the brackets? :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how they got there, removed :)

from deepcave.plugins.objective.configuration_cube import ConfigurationCube
from deepcave.plugins.objective.cost_over_time import CostOverTime
from deepcave.plugins.objective.parallel_coordinates import ParallelCoordinates
Expand All @@ -75,6 +78,7 @@ def PLUGINS(self) -> Dict[str, List["Plugin"]]:
"Hyperparameter Analysis": [
Importances(),
PartialDependencies(),
SymbolicExplanations(),
],
}
return plugins
Expand Down
2 changes: 1 addition & 1 deletion deepcave/evaluators/epm/random_forest.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def _impute_inactive(self, X: np.ndarray) -> np.ndarray:

def _check_dimensions(self, X: np.ndarray, Y: Optional[np.ndarray] = None) -> None:
"""
Checks if the dimensions of X and Y are correct wrt features.
Checks if the dimensions of X and Y are correct with respect to features.

Parameters
----------
Expand Down
2 changes: 1 addition & 1 deletion deepcave/evaluators/fanova.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def calculate(
seed: int = 0,
) -> None:
"""
Get the data wrt budget and trains the forest on the encoded data.
Get the data with respect to budget and trains the forest on the encoded data.

Note
----
Expand Down
42 changes: 40 additions & 2 deletions deepcave/layouts/header.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import os
import time

import dash_bootstrap_components as dbc
from dash import dcc, html
from dash.dependencies import Input, Output

from deepcave import app, c
from deepcave import app, c, queue
from deepcave.layouts import Layout


class HeaderLayout(Layout):
def register_callbacks(self) -> None:
super().register_callbacks()
self._callback_update_matplotlib_mode()
self._callback_delete_jobs()
self._callback_terminate_deepcave()

def _callback_update_matplotlib_mode(self) -> None:
outputs = [
Output("matplotlib-mode-toggle", "color"),
Output("matplotlib-mode-badge", "children"),
Expand All @@ -21,7 +28,7 @@ def register_callbacks(self) -> None:
]

@app.callback(outputs, inputs)
def update_matplotlib_mode(n_clicks, pathname):
def callback(n_clicks, pathname):
update = None
mode = c.get("matplotlib-mode")
if mode is None:
Expand All @@ -37,6 +44,34 @@ def update_matplotlib_mode(n_clicks, pathname):
else:
return "secondary", "off", update

def _callback_delete_jobs(self) -> None:
inputs = [Input("exit-deepcave", "n_clicks")]
outputs = [
Output("exit-deepcave", "color"),
Output("exit-deepcave", "children"),
Output("exit-deepcave", "disabled"),
]

@app.callback(inputs, outputs)
def callback(n_clicks):
# When clicking the Exit button, we first want to delete existing jobs and update the button
if n_clicks is not None:
queue.delete_jobs()
return "danger", "Terminated DeepCAVE", True
else:
return "primary", "Exit", False

def _callback_terminate_deepcave(self) -> None:
inputs = [Input("exit-deepcave", "n_clicks")]
outputs = []

@app.callback(inputs, outputs)
def callback(n_clicks):
# Then we want to terminate DeepCAVE
if n_clicks is not None:
time.sleep(1)
os._exit(130)

def __call__(self) -> html.Header:
return html.Header(
className="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow",
Expand All @@ -59,5 +94,8 @@ def __call__(self) -> html.Header:
className="me-2",
id="matplotlib-mode-toggle",
),
dbc.Button(
"Exit", color="secondary", className="me-2", id="exit-deepcave", disabled=False
),
],
)
21 changes: 13 additions & 8 deletions deepcave/plugins/hyperparameter/pdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
class PartialDependencies(StaticPlugin):
id = "pdp"
name = "Partial Dependencies"
icon = "far fa-grip-lines"
icon = "fas fa-grip-lines"
help = "docs/plugins/partial_dependencies.rst"
activate_run_selection = True

Expand Down Expand Up @@ -149,9 +149,7 @@ def load_dependency_inputs(self, run, previous_inputs, inputs):

if objective_value is None:
objective_value = objective_ids[0]
if budget_value is None:
budget_value = budget_ids[-1]
if hp1_value is None:
hp1_value = hp_names[0]

return {
Expand All @@ -163,7 +161,6 @@ def load_dependency_inputs(self, run, previous_inputs, inputs):
},
"hyperparameter_name_2": {
"options": get_checklist_options([None] + hp_names),
"value": hp2_value,
},
}

Expand Down Expand Up @@ -237,7 +234,7 @@ def get_output_layout(register):
return dcc.Graph(register("graph", "figure"), style={"height": Config.FIGURE_HEIGHT})

@staticmethod
def load_outputs(run, inputs, outputs):
def get_pdp_figure(run, inputs, outputs, show_confidence, show_ice, title=None):
# Parse inputs
hp1_name = inputs["hyperparameter_name_1"]
hp1_idx = run.configspace.get_idx_by_hyperparameter_name(hp1_name)
Expand All @@ -250,9 +247,6 @@ def load_outputs(run, inputs, outputs):
hp2_idx = run.configspace.get_idx_by_hyperparameter_name(hp2_name)
hp2 = run.configspace.get_hyperparameter(hp2_name)

show_confidence = inputs["show_confidence"]
show_ice = inputs["show_ice"]

objective = run.get_objective(inputs["objective_id"])
objective_name = objective.name

Expand Down Expand Up @@ -323,6 +317,7 @@ def load_outputs(run, inputs, outputs):
"yaxis": {
"title": objective_name,
},
"title": title
}
)
else:
Expand All @@ -349,10 +344,20 @@ def load_outputs(run, inputs, outputs):
xaxis=dict(tickvals=x_tickvals, ticktext=x_ticktext, title=hp1_name),
yaxis=dict(tickvals=y_tickvals, ticktext=y_ticktext, title=hp2_name),
margin=Config.FIGURE_MARGIN,
title=title
)
)

figure = go.Figure(data=traces, layout=layout)
save_image(figure, "pdp.pdf")

return figure

@staticmethod
def load_outputs(run, inputs, outputs):
show_confidence = inputs["show_confidence"]
show_ice = inputs["show_ice"]

figure = PartialDependencies.get_pdp_figure(run, inputs, outputs, show_confidence, show_ice)

return figure
Loading
Loading