[1]:
# Import to be able to import python package from src
import sys
sys.path.insert(0, '../src')
[2]:
import pandas as pd
import numpy as np
import ontime as on

Models#

This class implement a generic way to load models in onTime. It is compatible with Darts, Scikit-learn and PyTorch, with an aim to add other librairies and models soon. Let’s see how to use it.

Let’s generate random TimeSeries#

[3]:
# Start and end dates
start_date = pd.Timestamp('2022-01-01')
end_date = pd.Timestamp('2022-12-31')

# Make a random walk
ts = on.generators.random_walk().generate(start=start_date, end=end_date)
ts = ts.astype(np.float32)

# Make another random walk
new_ts = on.generators.random_walk().generate(start=start_date, end=end_date)
new_ts = ts.astype(np.float32)

Using a Darts model#

The model can be loaded from the desired library

[4]:
from darts.models import BlockRNNModel

Then, it can be used with this generic interface.

[5]:
model = on.Model(BlockRNNModel,
                 input_chunk_length=12,
                 output_chunk_length=6,
                 n_rnn_layers=2,
                 n_epochs=50
                 )

Finally, the training and inference functions are the same than in Scikit Learn for instance :

[6]:
# To train the model
model.fit(ts)
darts.models.forecasting.torch_forecasting_model INFO  Train dataset contains 348 samples.
darts.models.forecasting.torch_forecasting_model INFO  Time series values are 32-bits; casting model to float32.
INFO: GPU available: True (cuda), used: True
lightning.pytorch.utilities.rank_zero INFO  GPU available: True (cuda), used: True
INFO: TPU available: False, using: 0 TPU cores
lightning.pytorch.utilities.rank_zero INFO  TPU available: False, using: 0 TPU cores
INFO: HPU available: False, using: 0 HPUs
lightning.pytorch.utilities.rank_zero INFO  HPU available: False, using: 0 HPUs
INFO: You are using a CUDA device ('NVIDIA GeForce RTX 3070 Ti Laptop GPU') that has Tensor Cores. To properly utilize them, you should set `torch.set_float32_matmul_precision('medium' | 'high')` which will trade-off precision for performance. For more details, read https://pytorch.org/docs/stable/generated/torch.set_float32_matmul_precision.html#torch.set_float32_matmul_precision
lightning.pytorch.utilities.rank_zero INFO  You are using a CUDA device ('NVIDIA GeForce RTX 3070 Ti Laptop GPU') that has Tensor Cores. To properly utilize them, you should set `torch.set_float32_matmul_precision('medium' | 'high')` which will trade-off precision for performance. For more details, read https://pytorch.org/docs/stable/generated/torch.set_float32_matmul_precision.html#torch.set_float32_matmul_precision
LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]

  | Name            | Type             | Params | Mode
-------------------------------------------------------------
0 | criterion       | MSELoss          | 0      | train
1 | train_criterion | MSELoss          | 0      | train
2 | val_criterion   | MSELoss          | 0      | train
3 | train_metrics   | MetricCollection | 0      | train
4 | val_metrics     | MetricCollection | 0      | train
5 | rnn             | RNN              | 2.0 K  | train
6 | fc              | Sequential       | 156    | train
-------------------------------------------------------------
2.2 K     Trainable params
0         Non-trainable params
2.2 K     Total params
0.009     Total estimated model params size (MB)
8         Modules in train mode
0         Modules in eval mode
INFO: `Trainer.fit` stopped: `max_epochs=50` reached.
lightning.pytorch.utilities.rank_zero INFO  `Trainer.fit` stopped: `max_epochs=50` reached.
[6]:
<ontime.core.modelling.model.Model at 0x7f7accf17460>
[7]:
# To make a prediction from train series
darts_pred = model.predict(10)
INFO: GPU available: True (cuda), used: True
lightning.pytorch.utilities.rank_zero INFO  GPU available: True (cuda), used: True
INFO: TPU available: False, using: 0 TPU cores
lightning.pytorch.utilities.rank_zero INFO  TPU available: False, using: 0 TPU cores
INFO: HPU available: False, using: 0 HPUs
lightning.pytorch.utilities.rank_zero INFO  HPU available: False, using: 0 HPUs
LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]

Once trained, the model can also infer on a new series, or even on a list of series (batched inference)

[8]:
# on a new single series
model.predict(n=10, ts=new_ts)
# on many series
model.predict(n=10, ts=[new_ts]*3)
INFO: GPU available: True (cuda), used: True
lightning.pytorch.utilities.rank_zero INFO  GPU available: True (cuda), used: True
INFO: TPU available: False, using: 0 TPU cores
lightning.pytorch.utilities.rank_zero INFO  TPU available: False, using: 0 TPU cores
INFO: HPU available: False, using: 0 HPUs
lightning.pytorch.utilities.rank_zero INFO  HPU available: False, using: 0 HPUs
LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]
INFO: GPU available: True (cuda), used: True
lightning.pytorch.utilities.rank_zero INFO  GPU available: True (cuda), used: True
INFO: TPU available: False, using: 0 TPU cores
lightning.pytorch.utilities.rank_zero INFO  TPU available: False, using: 0 TPU cores
INFO: HPU available: False, using: 0 HPUs
lightning.pytorch.utilities.rank_zero INFO  HPU available: False, using: 0 HPUs
LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]
[8]:
[<TimeSeries (DataArray) (time: 10, component: 1, sample: 1)> Size: 40B
 array([[[14.379956]],

        [[13.368615]],

        [[13.446395]],

        [[15.118863]],

        [[14.379672]],

        [[13.368358]],

        [[13.446113]],

        [[15.118556]],

        [[13.832502]],

        [[14.525592]]], dtype=float32)
 Coordinates:
   * time       (time) datetime64[ns] 80B 2023-01-01 2023-01-02 ... 2023-01-10
   * component  (component) object 8B 'random_walk'
 Dimensions without coordinates: sample
 Attributes:
     static_covariates:  None
     hierarchy:          None,
 <TimeSeries (DataArray) (time: 10, component: 1, sample: 1)> Size: 40B
 array([[[14.379956]],

        [[13.368615]],

        [[13.446395]],

        [[15.118863]],

        [[14.379672]],

        [[13.368358]],

        [[13.446113]],

        [[15.118556]],

        [[13.832502]],

        [[14.525592]]], dtype=float32)
 Coordinates:
   * time       (time) datetime64[ns] 80B 2023-01-01 2023-01-02 ... 2023-01-10
   * component  (component) object 8B 'random_walk'
 Dimensions without coordinates: sample
 Attributes:
     static_covariates:  None
     hierarchy:          None,
 <TimeSeries (DataArray) (time: 10, component: 1, sample: 1)> Size: 40B
 array([[[14.379956]],

        [[13.368615]],

        [[13.446395]],

        [[15.118863]],

        [[14.379672]],

        [[13.368358]],

        [[13.446113]],

        [[15.118556]],

        [[13.832502]],

        [[14.525592]]], dtype=float32)
 Coordinates:
   * time       (time) datetime64[ns] 80B 2023-01-01 2023-01-02 ... 2023-01-10
   * component  (component) object 8B 'random_walk'
 Dimensions without coordinates: sample
 Attributes:
     static_covariates:  None
     hierarchy:          None]
[9]:
darts_pred = darts_pred.rename({'random_walk': 'darts prediction'}) # for plotting

Using a Scikit-learn API compatible model#

[10]:
from sklearn.neural_network import MLPRegressor
[11]:
model = on.Model(MLPRegressor(),
                 lags=30)
model.fit(ts)
sk_pred = model.predict(10)
sk_pred = sk_pred.rename({'pred': 'sklearn prediction'})
/home/benjy/.cache/pypoetry/virtualenvs/ontime-2OQVvbNf-py3.10/lib/python3.10/site-packages/sklearn/neural_network/_multilayer_perceptron.py:691: ConvergenceWarning: Stochastic Optimizer: Maximum iterations (200) reached and the optimization hasn't converged yet.
  warnings.warn(
[12]:
# on a new single series (for now, inference can only be performed on single series)
model.predict(n=10, ts=new_ts)
[12]:
<TimeSeries (DataArray) (time: 10, component: 1, sample: 1)> Size: 80B
array([[[26.51311037]],

       [[26.79350356]],

       [[27.33226923]],

       [[26.90249441]],

       [[27.77318538]],

       [[27.31159983]],

       [[27.34664187]],

       [[26.14925155]],

       [[26.61025269]],

       [[25.49064079]]])
Coordinates:
  * time       (time) datetime64[ns] 80B 2023-01-01 2023-01-02 ... 2023-01-10
  * component  (component) object 8B 'pred'
Dimensions without coordinates: sample
Attributes:
    static_covariates:  None
    hierarchy:          None

Using a PyTorch model#

You can create and use custom PyTorch models and wrap them in our wrapper. It allows to quickly test its PyTorch model.
For more complex behaviors, you can also create a new Darts model by inheriting one of the existing class in the library. You can find existing model implementations here.
[13]:
import torch.nn as nn
[14]:
class SimpleGRU(nn.Module):
    def __init__(self, input_dim=1, hidden_dim=32, output_dim=1, num_layers=1):
        super(SimpleGRU, self).__init__()
        self.hidden_dim = hidden_dim
        self.output_size = output_dim

        self.gru = nn.GRU(input_dim, hidden_dim, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_dim, output_dim)  # Map hidden state to output dimension

    def forward(self, x):
        # Pass through GRU
        out, _ = self.gru(x)
        out = self.fc(out)
        out = out[:, -6:, :] # the model output 6 features
        return out
[15]:
model = on.Model(
    SimpleGRU,
    input_chunk_length=12,
    output_chunk_length=6, # should be equal what the output size the model was trained with
    num_layers=2,
    n_epochs=50,
        train_data_module_params={
        "val_split":0.2
    }
)
[16]:
model.fit(ts)
INFO: GPU available: True (cuda), used: True
lightning.pytorch.utilities.rank_zero INFO  GPU available: True (cuda), used: True
INFO: TPU available: False, using: 0 TPU cores
lightning.pytorch.utilities.rank_zero INFO  TPU available: False, using: 0 TPU cores
INFO: HPU available: False, using: 0 HPUs
lightning.pytorch.utilities.rank_zero INFO  HPU available: False, using: 0 HPUs
INFO: You are using a CUDA device ('NVIDIA GeForce RTX 3070 Ti Laptop GPU') that has Tensor Cores. To properly utilize them, you should set `torch.set_float32_matmul_precision('medium' | 'high')` which will trade-off precision for performance. For more details, read https://pytorch.org/docs/stable/generated/torch.set_float32_matmul_precision.html#torch.set_float32_matmul_precision
lightning.pytorch.utilities.rank_zero INFO  You are using a CUDA device ('NVIDIA GeForce RTX 3070 Ti Laptop GPU') that has Tensor Cores. To properly utilize them, you should set `torch.set_float32_matmul_precision('medium' | 'high')` which will trade-off precision for performance. For more details, read https://pytorch.org/docs/stable/generated/torch.set_float32_matmul_precision.html#torch.set_float32_matmul_precision
2025-01-06 16:26:59.828777: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.
2025-01-06 16:27:00.179475: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:477] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
E0000 00:00:1736177220.320421  284635 cuda_dnn.cc:8310] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered
E0000 00:00:1736177220.361063  284635 cuda_blas.cc:1418] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered
2025-01-06 16:27:00.703629: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 AVX_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
INFO: LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]
lightning.pytorch.accelerators.cuda INFO  LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]
INFO:
  | Name    | Type      | Params | Mode
----------------------------------------------
0 | model   | SimpleGRU | 9.7 K  | train
1 | loss_fn | MSELoss   | 0      | train
----------------------------------------------
9.7 K     Trainable params
0         Non-trainable params
9.7 K     Total params
0.039     Total estimated model params size (MB)
4         Modules in train mode
0         Modules in eval mode
lightning.pytorch.callbacks.model_summary INFO
  | Name    | Type      | Params | Mode
----------------------------------------------
0 | model   | SimpleGRU | 9.7 K  | train
1 | loss_fn | MSELoss   | 0      | train
----------------------------------------------
9.7 K     Trainable params
0         Non-trainable params
9.7 K     Total params
0.039     Total estimated model params size (MB)
4         Modules in train mode
0         Modules in eval mode
/home/benjy/.cache/pypoetry/virtualenvs/ontime-2OQVvbNf-py3.10/lib/python3.10/site-packages/lightning/pytorch/loops/fit_loop.py:298: The number of training batches (9) is smaller than the logging interval Trainer(log_every_n_steps=50). Set a lower value for log_every_n_steps if you want to see logs for the training epoch.
INFO: `Trainer.fit` stopped: `max_epochs=50` reached.
lightning.pytorch.utilities.rank_zero INFO  `Trainer.fit` stopped: `max_epochs=50` reached.
[16]:
<ontime.core.modelling.model.Model at 0x7f7aca538310>
[17]:
torch_pred = model.predict(10)
torch_pred = torch_pred.rename({'0': 'torch prediction'})
/home/benjy/projects_dev/ontime/src/ontime/core/modelling/libs/pytorch/pytorch_forecasting_model.py:97: UserWarning: The requested prediction horizon (n=10) exceeds the model's output_chunk_length (6). The model will use an iterative forecasting approach, which may result in reduced accuracy due to error propagation.
  warnings.warn(
[18]:
# on a new single series
model.predict(n=10, ts=new_ts)
# on many series
model.predict(n=10, ts=[new_ts]*3)
/home/benjy/projects_dev/ontime/src/ontime/core/modelling/libs/pytorch/pytorch_forecasting_model.py:97: UserWarning: The requested prediction horizon (n=10) exceeds the model's output_chunk_length (6). The model will use an iterative forecasting approach, which may result in reduced accuracy due to error propagation.
  warnings.warn(
/home/benjy/projects_dev/ontime/src/ontime/core/modelling/libs/pytorch/pytorch_forecasting_model.py:97: UserWarning: The requested prediction horizon (n=10) exceeds the model's output_chunk_length (6). The model will use an iterative forecasting approach, which may result in reduced accuracy due to error propagation.
  warnings.warn(
[18]:
[<TimeSeries (DataArray) (time: 10, component: 1, sample: 1)> Size: 40B
 array([[[15.901117 ]],

        [[15.901123 ]],

        [[15.9012165]],

        [[15.901222 ]],

        [[15.90128  ]],

        [[15.901265 ]],

        [[15.900374 ]],

        [[15.900061 ]],

        [[15.899954 ]],

        [[15.899914 ]]], dtype=float32)
 Coordinates:
   * time       (time) datetime64[ns] 80B 2023-01-01 2023-01-02 ... 2023-01-10
   * component  (component) <U1 4B '0'
 Dimensions without coordinates: sample
 Attributes:
     static_covariates:  None
     hierarchy:          None,
 <TimeSeries (DataArray) (time: 10, component: 1, sample: 1)> Size: 40B
 array([[[15.901117 ]],

        [[15.901123 ]],

        [[15.9012165]],

        [[15.901222 ]],

        [[15.90128  ]],

        [[15.901265 ]],

        [[15.900374 ]],

        [[15.900061 ]],

        [[15.899954 ]],

        [[15.899914 ]]], dtype=float32)
 Coordinates:
   * time       (time) datetime64[ns] 80B 2023-01-01 2023-01-02 ... 2023-01-10
   * component  (component) <U1 4B '0'
 Dimensions without coordinates: sample
 Attributes:
     static_covariates:  None
     hierarchy:          None,
 <TimeSeries (DataArray) (time: 10, component: 1, sample: 1)> Size: 40B
 array([[[15.901117 ]],

        [[15.901123 ]],

        [[15.9012165]],

        [[15.901222 ]],

        [[15.90128  ]],

        [[15.901265 ]],

        [[15.900374 ]],

        [[15.900061 ]],

        [[15.899954 ]],

        [[15.899914 ]]], dtype=float32)
 Coordinates:
   * time       (time) datetime64[ns] 80B 2023-01-01 2023-01-02 ... 2023-01-10
   * component  (component) <U1 4B '0'
 Dimensions without coordinates: sample
 Attributes:
     static_covariates:  None
     hierarchy:          None]

Comparing predictions#

[19]:
(on.Plot()
    .add(on.marks.line, ts[-50:])
    .add(on.marks.line, darts_pred)
    .add(on.marks.line, sk_pred)
    .add(on.marks.line, torch_pred)
    .properties(width=600, height=300)
    .show()
)
[19]: