Skip to content

Commit 128cfdd

Browse files
mpolson64facebook-github-bot
authored andcommitted
Remove uneccesary Tensorboard metrics (facebook#2487)
Summary: Pull Request resolved: facebook#2487 Reviewed By: mgarrard Differential Revision: D57920811 fbshipit-source-id: e1861073cfcb5874930a0720bc46c0e966eea36c
1 parent abacaea commit 128cfdd

File tree

2 files changed

+3
-289
lines changed

2 files changed

+3
-289
lines changed

ax/metrics/tensorboard.py

Lines changed: 2 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@
1111
import logging
1212

1313
from logging import Logger
14-
from typing import Any, Dict, Iterable, List, NamedTuple, Optional, Set, Union
14+
from typing import Any, Dict, List, Optional
1515

1616
import pandas as pd
1717
from ax.core.base_trial import BaseTrial
1818
from ax.core.map_data import MapData, MapKeyInfo
1919
from ax.core.map_metric import MapMetric
2020
from ax.core.metric import Metric, MetricFetchE, MetricFetchResult
2121
from ax.core.trial import Trial
22-
from ax.metrics.curve import AbstractCurveMetric
2322
from ax.utils.common.logger import get_logger
2423
from ax.utils.common.result import Err, Ok
2524
from pyre_extensions import assert_is_instance
@@ -33,7 +32,6 @@
3332
from tensorboard.backend.event_processing import (
3433
plugin_event_multiplexer as event_multiplexer,
3534
)
36-
from tensorboard.compat.proto import types_pb2
3735

3836
logging.getLogger("tensorboard").setLevel(logging.CRITICAL)
3937

@@ -218,120 +216,9 @@ def _get_event_multiplexer_for_trial(
218216

219217
return mul
220218

221-
class TensorboardCurveMetric(AbstractCurveMetric):
222-
"""A `CurveMetric` for getting Tensorboard curves."""
223-
224-
map_key_info: MapKeyInfo[float] = MapKeyInfo(key="steps", default_value=0.0)
225-
226-
def get_curves_from_ids(
227-
self,
228-
ids: Iterable[Union[int, str]],
229-
names: Optional[Set[str]] = None,
230-
) -> Dict[Union[int, str], Dict[str, pd.Series]]:
231-
"""Get curve data from tensorboard logs.
232-
233-
NOTE: If the ids are not simple paths/posix locations, subclass this metric
234-
and replace this method with an appropriate one that retrieves the log
235-
results.
236-
237-
Args:
238-
ids: A list of string paths to tensorboard log directories.
239-
names: The names of the tags for which to fetch the curves.
240-
If omitted, all tags are returned.
241-
242-
Returns:
243-
A nested dictionary mapping ids (first level) and metric names (second
244-
level) to pandas Series of data.
245-
"""
246-
return {idx: get_tb_from_posix(path=str(idx), tags=names) for idx in ids}
247-
248-
def get_tb_from_posix(
249-
path: str,
250-
tags: Optional[Set[str]] = None,
251-
) -> Dict[str, pd.Series]:
252-
r"""Get Tensorboard data from a posix path.
253-
254-
Args:
255-
path: The posix path for the directory that contains the tensorboard logs.
256-
tags: The names of the tags for which to fetch the curves. If omitted,
257-
all tags are returned.
258-
Returns:
259-
A dictionary mapping tag names to pandas Series of data.
260-
"""
261-
logger.debug(f"Reading TB logs from {path}.")
262-
mul = event_multiplexer.EventMultiplexer(max_reload_threads=20)
263-
mul.AddRunsFromDirectory(path, None)
264-
mul.Reload()
265-
scalar_dict = mul.PluginRunToTagToContent("scalars")
266-
267-
raw_result = [
268-
{"tag": tag, "event": mul.Tensors(run, tag)}
269-
for run, run_dict in scalar_dict.items()
270-
for tag in run_dict
271-
if tags is None or tag in tags
272-
]
273-
tb_run_data = {}
274-
for item in raw_result:
275-
latest_start_time = _get_latest_start_time(item["event"])
276-
steps = [e.step for e in item["event"] if e.wall_time >= latest_start_time]
277-
vals = [
278-
_get_event_value(e)
279-
for e in item["event"]
280-
if e.wall_time >= latest_start_time
281-
]
282-
key = item["tag"]
283-
series = pd.Series(index=steps, data=vals).dropna()
284-
if key in tb_run_data:
285-
tb_run_data[key] = pd.concat(objs=[tb_run_data[key], series])
286-
else:
287-
tb_run_data[key] = series
288-
for key, series in tb_run_data.items():
289-
if any(series.index.duplicated()):
290-
# take average of repeated observations of the same "step"
291-
series = series.groupby(series.index).mean()
292-
logger.debug(
293-
f"Found duplicate steps for tag {key}. "
294-
"Removing duplicates by averaging."
295-
)
296-
tb_run_data[key] = series
297-
return tb_run_data
298-
299-
# pyre-fixme[24]: Generic type `list` expects 1 type parameter, use
300-
# `typing.List` to avoid runtime subscripting errors.
301-
def _get_latest_start_time(events: List) -> float:
302-
"""In each directory, there may be previous training runs due to restarting
303-
training jobs.
304-
305-
Args:
306-
events: A list of TensorEvents.
307-
308-
Returns:
309-
The start time of the latest training run.
310-
"""
311-
events.sort(key=lambda e: e.wall_time)
312-
start_time = events[0].wall_time
313-
for i in range(1, len(events)):
314-
# detect points in time where restarts occurred
315-
if events[i].step < events[i - 1].step:
316-
start_time = events[i].wall_time
317-
return start_time
318-
319-
def _get_event_value(e: NamedTuple) -> float:
320-
r"""Helper function to check the dtype and then get the value
321-
stored in a TensorEvent."""
322-
tensor = e.tensor_proto # pyre-ignore[16]
323-
if tensor.dtype == types_pb2.DT_FLOAT:
324-
return tensor.float_val[0]
325-
elif tensor.dtype == types_pb2.DT_DOUBLE:
326-
return tensor.double_val[0]
327-
elif tensor.dtype == types_pb2.DT_INT32:
328-
return tensor.int_val[0]
329-
else:
330-
raise ValueError(f"Tensorboard dtype {tensor.dtype} not supported.")
331-
332219
except ImportError:
333220
logger.warning(
334221
"tensorboard package not found. If you would like to use "
335-
"TensorboardCurveMetric, please install tensorboard."
222+
"TensorboardMetric, please install tensorboard."
336223
)
337224
pass

ax/metrics/tests/test_tensorboard.py

Lines changed: 1 addition & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from ax.core.map_data import MapData
1919
from ax.core.objective import Objective
2020
from ax.core.optimization_config import OptimizationConfig
21-
from ax.metrics.tensorboard import TensorboardCurveMetric, TensorboardMetric
21+
from ax.metrics.tensorboard import TensorboardMetric
2222
from ax.runners.synthetic import SyntheticRunner
2323
from ax.utils.common.testutils import TestCase
2424
from ax.utils.testing.core_stubs import get_branin_search_space, get_trial
@@ -165,176 +165,3 @@ def test_cumulative_best(self) -> None:
165165
)
166166

167167
self.assertTrue(df.equals(expected_df))
168-
169-
170-
class TensorboardCurveMetricTest(TestCase):
171-
def test_tensorboard_curve_metric(self) -> None:
172-
def mock_get_tb_from_posix(
173-
path: str, tags: Optional[List[str]] = None
174-
) -> Dict[str, pd.Series]:
175-
data = np.array([10, 3, 5, 2, 7, 1])
176-
return {"test_curve": pd.Series((int(path) + 1) * data)}
177-
178-
mock_path = "ax.metrics.tensorboard.get_tb_from_posix"
179-
180-
class FakeTensorboardCurveMetric(TensorboardCurveMetric):
181-
@classmethod
182-
def get_ids_from_trials(
183-
cls, trials: Iterable[BaseTrial]
184-
) -> Dict[int, Union[int, str]]:
185-
result = {}
186-
for trial in trials:
187-
result[trial.index] = trial.index
188-
return result
189-
190-
with mock.patch(mock_path, side_effect=mock_get_tb_from_posix):
191-
# test simple
192-
experiment = Experiment(
193-
name="dummy_experiment",
194-
search_space=get_branin_search_space(),
195-
optimization_config=OptimizationConfig(
196-
objective=Objective(
197-
metric=FakeTensorboardCurveMetric(
198-
name="test_metric",
199-
curve_name="test_curve",
200-
lower_is_better=True,
201-
cumulative_best=False,
202-
),
203-
minimize=True,
204-
)
205-
),
206-
runner=SyntheticRunner(),
207-
)
208-
for param in range(0, 2):
209-
trial = experiment.new_trial()
210-
trial.add_arm(Arm(parameters={"x1": float(param), "x2": 0.0}))
211-
trial.run()
212-
213-
self.assertTrue(
214-
np.allclose(
215-
# pyre-fixme[16]: `Data` has no attribute `map_df`.
216-
experiment.fetch_data().map_df["mean"].to_numpy(),
217-
np.array(
218-
[10.0, 3.0, 5.0, 2.0, 7.0, 1.0, 20.0, 6.0, 10.0, 4.0, 14.0, 2.0]
219-
),
220-
)
221-
)
222-
223-
# test cumulative best
224-
experiment = Experiment(
225-
name="dummy_experiment",
226-
search_space=get_branin_search_space(),
227-
optimization_config=OptimizationConfig(
228-
objective=Objective(
229-
metric=FakeTensorboardCurveMetric(
230-
name="test_metric",
231-
curve_name="test_curve",
232-
lower_is_better=True,
233-
cumulative_best=True,
234-
),
235-
minimize=True,
236-
)
237-
),
238-
runner=SyntheticRunner(),
239-
)
240-
for param in range(0, 2):
241-
trial = experiment.new_trial()
242-
trial.add_arm(Arm(parameters={"x1": float(param), "x2": 0.0}))
243-
trial.run()
244-
245-
self.assertTrue(
246-
np.allclose(
247-
experiment.fetch_data().map_df["mean"].to_numpy(),
248-
np.array(
249-
[10.0, 3.0, 3.0, 2.0, 2.0, 1.0, 20.0, 6.0, 6.0, 4.0, 4.0, 2.0]
250-
),
251-
)
252-
)
253-
254-
# test cumulative best (lower is worse)
255-
experiment = Experiment(
256-
name="dummy_experiment",
257-
search_space=get_branin_search_space(),
258-
optimization_config=OptimizationConfig(
259-
objective=Objective(
260-
metric=FakeTensorboardCurveMetric(
261-
name="test_metric",
262-
curve_name="test_curve",
263-
lower_is_better=False,
264-
cumulative_best=True,
265-
),
266-
minimize=False,
267-
)
268-
),
269-
runner=SyntheticRunner(),
270-
)
271-
for param in range(0, 2):
272-
trial = experiment.new_trial()
273-
trial.add_arm(Arm(parameters={"x1": float(param), "x2": 0.0}))
274-
trial.run()
275-
276-
self.assertTrue(
277-
np.allclose(
278-
experiment.fetch_data().map_df["mean"].to_numpy(),
279-
np.array(
280-
[
281-
10.0,
282-
10.0,
283-
10.0,
284-
10.0,
285-
10.0,
286-
10.0,
287-
20.0,
288-
20.0,
289-
20.0,
290-
20.0,
291-
20.0,
292-
20.0,
293-
]
294-
),
295-
),
296-
)
297-
298-
# test smoothing
299-
experiment = Experiment(
300-
name="dummy_experiment",
301-
search_space=get_branin_search_space(),
302-
optimization_config=OptimizationConfig(
303-
objective=Objective(
304-
metric=FakeTensorboardCurveMetric(
305-
name="test_metric",
306-
curve_name="test_curve",
307-
lower_is_better=True,
308-
cumulative_best=False,
309-
smoothing_window=3,
310-
),
311-
minimize=True,
312-
)
313-
),
314-
runner=SyntheticRunner(),
315-
)
316-
for param in range(0, 2):
317-
trial = experiment.new_trial()
318-
trial.add_arm(Arm(parameters={"x1": float(param), "x2": 0.0}))
319-
trial.run()
320-
self.assertTrue(
321-
np.allclose(
322-
experiment.fetch_data().map_df["mean"].to_numpy(),
323-
np.array(
324-
[
325-
6.00000000,
326-
6.00000000,
327-
6.00000000,
328-
3.33333333,
329-
4.66666667,
330-
3.33333333,
331-
12.0,
332-
12.0,
333-
12.0,
334-
6.66666667,
335-
9.33333333,
336-
6.66666667,
337-
]
338-
),
339-
)
340-
)

0 commit comments

Comments
 (0)