Skip to content

Commit 205e281

Browse files
committed
Add tests for SMT estimator
1 parent d9850b6 commit 205e281

1 file changed

Lines changed: 115 additions & 0 deletions

File tree

test/torch/model/test_smt.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License").
4+
# You may not use this file except in compliance with the License.
5+
# A copy of the License is located at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# or in the "license" file accompanying this file. This file is distributed
10+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
# express or implied. See the License for the specific language governing
12+
# permissions and limitations under the License.
13+
14+
from itertools import islice
15+
16+
import numpy as np
17+
import pytest
18+
import torch
19+
20+
from gluonts.dataset.common import ListDataset
21+
from gluonts.evaluation import make_evaluation_predictions, Evaluator
22+
from gluonts.torch.distributions import StudentTOutput, NormalOutput
23+
from gluonts.torch.model.smt import SMTEstimator
24+
25+
26+
PREDICTION_LENGTH = 6
27+
FREQ = "h"
28+
29+
30+
@pytest.fixture(autouse=True)
31+
def _allow_full_torch_load(monkeypatch):
32+
# torch>=2.6 defaults torch.load(weights_only=True), which rejects the
33+
# pickled hyperparameters in Lightning checkpoints reloaded by the
34+
# estimator. Relax it for the duration of each test.
35+
orig = torch.load
36+
monkeypatch.setattr(
37+
torch, "load", lambda *a, **k: orig(*a, **{**k, "weights_only": False})
38+
)
39+
40+
41+
def _univariate_dataset(num_series=3, length=200, seed=0):
42+
rng = np.random.default_rng(seed)
43+
t = np.arange(length)
44+
return ListDataset(
45+
[
46+
{
47+
"start": "2021-01-01 00:00:00",
48+
"target": (
49+
10.0
50+
+ 5.0 * np.sin(2 * np.pi * t / 24)
51+
+ rng.standard_normal(length)
52+
),
53+
}
54+
for _ in range(num_series)
55+
],
56+
freq=FREQ,
57+
)
58+
59+
60+
def _estimator(**kwargs):
61+
defaults = dict(
62+
freq=FREQ,
63+
prediction_length=PREDICTION_LENGTH,
64+
context_length=2 * PREDICTION_LENGTH,
65+
d_model=16,
66+
nhead=2,
67+
num_encoder_layers=1,
68+
num_decoder_layers=1,
69+
num_rnn_layers=1,
70+
mem_tokens=2,
71+
batch_size=8,
72+
num_batches_per_epoch=4,
73+
num_parallel_samples=20,
74+
trainer_kwargs=dict(
75+
max_epochs=2, accelerator="cpu", enable_progress_bar=False
76+
),
77+
)
78+
defaults.update(kwargs)
79+
return SMTEstimator(**defaults)
80+
81+
82+
def test_smt_univariate_train_predict():
83+
"""SMT trains and forecasts on univariate series with the right shapes."""
84+
dataset = _univariate_dataset()
85+
predictor = _estimator().train(dataset)
86+
87+
forecasts = list(predictor.predict(dataset, num_samples=20))
88+
89+
assert len(forecasts) == 3
90+
for forecast in forecasts:
91+
assert forecast.samples.shape == (20, PREDICTION_LENGTH)
92+
assert np.isfinite(forecast.mean).all()
93+
94+
95+
def test_smt_evaluation_runs():
96+
"""The full make_evaluation_predictions + Evaluator path produces finite
97+
metrics."""
98+
dataset = _univariate_dataset()
99+
predictor = _estimator().train(dataset)
100+
101+
forecast_it, ts_it = make_evaluation_predictions(
102+
dataset=dataset, predictor=predictor, num_samples=20
103+
)
104+
agg, _ = Evaluator(quantiles=[0.5])(list(ts_it), list(forecast_it))
105+
106+
assert np.isfinite(agg["MASE"])
107+
assert np.isfinite(agg["mean_wQuantileLoss"])
108+
109+
110+
@pytest.mark.parametrize("distr_output", [StudentTOutput(), NormalOutput()])
111+
def test_smt_distr_outputs(distr_output):
112+
dataset = _univariate_dataset()
113+
predictor = _estimator(distr_output=distr_output).train(dataset)
114+
forecasts = list(islice(predictor.predict(dataset), 1))
115+
assert forecasts[0].samples.shape == (20, PREDICTION_LENGTH)

0 commit comments

Comments
 (0)