-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement basic functionality to provide the user with evaluation metrics visualization as described in #281. Tensorboard should run on the supervisor and visualize evaluation metrics over time.
- Loading branch information
1 parent
93c3b55
commit 013e7af
Showing
17 changed files
with
275 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -177,3 +177,6 @@ model_storage: | |
evaluator: | ||
hostname: "evaluator" | ||
port: "50061" | ||
|
||
tensorboard: | ||
port: "50062" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
modyn/supervisor/internal/evaluation_result_writer/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
"""Supervisor module. The supervisor initiates a pipeline and coordinates all components. | ||
""" | ||
import os | ||
|
||
from .abstract_evaluation_result_writer import AbstractEvaluationResultWriter # noqa: F401 | ||
from .json_result_writer import JsonResultWriter # noqa: F401 | ||
from .tensorboard_result_writer import TensorboardResultWriter # noqa: F401 | ||
|
||
files = os.listdir(os.path.dirname(__file__)) | ||
files.remove("__init__.py") | ||
__all__ = [f[:-3] for f in files if f.endswith(".py")] |
35 changes: 35 additions & 0 deletions
35
modyn/supervisor/internal/evaluation_result_writer/abstract_evaluation_result_writer.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import pathlib | ||
from abc import ABC, abstractmethod | ||
|
||
# pylint: disable=no-name-in-module | ||
from modyn.evaluator.internal.grpc.generated.evaluator_pb2 import EvaluationData | ||
|
||
|
||
class AbstractEvaluationResultWriter(ABC): | ||
""" | ||
Abstract class used to write evaluation results to the evaluation directory | ||
""" | ||
|
||
def __init__(self, pipeline_id: int, trigger_id: int, eval_directory: pathlib.Path): | ||
self.pipeline_id = pipeline_id | ||
self.trigger_id = trigger_id | ||
self.eval_directory = eval_directory | ||
|
||
@abstractmethod | ||
def add_evaluation_data(self, dataset_id: str, dataset_size: int, evaluation_data: list[EvaluationData]) -> None: | ||
""" | ||
Called whenever a metric results are available for a particular dataset. | ||
Args: | ||
dataset_id: the involved dataset. | ||
dataset_size: the size (amount of samples) of the dataset. | ||
evaluation_data: contains the metric results. | ||
""" | ||
raise NotImplementedError() | ||
|
||
@abstractmethod | ||
def store_results(self) -> None: | ||
""" | ||
Called in the end to store the results. | ||
""" | ||
raise NotImplementedError() |
23 changes: 23 additions & 0 deletions
23
modyn/supervisor/internal/evaluation_result_writer/json_result_writer.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import json | ||
import pathlib | ||
|
||
# pylint: disable=no-name-in-module | ||
from modyn.evaluator.internal.grpc.generated.evaluator_pb2 import EvaluationData | ||
from modyn.supervisor.internal.evaluation_result_writer import AbstractEvaluationResultWriter | ||
|
||
|
||
class JsonResultWriter(AbstractEvaluationResultWriter): | ||
def __init__(self, pipeline_id: int, trigger_id: int, eval_directory: pathlib.Path): | ||
super().__init__(pipeline_id, trigger_id, eval_directory) | ||
self.results: dict = {"datasets": []} | ||
|
||
def add_evaluation_data(self, dataset_id: str, dataset_size: int, evaluation_data: list[EvaluationData]) -> None: | ||
dataset_results: dict = {"dataset_size": dataset_size, "metrics": []} | ||
for metric in evaluation_data: | ||
dataset_results["metrics"].append({"name": metric.metric, "result": metric.result}) | ||
self.results["datasets"].append({dataset_id: dataset_results}) | ||
|
||
def store_results(self) -> None: | ||
file_name = f"{self.pipeline_id}_{self.trigger_id}.eval" | ||
with open(self.eval_directory / file_name, "w+", encoding="utf-8") as output_file: | ||
json.dump(self.results, output_file) |
23 changes: 23 additions & 0 deletions
23
modyn/supervisor/internal/evaluation_result_writer/tensorboard_result_writer.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import pathlib | ||
|
||
# pylint: disable=no-name-in-module | ||
from modyn.evaluator.internal.grpc.generated.evaluator_pb2 import EvaluationData | ||
from modyn.supervisor.internal.evaluation_result_writer.abstract_evaluation_result_writer import ( | ||
AbstractEvaluationResultWriter, | ||
) | ||
from torch.utils.tensorboard import SummaryWriter | ||
|
||
|
||
class TensorboardResultWriter(AbstractEvaluationResultWriter): | ||
def __init__(self, pipeline_id: int, trigger_id: int, eval_directory: pathlib.Path): | ||
super().__init__(pipeline_id, trigger_id, eval_directory) | ||
self.writer = SummaryWriter(log_dir=str(eval_directory)) | ||
|
||
def add_evaluation_data(self, dataset_id: str, dataset_size: int, evaluation_data: list[EvaluationData]) -> None: | ||
for metric in evaluation_data: | ||
self.writer.add_scalar( | ||
f"pipeline_{self.pipeline_id}/{dataset_id}/{metric.metric}", metric.result, self.trigger_id | ||
) | ||
|
||
def store_results(self) -> None: | ||
self.writer.flush() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
...ts/supervisor/internal/evaluation_result_writer/test_abstract_evaluation_result_writer.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import pathlib | ||
|
||
from modyn.supervisor.internal.evaluation_result_writer import JsonResultWriter | ||
|
||
|
||
def test_init(): | ||
writer = JsonResultWriter(10, 15, pathlib.Path("")) | ||
assert writer.pipeline_id == 10 | ||
assert writer.trigger_id == 15 | ||
assert str(writer.eval_directory) == "." |
38 changes: 38 additions & 0 deletions
38
modyn/tests/supervisor/internal/evaluation_result_writer/test_json_result_writer.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import json | ||
import pathlib | ||
import tempfile | ||
|
||
# pylint: disable=no-name-in-module | ||
from modyn.evaluator.internal.grpc.generated.evaluator_pb2 import EvaluationData | ||
from modyn.supervisor.internal.evaluation_result_writer import JsonResultWriter | ||
|
||
|
||
def test_json_writer(): | ||
with tempfile.TemporaryDirectory() as path: | ||
eval_dir = pathlib.Path(path) | ||
writer = JsonResultWriter(10, 15, eval_dir) | ||
writer.add_evaluation_data("mnist", 1000, [EvaluationData(metric="Accuracy", result=0.5)]) | ||
writer.store_results() | ||
|
||
file_path = eval_dir / f"{10}_{15}.eval" | ||
assert file_path.exists() and file_path.is_file() | ||
|
||
with open(file_path, "r", encoding="utf-8") as eval_file: | ||
evaluation_results = json.load(eval_file) | ||
assert evaluation_results == json.loads( | ||
"""{ | ||
"datasets": [ | ||
{ | ||
"mnist": { | ||
"dataset_size": 1000, | ||
"metrics": [ | ||
{ | ||
"name": "Accuracy", | ||
"result": 0.5 | ||
} | ||
] | ||
} | ||
} | ||
] | ||
}""" | ||
) |
Oops, something went wrong.