Forecasting with Chronos¶
AutoGluon-TimeSeries (AG-TS) now features Chronos, a family of pretrained time series forecasting models. Chronos models are based on language model architectures, and work by quantizing time series into buckets which are treated as tokens. Language models are then trained on these token sequences using cross-entropy loss.
The current iteration of Chronos models, available on Hugging Face 🤗, is based on the T5 architecture and was trained on a large corpus of open-source time series data augmented with synthetic data generation techniques. The Chronos paper provides greater detail about the models and how they were trained.
AG-TS provides a robust and easy way to use Chronos through the familiar TimeSeriesPredictor API.
Chronos can be combined with other forecasting models to build accurate ensembles using the
"high_quality"and"best_quality"presets.Alternatively, Chronos can be used as a standalone zero-shot model with presets such as
"chronos_small"or"chronos_base".
from autogluon.timeseries import TimeSeriesDataFrame, TimeSeriesPredictor
Getting Started with Chronos¶
Chronos is available in 5 model sizes with different numbers of parameters: tiny (8M), mini (20M), small (46M), base (200M), and large (710M). Being a pretrained model for zero-shot forecasting, Chronos is different from other models available in AG-TS.
Specifically, Chronos models do not really fit time series data. However, when predict is called, they carry out a relatively more expensive computation that scales linearly with the number of time series in the dataset. In this aspect, they behave like local statistical models such as ETS or ARIMA, where expensive computation happens during inference. Differently from statistical models, however, computation in the larger Chronos models requires an accelerator chip to run in a reasonable amount of time.
The easiest way to get started with Chronos is through model-specific presets available in the TimeSeriesPredictor. As of v1.1, the TimeSeriesPredictor.fit method has a separate Chronos preset for each model size, such as "chronos_small" or "chronos_base".
Alternatively, Chronos can be combined with other time series models using presets "chronos_ensemble", "chronos_large_ensemble", "high_quality" and "best_quality". More details about these presets are available in the documentation for TimeSeriesPredictor.fit.
Note that the model sizes small and higher require a GPU to run. However, models tiny and mini can be run on the CPU as well.
Let’s work with a subset of the M4 competition data set to see Chronos-tiny in action.
data = TimeSeriesDataFrame(
"https://autogluon.s3.amazonaws.com/datasets/timeseries/m4_hourly_tiny/train.csv"
)
data.head()
| target | ||
|---|---|---|
| item_id | timestamp | |
| H1 | 1750-01-01 00:00:00 | 605.0 |
| 1750-01-01 01:00:00 | 586.0 | |
| 1750-01-01 02:00:00 | 586.0 | |
| 1750-01-01 03:00:00 | 559.0 | |
| 1750-01-01 04:00:00 | 511.0 |
prediction_length = 24
train_data, test_data = data.train_test_split(prediction_length)
predictor = TimeSeriesPredictor(prediction_length=prediction_length).fit(
train_data, presets="chronos_tiny",
)
Beginning AutoGluon training...
AutoGluon will save models to 'AutogluonModels/ag-20241108_175404'
=================== System Info ===================
AutoGluon Version: 1.1.2b20241108
Python Version: 3.11.9
Operating System: Linux
Platform Machine: x86_64
Platform Version: #1 SMP Tue Sep 24 10:00:37 UTC 2024
CPU Count: 8
GPU Count: 1
Memory Avail: 28.62 GB / 30.95 GB (92.5%)
Disk Space Avail: 213.67 GB / 255.99 GB (83.5%)
===================================================
Setting presets to: chronos_tiny
Fitting with arguments:
{'enable_ensemble': True,
'eval_metric': WQL,
'hyperparameters': {'Chronos': {'model_path': 'tiny'}},
'known_covariates_names': [],
'num_val_windows': 1,
'prediction_length': 24,
'quantile_levels': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9],
'random_seed': 123,
'refit_every_n_windows': 1,
'refit_full': False,
'skip_model_selection': True,
'target': 'target',
'verbosity': 2}
/home/ci/autogluon/timeseries/src/autogluon/timeseries/predictor.py:296: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
df[self.target] = df[self.target].astype("float64")
Inferred time series frequency: 'h'
Provided train_data has 13520 rows, 20 time series. Median time series length is 676 (min=676, max=676).
Provided data contains following columns:
target: 'target'
AutoGluon will gauge predictive performance using evaluation metric: 'WQL'
This metric's sign has been flipped to adhere to being higher_is_better. The metric score can be multiplied by -1 to get the metric value.
===================================================
Starting training. Start time is 2024-11-08 17:54:04
Models that will be trained: ['Chronos[tiny]']
Training timeseries model Chronos[tiny].
0.00 s = Training runtime
Training complete. Models trained: ['Chronos[tiny]']
Total runtime: 0.00 s
Best model: Chronos[tiny]
As promised, Chronos does not take any time to fit. The fit call merely serves as a proxy for the TimeSeriesPredictor to do some of its chores under the hood, such as inferring the frequency of time series and saving the predictor’s state to disk.
Let’s use the predict method to generate forecasts, and the plot method to visualize them.
predictions = predictor.predict(train_data)
predictor.plot(
data=data,
predictions=predictions,
item_ids=["H1", "H2"],
max_history_length=200,
);
/home/ci/autogluon/timeseries/src/autogluon/timeseries/predictor.py:296: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
df[self.target] = df[self.target].astype("float64")
Model not specified in predict, will default to the model with the best validation score: Chronos[tiny]
Model Chronos[tiny] failed to predict with the following exception:
Traceback (most recent call last):
File "/home/ci/autogluon/timeseries/src/autogluon/timeseries/trainer/abstract_trainer.py", line 1209, in get_model_pred_dict
model_pred_dict[model_name] = self._predict_model(
^^^^^^^^^^^^^^^^^^^^
File "/home/ci/autogluon/timeseries/src/autogluon/timeseries/trainer/abstract_trainer.py", line 1136, in _predict_model
return model.predict(data, known_covariates=known_covariates)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ci/autogluon/timeseries/src/autogluon/timeseries/models/abstract/abstract_timeseries_model.py", line 330, in predict
predictions = self._predict(data=data, known_covariates=known_covariates, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ci/autogluon/timeseries/src/autogluon/timeseries/models/chronos/model.py", line 329, in _predict
np.concatenate(batch_quantiles, axis=0).reshape(-1, len(self.quantile_levels)),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: all the input array dimensions except for the concatenation axis must match exactly, but along dimension 1, the array at index 0 has size 16 and the array at index 1 has size 4
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
Cell In[4], line 1
----> 1 predictions = predictor.predict(train_data)
2 predictor.plot(
3 data=data,
4 predictions=predictions,
5 item_ids=["H1", "H2"],
6 max_history_length=200,
7 );
File ~/autogluon/timeseries/src/autogluon/timeseries/predictor.py:850, in TimeSeriesPredictor.predict(self, data, known_covariates, model, use_cache, random_seed)
848 if known_covariates is not None:
849 known_covariates = self._to_data_frame(known_covariates)
--> 850 predictions = self._learner.predict(
851 data,
852 known_covariates=known_covariates,
853 model=model,
854 use_cache=use_cache,
855 random_seed=random_seed,
856 )
857 return predictions.reindex(original_item_id_order, level=ITEMID)
File ~/autogluon/timeseries/src/autogluon/timeseries/learner.py:185, in TimeSeriesLearner.predict(self, data, known_covariates, model, use_cache, random_seed, **kwargs)
183 known_covariates = self.feature_generator.transform_future_known_covariates(known_covariates)
184 known_covariates = self._align_covariates_with_forecast_index(known_covariates=known_covariates, data=data)
--> 185 return self.load_trainer().predict(
186 data=data,
187 known_covariates=known_covariates,
188 model=model,
189 use_cache=use_cache,
190 random_seed=random_seed,
191 **kwargs,
192 )
File ~/autogluon/timeseries/src/autogluon/timeseries/trainer/abstract_trainer.py:922, in AbstractTimeSeriesTrainer.predict(self, data, known_covariates, model, use_cache, random_seed, **kwargs)
912 def predict(
913 self,
914 data: TimeSeriesDataFrame,
(...)
919 **kwargs,
920 ) -> TimeSeriesDataFrame:
921 model_name = self._get_model_for_prediction(model)
--> 922 model_pred_dict = self.get_model_pred_dict(
923 model_names=[model_name],
924 data=data,
925 known_covariates=known_covariates,
926 use_cache=use_cache,
927 random_seed=random_seed,
928 )
929 return model_pred_dict[model_name]
File ~/autogluon/timeseries/src/autogluon/timeseries/trainer/abstract_trainer.py:1224, in AbstractTimeSeriesTrainer.get_model_pred_dict(self, model_names, data, known_covariates, record_pred_time, raise_exception_if_failed, use_cache, random_seed)
1221 pred_time_dict_marginal[model_name] = None
1223 if len(failed_models) > 0 and raise_exception_if_failed:
-> 1224 raise RuntimeError(f"Following models failed to predict: {failed_models}")
1225 if self.cache_predictions and use_cache:
1226 self._save_cached_pred_dicts(
1227 dataset_hash, model_pred_dict=model_pred_dict, pred_time_dict=pred_time_dict_marginal
1228 )
RuntimeError: Following models failed to predict: ['Chronos[tiny]']
Configuring for Performance¶
Looks good! As with all large deep learning models, however, some fine-grained control of inference parameters can be needed to both optimize the speed and avoid out-of-memory issues on specific hardware. For this, we will need to dive a bit deeper, configuring hyperparameters of the TimeSeriesPredictor directly.
predictor = TimeSeriesPredictor(prediction_length=prediction_length).fit(
train_data,
hyperparameters={
"Chronos": {
"model_path": "tiny",
"batch_size": 64,
"device": "cpu",
}
},
skip_model_selection=True,
verbosity=0,
)
%%time
predictions = predictor.predict(train_data)
CPU times: user 2min 4s, sys: 42.4 s, total: 2min 47s
Wall time: 11.2 s
Above, we used the following configuration options for the TimeSeriesPredictor:
we set
skip_model_selection=Trueto skip running backtests duringfit, as we will only consider a single model.in the
hyperparametersfor the Chronos model,model_pathallows us to change the model size or select different pretrained weights. This parameter can be a model string liketinyorbase, a Hugging Face path likeamazon/chronos-t5-mini, or a path to a local folder with custom weights.batch_sizeconfigures the number of time series for which predictions are generated in parallel.deviceinstructs Chronos to run the model on CPU.
As we see, inference speed is slower on the CPU compared to the GPU, taking about 400ms per time series. To overcome this limitation, AutoGluon implementation of Chronos supports several deep learning compilers that can optimize model performance on CPUs.
For example, we can set optimization_strategy="openvino" to use the OpenVINO compiler for Intel CPUs to speed up Chronos inference. Behind the scenes, AutoGluon will use Hugging Face optimum for this conversion.
Note that this requires installing the optional OpenVINO dependency for AG-TS.
!pip install -q "autogluon.timeseries[chronos-openvino]"
To speed up the inference even further, we can persist the model after calling fit. The TimeSeriesPredictor.persist method tells AutoGluon to keep the Chronos model in device memory for fast, on-demand inference instead of loading the model from disk each time.
%%capture
predictor = TimeSeriesPredictor(prediction_length=prediction_length).fit(
train_data,
hyperparameters={
"Chronos": {
"model_path": "tiny",
"batch_size": 64,
"device": "cpu",
"optimization_strategy": "openvino",
}
},
skip_model_selection=True,
verbosity=0,
)
predictor.persist()
%%time
predictions = predictor.predict(train_data)
CPU times: user 1min 8s, sys: 9.19 s, total: 1min 17s
Wall time: 2.9 s
That reduced the inference time by ~3x!
We could have also used the ONNX runtime by providing optimization_strategy="onnx". For a discussion of these and other hyperparameters of Chronos, see the Chronos model documentation.
FAQ¶
How accurate is Chronos?¶
In several independent evaluations we found Chronos to be effective in zero-shot forecasting.
The accuracy of Chronos-large often exceeds statistical baseline models, and is often comparable to deep learning
models such as TemporalFusionTransformer or PatchTST.
What hardware do larger Chronos models require?¶
We tested Chronos on AWS g5.2xlarge and p3.2xlarge instances that feature NVIDIA A10G and V100 GPUs, with at least 16GiB of GPU memory and 32GiB of main memory.
Can I fine-tune Chronos?¶
The current iteration of Chronos on AutoGluon does not support fine tuning, although we will provide this functionality in later versions of AutoGluon.
Does Chronos work with covariates or features?¶
The current iteration of Chronos does not support covariates or features, however we will provide this functionality in
later versions. In the meanwhile, presets such as chronos_ensemble combine Chronos with models that do take advantage of features.
Where can I ask specific questions on Chronos?¶
The AutoGluon team are among the core developers of Chronos. So you can ask Chronos-related questions on AutoGluon channels such as the Discord server, or GitHub. You can also join the discussion on the Chronos GitHub page.