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.

Files changed (62) hide show
  1. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/PKG-INFO +1 -1
  2. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/learner.py +28 -1
  3. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/abstract/abstract_timeseries_model.py +7 -0
  4. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/chronos/model.py +57 -11
  5. 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
  6. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/multi_window/multi_window_model.py +5 -0
  7. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/predictor.py +42 -1
  8. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/trainer/abstract_trainer.py +70 -18
  9. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/version.py +1 -1
  10. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/PKG-INFO +1 -1
  11. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/SOURCES.txt +1 -1
  12. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/requires.txt +4 -4
  13. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/setup.cfg +0 -0
  14. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/setup.py +0 -0
  15. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/__init__.py +0 -0
  16. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/configs/__init__.py +0 -0
  17. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/configs/presets_configs.py +0 -0
  18. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/dataset/__init__.py +0 -0
  19. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/dataset/ts_dataframe.py +0 -0
  20. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/evaluator.py +0 -0
  21. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/metrics/__init__.py +0 -0
  22. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/metrics/abstract.py +0 -0
  23. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/metrics/point.py +0 -0
  24. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/metrics/quantile.py +0 -0
  25. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/metrics/utils.py +0 -0
  26. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/__init__.py +0 -0
  27. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/abstract/__init__.py +0 -0
  28. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/abstract/model_trial.py +0 -0
  29. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/autogluon_tabular/__init__.py +0 -0
  30. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/autogluon_tabular/mlforecast.py +0 -0
  31. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/autogluon_tabular/utils.py +0 -0
  32. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/chronos/__init__.py +0 -0
  33. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/ensemble/__init__.py +0 -0
  34. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/ensemble/abstract_timeseries_ensemble.py +0 -0
  35. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/ensemble/greedy_ensemble.py +0 -0
  36. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/gluonts/__init__.py +0 -0
  37. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/gluonts/abstract_gluonts.py +0 -0
  38. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/gluonts/torch/__init__.py +0 -0
  39. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/gluonts/torch/models.py +0 -0
  40. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/local/__init__.py +0 -0
  41. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/local/abstract_local_model.py +0 -0
  42. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/local/naive.py +0 -0
  43. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/local/npts.py +0 -0
  44. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/local/statsforecast.py +0 -0
  45. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/multi_window/__init__.py +0 -0
  46. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/models/presets.py +0 -0
  47. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/splitter.py +0 -0
  48. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/trainer/__init__.py +0 -0
  49. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/trainer/auto_trainer.py +0 -0
  50. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/__init__.py +0 -0
  51. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/datetime/__init__.py +0 -0
  52. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/datetime/base.py +0 -0
  53. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/datetime/lags.py +0 -0
  54. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/datetime/seasonality.py +0 -0
  55. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/datetime/time_features.py +0 -0
  56. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/features.py +0 -0
  57. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/forecast.py +0 -0
  58. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon/timeseries/utils/warning_filters.py +0 -0
  59. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/dependency_links.txt +0 -0
  60. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/namespace_packages.txt +0 -0
  61. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/top_level.txt +0 -0
  62. {autogluon.timeseries-1.0.1b20240327 → autogluon.timeseries-1.0.1b20240329}/src/autogluon.timeseries.egg-info/zip-safe +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: autogluon.timeseries
3
- Version: 1.0.1b20240327
3
+ Version: 1.0.1b20240329
4
4
  Summary: AutoML for Image, Text, and Tabular Data
5
5
  Home-page: https://github.com/autogluon/autogluon
6
6
  Author: AutoGluon Community
@@ -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", "auto" if self.min_num_gpus == 0 else "bfloat16")
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 min_num_gpus(self):
204
- return MODEL_CONFIGS.get(self.model_path, {}).get("num_gpus", 0)
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 .chronos import OptimizedChronosPipeline
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
- # load model pipeline to device memory
287
- self.load_model_pipeline(context_length=context_length)
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
- # Author: Lorenzo Stella <stellalo@amazon.com>
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
- decoding_context
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, decoding_context: Any) -> 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
- decoding_context
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
- self.device = model.device
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
- if isinstance(context, list):
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(self, model: Union[str, AbstractTimeSeriesModel], include_self: bool = True) -> list:
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
 
@@ -1,3 +1,3 @@
1
1
  """This is the autogluon version file."""
2
- __version__ = '1.0.1b20240327'
2
+ __version__ = '1.0.1b20240329'
3
3
  __lite__ = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: autogluon.timeseries
3
- Version: 1.0.1b20240327
3
+ Version: 1.0.1b20240329
4
4
  Summary: AutoML for Image, Text, and Tabular Data
5
5
  Home-page: https://github.com/autogluon/autogluon
6
6
  Author: AutoGluon Community
@@ -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.1b20240327
20
- autogluon.common==1.0.1b20240327
21
- autogluon.tabular[catboost,lightgbm,xgboost]==1.0.1b20240327
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