autogluon.timeseries 1.2.1b20250415__py3-none-any.whl → 1.2.1b20250417__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.
- autogluon/timeseries/learner.py +7 -5
- autogluon/timeseries/metrics/abstract.py +1 -1
- autogluon/timeseries/metrics/point.py +4 -4
- autogluon/timeseries/metrics/quantile.py +2 -2
- autogluon/timeseries/models/abstract/abstract_timeseries_model.py +115 -196
- autogluon/timeseries/models/autogluon_tabular/mlforecast.py +7 -7
- autogluon/timeseries/models/chronos/model.py +37 -30
- autogluon/timeseries/models/gluonts/abstract_gluonts.py +10 -10
- autogluon/timeseries/models/gluonts/torch/models.py +8 -8
- autogluon/timeseries/models/local/abstract_local_model.py +1 -1
- autogluon/timeseries/models/local/naive.py +2 -2
- autogluon/timeseries/models/multi_window/multi_window_model.py +0 -3
- autogluon/timeseries/predictor.py +74 -58
- autogluon/timeseries/trainer.py +3 -3
- autogluon/timeseries/utils/forecast.py +13 -8
- autogluon/timeseries/version.py +1 -1
- {autogluon.timeseries-1.2.1b20250415.dist-info → autogluon.timeseries-1.2.1b20250417.dist-info}/METADATA +4 -4
- {autogluon.timeseries-1.2.1b20250415.dist-info → autogluon.timeseries-1.2.1b20250417.dist-info}/RECORD +25 -25
- /autogluon.timeseries-1.2.1b20250415-py3.9-nspkg.pth → /autogluon.timeseries-1.2.1b20250417-py3.9-nspkg.pth +0 -0
- {autogluon.timeseries-1.2.1b20250415.dist-info → autogluon.timeseries-1.2.1b20250417.dist-info}/LICENSE +0 -0
- {autogluon.timeseries-1.2.1b20250415.dist-info → autogluon.timeseries-1.2.1b20250417.dist-info}/NOTICE +0 -0
- {autogluon.timeseries-1.2.1b20250415.dist-info → autogluon.timeseries-1.2.1b20250417.dist-info}/WHEEL +0 -0
- {autogluon.timeseries-1.2.1b20250415.dist-info → autogluon.timeseries-1.2.1b20250417.dist-info}/namespace_packages.txt +0 -0
- {autogluon.timeseries-1.2.1b20250415.dist-info → autogluon.timeseries-1.2.1b20250417.dist-info}/top_level.txt +0 -0
- {autogluon.timeseries-1.2.1b20250415.dist-info → autogluon.timeseries-1.2.1b20250417.dist-info}/zip-safe +0 -0
autogluon/timeseries/learner.py
CHANGED
@@ -12,7 +12,7 @@ from autogluon.timeseries.models.abstract import AbstractTimeSeriesModel
|
|
12
12
|
from autogluon.timeseries.splitter import AbstractWindowSplitter
|
13
13
|
from autogluon.timeseries.trainer import TimeSeriesTrainer
|
14
14
|
from autogluon.timeseries.utils.features import TimeSeriesFeatureGenerator
|
15
|
-
from autogluon.timeseries.utils.forecast import
|
15
|
+
from autogluon.timeseries.utils.forecast import make_future_data_frame
|
16
16
|
|
17
17
|
logger = logging.getLogger(__name__)
|
18
18
|
|
@@ -148,15 +148,17 @@ class TimeSeriesLearner(AbstractLearner):
|
|
148
148
|
f"known_covariates are missing information for the following item_ids: {reprlib.repr(missing_item_ids.to_list())}."
|
149
149
|
)
|
150
150
|
|
151
|
-
forecast_index =
|
152
|
-
data, prediction_length=self.prediction_length, freq=self.freq
|
151
|
+
forecast_index = pd.MultiIndex.from_frame(
|
152
|
+
make_future_data_frame(data, prediction_length=self.prediction_length, freq=self.freq)
|
153
153
|
)
|
154
154
|
try:
|
155
155
|
known_covariates = known_covariates.loc[forecast_index] # type: ignore
|
156
156
|
except KeyError:
|
157
157
|
raise ValueError(
|
158
|
-
|
159
|
-
"
|
158
|
+
"`known_covariates` should include the `item_id` and `timestamp` values covering the forecast horizon "
|
159
|
+
"(i.e., the next `prediction_length` time steps following the end of each time series in the input "
|
160
|
+
"data). Use `TimeSeriesPredictor.make_future_data_frame` to generate the required `item_id` and "
|
161
|
+
"`timestamp` combinations for the `known_covariates`."
|
160
162
|
)
|
161
163
|
return known_covariates
|
162
164
|
|
@@ -140,7 +140,7 @@ class TimeSeriesScorer:
|
|
140
140
|
) -> None:
|
141
141
|
"""Compute auxiliary metrics on past data (before forecast horizon), if the chosen metric requires it.
|
142
142
|
|
143
|
-
This method should only be implemented by metrics that rely on
|
143
|
+
This method should only be implemented by metrics that rely on historical (in-sample) data, such as Mean Absolute
|
144
144
|
Scaled Error (MASE) https://en.wikipedia.org/wiki/Mean_absolute_scaled_error.
|
145
145
|
|
146
146
|
We keep this method separate from :meth:`compute_metric` to avoid redundant computations when fitting ensemble.
|
@@ -196,13 +196,13 @@ class MAPE(TimeSeriesScorer):
|
|
196
196
|
class MASE(TimeSeriesScorer):
|
197
197
|
r"""Mean absolute scaled error.
|
198
198
|
|
199
|
-
Normalizes the absolute error for each time series by the
|
199
|
+
Normalizes the absolute error for each time series by the historical seasonal error of this time series.
|
200
200
|
|
201
201
|
.. math::
|
202
202
|
|
203
203
|
\operatorname{MASE} = \frac{1}{N} \frac{1}{H} \sum_{i=1}^{N} \frac{1}{a_i} \sum_{t=T+1}^{T+H} |y_{i,t} - f_{i,t}|
|
204
204
|
|
205
|
-
where :math:`a_i` is the
|
205
|
+
where :math:`a_i` is the historical absolute seasonal error defined as
|
206
206
|
|
207
207
|
.. math::
|
208
208
|
|
@@ -255,13 +255,13 @@ class MASE(TimeSeriesScorer):
|
|
255
255
|
class RMSSE(TimeSeriesScorer):
|
256
256
|
r"""Root mean squared scaled error.
|
257
257
|
|
258
|
-
Normalizes the absolute error for each time series by the
|
258
|
+
Normalizes the absolute error for each time series by the historical seasonal error of this time series.
|
259
259
|
|
260
260
|
.. math::
|
261
261
|
|
262
262
|
\operatorname{RMSSE} = \sqrt{\frac{1}{N} \frac{1}{H} \sum_{i=1}^{N} \frac{1}{s_i} \sum_{t=T+1}^{T+H} (y_{i,t} - f_{i,t})^2}
|
263
263
|
|
264
|
-
where :math:`s_i` is the
|
264
|
+
where :math:`s_i` is the historical squared seasonal error defined as
|
265
265
|
|
266
266
|
.. math::
|
267
267
|
|
@@ -51,13 +51,13 @@ class SQL(TimeSeriesScorer):
|
|
51
51
|
|
52
52
|
Also known as scaled pinball loss.
|
53
53
|
|
54
|
-
Normalizes the quantile loss for each time series by the
|
54
|
+
Normalizes the quantile loss for each time series by the historical seasonal error of this time series.
|
55
55
|
|
56
56
|
.. math::
|
57
57
|
|
58
58
|
\operatorname{SQL} = \frac{1}{N} \frac{1}{H} \sum_{i=1}^{N} \frac{1}{a_i} \sum_{t=T+1}^{T+H} \sum_{q} \rho_q(y_{i,t}, f^q_{i,t})
|
59
59
|
|
60
|
-
where :math:`a_i` is the
|
60
|
+
where :math:`a_i` is the historical absolute seasonal error defined as
|
61
61
|
|
62
62
|
.. math::
|
63
63
|
|
@@ -16,7 +16,7 @@ from autogluon.common.loaders import load_pkl
|
|
16
16
|
from autogluon.common.savers import save_pkl
|
17
17
|
from autogluon.common.utils.resource_utils import get_resource_manager
|
18
18
|
from autogluon.common.utils.utils import setup_outputdir
|
19
|
-
from autogluon.core.constants import
|
19
|
+
from autogluon.core.constants import AG_ARGS_FIT, REFIT_FULL_SUFFIX
|
20
20
|
from autogluon.core.models import ModelBase
|
21
21
|
from autogluon.core.utils.exceptions import TimeLimitExceeded
|
22
22
|
from autogluon.timeseries.dataset import TimeSeriesDataFrame
|
@@ -24,90 +24,13 @@ from autogluon.timeseries.metrics import TimeSeriesScorer, check_get_evaluation_
|
|
24
24
|
from autogluon.timeseries.regressor import CovariateRegressor, get_covariate_regressor
|
25
25
|
from autogluon.timeseries.transforms import CovariateScaler, TargetScaler, get_covariate_scaler, get_target_scaler
|
26
26
|
from autogluon.timeseries.utils.features import CovariateMetadata
|
27
|
-
from autogluon.timeseries.utils.forecast import
|
27
|
+
from autogluon.timeseries.utils.forecast import make_future_data_frame
|
28
28
|
|
29
29
|
from .tunable import TimeSeriesTunable
|
30
30
|
|
31
31
|
logger = logging.getLogger(__name__)
|
32
32
|
|
33
33
|
|
34
|
-
# TODO: refactor and move to util. We do not need to use "params_aux" in time series
|
35
|
-
def check_and_split_hyperparameters(
|
36
|
-
params: Optional[Dict[str, Any]] = None,
|
37
|
-
ag_args_fit: str = AG_ARGS_FIT,
|
38
|
-
ag_arg_prefix: str = AG_ARG_PREFIX,
|
39
|
-
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
40
|
-
"""
|
41
|
-
Given the user-specified hyperparameters, split into `params` and `params_aux`.
|
42
|
-
|
43
|
-
Parameters
|
44
|
-
----------
|
45
|
-
params : Optional[Dict[str, Any]], default = None
|
46
|
-
The model hyperparameters dictionary
|
47
|
-
ag_args_fit : str, default = "ag_args_fit"
|
48
|
-
The params key to look for that contains params_aux.
|
49
|
-
If the key is present, the value is used for params_aux and popped from params.
|
50
|
-
If no such key is found, then initialize params_aux as an empty dictionary.
|
51
|
-
ag_arg_prefix : str, default = "ag."
|
52
|
-
The key prefix to look for that indicates a parameter is intended for params_aux.
|
53
|
-
If None, this logic is skipped.
|
54
|
-
If a key starts with this prefix, it is popped from params and added to params_aux with the prefix removed.
|
55
|
-
For example:
|
56
|
-
input: params={'ag.foo': 2, 'abc': 7}, params_aux={'bar': 3}, and ag_arg_prefix='.ag',
|
57
|
-
output: params={'abc': 7}, params_aux={'bar': 3, 'foo': 2}
|
58
|
-
In cases where the key is specified multiple times, the value of the key with the prefix will always take priority.
|
59
|
-
A warning will be logged if a key is present multiple times.
|
60
|
-
For example, given the most complex scenario:
|
61
|
-
input: params={'ag.foo': 1, 'foo': 2, 'ag_args_fit': {'ag.foo': 3, 'foo': 4}}
|
62
|
-
output: params={'foo': 2}, params_aux={'foo': 1}
|
63
|
-
|
64
|
-
Returns
|
65
|
-
-------
|
66
|
-
params, params_aux : (Dict[str, Any], Dict[str, Any])
|
67
|
-
params will contain the native model hyperparameters
|
68
|
-
params_aux will contain special auxiliary hyperparameters
|
69
|
-
"""
|
70
|
-
params = copy.deepcopy(params) if params is not None else dict()
|
71
|
-
assert isinstance(params, dict), f"Invalid dtype of params! Expected dict, but got {type(params)}"
|
72
|
-
for k in params.keys():
|
73
|
-
if not isinstance(k, str):
|
74
|
-
logger.warning(
|
75
|
-
f"Warning: Specified hyperparameter key is not of type str: {k} (type={type(k)}). "
|
76
|
-
f"There might be a bug in your configuration."
|
77
|
-
)
|
78
|
-
|
79
|
-
params_aux = params.pop(ag_args_fit, dict())
|
80
|
-
if params_aux is None:
|
81
|
-
params_aux = dict()
|
82
|
-
assert isinstance(params_aux, dict), f"Invalid dtype of params_aux! Expected dict, but got {type(params_aux)}"
|
83
|
-
if ag_arg_prefix is not None:
|
84
|
-
param_aux_keys = list(params_aux.keys())
|
85
|
-
for k in param_aux_keys:
|
86
|
-
if isinstance(k, str) and k.startswith(ag_arg_prefix):
|
87
|
-
k_no_prefix = k[len(ag_arg_prefix) :]
|
88
|
-
if k_no_prefix in params_aux:
|
89
|
-
logger.warning(
|
90
|
-
f'Warning: hyperparameter "{k}" is present '
|
91
|
-
f'in `ag_args_fit` as both "{k}" and "{k_no_prefix}". '
|
92
|
-
f'Will use "{k}" and ignore "{k_no_prefix}".'
|
93
|
-
)
|
94
|
-
params_aux[k_no_prefix] = params_aux.pop(k)
|
95
|
-
param_keys = list(params.keys())
|
96
|
-
for k in param_keys:
|
97
|
-
if isinstance(k, str) and k.startswith(ag_arg_prefix):
|
98
|
-
k_no_prefix = k[len(ag_arg_prefix) :]
|
99
|
-
if k_no_prefix in params_aux:
|
100
|
-
logger.warning(
|
101
|
-
f'Warning: hyperparameter "{k}" is present '
|
102
|
-
f"in both `ag_args_fit` and `hyperparameters`. "
|
103
|
-
f"Will use `hyperparameters` value."
|
104
|
-
)
|
105
|
-
params_aux[k_no_prefix] = params.pop(k)
|
106
|
-
return params, params_aux
|
107
|
-
|
108
|
-
|
109
|
-
# TODO: refactor. remove params_aux, etc. make overrides and abstract
|
110
|
-
# methods clear, et al.
|
111
34
|
class TimeSeriesModelBase(ModelBase, ABC):
|
112
35
|
"""Abstract class for all `Model` objects in autogluon.timeseries.
|
113
36
|
|
@@ -171,7 +94,8 @@ class TimeSeriesModelBase(ModelBase, ABC):
|
|
171
94
|
self.path_root = path
|
172
95
|
if self.path_root is None:
|
173
96
|
path_suffix = self.name
|
174
|
-
# TODO: Would be ideal to not create dir, but still track that it is unique. However, this isn't possible
|
97
|
+
# TODO: Would be ideal to not create dir, but still track that it is unique. However, this isn't possible
|
98
|
+
# to do without a global list of used dirs or using UUID.
|
175
99
|
path_cur = setup_outputdir(path=None, create_dir=True, path_suffix=path_suffix)
|
176
100
|
self.path_root = path_cur.rsplit(self.name, 1)[0]
|
177
101
|
logger.log(20, f"Warning: No path was specified for model, defaulting to: {self.path_root}")
|
@@ -198,22 +122,15 @@ class TimeSeriesModelBase(ModelBase, ABC):
|
|
198
122
|
else:
|
199
123
|
self.must_drop_median = False
|
200
124
|
|
201
|
-
self._user_params, self._user_params_aux = check_and_split_hyperparameters(hyperparameters)
|
202
125
|
self._oof_predictions: Optional[List[TimeSeriesDataFrame]] = None
|
203
126
|
|
204
|
-
|
205
|
-
self.
|
206
|
-
self._init_params_aux()
|
207
|
-
self._init_params()
|
208
|
-
self._is_initialized = True
|
209
|
-
|
210
|
-
# TODO: remove the variables below
|
211
|
-
self.nondefault_params: List[str] = []
|
127
|
+
# user provided hyperparameters and extra arguments that are used during model training
|
128
|
+
self._hyperparameters, self._extra_ag_args = self._check_and_split_hyperparameters(hyperparameters)
|
212
129
|
|
213
130
|
self.fit_time: Optional[float] = None # Time taken to fit in seconds (Training data)
|
214
131
|
self.predict_time: Optional[float] = None # Time taken to predict in seconds (Validation data)
|
215
132
|
self.predict_1_time: Optional[float] = (
|
216
|
-
None # Time taken to predict 1 row of data in seconds (with batch size `predict_1_batch_size`
|
133
|
+
None # Time taken to predict 1 row of data in seconds (with batch size `predict_1_batch_size`)
|
217
134
|
)
|
218
135
|
self.val_score: Optional[float] = None # Score with eval_metric (Validation data)
|
219
136
|
|
@@ -236,6 +153,44 @@ class TimeSeriesModelBase(ModelBase, ABC):
|
|
236
153
|
self.path = path_context
|
237
154
|
self.path_root = self.path.rsplit(self.name, 1)[0]
|
238
155
|
|
156
|
+
@classmethod
|
157
|
+
def _check_and_split_hyperparameters(
|
158
|
+
cls, hyperparameters: Optional[Dict[str, Any]] = None
|
159
|
+
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
160
|
+
"""
|
161
|
+
Given the user-specified hyperparameters, split into `hyperparameters` and `extra_ag_args`, intended
|
162
|
+
to be used during model initialization.
|
163
|
+
|
164
|
+
Parameters
|
165
|
+
----------
|
166
|
+
hyperparameters : Optional[Dict[str, Any]], default = None
|
167
|
+
The model hyperparameters dictionary provided to the model constructor.
|
168
|
+
|
169
|
+
Returns
|
170
|
+
-------
|
171
|
+
hyperparameters: Dict[str, Any]
|
172
|
+
Native model hyperparameters that are passed into the "inner model" AutoGluon wraps
|
173
|
+
extra_ag_args: Dict[str, Any]
|
174
|
+
Special auxiliary parameters that modify the model training process used by AutoGluon
|
175
|
+
"""
|
176
|
+
hyperparameters = copy.deepcopy(hyperparameters) if hyperparameters is not None else dict()
|
177
|
+
assert isinstance(hyperparameters, dict), (
|
178
|
+
f"Invalid dtype for hyperparameters. Expected dict, but got {type(hyperparameters)}"
|
179
|
+
)
|
180
|
+
for k in hyperparameters.keys():
|
181
|
+
if not isinstance(k, str):
|
182
|
+
logger.warning(
|
183
|
+
f"Warning: Specified hyperparameter key is not of type str: {k} (type={type(k)}). "
|
184
|
+
f"There might be a bug in your configuration."
|
185
|
+
)
|
186
|
+
|
187
|
+
extra_ag_args = hyperparameters.pop(AG_ARGS_FIT, {})
|
188
|
+
if not isinstance(extra_ag_args, dict):
|
189
|
+
raise ValueError(
|
190
|
+
f"Invalid hyperparameter type for `{AG_ARGS_FIT}`. Expected dict, but got {type(extra_ag_args)}"
|
191
|
+
)
|
192
|
+
return hyperparameters, extra_ag_args
|
193
|
+
|
239
194
|
def save(self, path: Optional[str] = None, verbose=True) -> str:
|
240
195
|
if path is None:
|
241
196
|
path = self.path
|
@@ -262,9 +217,6 @@ class TimeSeriesModelBase(ModelBase, ABC):
|
|
262
217
|
model = load_pkl.load(path=file_path, verbose=verbose)
|
263
218
|
if reset_paths:
|
264
219
|
model.set_contexts(path)
|
265
|
-
if hasattr(model, "_compiler"):
|
266
|
-
if model._compiler is not None and not model._compiler.save_in_pkl:
|
267
|
-
model.model = model._compiler.load(path=path)
|
268
220
|
if load_oof and model._oof_predictions is None:
|
269
221
|
model._oof_predictions = cls.load_oof_predictions(path=path, verbose=verbose)
|
270
222
|
return model
|
@@ -277,7 +229,7 @@ class TimeSeriesModelBase(ModelBase, ABC):
|
|
277
229
|
@property
|
278
230
|
def supports_known_covariates(self) -> bool:
|
279
231
|
return (
|
280
|
-
self.
|
232
|
+
self.get_hyperparameters().get("covariate_regressor") is not None
|
281
233
|
or self.__class__._supports_known_covariates
|
282
234
|
)
|
283
235
|
|
@@ -288,7 +240,8 @@ class TimeSeriesModelBase(ModelBase, ABC):
|
|
288
240
|
@property
|
289
241
|
def supports_static_features(self) -> bool:
|
290
242
|
return (
|
291
|
-
self.
|
243
|
+
self.get_hyperparameters().get("covariate_regressor") is not None
|
244
|
+
or self.__class__._supports_static_features
|
292
245
|
)
|
293
246
|
|
294
247
|
def get_oof_predictions(self):
|
@@ -296,67 +249,27 @@ class TimeSeriesModelBase(ModelBase, ABC):
|
|
296
249
|
self._oof_predictions = self.load_oof_predictions(self.path)
|
297
250
|
return self._oof_predictions
|
298
251
|
|
299
|
-
def _init_params(self):
|
300
|
-
"""Initializes model hyperparameters"""
|
301
|
-
hyperparameters = self._user_params
|
302
|
-
self.nondefault_params = []
|
303
|
-
if hyperparameters is not None:
|
304
|
-
self.params.update(hyperparameters)
|
305
|
-
# These are hyperparameters that user has specified.
|
306
|
-
self.nondefault_params = list(hyperparameters.keys())[:]
|
307
|
-
self.params_trained = {}
|
308
|
-
|
309
|
-
def _init_params_aux(self):
|
310
|
-
"""
|
311
|
-
Initializes auxiliary hyperparameters.
|
312
|
-
These parameters are generally not model specific and can have a wide variety of effects.
|
313
|
-
For documentation on some of the available options and their defaults, refer to `self._get_default_auxiliary_params`.
|
314
|
-
"""
|
315
|
-
hyperparameters_aux = self._user_params_aux or {}
|
316
|
-
default_aux_params = dict(
|
317
|
-
# ratio of given time_limit to use during fit(). If time_limit == 10 and max_time_limit_ratio=0.3,
|
318
|
-
# time_limit would be changed to 3.
|
319
|
-
max_time_limit_ratio=self.default_max_time_limit_ratio,
|
320
|
-
# max time_limit value during fit(). If the provided time_limit is greater than this value, it will be
|
321
|
-
# replaced by max_time_limit. Occurs after max_time_limit_ratio is applied.
|
322
|
-
max_time_limit=None,
|
323
|
-
)
|
324
|
-
self.params_aux = {**default_aux_params, **hyperparameters_aux}
|
325
|
-
|
326
252
|
def _initialize_transforms_and_regressor(self) -> None:
|
327
|
-
self.target_scaler = get_target_scaler(self.
|
253
|
+
self.target_scaler = get_target_scaler(self.get_hyperparameters().get("target_scaler"), target=self.target)
|
328
254
|
self.covariate_scaler = get_covariate_scaler(
|
329
|
-
self.
|
255
|
+
self.get_hyperparameters().get("covariate_scaler"),
|
330
256
|
covariate_metadata=self.metadata,
|
331
257
|
use_static_features=self.supports_static_features,
|
332
258
|
use_known_covariates=self.supports_known_covariates,
|
333
259
|
use_past_covariates=self.supports_past_covariates,
|
334
260
|
)
|
335
261
|
self.covariate_regressor = get_covariate_regressor(
|
336
|
-
self.
|
262
|
+
self.get_hyperparameters().get("covariate_regressor"),
|
337
263
|
target=self.target,
|
338
264
|
covariate_metadata=self.metadata,
|
339
265
|
)
|
340
266
|
|
341
|
-
def
|
342
|
-
return
|
343
|
-
|
344
|
-
def get_params(self) -> dict:
|
345
|
-
hyperparameters = self._user_params.copy()
|
346
|
-
if self._user_params_aux:
|
347
|
-
hyperparameters[AG_ARGS_FIT] = self._user_params_aux.copy()
|
267
|
+
def _get_default_hyperparameters(self) -> dict:
|
268
|
+
return {}
|
348
269
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
eval_metric=self.eval_metric,
|
353
|
-
hyperparameters=hyperparameters,
|
354
|
-
freq=self.freq,
|
355
|
-
prediction_length=self.prediction_length,
|
356
|
-
quantile_levels=self.quantile_levels,
|
357
|
-
metadata=self.metadata,
|
358
|
-
target=self.target,
|
359
|
-
)
|
270
|
+
def get_hyperparameters(self) -> dict:
|
271
|
+
"""Get hyperparameters that will be passed to the "inner model" that AutoGluon wraps."""
|
272
|
+
return {**self._get_default_hyperparameters(), **self._hyperparameters}
|
360
273
|
|
361
274
|
def get_info(self) -> dict:
|
362
275
|
"""
|
@@ -373,7 +286,7 @@ class TimeSeriesModelBase(ModelBase, ABC):
|
|
373
286
|
"prediction_length": self.prediction_length,
|
374
287
|
"quantile_levels": self.quantile_levels,
|
375
288
|
"val_score": self.val_score,
|
376
|
-
"hyperparameters": self.
|
289
|
+
"hyperparameters": self.get_hyperparameters(),
|
377
290
|
}
|
378
291
|
return info
|
379
292
|
|
@@ -495,49 +408,6 @@ class TimeSeriesModelBase(ModelBase, ABC):
|
|
495
408
|
"""
|
496
409
|
return self
|
497
410
|
|
498
|
-
def convert_to_refit_full_via_copy(self) -> Self:
|
499
|
-
# save the model as a new model on disk
|
500
|
-
previous_name = self.name
|
501
|
-
self.rename(self.name + REFIT_FULL_SUFFIX)
|
502
|
-
refit_model_path = self.path
|
503
|
-
self.save(path=self.path, verbose=False)
|
504
|
-
|
505
|
-
self.rename(previous_name)
|
506
|
-
|
507
|
-
refit_model = self.load(path=refit_model_path, verbose=False)
|
508
|
-
refit_model.val_score = None
|
509
|
-
refit_model.predict_time = None
|
510
|
-
|
511
|
-
return refit_model
|
512
|
-
|
513
|
-
def convert_to_refit_full_template(self):
|
514
|
-
"""
|
515
|
-
After calling this function, returned model should be able to be fit without X_val, y_val using the iterations trained by the original model.
|
516
|
-
|
517
|
-
Increase max_memory_usage_ratio by 25% to reduce the chance that the refit model will trigger NotEnoughMemoryError and skip training.
|
518
|
-
This can happen without the 25% increase since the refit model generally will use more training data and thus require more memory.
|
519
|
-
"""
|
520
|
-
params = copy.deepcopy(self.get_params())
|
521
|
-
|
522
|
-
if "hyperparameters" not in params:
|
523
|
-
params["hyperparameters"] = dict()
|
524
|
-
|
525
|
-
if AG_ARGS_FIT not in params["hyperparameters"]:
|
526
|
-
params["hyperparameters"][AG_ARGS_FIT] = dict()
|
527
|
-
|
528
|
-
params["hyperparameters"].update(self.params_trained)
|
529
|
-
params["name"] = params["name"] + REFIT_FULL_SUFFIX
|
530
|
-
template = self.__class__(**params)
|
531
|
-
|
532
|
-
return template
|
533
|
-
|
534
|
-
def get_user_params(self) -> dict:
|
535
|
-
"""Used to access user-specified parameters for the model before initialization."""
|
536
|
-
if self._user_params is None:
|
537
|
-
return {}
|
538
|
-
else:
|
539
|
-
return self._user_params.copy()
|
540
|
-
|
541
411
|
def _more_tags(self) -> dict:
|
542
412
|
"""Encode model properties using tags, similar to sklearn & autogluon.tabular.
|
543
413
|
|
@@ -668,10 +538,12 @@ class AbstractTimeSeriesModel(TimeSeriesModelBase, TimeSeriesTunable, ABC):
|
|
668
538
|
"""
|
669
539
|
pass
|
670
540
|
|
671
|
-
# TODO:
|
541
|
+
# TODO: this check cannot be moved inside fit because of the complex way in which
|
542
|
+
# MultiWindowBacktestingModel handles hyperparameter spaces during initialization.
|
543
|
+
# Move inside fit() after refactoring MultiWindowBacktestingModel
|
672
544
|
def _check_fit_params(self):
|
673
545
|
# gracefully handle hyperparameter specifications if they are provided to fit instead
|
674
|
-
if any(isinstance(v, space.Space) for v in self.
|
546
|
+
if any(isinstance(v, space.Space) for v in self.get_hyperparameters().values()):
|
675
547
|
raise ValueError(
|
676
548
|
"Hyperparameter spaces provided to `fit`. Please provide concrete values "
|
677
549
|
"as hyperparameters when initializing or use `hyperparameter_tune` instead."
|
@@ -705,9 +577,6 @@ class AbstractTimeSeriesModel(TimeSeriesModelBase, TimeSeriesTunable, ABC):
|
|
705
577
|
data is given as a separate forecast item in the dictionary, keyed by the `item_id`s
|
706
578
|
of input items.
|
707
579
|
"""
|
708
|
-
# TODO: the method signature is not aligned with the model interface in general as it allows dict
|
709
|
-
assert isinstance(data, TimeSeriesDataFrame)
|
710
|
-
|
711
580
|
if self.target_scaler is not None:
|
712
581
|
data = self.target_scaler.fit_transform(data)
|
713
582
|
if self.covariate_scaler is not None:
|
@@ -748,9 +617,59 @@ class AbstractTimeSeriesModel(TimeSeriesModelBase, TimeSeriesTunable, ABC):
|
|
748
617
|
predictions = self.target_scaler.inverse_transform(predictions)
|
749
618
|
return predictions
|
750
619
|
|
620
|
+
def convert_to_refit_full_via_copy(self) -> Self:
|
621
|
+
# save the model as a new model on disk
|
622
|
+
previous_name = self.name
|
623
|
+
self.rename(self.name + REFIT_FULL_SUFFIX)
|
624
|
+
refit_model_path = self.path
|
625
|
+
self.save(path=self.path, verbose=False)
|
626
|
+
|
627
|
+
self.rename(previous_name)
|
628
|
+
|
629
|
+
refit_model = self.load(path=refit_model_path, verbose=False)
|
630
|
+
refit_model.val_score = None
|
631
|
+
refit_model.predict_time = None
|
632
|
+
|
633
|
+
return refit_model
|
634
|
+
|
635
|
+
def convert_to_refit_full_template(self):
|
636
|
+
"""After calling this function, returned model should be able to be fit without `val_data`."""
|
637
|
+
params = copy.deepcopy(self.get_params())
|
638
|
+
|
639
|
+
if "hyperparameters" not in params:
|
640
|
+
params["hyperparameters"] = dict()
|
641
|
+
|
642
|
+
if AG_ARGS_FIT not in params["hyperparameters"]:
|
643
|
+
params["hyperparameters"][AG_ARGS_FIT] = dict()
|
644
|
+
|
645
|
+
params["name"] = params["name"] + REFIT_FULL_SUFFIX
|
646
|
+
template = self.__class__(**params)
|
647
|
+
|
648
|
+
return template
|
649
|
+
|
650
|
+
def get_params(self) -> dict:
|
651
|
+
"""Get the constructor parameters required for cloning this model object"""
|
652
|
+
hyperparameters = self.get_hyperparameters().copy()
|
653
|
+
if self._extra_ag_args:
|
654
|
+
hyperparameters[AG_ARGS_FIT] = self._extra_ag_args.copy()
|
655
|
+
|
656
|
+
return dict(
|
657
|
+
path=self.path_root,
|
658
|
+
name=self.name,
|
659
|
+
eval_metric=self.eval_metric,
|
660
|
+
hyperparameters=hyperparameters,
|
661
|
+
freq=self.freq,
|
662
|
+
prediction_length=self.prediction_length,
|
663
|
+
quantile_levels=self.quantile_levels,
|
664
|
+
metadata=self.metadata,
|
665
|
+
target=self.target,
|
666
|
+
)
|
667
|
+
|
751
668
|
def get_forecast_horizon_index(self, data: TimeSeriesDataFrame) -> pd.MultiIndex:
|
752
669
|
"""For each item in the dataframe, get timestamps for the next `prediction_length` time steps into the future."""
|
753
|
-
return
|
670
|
+
return pd.MultiIndex.from_frame(
|
671
|
+
make_future_data_frame(data, prediction_length=self.prediction_length, freq=self.freq)
|
672
|
+
)
|
754
673
|
|
755
674
|
@abstractmethod
|
756
675
|
def _predict(
|
@@ -764,8 +683,8 @@ class AbstractTimeSeriesModel(TimeSeriesModelBase, TimeSeriesTunable, ABC):
|
|
764
683
|
|
765
684
|
def _preprocess_time_limit(self, time_limit: float) -> float:
|
766
685
|
original_time_limit = time_limit
|
767
|
-
max_time_limit_ratio = self.
|
768
|
-
max_time_limit = self.
|
686
|
+
max_time_limit_ratio = self._extra_ag_args.get("max_time_limit_ratio", self.default_max_time_limit_ratio)
|
687
|
+
max_time_limit = self._extra_ag_args.get("max_time_limit")
|
769
688
|
|
770
689
|
time_limit *= max_time_limit_ratio
|
771
690
|
|
@@ -788,5 +707,5 @@ class AbstractTimeSeriesModel(TimeSeriesModelBase, TimeSeriesTunable, ABC):
|
|
788
707
|
"""Sets up default search space for HPO. Each hyperparameter which user did not specify is converted from
|
789
708
|
default fixed value to default search space.
|
790
709
|
"""
|
791
|
-
params = self.
|
710
|
+
params = self._hyperparameters.copy()
|
792
711
|
return params
|
@@ -134,8 +134,8 @@ class AbstractMLForecastModel(AbstractTimeSeriesModel):
|
|
134
134
|
def _get_extra_tabular_init_kwargs(self) -> dict:
|
135
135
|
raise NotImplementedError
|
136
136
|
|
137
|
-
def
|
138
|
-
model_params = super().
|
137
|
+
def get_hyperparameters(self) -> dict:
|
138
|
+
model_params = super().get_hyperparameters().copy()
|
139
139
|
model_params.setdefault("max_num_items", 20_000)
|
140
140
|
model_params.setdefault("max_num_samples", 1_000_000)
|
141
141
|
model_params.setdefault("tabular_hyperparameters", {"GBM": {}})
|
@@ -301,7 +301,7 @@ class AbstractMLForecastModel(AbstractTimeSeriesModel):
|
|
301
301
|
if not set(train_data[col].unique()) == set([0, 1]):
|
302
302
|
self._non_boolean_real_covariates.append(col)
|
303
303
|
# TabularEstimator is passed to MLForecast later to include tuning_data
|
304
|
-
model_params = self.
|
304
|
+
model_params = self.get_hyperparameters()
|
305
305
|
|
306
306
|
mlforecast_init_args = self._get_mlforecast_init_args(train_data, model_params)
|
307
307
|
self._mlf = MLForecast(models={}, freq=self.freq, **mlforecast_init_args)
|
@@ -473,8 +473,8 @@ class DirectTabularModel(AbstractMLForecastModel):
|
|
473
473
|
def is_quantile_model(self) -> bool:
|
474
474
|
return self.eval_metric.needs_quantile
|
475
475
|
|
476
|
-
def
|
477
|
-
model_params = super().
|
476
|
+
def get_hyperparameters(self) -> dict:
|
477
|
+
model_params = super().get_hyperparameters()
|
478
478
|
model_params.setdefault("target_scaler", "mean_abs")
|
479
479
|
if "differences" not in model_params or model_params["differences"] is None:
|
480
480
|
model_params["differences"] = []
|
@@ -622,8 +622,8 @@ class RecursiveTabularModel(AbstractMLForecastModel):
|
|
622
622
|
end of each time series).
|
623
623
|
"""
|
624
624
|
|
625
|
-
def
|
626
|
-
model_params = super().
|
625
|
+
def get_hyperparameters(self) -> dict:
|
626
|
+
model_params = super().get_hyperparameters()
|
627
627
|
model_params.setdefault("target_scaler", "standard")
|
628
628
|
if "differences" not in model_params or model_params["differences"] is None:
|
629
629
|
model_params["differences"] = [get_seasonality(self.freq)]
|