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

[ENH] parallelization support and config for forecasting performance metrics #5813

Merged
merged 1 commit into from Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions sktime/performance_metrics/forecasting/_classes.py
Expand Up @@ -273,12 +273,17 @@ def _evaluate_vectorized(self, y_true, y_pred, **kwargs):
y_pred : VectorizedDF
non-time-like instances of y_true, y_pred must be identical
"""
backend = dict()
backend["backend"] = self.get_config()["backend:parallel"]
backend["backend_params"] = self.get_config()["backend:parallel:params"]

eval_result = y_true.vectorize_est(
estimator=self.clone(),
method="_evaluate",
varname_of_self="y_true",
args={**kwargs, "y_pred": y_pred},
colname_default=self.name,
**backend,
)

if isinstance(self.multioutput, str) and self.multioutput == "raw_values":
Expand All @@ -305,13 +310,18 @@ def _evaluate_by_index_vectorized(self, y_true, y_pred, **kwargs):
y_pred : VectorizedDF
non-time-like instances of y_true, y_pred must be identical
"""
backend = dict()
backend["backend"] = self.get_config()["backend:parallel"]
backend["backend_params"] = self.get_config()["backend:parallel:params"]

eval_result = y_true.vectorize_est(
estimator=self.clone().set_params(**{"multilevel": "uniform_average"}),
method="_evaluate_by_index",
varname_of_self="y_true",
args={**kwargs, "y_pred": y_pred},
colname_default=self.name,
return_type="list",
**backend,
)

eval_result = y_true.reconstruct(eval_result)
Expand Down
12 changes: 10 additions & 2 deletions sktime/performance_metrics/tests/test_metrics_classes.py
Expand Up @@ -14,6 +14,7 @@
from sktime.utils._testing.hierarchical import _make_hierarchical
from sktime.utils._testing.panel import _make_panel
from sktime.utils._testing.series import _make_series
from sktime.utils.parallel import _get_parallel_test_fixtures

metric_classes = getmembers(_classes, isclass)

Expand All @@ -24,6 +25,9 @@

MULTIOUTPUT = ["uniform_average", "raw_values", "numpy"]

# list of parallelization backends to test
BACKENDS = _get_parallel_test_fixtures("config")


@pytest.mark.parametrize("n_columns", [1, 2])
@pytest.mark.parametrize("multioutput", MULTIOUTPUT)
Expand Down Expand Up @@ -76,12 +80,13 @@ def test_metric_output_direct(metric, multioutput, n_columns):
assert np.allclose(res[1], res[2])


@pytest.mark.parametrize("backend", BACKENDS)
@pytest.mark.parametrize("n_columns", [1, 2])
@pytest.mark.parametrize(
"multilevel", ["uniform_average", "uniform_average_time", "raw_values"]
)
@pytest.mark.parametrize("multioutput", MULTIOUTPUT)
def test_metric_hierarchical(multioutput, multilevel, n_columns):
def test_metric_hierarchical(multioutput, multilevel, n_columns, backend):
"""Test hierarchical input for metrics."""
# create numpy weights based on n_columns
if multioutput == "numpy":
Expand All @@ -94,6 +99,7 @@ def test_metric_hierarchical(multioutput, multilevel, n_columns):
y_true = _make_hierarchical(random_state=42, n_columns=n_columns)

metric = MeanSquaredError(multioutput=multioutput, multilevel=multilevel)
metric.set_config(**backend)

res = metric(
y_true=y_true,
Expand Down Expand Up @@ -183,10 +189,11 @@ def test_metric_output_by_instance(metric, multioutput, n_columns):
assert (res.index == y_true.index).all()


@pytest.mark.parametrize("backend", BACKENDS)
@pytest.mark.parametrize("n_columns", [1, 2])
@pytest.mark.parametrize("multilevel", ["uniform_average", "raw_values"])
@pytest.mark.parametrize("multioutput", MULTIOUTPUT)
def test_metric_hierarchical_by_index(multioutput, multilevel, n_columns):
def test_metric_hierarchical_by_index(multioutput, multilevel, n_columns, backend):
"""Test hierarchical input for metrics."""
# create numpy weights based on n_columns
if multioutput == "numpy":
Expand All @@ -199,6 +206,7 @@ def test_metric_hierarchical_by_index(multioutput, multilevel, n_columns):
y_true = _make_hierarchical(random_state=42, n_columns=n_columns)

metric = MeanSquaredError(multioutput=multioutput, multilevel=multilevel)
metric.set_config(**backend)

res = metric.evaluate_by_index(
y_true=y_true,
Expand Down