autogluon.timeseries 1.3.2b20250712__py3-none-any.whl → 1.4.1b20251116__py3-none-any.whl

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.
Files changed (90) hide show
  1. autogluon/timeseries/configs/__init__.py +3 -2
  2. autogluon/timeseries/configs/hyperparameter_presets.py +62 -0
  3. autogluon/timeseries/configs/predictor_presets.py +84 -0
  4. autogluon/timeseries/dataset/ts_dataframe.py +98 -72
  5. autogluon/timeseries/learner.py +19 -18
  6. autogluon/timeseries/metrics/__init__.py +5 -5
  7. autogluon/timeseries/metrics/abstract.py +17 -17
  8. autogluon/timeseries/metrics/point.py +1 -1
  9. autogluon/timeseries/metrics/quantile.py +2 -2
  10. autogluon/timeseries/metrics/utils.py +4 -4
  11. autogluon/timeseries/models/__init__.py +4 -0
  12. autogluon/timeseries/models/abstract/abstract_timeseries_model.py +52 -75
  13. autogluon/timeseries/models/abstract/tunable.py +6 -6
  14. autogluon/timeseries/models/autogluon_tabular/mlforecast.py +72 -76
  15. autogluon/timeseries/models/autogluon_tabular/per_step.py +104 -46
  16. autogluon/timeseries/models/autogluon_tabular/transforms.py +9 -7
  17. autogluon/timeseries/models/chronos/model.py +115 -78
  18. autogluon/timeseries/models/chronos/{pipeline/utils.py → utils.py} +76 -44
  19. autogluon/timeseries/models/ensemble/__init__.py +29 -2
  20. autogluon/timeseries/models/ensemble/abstract.py +16 -52
  21. autogluon/timeseries/models/ensemble/array_based/__init__.py +3 -0
  22. autogluon/timeseries/models/ensemble/array_based/abstract.py +247 -0
  23. autogluon/timeseries/models/ensemble/array_based/models.py +50 -0
  24. autogluon/timeseries/models/ensemble/array_based/regressor/__init__.py +10 -0
  25. autogluon/timeseries/models/ensemble/array_based/regressor/abstract.py +87 -0
  26. autogluon/timeseries/models/ensemble/array_based/regressor/per_quantile_tabular.py +133 -0
  27. autogluon/timeseries/models/ensemble/array_based/regressor/tabular.py +141 -0
  28. autogluon/timeseries/models/ensemble/weighted/__init__.py +8 -0
  29. autogluon/timeseries/models/ensemble/weighted/abstract.py +41 -0
  30. autogluon/timeseries/models/ensemble/{basic.py → weighted/basic.py} +8 -18
  31. autogluon/timeseries/models/ensemble/{greedy.py → weighted/greedy.py} +13 -13
  32. autogluon/timeseries/models/gluonts/abstract.py +26 -26
  33. autogluon/timeseries/models/gluonts/dataset.py +4 -4
  34. autogluon/timeseries/models/gluonts/models.py +27 -12
  35. autogluon/timeseries/models/local/abstract_local_model.py +14 -14
  36. autogluon/timeseries/models/local/naive.py +4 -0
  37. autogluon/timeseries/models/local/npts.py +1 -0
  38. autogluon/timeseries/models/local/statsforecast.py +30 -14
  39. autogluon/timeseries/models/multi_window/multi_window_model.py +34 -23
  40. autogluon/timeseries/models/registry.py +65 -0
  41. autogluon/timeseries/models/toto/__init__.py +3 -0
  42. autogluon/timeseries/models/toto/_internal/__init__.py +9 -0
  43. autogluon/timeseries/models/toto/_internal/backbone/__init__.py +3 -0
  44. autogluon/timeseries/models/toto/_internal/backbone/attention.py +197 -0
  45. autogluon/timeseries/models/toto/_internal/backbone/backbone.py +262 -0
  46. autogluon/timeseries/models/toto/_internal/backbone/distribution.py +70 -0
  47. autogluon/timeseries/models/toto/_internal/backbone/kvcache.py +136 -0
  48. autogluon/timeseries/models/toto/_internal/backbone/rope.py +94 -0
  49. autogluon/timeseries/models/toto/_internal/backbone/scaler.py +306 -0
  50. autogluon/timeseries/models/toto/_internal/backbone/transformer.py +333 -0
  51. autogluon/timeseries/models/toto/_internal/dataset.py +165 -0
  52. autogluon/timeseries/models/toto/_internal/forecaster.py +423 -0
  53. autogluon/timeseries/models/toto/dataloader.py +108 -0
  54. autogluon/timeseries/models/toto/hf_pretrained_model.py +119 -0
  55. autogluon/timeseries/models/toto/model.py +236 -0
  56. autogluon/timeseries/predictor.py +94 -107
  57. autogluon/timeseries/regressor.py +31 -27
  58. autogluon/timeseries/splitter.py +7 -31
  59. autogluon/timeseries/trainer/__init__.py +3 -0
  60. autogluon/timeseries/trainer/ensemble_composer.py +250 -0
  61. autogluon/timeseries/trainer/model_set_builder.py +256 -0
  62. autogluon/timeseries/trainer/prediction_cache.py +149 -0
  63. autogluon/timeseries/{trainer.py → trainer/trainer.py} +182 -307
  64. autogluon/timeseries/trainer/utils.py +18 -0
  65. autogluon/timeseries/transforms/covariate_scaler.py +4 -4
  66. autogluon/timeseries/transforms/target_scaler.py +14 -14
  67. autogluon/timeseries/utils/datetime/lags.py +2 -2
  68. autogluon/timeseries/utils/datetime/time_features.py +2 -2
  69. autogluon/timeseries/utils/features.py +41 -37
  70. autogluon/timeseries/utils/forecast.py +5 -5
  71. autogluon/timeseries/utils/warning_filters.py +3 -1
  72. autogluon/timeseries/version.py +1 -1
  73. autogluon.timeseries-1.4.1b20251116-py3.9-nspkg.pth +1 -0
  74. {autogluon.timeseries-1.3.2b20250712.dist-info → autogluon_timeseries-1.4.1b20251116.dist-info}/METADATA +32 -17
  75. autogluon_timeseries-1.4.1b20251116.dist-info/RECORD +96 -0
  76. {autogluon.timeseries-1.3.2b20250712.dist-info → autogluon_timeseries-1.4.1b20251116.dist-info}/WHEEL +1 -1
  77. autogluon/timeseries/configs/presets_configs.py +0 -79
  78. autogluon/timeseries/evaluator.py +0 -6
  79. autogluon/timeseries/models/chronos/pipeline/__init__.py +0 -10
  80. autogluon/timeseries/models/chronos/pipeline/base.py +0 -160
  81. autogluon/timeseries/models/chronos/pipeline/chronos.py +0 -544
  82. autogluon/timeseries/models/chronos/pipeline/chronos_bolt.py +0 -530
  83. autogluon/timeseries/models/presets.py +0 -358
  84. autogluon.timeseries-1.3.2b20250712-py3.9-nspkg.pth +0 -1
  85. autogluon.timeseries-1.3.2b20250712.dist-info/RECORD +0 -71
  86. {autogluon.timeseries-1.3.2b20250712.dist-info → autogluon_timeseries-1.4.1b20251116.dist-info/licenses}/LICENSE +0 -0
  87. {autogluon.timeseries-1.3.2b20250712.dist-info → autogluon_timeseries-1.4.1b20251116.dist-info/licenses}/NOTICE +0 -0
  88. {autogluon.timeseries-1.3.2b20250712.dist-info → autogluon_timeseries-1.4.1b20251116.dist-info}/namespace_packages.txt +0 -0
  89. {autogluon.timeseries-1.3.2b20250712.dist-info → autogluon_timeseries-1.4.1b20251116.dist-info}/top_level.txt +0 -0
  90. {autogluon.timeseries-1.3.2b20250712.dist-info → autogluon_timeseries-1.4.1b20251116.dist-info}/zip-safe +0 -0
@@ -5,14 +5,14 @@ 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, Literal, Optional, Tuple, Type, Union
8
+ from typing import Any, Literal, Optional, Union
9
9
 
10
10
  import networkx as nx
11
11
  import numpy as np
12
12
  import pandas as pd
13
13
  from tqdm import tqdm
14
14
 
15
- from autogluon.common.utils.utils import hash_pandas_df, seed_everything
15
+ from autogluon.common.utils.utils import seed_everything
16
16
  from autogluon.core.trainer.abstract_trainer import AbstractTrainer
17
17
  from autogluon.core.utils.exceptions import TimeLimitExceeded
18
18
  from autogluon.core.utils.loaders import load_pkl
@@ -20,23 +20,25 @@ from autogluon.core.utils.savers import save_pkl
20
20
  from autogluon.timeseries import TimeSeriesDataFrame
21
21
  from autogluon.timeseries.metrics import TimeSeriesScorer, check_get_evaluation_metric
22
22
  from autogluon.timeseries.models.abstract import AbstractTimeSeriesModel, TimeSeriesModelBase
23
- from autogluon.timeseries.models.ensemble import AbstractTimeSeriesEnsembleModel, GreedyEnsemble
23
+ from autogluon.timeseries.models.ensemble import AbstractTimeSeriesEnsembleModel
24
24
  from autogluon.timeseries.models.multi_window import MultiWindowBacktestingModel
25
- from autogluon.timeseries.models.presets import contains_searchspace, get_preset_models
26
25
  from autogluon.timeseries.splitter import AbstractWindowSplitter, ExpandingWindowSplitter
26
+ from autogluon.timeseries.trainer.ensemble_composer import EnsembleComposer, validate_ensemble_hyperparameters
27
27
  from autogluon.timeseries.utils.features import (
28
28
  ConstantReplacementFeatureImportanceTransform,
29
29
  CovariateMetadata,
30
30
  PermutationFeatureImportanceTransform,
31
31
  )
32
- from autogluon.timeseries.utils.warning_filters import disable_tqdm, warning_filter
32
+ from autogluon.timeseries.utils.warning_filters import disable_tqdm
33
+
34
+ from .model_set_builder import TrainableModelSetBuilder, contains_searchspace
35
+ from .prediction_cache import PredictionCache, get_prediction_cache
36
+ from .utils import log_scores_and_times
33
37
 
34
38
  logger = logging.getLogger("autogluon.timeseries.trainer")
35
39
 
36
40
 
37
41
  class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
38
- _cached_predictions_filename = "cached_predictions.pkl"
39
-
40
42
  max_rel_importance_score: float = 1e5
41
43
  eps_abs_importance_score: float = 1e-5
42
44
  max_ensemble_time_limit: float = 600.0
@@ -50,11 +52,11 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
50
52
  skip_model_selection: bool = False,
51
53
  enable_ensemble: bool = True,
52
54
  verbosity: int = 2,
53
- val_splitter: Optional[AbstractWindowSplitter] = None,
55
+ num_val_windows: Optional[int] = None,
56
+ val_step_size: Optional[int] = None,
54
57
  refit_every_n_windows: Optional[int] = 1,
55
58
  # TODO: Set cache_predictions=False by default once all models in default presets have a reasonable inference speed
56
59
  cache_predictions: bool = True,
57
- ensemble_model_type: Optional[Type] = None,
58
60
  **kwargs,
59
61
  ):
60
62
  super().__init__(
@@ -71,32 +73,27 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
71
73
  self.skip_model_selection = skip_model_selection
72
74
  # Ensemble cannot be fit if val_scores are not computed
73
75
  self.enable_ensemble = enable_ensemble and not skip_model_selection
74
- if ensemble_model_type is None:
75
- ensemble_model_type = GreedyEnsemble
76
- else:
76
+ if kwargs.get("ensemble_model_type") is not None:
77
77
  logger.warning(
78
- "Using a custom `ensemble_model_type` is experimental functionality that may break in future versions."
78
+ "Using a custom `ensemble_model_type` is no longer supported. Use the `ensemble_hyperparameters` "
79
+ "argument to `fit` instead."
79
80
  )
80
- self.ensemble_model_type: Type[AbstractTimeSeriesEnsembleModel] = ensemble_model_type
81
81
 
82
82
  self.verbosity = verbosity
83
83
 
84
- #: Dict of normal model -> FULL model. FULL models are produced by
84
+ #: dict of normal model -> FULL model. FULL models are produced by
85
85
  #: self.refit_single_full() and self.refit_full().
86
86
  self.model_refit_map = {}
87
87
 
88
88
  self.eval_metric = check_get_evaluation_metric(eval_metric, prediction_length=prediction_length)
89
- if val_splitter is None:
90
- val_splitter = ExpandingWindowSplitter(prediction_length=self.prediction_length)
91
- assert isinstance(val_splitter, AbstractWindowSplitter), "val_splitter must be of type AbstractWindowSplitter"
92
- self.val_splitter = val_splitter
89
+
90
+ self.num_val_windows = num_val_windows
91
+ self.val_step_size = val_step_size
93
92
  self.refit_every_n_windows = refit_every_n_windows
94
- self.cache_predictions = cache_predictions
95
93
  self.hpo_results = {}
96
94
 
97
- if self._cached_predictions_path.exists():
98
- logger.debug(f"Removing existing cached predictions file {self._cached_predictions_path}")
99
- self._cached_predictions_path.unlink()
95
+ self.prediction_cache: PredictionCache = get_prediction_cache(cache_predictions, self.path)
96
+ self.prediction_cache.clear()
100
97
 
101
98
  @property
102
99
  def path_pkl(self) -> str:
@@ -121,7 +118,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
121
118
  else:
122
119
  return None
123
120
 
124
- def load_data(self) -> Tuple[TimeSeriesDataFrame, Optional[TimeSeriesDataFrame]]:
121
+ def load_data(self) -> tuple[TimeSeriesDataFrame, Optional[TimeSeriesDataFrame]]:
125
122
  train_data = self.load_train_data()
126
123
  val_data = self.load_val_data()
127
124
  return train_data, val_data
@@ -136,7 +133,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
136
133
 
137
134
  self.models = models
138
135
 
139
- def _get_model_oof_predictions(self, model_name: str) -> List[TimeSeriesDataFrame]:
136
+ def _get_model_oof_predictions(self, model_name: str) -> list[TimeSeriesDataFrame]:
140
137
  model_path = os.path.join(self.path, self.get_model_attribute(model=model_name, attribute="path"))
141
138
  model_type = self.get_model_attribute(model=model_name, attribute="type")
142
139
  return model_type.load_oof_predictions(path=model_path)
@@ -144,16 +141,16 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
144
141
  def _add_model(
145
142
  self,
146
143
  model: TimeSeriesModelBase,
147
- base_models: Optional[List[str]] = None,
144
+ base_models: Optional[list[str]] = None,
148
145
  ):
149
146
  """Add a model to the model graph of the trainer. If the model is an ensemble, also add
150
147
  information about dependencies to the model graph (list of models specified via ``base_models``).
151
148
 
152
149
  Parameters
153
150
  ----------
154
- model : TimeSeriesModelBase
151
+ model
155
152
  The model to be added to the model graph.
156
- base_models : List[str], optional, default None
153
+ base_models
157
154
  If the model is an ensemble, the list of base model names that are included in the ensemble.
158
155
  Expected only when ``model`` is a ``AbstractTimeSeriesEnsembleModel``.
159
156
 
@@ -176,7 +173,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
176
173
  for base_model in base_models:
177
174
  self.model_graph.add_edge(base_model, model.name)
178
175
 
179
- def _get_model_levels(self) -> Dict[str, int]:
176
+ def _get_model_levels(self) -> dict[str, int]:
180
177
  """Get a dictionary mapping each model to their level in the model graph"""
181
178
 
182
179
  # get nodes without a parent
@@ -197,7 +194,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
197
194
 
198
195
  return levels
199
196
 
200
- def get_models_attribute_dict(self, attribute: str, models: Optional[List[str]] = None) -> Dict[str, Any]:
197
+ def get_models_attribute_dict(self, attribute: str, models: Optional[list[str]] = None) -> dict[str, Any]:
201
198
  """Get an attribute from the `model_graph` for each of the model names
202
199
  specified. If `models` is none, the attribute will be returned for all models"""
203
200
  results = {}
@@ -230,13 +227,13 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
230
227
  key=lambda mns: (mns[1], -mns[2]), # (score, -level)
231
228
  )[0]
232
229
 
233
- def get_model_names(self, level: Optional[int] = None) -> List[str]:
230
+ def get_model_names(self, level: Optional[int] = None) -> list[str]:
234
231
  """Get model names that are registered in the model graph"""
235
232
  if level is not None:
236
233
  return list(node for node, l in self._get_model_levels().items() if l == level) # noqa: E741
237
234
  return list(self.model_graph.nodes)
238
235
 
239
- def get_info(self, include_model_info: bool = False) -> Dict[str, Any]:
236
+ def get_info(self, include_model_info: bool = False) -> dict[str, Any]:
240
237
  num_models_trained = len(self.get_model_names())
241
238
  if self.model_best is not None:
242
239
  best_model = self.model_best
@@ -261,25 +258,6 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
261
258
 
262
259
  return info
263
260
 
264
- def _train_single(
265
- self,
266
- train_data: TimeSeriesDataFrame,
267
- model: AbstractTimeSeriesModel,
268
- val_data: Optional[TimeSeriesDataFrame] = None,
269
- time_limit: Optional[float] = None,
270
- ) -> AbstractTimeSeriesModel:
271
- """Train the single model and return the model object that was fitted. This method
272
- does not save the resulting model."""
273
- model.fit(
274
- train_data=train_data,
275
- val_data=val_data,
276
- time_limit=time_limit,
277
- verbosity=self.verbosity,
278
- val_splitter=self.val_splitter,
279
- refit_every_n_windows=self.refit_every_n_windows,
280
- )
281
- return model
282
-
283
261
  def tune_model_hyperparameters(
284
262
  self,
285
263
  model: AbstractTimeSeriesModel,
@@ -302,7 +280,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
302
280
  hyperparameter_tune_kwargs=hyperparameter_tune_kwargs,
303
281
  time_limit=time_limit,
304
282
  default_num_trials=default_num_trials,
305
- val_splitter=self.val_splitter,
283
+ val_splitter=self._get_val_splitter(),
306
284
  refit_every_n_windows=self.refit_every_n_windows,
307
285
  )
308
286
  total_tuning_time = time.time() - tuning_start_time
@@ -339,12 +317,13 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
339
317
  model: AbstractTimeSeriesModel,
340
318
  val_data: Optional[TimeSeriesDataFrame] = None,
341
319
  time_limit: Optional[float] = None,
342
- ) -> List[str]:
320
+ ) -> list[str]:
343
321
  """Fit and save the given model on given training and validation data and save the trained model.
344
322
 
345
323
  Returns
346
324
  -------
347
- model_names_trained: the list of model names that were successfully trained
325
+ model_names_trained
326
+ the list of model names that were successfully trained
348
327
  """
349
328
  fit_start_time = time.time()
350
329
  model_names_trained = []
@@ -354,7 +333,15 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
354
333
  logger.info(f"\tSkipping {model.name} due to lack of time remaining.")
355
334
  return model_names_trained
356
335
 
357
- model = self._train_single(train_data, model, val_data=val_data, time_limit=time_limit)
336
+ model.fit(
337
+ train_data=train_data,
338
+ val_data=val_data,
339
+ time_limit=time_limit,
340
+ verbosity=self.verbosity,
341
+ val_splitter=self._get_val_splitter(),
342
+ refit_every_n_windows=self.refit_every_n_windows,
343
+ )
344
+
358
345
  fit_end_time = time.time()
359
346
  model.fit_time = model.fit_time or (fit_end_time - fit_start_time)
360
347
 
@@ -365,7 +352,12 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
365
352
  val_data, store_val_score=True, store_predict_time=True, time_limit=time_limit
366
353
  )
367
354
 
368
- self._log_scores_and_times(model.val_score, model.fit_time, model.predict_time)
355
+ log_scores_and_times(
356
+ val_score=model.val_score,
357
+ fit_time=model.fit_time,
358
+ predict_time=model.predict_time,
359
+ eval_metric_name=self.eval_metric.name_with_sign,
360
+ )
369
361
 
370
362
  self.save_model(model=model)
371
363
  except TimeLimitExceeded:
@@ -381,31 +373,51 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
381
373
 
382
374
  return model_names_trained
383
375
 
384
- def _log_scores_and_times(
385
- self,
386
- val_score: Optional[float] = None,
387
- fit_time: Optional[float] = None,
388
- predict_time: Optional[float] = None,
389
- ):
390
- if val_score is not None:
391
- logger.info(f"\t{val_score:<7.4f}".ljust(15) + f"= Validation score ({self.eval_metric.name_with_sign})")
392
- if fit_time is not None:
393
- logger.info(f"\t{fit_time:<7.2f} s".ljust(15) + "= Training runtime")
394
- if predict_time is not None:
395
- logger.info(f"\t{predict_time:<7.2f} s".ljust(15) + "= Validation (prediction) runtime")
396
-
397
- def _train_multi(
376
+ def fit(
398
377
  self,
399
378
  train_data: TimeSeriesDataFrame,
400
- hyperparameters: Union[str, Dict],
379
+ hyperparameters: Union[str, dict[Any, dict]],
401
380
  val_data: Optional[TimeSeriesDataFrame] = None,
381
+ ensemble_hyperparameters: Optional[dict] = None,
402
382
  hyperparameter_tune_kwargs: Optional[Union[str, dict]] = None,
403
- excluded_model_types: Optional[List[str]] = None,
383
+ excluded_model_types: Optional[list[str]] = None,
404
384
  time_limit: Optional[float] = None,
405
385
  random_seed: Optional[int] = None,
406
- ) -> List[str]:
386
+ ):
387
+ """Fit a set of timeseries models specified by the `hyperparameters`
388
+ dictionary that maps model names to their specified hyperparameters.
389
+
390
+ Parameters
391
+ ----------
392
+ train_data
393
+ Training data for fitting time series timeseries models.
394
+ hyperparameters
395
+ A dictionary mapping selected model names, model classes or model factory to hyperparameter
396
+ settings. Model names should be present in `trainer.presets.DEFAULT_MODEL_NAMES`. Optionally,
397
+ the user may provide one of "default", "light" and "very_light" to specify presets.
398
+ val_data
399
+ Optional validation data set to report validation scores on.
400
+ ensemble_hyperparameters
401
+ A dictionary mapping ensemble names to their specified hyperparameters. Ensemble names
402
+ should be defined in the models.ensemble namespace. defaults to `{"GreedyEnsemble": {}}`
403
+ which only fits a greedy weighted ensemble with default hyperparameters. Providing an
404
+ empty dictionary disables ensemble training.
405
+ hyperparameter_tune_kwargs
406
+ Args for hyperparameter tuning
407
+ excluded_model_types
408
+ Names of models that should not be trained, even if listed in `hyperparameters`.
409
+ time_limit
410
+ Time limit for training
411
+ random_seed
412
+ Random seed that will be set to each model during training
413
+ """
407
414
  logger.info(f"\nStarting training. Start time is {time.strftime('%Y-%m-%d %H:%M:%S')}")
408
415
 
416
+ # Handle ensemble hyperparameters
417
+ if ensemble_hyperparameters is None:
418
+ ensemble_hyperparameters = {"GreedyEnsemble": {}}
419
+ ensemble_hyperparameters = validate_ensemble_hyperparameters(ensemble_hyperparameters)
420
+
409
421
  time_start = time.time()
410
422
  hyperparameters = copy.deepcopy(hyperparameters)
411
423
 
@@ -415,11 +427,11 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
415
427
  self.save_val_data(val_data)
416
428
  self.is_data_saved = True
417
429
 
418
- models = self.construct_model_templates(
430
+ models = self.get_trainable_base_models(
419
431
  hyperparameters=hyperparameters,
420
432
  hyperparameter_tune=hyperparameter_tune_kwargs is not None, # TODO: remove hyperparameter_tune
421
433
  freq=train_data.freq,
422
- multi_window=self.val_splitter.num_val_windows > 0,
434
+ multi_window=self._get_val_splitter().num_val_windows > 0,
423
435
  excluded_model_types=excluded_model_types,
424
436
  )
425
437
 
@@ -439,8 +451,6 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
439
451
  num_base_models = len(models)
440
452
  model_names_trained = []
441
453
  for i, model in enumerate(models):
442
- assert isinstance(model, AbstractTimeSeriesModel)
443
-
444
454
  if time_limit is None:
445
455
  time_left = None
446
456
  time_left_for_model = None
@@ -490,42 +500,13 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
490
500
  train_data, model=model, val_data=val_data, time_limit=time_left_for_model
491
501
  )
492
502
 
493
- if self.enable_ensemble:
494
- models_available_for_ensemble = self.get_model_names(level=0)
495
-
496
- time_left_for_ensemble = None
497
- if time_limit is not None:
498
- time_left_for_ensemble = time_limit - (time.time() - time_start)
499
-
500
- if time_left_for_ensemble is not None and time_left_for_ensemble <= 0:
501
- logger.info(
502
- "Not fitting ensemble due to lack of time remaining. "
503
- f"Time left: {time_left_for_ensemble:.1f} seconds"
504
- )
505
- elif len(models_available_for_ensemble) <= 1:
506
- logger.info(
507
- "Not fitting ensemble as "
508
- + (
509
- "no models were successfully trained."
510
- if not models_available_for_ensemble
511
- else "only 1 model was trained."
512
- )
513
- )
514
- else:
515
- try:
516
- model_names_trained.append(
517
- self.fit_ensemble(
518
- data_per_window=self._get_ensemble_oof_data(train_data=train_data, val_data=val_data),
519
- model_names=models_available_for_ensemble,
520
- time_limit=time_left_for_ensemble,
521
- )
522
- )
523
- except Exception as err: # noqa
524
- logger.error(
525
- "\tWarning: Exception caused ensemble to fail during training... Skipping this model."
526
- )
527
- logger.error(f"\t{err}")
528
- logger.debug(traceback.format_exc())
503
+ ensemble_names = self._fit_ensembles(
504
+ train_data=train_data,
505
+ val_data=val_data,
506
+ time_limit=None if time_limit is None else time_limit - (time.time() - time_start),
507
+ ensemble_hyperparameters=ensemble_hyperparameters,
508
+ )
509
+ model_names_trained.extend(ensemble_names)
529
510
 
530
511
  logger.info(f"Training complete. Models trained: {model_names_trained}")
531
512
  logger.info(f"Total runtime: {time.time() - time_start:.2f} s")
@@ -539,82 +520,72 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
539
520
 
540
521
  return model_names_trained
541
522
 
542
- def _get_ensemble_oof_data(
543
- self, train_data: TimeSeriesDataFrame, val_data: Optional[TimeSeriesDataFrame]
544
- ) -> List[TimeSeriesDataFrame]:
545
- if val_data is None:
546
- return [val_fold for _, val_fold in self.val_splitter.split(train_data)]
547
- else:
548
- return [val_data]
549
-
550
- def _get_ensemble_model_name(self) -> str:
551
- """Ensure we don't have name collisions in the ensemble model name"""
552
- ensemble_name = "WeightedEnsemble"
553
- increment = 1
554
- while ensemble_name in self._get_banned_model_names():
555
- increment += 1
556
- ensemble_name = f"WeightedEnsemble_{increment}"
557
- return ensemble_name
558
-
559
- def fit_ensemble(
523
+ def _fit_ensembles(
560
524
  self,
561
- data_per_window: List[TimeSeriesDataFrame],
562
- model_names: List[str],
563
- time_limit: Optional[float] = None,
564
- ) -> str:
565
- logger.info("Fitting simple weighted ensemble.")
525
+ *,
526
+ train_data: TimeSeriesDataFrame,
527
+ val_data: Optional[TimeSeriesDataFrame],
528
+ time_limit: Optional[float],
529
+ ensemble_hyperparameters: dict,
530
+ ) -> list[str]:
531
+ if not self.enable_ensemble or not ensemble_hyperparameters:
532
+ logger.warning("Ensemble training is disabled. Skipping ensemble training.")
533
+ return []
534
+
535
+ ensemble_composer = self._get_ensemble_composer(ensemble_hyperparameters).fit(
536
+ train_data,
537
+ val_data,
538
+ time_limit,
539
+ )
566
540
 
567
- predictions_per_window: Dict[str, List[TimeSeriesDataFrame]] = {}
568
- base_model_scores = self.get_models_attribute_dict(attribute="val_score", models=self.get_model_names(0))
541
+ ensembles_trained = []
542
+ for _, model, base_models in ensemble_composer.iter_ensembles():
543
+ self._add_model(model=model, base_models=base_models)
544
+ self.save_model(model=model)
545
+ ensembles_trained.append(model.name)
569
546
 
570
- for model_name in model_names:
571
- predictions_per_window[model_name] = self._get_model_oof_predictions(model_name=model_name)
547
+ return ensembles_trained if ensembles_trained else []
572
548
 
573
- time_start = time.time()
574
- ensemble = self.ensemble_model_type(
575
- name=self._get_ensemble_model_name(),
549
+ def _get_val_splitter(self) -> AbstractWindowSplitter:
550
+ if self.num_val_windows is None:
551
+ val_splitter = ExpandingWindowSplitter(prediction_length=self.prediction_length)
552
+ else:
553
+ val_splitter = ExpandingWindowSplitter(
554
+ prediction_length=self.prediction_length,
555
+ num_val_windows=self.num_val_windows,
556
+ val_step_size=self.val_step_size,
557
+ )
558
+ return val_splitter
559
+
560
+ def _get_ensemble_composer(self, ensemble_hyperparameters: dict) -> "EnsembleComposer":
561
+ """Create an ensemble composer instance for delegation."""
562
+ return EnsembleComposer(
563
+ path=self.path,
564
+ prediction_length=self.prediction_length,
576
565
  eval_metric=self.eval_metric,
577
566
  target=self.target,
578
- prediction_length=self.prediction_length,
579
- path=self.path,
580
- freq=data_per_window[0].freq,
581
567
  quantile_levels=self.quantile_levels,
582
- covariate_metadata=self.covariate_metadata,
568
+ model_graph=self.model_graph,
569
+ ensemble_hyperparameters=ensemble_hyperparameters,
570
+ window_splitter=self._get_val_splitter(),
583
571
  )
584
- with warning_filter():
585
- ensemble.fit(
586
- predictions_per_window=predictions_per_window,
587
- data_per_window=data_per_window,
588
- model_scores=base_model_scores,
589
- time_limit=time_limit,
590
- )
591
- ensemble.fit_time = time.time() - time_start
592
-
593
- predict_time = 0
594
- for m in ensemble.model_names:
595
- predict_time += self.get_model_attribute(model=m, attribute="predict_time")
596
- ensemble.predict_time = predict_time
597
-
598
- score_per_fold = []
599
- for window_idx, data in enumerate(data_per_window):
600
- predictions = ensemble.predict({n: predictions_per_window[n][window_idx] for n in ensemble.model_names})
601
- score_per_fold.append(self._score_with_predictions(data, predictions))
602
- ensemble.val_score = float(np.mean(score_per_fold, dtype=np.float64))
603
-
604
- self._log_scores_and_times(
605
- val_score=ensemble.val_score,
606
- fit_time=ensemble.fit_time,
607
- predict_time=ensemble.predict_time,
608
- )
609
- self._add_model(model=ensemble, base_models=ensemble.model_names)
610
- self.save_model(model=ensemble)
611
- return ensemble.name
572
+
573
+ def _get_validation_windows(
574
+ self, train_data: TimeSeriesDataFrame, val_data: Optional[TimeSeriesDataFrame]
575
+ ) -> list[TimeSeriesDataFrame]:
576
+ """If validation data is provided, return this as a single validation window. If not,
577
+ use the validation splitter to create a list of validation splits.
578
+ """
579
+ if val_data is None:
580
+ return [val_fold for _, val_fold in self._get_val_splitter().split(train_data)]
581
+ else:
582
+ return [val_data]
612
583
 
613
584
  def leaderboard(
614
585
  self,
615
586
  data: Optional[TimeSeriesDataFrame] = None,
616
587
  extra_info: bool = False,
617
- extra_metrics: Optional[List[Union[str, TimeSeriesScorer]]] = None,
588
+ extra_metrics: Optional[list[Union[str, TimeSeriesScorer]]] = None,
618
589
  use_cache: bool = True,
619
590
  ) -> pd.DataFrame:
620
591
  logger.debug("Generating leaderboard for all models trained")
@@ -704,8 +675,8 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
704
675
  return df[explicit_column_order]
705
676
 
706
677
  def persist(
707
- self, model_names: Union[Literal["all", "best"], List[str]] = "all", with_ancestors: bool = False
708
- ) -> List[str]:
678
+ self, model_names: Union[Literal["all", "best"], list[str]] = "all", with_ancestors: bool = False
679
+ ) -> list[str]:
709
680
  if model_names == "all":
710
681
  model_names = self.get_model_names()
711
682
  elif model_names == "best":
@@ -729,7 +700,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
729
700
 
730
701
  return model_names
731
702
 
732
- def unpersist(self, model_names: Union[Literal["all"], List[str]] = "all") -> List[str]:
703
+ def unpersist(self, model_names: Union[Literal["all"], list[str]] = "all") -> list[str]:
733
704
  if model_names == "all":
734
705
  model_names = list(self.models.keys())
735
706
  if not isinstance(model_names, list):
@@ -826,9 +797,9 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
826
797
  self,
827
798
  data: TimeSeriesDataFrame,
828
799
  model: Optional[Union[str, TimeSeriesModelBase]] = None,
829
- metrics: Optional[Union[str, TimeSeriesScorer, List[Union[str, TimeSeriesScorer]]]] = None,
800
+ metrics: Optional[Union[str, TimeSeriesScorer, list[Union[str, TimeSeriesScorer]]]] = None,
830
801
  use_cache: bool = True,
831
- ) -> Dict[str, float]:
802
+ ) -> dict[str, float]:
832
803
  past_data, known_covariates = data.get_model_inputs_for_scoring(
833
804
  prediction_length=self.prediction_length, known_covariates_names=self.covariate_metadata.known_covariates
834
805
  )
@@ -846,7 +817,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
846
817
  def get_feature_importance(
847
818
  self,
848
819
  data: TimeSeriesDataFrame,
849
- features: List[str],
820
+ features: list[str],
850
821
  model: Optional[Union[str, TimeSeriesModelBase]] = None,
851
822
  metric: Optional[Union[str, TimeSeriesScorer]] = None,
852
823
  time_limit: Optional[float] = None,
@@ -996,7 +967,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
996
967
  self,
997
968
  model: Union[str, TimeSeriesModelBase],
998
969
  data: TimeSeriesDataFrame,
999
- model_pred_dict: Dict[str, Optional[TimeSeriesDataFrame]],
970
+ model_pred_dict: dict[str, Optional[TimeSeriesDataFrame]],
1000
971
  known_covariates: Optional[TimeSeriesDataFrame] = None,
1001
972
  ) -> TimeSeriesDataFrame:
1002
973
  """Generate predictions using the given model.
@@ -1012,8 +983,8 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
1012
983
  self,
1013
984
  model: Union[str, TimeSeriesModelBase],
1014
985
  data: TimeSeriesDataFrame,
1015
- model_pred_dict: Dict[str, Optional[TimeSeriesDataFrame]],
1016
- ) -> Union[TimeSeriesDataFrame, Dict[str, Optional[TimeSeriesDataFrame]]]:
986
+ model_pred_dict: dict[str, Optional[TimeSeriesDataFrame]],
987
+ ) -> Union[TimeSeriesDataFrame, dict[str, Optional[TimeSeriesDataFrame]]]:
1017
988
  """Get the first argument that should be passed to model.predict.
1018
989
 
1019
990
  This method assumes that model_pred_dict contains the predictions of all base models, if model is an ensemble.
@@ -1029,13 +1000,13 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
1029
1000
 
1030
1001
  def get_model_pred_dict(
1031
1002
  self,
1032
- model_names: List[str],
1003
+ model_names: list[str],
1033
1004
  data: TimeSeriesDataFrame,
1034
1005
  known_covariates: Optional[TimeSeriesDataFrame] = None,
1035
1006
  raise_exception_if_failed: bool = True,
1036
1007
  use_cache: bool = True,
1037
1008
  random_seed: Optional[int] = None,
1038
- ) -> Tuple[Dict[str, Optional[TimeSeriesDataFrame]], Dict[str, float]]:
1009
+ ) -> tuple[dict[str, Optional[TimeSeriesDataFrame]], dict[str, float]]:
1039
1010
  """Return a dictionary with predictions of all models for the given dataset.
1040
1011
 
1041
1012
  Parameters
@@ -1055,12 +1026,13 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
1055
1026
  use_cache
1056
1027
  If False, will ignore the cache even if it's available.
1057
1028
  """
1058
- if self.cache_predictions and use_cache:
1059
- dataset_hash = self._compute_dataset_hash(data=data, known_covariates=known_covariates)
1060
- model_pred_dict, pred_time_dict_marginal = self._get_cached_pred_dicts(dataset_hash)
1029
+ if use_cache:
1030
+ model_pred_dict, pred_time_dict_marginal = self.prediction_cache.get(
1031
+ data=data, known_covariates=known_covariates
1032
+ )
1061
1033
  else:
1062
1034
  model_pred_dict = {}
1063
- pred_time_dict_marginal: Dict[str, Any] = {}
1035
+ pred_time_dict_marginal: dict[str, Any] = {}
1064
1036
 
1065
1037
  model_set = set()
1066
1038
  for model_name in model_names:
@@ -1093,9 +1065,11 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
1093
1065
 
1094
1066
  if len(failed_models) > 0 and raise_exception_if_failed:
1095
1067
  raise RuntimeError(f"Following models failed to predict: {failed_models}")
1096
- if self.cache_predictions and use_cache:
1097
- self._save_cached_pred_dicts(
1098
- dataset_hash, # type: ignore
1068
+
1069
+ if use_cache:
1070
+ self.prediction_cache.put(
1071
+ data=data,
1072
+ known_covariates=known_covariates,
1099
1073
  model_pred_dict=model_pred_dict,
1100
1074
  pred_time_dict=pred_time_dict_marginal,
1101
1075
  )
@@ -1106,7 +1080,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
1106
1080
 
1107
1081
  return final_model_pred_dict, final_pred_time_dict_total
1108
1082
 
1109
- def _get_total_pred_time_from_marginal(self, pred_time_dict_marginal: Dict[str, float]) -> Dict[str, float]:
1083
+ def _get_total_pred_time_from_marginal(self, pred_time_dict_marginal: dict[str, float]) -> dict[str, float]:
1110
1084
  pred_time_dict_total = defaultdict(float)
1111
1085
  for model_name in pred_time_dict_marginal.keys():
1112
1086
  for base_model in self.get_minimum_model_set(model_name):
@@ -1114,62 +1088,6 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
1114
1088
  pred_time_dict_total[model_name] += pred_time_dict_marginal[base_model]
1115
1089
  return dict(pred_time_dict_total)
1116
1090
 
1117
- @property
1118
- def _cached_predictions_path(self) -> Path:
1119
- return Path(self.path) / self._cached_predictions_filename
1120
-
1121
- @staticmethod
1122
- def _compute_dataset_hash(
1123
- data: TimeSeriesDataFrame, known_covariates: Optional[TimeSeriesDataFrame] = None
1124
- ) -> str:
1125
- """Compute a unique string that identifies the time series dataset."""
1126
- combined_hash = hash_pandas_df(data) + hash_pandas_df(known_covariates) + hash_pandas_df(data.static_features)
1127
- return combined_hash
1128
-
1129
- def _load_cached_predictions(self) -> dict[str, dict[str, dict[str, Any]]]:
1130
- """Load cached predictions from disk. If loading fails, an empty dictionary is returned."""
1131
- if self._cached_predictions_path.exists():
1132
- try:
1133
- cached_predictions = load_pkl.load(str(self._cached_predictions_path))
1134
- except Exception:
1135
- cached_predictions = {}
1136
- else:
1137
- cached_predictions = {}
1138
- return cached_predictions
1139
-
1140
- def _get_cached_pred_dicts(
1141
- self, dataset_hash: str
1142
- ) -> Tuple[Dict[str, Optional[TimeSeriesDataFrame]], Dict[str, float]]:
1143
- """Load cached predictions for given dataset_hash from disk, if possible.
1144
-
1145
- If loading fails for any reason, empty dicts are returned.
1146
- """
1147
- cached_predictions = self._load_cached_predictions()
1148
- if dataset_hash in cached_predictions:
1149
- try:
1150
- model_pred_dict = cached_predictions[dataset_hash]["model_pred_dict"]
1151
- pred_time_dict = cached_predictions[dataset_hash]["pred_time_dict"]
1152
- assert model_pred_dict.keys() == pred_time_dict.keys()
1153
- return model_pred_dict, pred_time_dict
1154
- except Exception:
1155
- logger.warning("Cached predictions are corrupted. Predictions will be made from scratch.")
1156
- return {}, {}
1157
-
1158
- def _save_cached_pred_dicts(
1159
- self,
1160
- dataset_hash: str,
1161
- model_pred_dict: Dict[str, Optional[TimeSeriesDataFrame]],
1162
- pred_time_dict: Dict[str, float],
1163
- ) -> None:
1164
- cached_predictions = self._load_cached_predictions()
1165
- # Do not save results for models that failed
1166
- cached_predictions[dataset_hash] = {
1167
- "model_pred_dict": {k: v for k, v in model_pred_dict.items() if v is not None},
1168
- "pred_time_dict": {k: v for k, v in pred_time_dict.items() if v is not None},
1169
- }
1170
- save_pkl.save(str(self._cached_predictions_path), object=cached_predictions)
1171
- logger.debug(f"Cached predictions saved to {self._cached_predictions_path}")
1172
-
1173
1091
  def _merge_refit_full_data(
1174
1092
  self, train_data: TimeSeriesDataFrame, val_data: Optional[TimeSeriesDataFrame]
1175
1093
  ) -> TimeSeriesDataFrame:
@@ -1183,8 +1101,8 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
1183
1101
  self,
1184
1102
  train_data: Optional[TimeSeriesDataFrame] = None,
1185
1103
  val_data: Optional[TimeSeriesDataFrame] = None,
1186
- models: Optional[List[str]] = None,
1187
- ) -> List[str]:
1104
+ models: Optional[list[str]] = None,
1105
+ ) -> list[str]:
1188
1106
  train_data = train_data or self.load_train_data()
1189
1107
  val_data = val_data or self.load_val_data()
1190
1108
  refit_full_data = self._merge_refit_full_data(train_data, val_data)
@@ -1228,7 +1146,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
1228
1146
  self.save()
1229
1147
  return models_trained_full
1230
1148
 
1231
- def refit_full(self, model: str = "all") -> Dict[str, str]:
1149
+ def refit_full(self, model: str = "all") -> dict[str, str]:
1232
1150
  time_start = time.time()
1233
1151
  existing_models = self.get_model_names()
1234
1152
  if model == "all":
@@ -1260,70 +1178,27 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
1260
1178
  logger.info(f"Total runtime: {time.time() - time_start:.2f} s")
1261
1179
  return copy.deepcopy(self.model_refit_map)
1262
1180
 
1263
- def construct_model_templates(
1181
+ def get_trainable_base_models(
1264
1182
  self,
1265
- hyperparameters: Union[str, Dict[str, Any]],
1183
+ hyperparameters: Union[str, dict[str, Any]],
1266
1184
  *,
1267
1185
  multi_window: bool = False,
1268
1186
  freq: Optional[str] = None,
1269
- excluded_model_types: Optional[List[str]] = None,
1187
+ excluded_model_types: Optional[list[str]] = None,
1270
1188
  hyperparameter_tune: bool = False,
1271
- ) -> List[TimeSeriesModelBase]:
1272
- return get_preset_models(
1189
+ ) -> list[AbstractTimeSeriesModel]:
1190
+ return TrainableModelSetBuilder(
1191
+ freq=freq,
1192
+ prediction_length=self.prediction_length,
1273
1193
  path=self.path,
1274
1194
  eval_metric=self.eval_metric,
1275
- prediction_length=self.prediction_length,
1276
- freq=freq,
1277
- hyperparameters=hyperparameters,
1278
- hyperparameter_tune=hyperparameter_tune,
1279
1195
  quantile_levels=self.quantile_levels,
1280
- all_assigned_names=self._get_banned_model_names(),
1281
1196
  target=self.target,
1282
1197
  covariate_metadata=self.covariate_metadata,
1283
- excluded_model_types=excluded_model_types,
1284
- # if skip_model_selection = True, we skip backtesting
1285
1198
  multi_window=multi_window and not self.skip_model_selection,
1286
- )
1287
-
1288
- def fit(
1289
- self,
1290
- train_data: TimeSeriesDataFrame,
1291
- hyperparameters: Union[str, Dict[Any, Dict]],
1292
- val_data: Optional[TimeSeriesDataFrame] = None,
1293
- hyperparameter_tune_kwargs: Optional[Union[str, Dict]] = None,
1294
- excluded_model_types: Optional[List[str]] = None,
1295
- time_limit: Optional[float] = None,
1296
- random_seed: Optional[int] = None,
1297
- ):
1298
- """
1299
- Fit a set of timeseries models specified by the `hyperparameters`
1300
- dictionary that maps model names to their specified hyperparameters.
1301
-
1302
- Parameters
1303
- ----------
1304
- train_data: TimeSeriesDataFrame
1305
- Training data for fitting time series timeseries models.
1306
- hyperparameters: str or Dict
1307
- A dictionary mapping selected model names, model classes or model factory to hyperparameter
1308
- settings. Model names should be present in `trainer.presets.DEFAULT_MODEL_NAMES`. Optionally,
1309
- the user may provide one of "default", "light" and "very_light" to specify presets.
1310
- val_data: TimeSeriesDataFrame
1311
- Optional validation data set to report validation scores on.
1312
- hyperparameter_tune_kwargs
1313
- Args for hyperparameter tuning
1314
- excluded_model_types
1315
- Names of models that should not be trained, even if listed in `hyperparameters`.
1316
- time_limit
1317
- Time limit for training
1318
- random_seed
1319
- Random seed that will be set to each model during training
1320
- """
1321
- self._train_multi(
1322
- train_data,
1323
- val_data=val_data,
1199
+ ).get_model_set(
1324
1200
  hyperparameters=hyperparameters,
1325
- hyperparameter_tune_kwargs=hyperparameter_tune_kwargs,
1201
+ hyperparameter_tune=hyperparameter_tune,
1326
1202
  excluded_model_types=excluded_model_types,
1327
- time_limit=time_limit,
1328
- random_seed=random_seed,
1203
+ banned_model_names=self._get_banned_model_names(),
1329
1204
  )