[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]: