From eb67bf0b9a5edfd074576a497185845698b4b6fc Mon Sep 17 00:00:00 2001 From: "Dr. Kashif Rasul" Date: Tue, 19 Nov 2019 16:45:05 +0100 Subject: [PATCH] added initial artifical const dataset --- pts/dataset/__init__.py | 1 + pts/dataset/artificial.py | 70 +++++++++++++++++++++ pts/dataset/common.py | 20 +++++- pts/model/__init__.py | 1 + test/model/deepar/test_auxillary_outputs.py | 69 ++++++++++++++++++++ 5 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 pts/dataset/artificial.py create mode 100644 test/model/deepar/test_auxillary_outputs.py diff --git a/pts/dataset/__init__.py b/pts/dataset/__init__.py index 3753289..0e8a474 100644 --- a/pts/dataset/__init__.py +++ b/pts/dataset/__init__.py @@ -11,3 +11,4 @@ from .sampler import ( from .process import ProcessStartField, ProcessDataEntry from .utils import to_pandas from .stat import ScaleHistogram, calculate_dataset_statistics +from .artificial import constant_dataset \ No newline at end of file diff --git a/pts/dataset/artificial.py b/pts/dataset/artificial.py new file mode 100644 index 0000000..5c5d587 --- /dev/null +++ b/pts/dataset/artificial.py @@ -0,0 +1,70 @@ +from typing import Callable, List, NamedTuple, Optional, Tuple, Union + +from .common import MetaData, CategoricalFeatureInfo, BasicFeatureInfo, FieldName, Dataset +from .list_dataset import ListDataset +from .stat import DatasetStatistics, calculate_dataset_statistics + +class DatasetInfo(NamedTuple): + """ + Information stored on a dataset. When downloading from the repository, the + dataset repository checks that the obtained version matches the one + declared in dataset_info/dataset_name.json. + """ + + name: str + metadata: MetaData + prediction_length: int + train_statistics: DatasetStatistics + test_statistics: DatasetStatistics + + +def constant_dataset() -> Tuple[DatasetInfo, Dataset, Dataset]: + metadata = MetaData( + freq="1H", + feat_static_cat=[ + CategoricalFeatureInfo( + name="feat_static_cat_000", cardinality="10" + ) + ], + feat_static_real=[BasicFeatureInfo(name="feat_static_real_000")], + ) + + start_date = "2000-01-01 00:00:00" + + train_ds = ListDataset( + data_iter=[ + { + FieldName.ITEM_ID: str(i), + FieldName.START: start_date, + FieldName.TARGET: [float(i)] * 24, + FieldName.FEAT_STATIC_CAT: [i], + FieldName.FEAT_STATIC_REAL: [float(i)], + } + for i in range(10) + ], + freq=metadata.freq, + ) + + test_ds = ListDataset( + data_iter=[ + { + FieldName.ITEM_ID: str(i), + FieldName.START: start_date, + FieldName.TARGET: [float(i)] * 30, + FieldName.FEAT_STATIC_CAT: [i], + FieldName.FEAT_STATIC_REAL: [float(i)], + } + for i in range(10) + ], + freq=metadata.freq, + ) + + info = DatasetInfo( + name="constant_dataset", + metadata=metadata, + prediction_length=2, + train_statistics=calculate_dataset_statistics(train_ds), + test_statistics=calculate_dataset_statistics(test_ds), + ) + + return info, train_ds, test_ds \ No newline at end of file diff --git a/pts/dataset/common.py b/pts/dataset/common.py index 8207ee6..7de2b39 100644 --- a/pts/dataset/common.py +++ b/pts/dataset/common.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import Any, Dict, Iterable, NamedTuple, Sized +from typing import Any, Dict, Iterable, NamedTuple, Sized, List, Optional DataEntry = Dict[str, Any] @@ -42,3 +42,21 @@ class Dataset(Sized, Iterable[DataEntry], ABC): @abstractmethod def __len__(self): pass + +class CategoricalFeatureInfo(): + name: str + cardinality: str + +class BasicFeatureInfo(): + name: str + +class MetaData(): + freq: str = None + target: Optional[BasicFeatureInfo] = None + + feat_static_cat: List[CategoricalFeatureInfo] = [] + feat_static_real: List[BasicFeatureInfo] = [] + feat_dynamic_real: List[BasicFeatureInfo] = [] + feat_dynamic_cat: List[CategoricalFeatureInfo] = [] + + prediction_length: Optional[int] = None \ No newline at end of file diff --git a/pts/model/__init__.py b/pts/model/__init__.py index 81a58d9..66ea016 100644 --- a/pts/model/__init__.py +++ b/pts/model/__init__.py @@ -2,3 +2,4 @@ from .estimator import Estimator, PTSEstimator from .forecast import Forecast from .predictor import Predictor from .quantile import Quantile +from .utils import get_module_forward_input_names \ No newline at end of file diff --git a/test/model/deepar/test_auxillary_outputs.py b/test/model/deepar/test_auxillary_outputs.py new file mode 100644 index 0000000..0e73b29 --- /dev/null +++ b/test/model/deepar/test_auxillary_outputs.py @@ -0,0 +1,69 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +from itertools import islice + +import torch + +from pts.modules import StudentTOutput +from pts.dataset import constant_dataset, TrainDataLoader +from pts.model.deepar import DeepAREstimator +from pts.model import get_module_forward_input_names +from pts import Trainer + + +ds_info, train_ds, test_ds = constant_dataset() +freq = ds_info.metadata.freq +prediction_length = ds_info.prediction_length + + +def test_distribution(): + """ + Makes sure additional tensors can be accessed and have expected shapes + """ + prediction_length = ds_info.prediction_length + estimator = DeepAREstimator( + freq=freq, + prediction_length=prediction_length, + trainer=Trainer(epochs=1, num_batches_per_epoch=1), + distr_output=StudentTOutput(), + ) + + train_output = estimator.train_model(train_ds) + + # todo adapt loader to anomaly detection use-case + batch_size = 2 + num_samples = 3 + + training_data_loader = TrainDataLoader( + dataset=train_ds, + transform=train_output.transformation, + batch_size=batch_size, + num_batches_per_epoch=estimator.trainer.num_batches_per_epoch, + device=torch.device("cpu") + ) + + seq_len = 2 * ds_info.prediction_length + + for data_entry in islice(training_data_loader, 1): + input_names = get_module_forward_input_names(train_output.trained_net) + + distr = train_output.trained_net.distribution( + *[data_entry[k] for k in input_names] + ) + + assert distr.sample(num_samples).shape == ( + num_samples, + batch_size, + seq_len, + )