Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@ but cannot always guarantee backwards compatibility. Changes that may **break co

### For users of the library:

**Improved**

**Fixed**

- Fixed rendering issues of `CustomBlockRNNModule` and `CustomRNNModule` in the documentation. [#3094](https://github.com/unit8co/darts/pull/3094) by [Zhihao Dai](https://github.com/daidahao).
- Fixed rendering issues of `20-SKLearnModel-examples` notebook in the documentation. [#3094](https://github.com/unit8co/darts/pull/3094) by [Zhihao Dai](https://github.com/daidahao).

### For developers of the library:

- Sped up the documentation build by utilizing multiple CPU cores. [#3094](https://github.com/unit8co/darts/pull/3094) by [Zhihao Dai](https://github.com/daidahao).

**Dependencies**

- Added a `uv` 7-day cooldown period to reduce supply-chain risk from newly published packages during dependency resolution. [#3096](https://github.com/unit8co/darts/pull/3096) by [Zhihao Dai](https://github.com/daidahao)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ series.plot()
This is useful for example for Day-Ahead Market forecasts, or when the covariates (or target series) are reported
with a delay.

* **Explainability:** Darts has the ability to *explain* some forecasting models using Shap values.
* **Explainability:** Darts has the ability to *explain* some forecasting models using SHAP values.

* **Data Processing:** Tools to easily apply (and revert) common transformations on
time series data (scaling, filling missing values, differencing, boxcox, ...)
Expand Down
15 changes: 10 additions & 5 deletions darts/models/forecasting/block_rnn_model.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
"""
Block Recurrent Neural Networks
-------------------------------
.. autoclass:: CustomBlockRNNModule
:members: forward
:no-inherited-members:
:no-undoc-members:
:no-special-members:
"""

import inspect
Expand Down Expand Up @@ -42,13 +47,13 @@ def __init__(

To create a new module, subclass from :class:`CustomBlockRNNModule` and:

* Define the architecture in the module constructor (`__init__()`)
* Define the architecture in the module constructor (``__init__()``)

* Add the `forward()` method and define the logic of your module's forward pass
* Add the ``forward()`` method and define the logic of your module's forward pass

* Use the custom module class when creating a new :class:`BlockRNNModel` with parameter `model`.
* Use the custom module class when creating a new :class:`BlockRNNModel` with parameter ``model``.

You can use `darts.models.forecasting.block_rnn_model._BlockRNNModule` as an example.
You can use ``darts.models.forecasting.block_rnn_model._BlockRNNModule`` as an example.

Parameters
----------
Expand Down Expand Up @@ -529,7 +534,7 @@ def encode_year(idx):

self._considers_static_covariates = use_static_covariates

def _create_model(self, train_sample: TorchTrainingSample) -> torch.nn.Module:
def _create_model(self, train_sample: TorchTrainingSample) -> PLForecastingModule:
# samples are made of (past target, past cov, historic future cov, future cov, static cov, future_target)
(past_target, past_covariates, _, future_covariates, static_covariates, _) = (
train_sample
Expand Down
60 changes: 36 additions & 24 deletions darts/models/forecasting/rnn_model.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
"""
Recurrent Neural Networks
-------------------------
.. autoclass:: CustomRNNModule
:members: forward
:no-inherited-members:
:no-undoc-members:
:no-special-members:
"""

import inspect
Expand All @@ -11,7 +16,7 @@
import torch.nn as nn

from darts import TimeSeries
from darts.logging import get_logger, raise_if_not, raise_log
from darts.logging import get_logger, raise_log
from darts.models.forecasting.pl_forecasting_module import (
PLForecastingModule,
io_processor,
Expand Down Expand Up @@ -44,13 +49,13 @@ def __init__(

To create a new module, subclass from :class:`CustomRNNModule` and:

* Define the architecture in the module constructor (`__init__()`)
* Define the architecture in the module constructor (``__init__()``)

* Add the `forward()` method and define the logic of your module's forward pass
* Add the ``forward()`` method and define the logic of your module's forward pass

* Use the custom module class when creating a new :class:`RNNModel` with parameter `model`.
* Use the custom module class when creating a new :class:`RNNModel` with parameter ``model``.

You can use `darts.models.forecasting.rnn_model._RNNModule` as an example.
You can use ``darts.models.forecasting.rnn_model._RNNModule`` as an example.

Parameters
----------
Expand Down Expand Up @@ -115,21 +120,20 @@ def _process_input_batch(self, input_batch: TorchBatch) -> PLModuleInput:
(
past_target,
_, # past covariates
historic_future_covariates,
_, # historic future covariates
future_covariates,
static_covariates,
) = input_batch
# For the RNN we concatenate the past_target with the future_covariates
# (they have the same length because we enforce a Shift dataset for RNNs)
return (
(
torch.cat([past_target, future_covariates], dim=2)
if future_covariates is not None
else past_target
),
input_batch = (
past_target,
future_covariates, # future covariates as past covariates for RNN input
None,
None,
static_covariates,
)
return super()._process_input_batch(input_batch)

def _produce_predict_output(
self, x: PLModuleInput, last_hidden_state: torch.Tensor | None = None
Expand Down Expand Up @@ -160,7 +164,7 @@ def _get_batch_prediction(
static_covariates,
) = input_batch

if historic_future_covariates is not None:
if historic_future_covariates is not None and future_covariates is not None:
# RNNs need as inputs (target[t] and covariates[t+1]) so here we shift the covariates
all_covariates = torch.cat(
[historic_future_covariates[:, 1:, :], future_covariates], dim=1
Expand Down Expand Up @@ -556,7 +560,7 @@ def encode_year(idx):
self.n_rnn_layers = n_rnn_layers
self.training_length = training_length

def _create_model(self, train_sample: TorchTrainingSample) -> torch.nn.Module:
def _create_model(self, train_sample: TorchTrainingSample) -> PLForecastingModule:
# samples are made of (past target, past cov, historic future cov, future cov, static cov, future target)
(past_target, _, _, future_covariates, _, _) = train_sample
input_dim = past_target.shape[1] + (
Expand Down Expand Up @@ -587,7 +591,7 @@ def _build_train_dataset(
series: Sequence[TimeSeries],
past_covariates: Sequence[TimeSeries] | None,
future_covariates: Sequence[TimeSeries] | None,
sample_weight: Sequence[TimeSeries] | None,
sample_weight: Sequence[TimeSeries] | str | None,
max_samples_per_ts: int | None,
stride: int = 1,
) -> ShiftedTorchTrainingDataset:
Expand All @@ -603,15 +607,23 @@ def _build_train_dataset(
sample_weight=sample_weight,
)

def _verify_train_dataset_type(self, train_dataset: ShiftedTorchTrainingDataset):
raise_if_not(
isinstance(train_dataset, ShiftedTorchTrainingDataset),
"RNNModel requires a training dataset of type `GenericShiftDataset`.",
)
raise_if_not(
train_dataset.shift == 1,
"RNNModel requires a shifted training dataset with shift=1.",
)
@staticmethod
def _verify_train_dataset_type(train_dataset: ShiftedTorchTrainingDataset):
if not isinstance(train_dataset, ShiftedTorchTrainingDataset):
raise_log(
ValueError(
"RNNModel requires a training dataset of type `GenericShiftDataset`. "
f"Got {type(train_dataset)} instead."
),
logger=logger,
)
if train_dataset.shift != 1:
raise_log(
ValueError(
f"RNNModel requires a shifted training dataset with shift=1. Got shift={train_dataset.shift}."
),
logger=logger,
)

@property
def min_train_samples(self) -> int:
Expand Down
2 changes: 1 addition & 1 deletion darts/models/forecasting/sklearn_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1488,7 +1488,7 @@ def lagged_feature_names(self) -> list[str] | None:

- ``{name}`` the static covariate name of the (first) series
- ``{comp}`` the target component name of the (first) that the static covariate act on. If the static
covariate acts globally on a multivariate target series, will show "global".
covariate acts globally on a multivariate target series, will show "global_components".
"""
return self._lagged_feature_names

Expand Down
2 changes: 1 addition & 1 deletion darts/models/forecasting/tcn_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ def _build_train_dataset(
series: Sequence[TimeSeries],
past_covariates: Sequence[TimeSeries] | None,
future_covariates: Sequence[TimeSeries] | None,
sample_weight: Sequence[TimeSeries] | None,
sample_weight: Sequence[TimeSeries] | str | None,
max_samples_per_ts: int | None,
stride: int = 1,
) -> TorchTrainingDataset:
Expand Down
4 changes: 2 additions & 2 deletions darts/tests/models/forecasting/test_RNN.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def __init__(self, **kwargs):

def forward(self, x_in, h=None):
x = self.linear(x_in[0])
return x.view(len(x), -1, self.target_size, self.nr_params)
return x.view(len(x), -1, self.target_size, self.nr_params), h


class TestRNNModel:
Expand Down Expand Up @@ -111,7 +111,7 @@ def test_creation(self):
**tfm_kwargs,
)
model3.fit(self.series)
preds3 = model2.predict(n=3)
preds3 = model3.predict(n=3)
assert preds3.all_values().shape == preds2.all_values().shape
assert preds3.time_index.equals(preds2.time_index)

Expand Down
2 changes: 1 addition & 1 deletion darts/utils/data/tabularization/tabularization.py
Original file line number Diff line number Diff line change
Expand Up @@ -897,7 +897,7 @@ def create_lagged_component_names(

- ``{name}`` the static covariate name of the (first) series
- ``{comp}`` the target component name of the (first) that the static covariate act on. If the static
covariate acts globally on a multivariate target series, will show "global".
covariate acts globally on a multivariate target series, will show "global_components".

The naming convention for labels is: ``"{name}_target_hrz{i}"``, where:

Expand Down
2 changes: 1 addition & 1 deletion docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ generate-api:
html:
# Note: this target has to be called "html" because its name is given as an argument to Sphinx
@echo "[Makefile] generating HTML pages using sphinx-build..."
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -j auto

build-all-docs: clean copy-examples copy-quickstart generate-release_notes generate-userguide generate-api html
build-api: clean generate-api html
Expand Down
2 changes: 2 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
"initialize_encoders",
"SplitTimeSeriesSequence",
"randint",
"CustomRNNModule",
"CustomBlockRNNModule",
]

autodoc_default_options = {
Expand Down
2 changes: 1 addition & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ Features

* **Forecast Start Shifting:** All global models support training and prediction on a shifted output window. This is useful for example for Day-Ahead Market forecasts, or when the covariates (or target series) are reported with a delay.

* **Explainability:** Darts has the ability to *explain* some forecasting models using Shap values.
* **Explainability:** Darts has the ability to *explain* some forecasting models using SHAP values.

* **Data Processing:** Tools to easily apply (and revert) common transformations on time series data (scaling, filling missing values, differencing, boxcox, ...)

Expand Down
45 changes: 35 additions & 10 deletions examples/20-SKLearnModel-examples.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
"id": "eacf6328-6b51-43e9-8b44-214f5df15684",
"metadata": {},
"source": [
"### Input Dataset\n",
"## Input Dataset\n",
"For this notebook, we use the Electricity Consumption Dataset from households in Zurich, Switzerland.\n",
"\n",
"The dataset has a quarter-hourly frequency (15 Min time intervals), but we resample it to hourly \n",
Expand Down Expand Up @@ -301,7 +301,7 @@
"id": "73f669b6-f14d-4a56-a9f2-069b4565d9d8",
"metadata": {},
"source": [
"### Covariates-based forecasting\n",
"### Forecasting with covariates\n",
"\n",
"To use external data next to the history of our target series, we specify past and/or future covariates lags and then pass `past_covariates` and/or `future_covariates` to `fit()` and `predict()`.\n",
"\n",
Expand Down Expand Up @@ -352,7 +352,7 @@
"id": "7c7a6c5e-04c3-4a95-9d4a-03ec3517cd63",
"metadata": {},
"source": [
"### Using only covariates\n",
"### Forecasting using only covariates\n",
"\n",
"Sometimes, we might also be interested in a forecasting model that purely relies on the covariates values.\n",
"\n",
Expand Down Expand Up @@ -654,7 +654,23 @@
"cell_type": "markdown",
"id": "fdc2cfab-3b39-41c4-8701-ac6ee67b4b63",
"metadata": {},
"source": "### Probabilistic forecasting\n\nTo make a model probabilistic, set parameter `likelihood` to `\"quantile\"`, `\"poisson\"`, or `\"gaussian\"` when creating a `SKLearnModel`. At prediction time, probabilistic models can either :\n- use Monte Carlo sampling to generate samples based on the fitted distribution parameters when `num_samples > 1`\n- return the fitted distribution parameters when `predict_likelihood_parameters=True`\n\nProbabilistic models will generate different forecasts each time `predict()` is called with `num_samples > 1`. To get reproducible results, set the random seed when creating the model and call the methods in the exact same order.\n\n<div class=\"alert alert-success\" role=\"alert\">\n\nWhen using the `\"quantile\"` regressor, each quantile will be fitted by a different model.\n\n**Tip:** `CatBoostModel` and `XGBModel` additionally support native multi-quantile regression (`likelihood=\"multiquantile\"`), which fits all quantiles in one model and is more efficient.\n\n</div>\n"
"source": [
"### Probabilistic forecasting\n",
"\n",
"To make a model probabilistic, set parameter `likelihood` to `\"quantile\"`, `\"poisson\"`, or `\"gaussian\"` when creating a `SKLearnModel`. At prediction time, probabilistic models can either :\n",
"- use Monte Carlo sampling to generate samples based on the fitted distribution parameters when `num_samples > 1`\n",
"- return the fitted distribution parameters when `predict_likelihood_parameters=True`\n",
"\n",
"Probabilistic models will generate different forecasts each time `predict()` is called with `num_samples > 1`. To get reproducible results, set the random seed when creating the model and call the methods in the exact same order.\n",
"\n",
"<div class=\"alert alert-success\" role=\"alert\">\n",
"\n",
"When using the `\"quantile\"` regressor, each quantile will be fitted by a different model.\n",
"\n",
"**Tip:** `CatBoostModel` and `XGBModel` additionally support native multi-quantile regression (`likelihood=\"multiquantile\"`), which fits all quantiles in one model and is more efficient.\n",
"\n",
"</div>\n"
]
},
{
"cell_type": "code",
Expand Down Expand Up @@ -829,7 +845,7 @@
"id": "4b995311-0bdc-4d05-9573-f82466aba6e0",
"metadata": {},
"source": [
"### SKLearnModel\n",
"### SKLearn models\n",
"`SKLearnModel` wraps the Darts API around any sklearn regression model. With this we can use the model in the same way as any other Darts forecasting models.\n",
"\n",
"As an example, fitting a Bayesian ridge regression on the example dataset takes only a few lines:"
Expand Down Expand Up @@ -923,14 +939,16 @@
"cell_type": "markdown",
"id": "4989e3b1-e875-4b11-8de7-554e5ebbad90",
"metadata": {},
"source": "One of the limitation of the `SKLearnModel` class is that it does not provide probabilistic forecasting out of the box but it is possible to implement it by creating a new class inheriting from both `SKLearnModel` and `_LikelihoodMixin` and implementing the missing methods (the `LinearRegressionModel` class can be used as a template)."
"source": [
"One of the limitation of the `SKLearnModel` class is that it does not provide probabilistic forecasting out of the box but it is possible to implement it by creating a new class inheriting from both `SKLearnModel` and `_LikelihoodMixin` and implementing the missing methods (the `LinearRegressionModel` class can be used as a template)."
]
},
{
"cell_type": "markdown",
"id": "c0e9aee2-7692-4407-a56c-e8ee2d37cb8d",
"metadata": {},
"source": [
"## Custom model\n",
"## Custom models\n",
"\n",
"You can even implement your own model as long as it works with tabular data and provides the `fit()` and `predict()` methods:"
]
Expand Down Expand Up @@ -1005,7 +1023,7 @@
"source": [
"## Example of the boosted tree models\n",
"\n",
"Make sure to have the dependencies for lightgbm and catboost installed. You can check out our installation guide [here](https://github.com/unit8co/darts/blob/master/INSTALL.md)"
"Make sure to have the dependencies for LightGBM, CatBoost, and XGBoost installed. You can check out our installation guide [here](https://github.com/unit8co/darts/blob/master/INSTALL.md)"
]
},
{
Expand Down Expand Up @@ -1071,7 +1089,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "darts",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
Expand All @@ -1085,7 +1103,14 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.9"
"version": "3.12.7"
},
"widgets": {
"application/vnd.jupyter.widget-state+json": {
"state": {},
"version_major": 2,
"version_minor": 0
}
}
},
"nbformat": 4,
Expand Down
Loading