autogluon.timeseries 1.0.1b20240327__tar.gz → 1.0.1b20240329__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of autogluon.timeseries might be problematic. Click here for more details.
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/PKG-INFO +1 -1
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/learner.py +28 -1
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/abstract/abstract_timeseries_model.py +7 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/chronos/model.py +57 -11
- autogluon.timeseries-1.0.1b20240327/src/autogluon/timeseries/models/chronos/chronos.py → autogluon.timeseries-1.0.1b20240329/src/autogluon/timeseries/models/chronos/pipeline.py +80 -19
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/multi_window/multi_window_model.py +5 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/predictor.py +42 -1
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/trainer/abstract_trainer.py +70 -18
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/version.py +1 -1
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/PKG-INFO +1 -1
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/SOURCES.txt +1 -1
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/requires.txt +4 -4
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/setup.cfg +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/setup.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/configs/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/configs/presets_configs.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/dataset/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/dataset/ts_dataframe.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/evaluator.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/metrics/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/metrics/abstract.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/metrics/point.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/metrics/quantile.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/metrics/utils.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/abstract/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/abstract/model_trial.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/autogluon_tabular/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/autogluon_tabular/mlforecast.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/autogluon_tabular/utils.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/chronos/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/ensemble/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/ensemble/abstract_timeseries_ensemble.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/ensemble/greedy_ensemble.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/gluonts/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/gluonts/abstract_gluonts.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/gluonts/torch/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/gluonts/torch/models.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/local/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/local/abstract_local_model.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/local/naive.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/local/npts.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/local/statsforecast.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/multi_window/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/presets.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/splitter.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/trainer/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/trainer/auto_trainer.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/datetime/__init__.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/datetime/base.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/datetime/lags.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/datetime/seasonality.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/datetime/time_features.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/features.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/forecast.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/warning_filters.py +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/dependency_links.txt +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/namespace_packages.txt +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/top_level.txt +0 -0
- {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/zip-safe +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import reprlib
|
|
3
3
|
import time
|
|
4
|
-
from typing import Any, Dict, List, Optional, Type, Union
|
|
4
|
+
from typing import Any, Dict, List, Literal, Optional, Type, Union
|
|
5
5
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
|
|
@@ -228,5 +228,32 @@ class TimeSeriesLearner(AbstractLearner):
|
|
|
228
228
|
learner_info.pop("random_state", None)
|
|
229
229
|
return learner_info
|
|
230
230
|
|
|
231
|
+
def persist_trainer(
|
|
232
|
+
self, models: Union[Literal["all", "best"], List[str]] = "all", with_ancestors: bool = False
|
|
233
|
+
) -> List[str]:
|
|
234
|
+
"""Loads models and trainer in memory so that they don't have to be
|
|
235
|
+
loaded during predictions
|
|
236
|
+
|
|
237
|
+
Returns
|
|
238
|
+
-------
|
|
239
|
+
list_of_models : List[str]
|
|
240
|
+
List of models persisted in memory
|
|
241
|
+
"""
|
|
242
|
+
self.trainer = self.load_trainer()
|
|
243
|
+
return self.trainer.persist(models, with_ancestors=with_ancestors)
|
|
244
|
+
|
|
245
|
+
def unpersist_trainer(self) -> List[str]:
|
|
246
|
+
"""Unloads models and trainer from memory. Models will have to be reloaded from disk
|
|
247
|
+
when predicting.
|
|
248
|
+
|
|
249
|
+
Returns
|
|
250
|
+
-------
|
|
251
|
+
list_of_models : List[str]
|
|
252
|
+
List of models removed from memory
|
|
253
|
+
"""
|
|
254
|
+
unpersisted_models = self.load_trainer().unpersist()
|
|
255
|
+
self.trainer = None
|
|
256
|
+
return unpersisted_models
|
|
257
|
+
|
|
231
258
|
def refit_full(self, model: str = "all") -> Dict[str, str]:
|
|
232
259
|
return self.load_trainer().refit_full(model=model)
|
|
@@ -415,6 +415,13 @@ class AbstractTimeSeriesModel(AbstractModel):
|
|
|
415
415
|
hpo_executor.register_resources(self, k_fold=1, **kwargs)
|
|
416
416
|
return self._hyperparameter_tune(hpo_executor=hpo_executor, **kwargs)
|
|
417
417
|
|
|
418
|
+
def persist(self) -> "AbstractTimeSeriesModel":
|
|
419
|
+
"""Ask the model to persist its assets in memory, i.e., to predict with low latency. In practice
|
|
420
|
+
this is used for pretrained models that have to lazy-load model parameters to device memory at
|
|
421
|
+
prediction time.
|
|
422
|
+
"""
|
|
423
|
+
return self
|
|
424
|
+
|
|
418
425
|
def _hyperparameter_tune(
|
|
419
426
|
self,
|
|
420
427
|
train_data: TimeSeriesDataFrame,
|
|
@@ -18,11 +18,29 @@ logger = logging.getLogger(__name__)
|
|
|
18
18
|
MODEL_CONFIGS = {
|
|
19
19
|
"amazon/chronos-t5-tiny": {
|
|
20
20
|
"num_gpus": 0, # minimum number of required GPUs
|
|
21
|
+
"default_torch_dtype": "auto",
|
|
22
|
+
"default_batch_size": 16,
|
|
23
|
+
},
|
|
24
|
+
"amazon/chronos-t5-mini": {
|
|
25
|
+
"num_gpus": 0,
|
|
26
|
+
"default_torch_dtype": "auto",
|
|
27
|
+
"default_batch_size": 16,
|
|
28
|
+
},
|
|
29
|
+
"amazon/chronos-t5-small": {
|
|
30
|
+
"num_gpus": 1,
|
|
31
|
+
"default_torch_dtype": "bfloat16",
|
|
32
|
+
"default_batch_size": 16,
|
|
33
|
+
},
|
|
34
|
+
"amazon/chronos-t5-base": {
|
|
35
|
+
"num_gpus": 1,
|
|
36
|
+
"default_torch_dtype": "bfloat16",
|
|
37
|
+
"default_batch_size": 16,
|
|
38
|
+
},
|
|
39
|
+
"amazon/chronos-t5-large": {
|
|
40
|
+
"num_gpus": 1,
|
|
41
|
+
"default_torch_dtype": "bfloat16",
|
|
42
|
+
"default_batch_size": 8,
|
|
21
43
|
},
|
|
22
|
-
"amazon/chronos-t5-mini": {"num_gpus": 0},
|
|
23
|
-
"amazon/chronos-t5-small": {"num_gpus": 1},
|
|
24
|
-
"amazon/chronos-t5-base": {"num_gpus": 1},
|
|
25
|
-
"amazon/chronos-t5-large": {"num_gpus": 1},
|
|
26
44
|
}
|
|
27
45
|
|
|
28
46
|
|
|
@@ -124,7 +142,6 @@ class ChronosModel(AbstractTimeSeriesModel):
|
|
|
124
142
|
|
|
125
143
|
# default number of samples for prediction
|
|
126
144
|
default_num_samples: int = 20
|
|
127
|
-
default_batch_size: int = 16
|
|
128
145
|
default_model_path = "amazon/chronos-t5-small"
|
|
129
146
|
maximum_context_length = 512
|
|
130
147
|
|
|
@@ -149,7 +166,7 @@ class ChronosModel(AbstractTimeSeriesModel):
|
|
|
149
166
|
self.device = hyperparameters.get("device")
|
|
150
167
|
|
|
151
168
|
# if the model requires a GPU, set the torch dtype to bfloat16
|
|
152
|
-
self.torch_dtype = hyperparameters.get("torch_dtype",
|
|
169
|
+
self.torch_dtype = hyperparameters.get("torch_dtype", self.default_torch_dtype)
|
|
153
170
|
|
|
154
171
|
self.data_loader_num_workers = hyperparameters.get("data_loader_num_workers", 0)
|
|
155
172
|
self.optimization_strategy: Optional[Literal["onnx", "openvino"]] = hyperparameters.get(
|
|
@@ -200,8 +217,32 @@ class ChronosModel(AbstractTimeSeriesModel):
|
|
|
200
217
|
return torch.cuda.is_available()
|
|
201
218
|
|
|
202
219
|
@property
|
|
203
|
-
def
|
|
204
|
-
|
|
220
|
+
def ag_default_config(self) -> Dict[str, Any]:
|
|
221
|
+
"""The default configuration of the model used by AutoGluon if the model is one of those
|
|
222
|
+
defined in MODEL_CONFIGS. For now, these are ``amazon/chronos-t5-*`` family of models.
|
|
223
|
+
"""
|
|
224
|
+
return MODEL_CONFIGS.get(self.model_path, {})
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def min_num_gpus(self) -> int:
|
|
228
|
+
"""Minimum number of GPUs required for the model. For models not defined in AutoGluon,
|
|
229
|
+
this value defaults to 0.
|
|
230
|
+
"""
|
|
231
|
+
return self.ag_default_config.get("num_gpus", 0)
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def default_batch_size(self) -> int:
|
|
235
|
+
"""Default batch size used for the model. For models not defined in AutoGluon, this value
|
|
236
|
+
defaults to 8.
|
|
237
|
+
"""
|
|
238
|
+
return self.ag_default_config.get("default_batch_size", 8)
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def default_torch_dtype(self) -> Any:
|
|
242
|
+
"""Default torch data type used for the model. For models not defined in AutoGluon, this value
|
|
243
|
+
defaults to "auto".
|
|
244
|
+
"""
|
|
245
|
+
return self.ag_default_config.get("default_torch_dtype", "auto")
|
|
205
246
|
|
|
206
247
|
def get_minimum_resources(self, is_gpu_available: bool = False) -> Dict[str, Union[int, float]]:
|
|
207
248
|
minimum_resources = {"num_cpus": 1}
|
|
@@ -211,7 +252,7 @@ class ChronosModel(AbstractTimeSeriesModel):
|
|
|
211
252
|
return minimum_resources
|
|
212
253
|
|
|
213
254
|
def load_model_pipeline(self, context_length: Optional[int] = None):
|
|
214
|
-
from .
|
|
255
|
+
from .pipeline import OptimizedChronosPipeline
|
|
215
256
|
|
|
216
257
|
gpu_available = self._is_gpu_available()
|
|
217
258
|
|
|
@@ -234,6 +275,10 @@ class ChronosModel(AbstractTimeSeriesModel):
|
|
|
234
275
|
|
|
235
276
|
self.model_pipeline = pipeline
|
|
236
277
|
|
|
278
|
+
def persist(self) -> "ChronosModel":
|
|
279
|
+
self.load_model_pipeline(context_length=self.context_length or self.maximum_context_length)
|
|
280
|
+
return self
|
|
281
|
+
|
|
237
282
|
def _fit(
|
|
238
283
|
self,
|
|
239
284
|
train_data: TimeSeriesDataFrame,
|
|
@@ -283,8 +328,9 @@ class ChronosModel(AbstractTimeSeriesModel):
|
|
|
283
328
|
with warning_filter(all_warnings=True):
|
|
284
329
|
import torch
|
|
285
330
|
|
|
286
|
-
|
|
287
|
-
|
|
331
|
+
if self.model_pipeline is None:
|
|
332
|
+
# load model pipeline to device memory
|
|
333
|
+
self.load_model_pipeline(context_length=context_length)
|
|
288
334
|
|
|
289
335
|
self.model_pipeline.model.eval()
|
|
290
336
|
with torch.inference_mode():
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
# Original Source: https://github.com/amazon-science/chronos-forecasting
|
|
5
|
-
#
|
|
5
|
+
# Authors: Lorenzo Stella <stellalo@amazon.com>, Abdul Fatir Ansari <ansarnd@amazon.com>
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
8
|
import warnings
|
|
@@ -18,6 +18,9 @@ from autogluon.timeseries.utils.warning_filters import set_loggers_level
|
|
|
18
18
|
logger = logging.getLogger(__name__)
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
__all__ = ["ChronosConfig", "ChronosPipeline", "OptimizedChronosPipeline"]
|
|
22
|
+
|
|
23
|
+
|
|
21
24
|
@dataclass
|
|
22
25
|
class ChronosConfig:
|
|
23
26
|
"""
|
|
@@ -81,14 +84,14 @@ class ChronosTokenizer:
|
|
|
81
84
|
A boolean tensor, same shape as ``token_ids``, indicating
|
|
82
85
|
which input observations are not ``torch.nan`` (i.e. not
|
|
83
86
|
missing nor padding).
|
|
84
|
-
|
|
87
|
+
tokenizer_state
|
|
85
88
|
An object that will be passed to ``output_transform``.
|
|
86
89
|
Contains the relevant context to decode output samples into
|
|
87
90
|
real values, such as location and scale parameters.
|
|
88
91
|
"""
|
|
89
92
|
raise NotImplementedError()
|
|
90
93
|
|
|
91
|
-
def output_transform(self, samples: torch.Tensor,
|
|
94
|
+
def output_transform(self, samples: torch.Tensor, tokenizer_state: Any) -> torch.Tensor:
|
|
92
95
|
"""
|
|
93
96
|
Turn a batch of sample token IDs into real values.
|
|
94
97
|
|
|
@@ -97,7 +100,7 @@ class ChronosTokenizer:
|
|
|
97
100
|
samples
|
|
98
101
|
A tensor of integers, shaped (batch_size, num_samples, time_length),
|
|
99
102
|
containing token IDs of sample trajectories.
|
|
100
|
-
|
|
103
|
+
tokenizer_state
|
|
101
104
|
An object returned by ``input_transform`` containing
|
|
102
105
|
relevant context to decode samples, such as location and scale.
|
|
103
106
|
The nature of this depends on the specific tokenizer.
|
|
@@ -132,13 +135,6 @@ class MeanScaleUniformBins(ChronosTokenizer):
|
|
|
132
135
|
|
|
133
136
|
if length > self.config.context_length:
|
|
134
137
|
context = context[..., -self.config.context_length :]
|
|
135
|
-
elif length < self.config.context_length:
|
|
136
|
-
padding_size = (
|
|
137
|
-
*context.shape[:-1],
|
|
138
|
-
self.config.context_length - length,
|
|
139
|
-
)
|
|
140
|
-
padding = torch.full(size=padding_size, fill_value=torch.nan)
|
|
141
|
-
context = torch.concat((padding, context), dim=-1)
|
|
142
138
|
|
|
143
139
|
attention_mask = ~torch.isnan(context)
|
|
144
140
|
scale = torch.nansum(torch.abs(context) * attention_mask, dim=-1) / torch.nansum(attention_mask, dim=-1)
|
|
@@ -191,7 +187,36 @@ class ChronosPretrainedModel(nn.Module):
|
|
|
191
187
|
super().__init__()
|
|
192
188
|
self.config = config
|
|
193
189
|
self.model = model
|
|
194
|
-
|
|
190
|
+
|
|
191
|
+
@property
|
|
192
|
+
def device(self):
|
|
193
|
+
return self.model.device
|
|
194
|
+
|
|
195
|
+
def encode(
|
|
196
|
+
self,
|
|
197
|
+
input_ids: torch.Tensor,
|
|
198
|
+
attention_mask: torch.Tensor,
|
|
199
|
+
):
|
|
200
|
+
"""
|
|
201
|
+
Extract the encoder embedding for the given token sequences.
|
|
202
|
+
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
input_ids
|
|
206
|
+
Tensor of indices of input sequence tokens in the vocabulary
|
|
207
|
+
with shape (batch_size, sequence_length).
|
|
208
|
+
attention_mask
|
|
209
|
+
A mask tensor of the same shape as input_ids to avoid attending
|
|
210
|
+
on padding or missing tokens.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
embedding
|
|
215
|
+
A tensor of encoder embeddings with shape
|
|
216
|
+
(batch_size, sequence_length, d_model).
|
|
217
|
+
"""
|
|
218
|
+
assert self.config.model_type == "seq2seq", "Encoder embeddings are only supported for encoder-decoder models"
|
|
219
|
+
return self.model.encoder(input_ids=input_ids, attention_mask=attention_mask).last_hidden_state
|
|
195
220
|
|
|
196
221
|
def forward(
|
|
197
222
|
self,
|
|
@@ -288,6 +313,48 @@ class ChronosPipeline:
|
|
|
288
313
|
self.tokenizer = tokenizer
|
|
289
314
|
self.model = model
|
|
290
315
|
|
|
316
|
+
def _prepare_and_validate_context(self, context: Union[torch.Tensor, List[torch.Tensor]]):
|
|
317
|
+
if isinstance(context, list):
|
|
318
|
+
context = left_pad_and_stack_1D(context)
|
|
319
|
+
assert isinstance(context, torch.Tensor)
|
|
320
|
+
if context.ndim == 1:
|
|
321
|
+
context = context.unsqueeze(0)
|
|
322
|
+
assert context.ndim == 2
|
|
323
|
+
|
|
324
|
+
return context
|
|
325
|
+
|
|
326
|
+
@torch.no_grad()
|
|
327
|
+
def embed(self, context: Union[torch.Tensor, List[torch.Tensor]]) -> Tuple[torch.Tensor, Any]:
|
|
328
|
+
"""
|
|
329
|
+
Get encoder embeddings for the given time series.
|
|
330
|
+
|
|
331
|
+
Parameters
|
|
332
|
+
----------
|
|
333
|
+
context
|
|
334
|
+
Input series. This is either a 1D tensor, or a list
|
|
335
|
+
of 1D tensors, or a 2D tensor whose first dimension
|
|
336
|
+
is batch. In the latter case, use left-padding with
|
|
337
|
+
``torch.nan`` to align series of different lengths.
|
|
338
|
+
|
|
339
|
+
Returns
|
|
340
|
+
-------
|
|
341
|
+
embeddings, tokenizer_state
|
|
342
|
+
A tuple of two tensors: the encoder embeddings and the tokenizer_state,
|
|
343
|
+
e.g., the scale of the time series in the case of mean scaling.
|
|
344
|
+
The encoder embeddings are shaped (batch_size, context_length, d_model)
|
|
345
|
+
or (batch_size, context_length + 1, d_model), where context_length
|
|
346
|
+
is the size of the context along the time axis if a 2D tensor was provided
|
|
347
|
+
or the length of the longest time series, if a list of 1D tensors was
|
|
348
|
+
provided, and the extra 1 is for EOS.
|
|
349
|
+
"""
|
|
350
|
+
context = self._prepare_and_validate_context(context=context)
|
|
351
|
+
token_ids, attention_mask, tokenizer_state = self.tokenizer.input_transform(context)
|
|
352
|
+
embeddings = self.model.encode(
|
|
353
|
+
input_ids=token_ids.to(self.model.device),
|
|
354
|
+
attention_mask=attention_mask.to(self.model.device),
|
|
355
|
+
).cpu()
|
|
356
|
+
return embeddings, tokenizer_state
|
|
357
|
+
|
|
291
358
|
def predict(
|
|
292
359
|
self,
|
|
293
360
|
context: Union[torch.Tensor, List[torch.Tensor]],
|
|
@@ -335,13 +402,7 @@ class ChronosPipeline:
|
|
|
335
402
|
Tensor of sample forecasts, of shape
|
|
336
403
|
(batch_size, num_samples, prediction_length).
|
|
337
404
|
"""
|
|
338
|
-
|
|
339
|
-
context = left_pad_and_stack_1D(context)
|
|
340
|
-
assert isinstance(context, torch.Tensor)
|
|
341
|
-
if context.ndim == 1:
|
|
342
|
-
context = context.unsqueeze(0)
|
|
343
|
-
assert context.ndim == 2
|
|
344
|
-
|
|
405
|
+
context = self._prepare_and_validate_context(context=context)
|
|
345
406
|
if prediction_length is None:
|
|
346
407
|
prediction_length = self.model.config.prediction_length
|
|
347
408
|
|
|
@@ -212,6 +212,11 @@ class MultiWindowBacktestingModel(AbstractTimeSeriesModel):
|
|
|
212
212
|
most_recent_model.save()
|
|
213
213
|
return save_path
|
|
214
214
|
|
|
215
|
+
def persist(self):
|
|
216
|
+
if self.most_recent_model is None:
|
|
217
|
+
raise ValueError(f"{self.name} must be fit before persisting")
|
|
218
|
+
self.most_recent_model.persist()
|
|
219
|
+
|
|
215
220
|
@classmethod
|
|
216
221
|
def load(
|
|
217
222
|
cls, path: str, reset_paths: bool = True, load_oof: bool = False, verbose: bool = True
|
|
@@ -5,7 +5,7 @@ import os
|
|
|
5
5
|
import pprint
|
|
6
6
|
import time
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Any, Dict, List, Optional, Tuple, Type, Union
|
|
8
|
+
from typing import Any, Dict, List, Literal, Optional, Tuple, Type, Union
|
|
9
9
|
|
|
10
10
|
import numpy as np
|
|
11
11
|
import pandas as pd
|
|
@@ -983,6 +983,47 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
|
|
|
983
983
|
return self._trainer.model_best
|
|
984
984
|
return self._trainer.get_model_best()
|
|
985
985
|
|
|
986
|
+
def persist(
|
|
987
|
+
self, models: Union[Literal["all", "best"], List[str]] = "best", with_ancestors: bool = True
|
|
988
|
+
) -> List[str]:
|
|
989
|
+
"""Persist models in memory for reduced inference latency. This is particularly important if the models are being used for online
|
|
990
|
+
inference where low latency is critical. If models are not persisted in memory, they are loaded from disk every time they are
|
|
991
|
+
asked to make predictions. This is especially cumbersome for large deep learning based models which have to be loaded into
|
|
992
|
+
accelerator (e.g., GPU) memory each time.
|
|
993
|
+
|
|
994
|
+
Parameters
|
|
995
|
+
----------
|
|
996
|
+
models : list of str or str, default = 'best'
|
|
997
|
+
Model names of models to persist.
|
|
998
|
+
If 'best' then the model with the highest validation score is persisted (this is the model used for prediction by default).
|
|
999
|
+
If 'all' then all models are persisted. Valid models are listed in this `predictor` by calling `predictor.model_names()`.
|
|
1000
|
+
with_ancestors : bool, default = True
|
|
1001
|
+
If True, all ancestor models of the provided models will also be persisted.
|
|
1002
|
+
If False, ensemble models will not have the models they depend on persisted unless those models were specified in `models`.
|
|
1003
|
+
This will slow down inference as the ancestor models will still need to be loaded from disk for each predict call.
|
|
1004
|
+
Only relevant for ensemble models.
|
|
1005
|
+
|
|
1006
|
+
Returns
|
|
1007
|
+
-------
|
|
1008
|
+
list_of_models : List[str]
|
|
1009
|
+
List of persisted model names.
|
|
1010
|
+
"""
|
|
1011
|
+
return self._learner.persist_trainer(models=models, with_ancestors=with_ancestors)
|
|
1012
|
+
|
|
1013
|
+
def unpersist(self) -> List[str]:
|
|
1014
|
+
"""Unpersist models in memory for reduced memory usage. If models are not persisted in memory, they are loaded from
|
|
1015
|
+
disk every time they are asked to make predictions.
|
|
1016
|
+
|
|
1017
|
+
Note: Another way to reset the predictor and unpersist models is to reload the predictor from disk
|
|
1018
|
+
via `predictor = TimeSeriesPredictor.load(predictor.path)`.
|
|
1019
|
+
|
|
1020
|
+
Returns
|
|
1021
|
+
-------
|
|
1022
|
+
list_of_models : List[str]
|
|
1023
|
+
List of unpersisted model names.
|
|
1024
|
+
"""
|
|
1025
|
+
return self._learner.unpersist_trainer()
|
|
1026
|
+
|
|
986
1027
|
def leaderboard(
|
|
987
1028
|
self,
|
|
988
1029
|
data: Optional[Union[TimeSeriesDataFrame, pd.DataFrame, Path, str]] = None,
|
|
@@ -5,7 +5,7 @@ import time
|
|
|
5
5
|
import traceback
|
|
6
6
|
from collections import defaultdict
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Any, Dict, List, Optional, Tuple, Type, Union
|
|
8
|
+
from typing import Any, Dict, List, Literal, Optional, Tuple, Type, Union
|
|
9
9
|
|
|
10
10
|
import networkx as nx
|
|
11
11
|
import numpy as np
|
|
@@ -71,21 +71,6 @@ class SimpleAbstractTrainer:
|
|
|
71
71
|
results[model] = self.model_graph.nodes[model][attribute]
|
|
72
72
|
return results
|
|
73
73
|
|
|
74
|
-
def get_model_best(self) -> str:
|
|
75
|
-
"""Return the name of the best model by model performance on the validation set."""
|
|
76
|
-
models = self.get_model_names()
|
|
77
|
-
if not models:
|
|
78
|
-
raise ValueError("Trainer has no fit models that can predict.")
|
|
79
|
-
if len(models) == 1:
|
|
80
|
-
return models[0]
|
|
81
|
-
model_performances = self.get_models_attribute_dict(attribute="val_score")
|
|
82
|
-
performances_list = [(m, model_performances[m]) for m in models if model_performances[m] is not None]
|
|
83
|
-
|
|
84
|
-
if not performances_list:
|
|
85
|
-
raise ValueError("No fitted models have validation scores computed.")
|
|
86
|
-
|
|
87
|
-
return max(performances_list, key=lambda i: i[1])[0]
|
|
88
|
-
|
|
89
74
|
def get_model_attribute(self, model: Union[str, AbstractModel], attribute: str):
|
|
90
75
|
"""Get a member attribute for given model from the `model_graph`."""
|
|
91
76
|
if not isinstance(model, str):
|
|
@@ -174,9 +159,12 @@ class SimpleAbstractTrainer:
|
|
|
174
159
|
raise NotImplementedError
|
|
175
160
|
|
|
176
161
|
# FIXME: Copy pasted from Tabular
|
|
177
|
-
def get_minimum_model_set(
|
|
162
|
+
def get_minimum_model_set(
|
|
163
|
+
self, model: Union[str, AbstractTimeSeriesModel], include_self: bool = True
|
|
164
|
+
) -> List[str]:
|
|
178
165
|
"""Gets the minimum set of models that the provided model depends on, including itself.
|
|
179
|
-
Returns a list of model names
|
|
166
|
+
Returns a list of model names
|
|
167
|
+
"""
|
|
180
168
|
if not isinstance(model, str):
|
|
181
169
|
model = model.name
|
|
182
170
|
minimum_model_set = list(nx.bfs_tree(self.model_graph, model, reverse=True))
|
|
@@ -219,6 +207,9 @@ class SimpleAbstractTrainer:
|
|
|
219
207
|
save_json.save(path=os.path.join(self.path, self.trainer_info_json_name), obj=info)
|
|
220
208
|
return info
|
|
221
209
|
|
|
210
|
+
def get_model_best(self, *args, **kwargs) -> AbstractModel:
|
|
211
|
+
raise NotImplementedError
|
|
212
|
+
|
|
222
213
|
def get_info(self, include_model_info: bool = False) -> Dict[str, Any]:
|
|
223
214
|
num_models_trained = len(self.get_model_names())
|
|
224
215
|
if self.model_best is not None:
|
|
@@ -389,6 +380,29 @@ class AbstractTimeSeriesTrainer(SimpleAbstractTrainer):
|
|
|
389
380
|
|
|
390
381
|
return levels
|
|
391
382
|
|
|
383
|
+
def get_model_best(self) -> str:
|
|
384
|
+
"""Return the name of the best model by model performance on the validation set."""
|
|
385
|
+
models = self.get_model_names()
|
|
386
|
+
if not models:
|
|
387
|
+
raise ValueError("Trainer has no fit models that can predict.")
|
|
388
|
+
if len(models) == 1:
|
|
389
|
+
return models[0]
|
|
390
|
+
model_performances = self.get_models_attribute_dict(attribute="val_score")
|
|
391
|
+
model_levels = self._get_model_levels()
|
|
392
|
+
model_name_score_level_list = [
|
|
393
|
+
(m, model_performances[m], model_levels.get(m, 0)) for m in models if model_performances[m] is not None
|
|
394
|
+
]
|
|
395
|
+
|
|
396
|
+
if not model_name_score_level_list:
|
|
397
|
+
raise ValueError("No fitted models have validation scores computed.")
|
|
398
|
+
|
|
399
|
+
# rank models in terms of validation score. if two models have the same validation score,
|
|
400
|
+
# rank them by their level in the model graph (lower level models are preferred).
|
|
401
|
+
return max(
|
|
402
|
+
model_name_score_level_list,
|
|
403
|
+
key=lambda mns: (mns[1], -mns[2]), # (score, -level)
|
|
404
|
+
)[0]
|
|
405
|
+
|
|
392
406
|
def get_model_names(self, level: Optional[int] = None, **kwargs) -> List[str]:
|
|
393
407
|
"""Get model names that are registered in the model graph"""
|
|
394
408
|
if level is not None:
|
|
@@ -797,6 +811,44 @@ class AbstractTimeSeriesTrainer(SimpleAbstractTrainer):
|
|
|
797
811
|
|
|
798
812
|
return df[explicit_column_order]
|
|
799
813
|
|
|
814
|
+
def persist(
|
|
815
|
+
self, model_names: Union[Literal["all", "best"], List[str]] = "all", with_ancestors: bool = False, **kwargs
|
|
816
|
+
) -> List[str]:
|
|
817
|
+
if model_names == "all":
|
|
818
|
+
model_names = self.get_model_names()
|
|
819
|
+
elif model_names == "best":
|
|
820
|
+
model_names = [self.get_model_best()]
|
|
821
|
+
if not isinstance(model_names, list):
|
|
822
|
+
raise ValueError(f"model_names must be a list of model names. Invalid value: {model_names}")
|
|
823
|
+
|
|
824
|
+
if with_ancestors:
|
|
825
|
+
models_with_ancestors = set()
|
|
826
|
+
for model_name in model_names:
|
|
827
|
+
models_with_ancestors = models_with_ancestors.union(self.get_minimum_model_set(model_name))
|
|
828
|
+
model_names = list(models_with_ancestors)
|
|
829
|
+
|
|
830
|
+
model_names_already_persisted = [model_name for model_name in model_names if model_name in self.models]
|
|
831
|
+
model_names = [model_name for model_name in model_names if model_name not in model_names_already_persisted]
|
|
832
|
+
|
|
833
|
+
for model_name in model_names:
|
|
834
|
+
model = self.load_model(model_name)
|
|
835
|
+
model.persist()
|
|
836
|
+
self.models[model.name] = model
|
|
837
|
+
|
|
838
|
+
return model_names
|
|
839
|
+
|
|
840
|
+
def unpersist(self, model_names: Union[Literal["all"], List[str]] = "all") -> List[str]:
|
|
841
|
+
if model_names == "all":
|
|
842
|
+
model_names = list(self.models.keys())
|
|
843
|
+
if not isinstance(model_names, list):
|
|
844
|
+
raise ValueError(f"model_names must be a list of model names. Invalid value: {model_names}")
|
|
845
|
+
unpersisted_models = []
|
|
846
|
+
for model in model_names:
|
|
847
|
+
if model in self.models:
|
|
848
|
+
self.models.pop(model)
|
|
849
|
+
unpersisted_models.append(model)
|
|
850
|
+
return unpersisted_models
|
|
851
|
+
|
|
800
852
|
def _get_model_for_prediction(self, model: Optional[Union[str, AbstractTimeSeriesModel]] = None) -> str:
|
|
801
853
|
"""Given an optional identifier or model object, return the name of the model with which to predict.
|
|
802
854
|
|
|
@@ -30,8 +30,8 @@ src/autogluon/timeseries/models/autogluon_tabular/__init__.py
|
|
|
30
30
|
src/autogluon/timeseries/models/autogluon_tabular/mlforecast.py
|
|
31
31
|
src/autogluon/timeseries/models/autogluon_tabular/utils.py
|
|
32
32
|
src/autogluon/timeseries/models/chronos/__init__.py
|
|
33
|
-
src/autogluon/timeseries/models/chronos/chronos.py
|
|
34
33
|
src/autogluon/timeseries/models/chronos/model.py
|
|
34
|
+
src/autogluon/timeseries/models/chronos/pipeline.py
|
|
35
35
|
src/autogluon/timeseries/models/ensemble/__init__.py
|
|
36
36
|
src/autogluon/timeseries/models/ensemble/abstract_timeseries_ensemble.py
|
|
37
37
|
src/autogluon/timeseries/models/ensemble/greedy_ensemble.py
|
|
@@ -16,13 +16,13 @@ utilsforecast<0.0.11,>=0.0.10
|
|
|
16
16
|
tqdm<5,>=4.38
|
|
17
17
|
orjson~=3.9
|
|
18
18
|
tensorboard<3,>=2.9
|
|
19
|
-
autogluon.core[raytune]==1.0.
|
|
20
|
-
autogluon.common==1.0.
|
|
21
|
-
autogluon.tabular[catboost,lightgbm,xgboost]==1.0.
|
|
19
|
+
autogluon.core[raytune]==1.0.1b20240329
|
|
20
|
+
autogluon.common==1.0.1b20240329
|
|
21
|
+
autogluon.tabular[catboost,lightgbm,xgboost]==1.0.1b20240329
|
|
22
22
|
|
|
23
23
|
[all]
|
|
24
|
-
optimum[nncf,openvino]<1.18,>=1.17
|
|
25
24
|
optimum[onnxruntime]<1.18,>=1.17
|
|
25
|
+
optimum[nncf,openvino]<1.18,>=1.17
|
|
26
26
|
|
|
27
27
|
[chronos-onnx]
|
|
28
28
|
optimum[onnxruntime]<1.18,>=1.17
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|