Skip to content

Commit

Permalink
Merge branch 'master' into model-unification
Browse files Browse the repository at this point in the history
  • Loading branch information
Mr-Geekman authored Jul 17, 2023
2 parents 911e5c1 + 6a9b6a8 commit 4f7f2ad
Show file tree
Hide file tree
Showing 23 changed files with 3,313 additions and 436 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `DeseasonalityTransform` ([#1307](https://github.com/tinkoff-ai/etna/pull/1307))
-
- Add extension with models from `statsforecast`: `StatsForecastARIMAModel`, `StatsForecastAutoARIMAModel`, `StatsForecastAutoCESModel`, `StatsForecastAutoETSModel`, `StatsForecastAutoThetaModel` ([#1295](https://github.com/tinkoff-ai/etna/pull/1297))
- Notebook `feature_selection` ([#875](https://github.com/tinkoff-ai/etna/pull/875))
-
-

Expand All @@ -24,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
-
-
- `mrmr` feature selection working with categoricals ([#1311](https://github.com/tinkoff-ai/etna/pull/1311))
-
- Fix version of `statsforecast` to 1.4 to avoid dependency conflicts during installation ([#1313](https://github.com/tinkoff-ai/etna/pull/1313))

### Removed
- Building docker images with cuda 10.2 ([#1306](https://github.com/tinkoff-ai/etna/pull/1306))
Expand All @@ -35,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Exogenous variables shift transform `ExogShiftTransform`([#1254](https://github.com/tinkoff-ai/etna/pull/1254))
- Parameter `start_timestamp` to forecast CLI command ([#1265](https://github.com/tinkoff-ai/etna/pull/1265))
- `DeepStateModel` ([#1253](https://github.com/tinkoff-ai/etna/pull/1253))
- `NBeatsGenericModel` and `NBeatsInterpretableModel` ([#1302](https://github.com/tinkoff-ai/etna/pull/1302))
- Function `estimate_max_n_folds` for folds number estimation ([#1279](https://github.com/tinkoff-ai/etna/pull/1279))
- Parameters `estimate_n_folds` and `context_size` to forecast and backtest CLI commands ([#1284](https://github.com/tinkoff-ai/etna/pull/1284))
- Class `Tune` for hyperparameter optimization within existing pipeline ([#1200](https://github.com/tinkoff-ai/etna/pull/1200))
Expand Down
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,19 @@ We have also prepared a set of tutorials for an easy introduction:
| [Get started](https://github.com/tinkoff-ai/etna/tree/master/examples/get_started.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/get_started.ipynb) |
| [Backtest](https://github.com/tinkoff-ai/etna/tree/master/examples/backtest.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/backtest.ipynb) |
| [EDA](https://github.com/tinkoff-ai/etna/tree/master/examples/EDA.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/EDA.ipynb) |
| [Outliers](https://github.com/tinkoff-ai/etna/tree/master/examples/outliers.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/outliers.ipynb) |
| [Clustering](https://github.com/tinkoff-ai/etna/tree/master/examples/clustering.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/clustering.ipynb) |
| [Regressors and exogenous data](https://github.com/tinkoff-ai/etna/tree/master/examples/exogenous_data.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/exogenous_data.ipynb) |
| [Custom model and transform](https://github.com/tinkoff-ai/etna/tree/master/examples/custom_transform_and_model.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/custom_transform_and_model.ipynb) |
| [Deep learning models](https://github.com/tinkoff-ai/etna/tree/master/examples/NN_examples.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/NN_examples.ipynb) |
| [Ensembles](https://github.com/tinkoff-ai/etna/tree/master/examples/ensembles.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/ensembles.ipynb) |
| [Custom Transform and Model](https://github.com/tinkoff-ai/etna/tree/master/examples/custom_transform_and_model.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/custom_transform_and_model.ipynb) |
| [Exogenous data](https://github.com/tinkoff-ai/etna/tree/master/examples/exogenous_data.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/exogenous_data.ipynb) |
| [Forecasting strategies](https://github.com/tinkoff-ai/etna/blob/master/examples/forecasting_strategies.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/forecasting_strategies.ipynb) |
| [Classification](https://github.com/tinkoff-ai/etna/blob/master/examples/classification.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/classification.ipynb) |
| [Outliers](https://github.com/tinkoff-ai/etna/tree/master/examples/outliers.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/outliers.ipynb) |
| [Forecasting strategies](https://github.com/tinkoff-ai/etna/tree/master/examples/forecasting_strategies.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/forecasting_strategies.ipynb) |
| [Forecast interpretation](https://github.com/tinkoff-ai/etna/tree/master/examples/forecast_interpretation.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/forecast_interpretation.ipynb) |
| [Clustering](https://github.com/tinkoff-ai/etna/tree/master/examples/clustering.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/clustering.ipynb) |
| [AutoML](https://github.com/tinkoff-ai/etna/tree/master/examples/automl.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/automl.ipynb) |
| [Inference: using saved pipeline on a new data](https://github.com/tinkoff-ai/etna/tree/master/examples/inference.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/inference.ipynb) |
| [Hierarchical time series](https://github.com/tinkoff-ai/etna/blob/master/examples/hierarchical_pipeline.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/hierarchical_pipeline.ipynb) |
| [Classification](https://github.com/tinkoff-ai/etna/blob/master/examples/classification.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/classification.ipynb) |
| [Feature selection](https://github.com/tinkoff-ai/etna/blob/master/examples/feature_selection.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tinkoff-ai/etna/master?filepath=examples/feature_selection.ipynb) |

## Documentation

Expand Down
1 change: 1 addition & 0 deletions etna/analysis/feature_relevance/relevance_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def _prepare_df(df: pd.DataFrame, df_exog: pd.DataFrame, segment: str, regressor

def get_statistics_relevance_table(df: pd.DataFrame, df_exog: pd.DataFrame) -> pd.DataFrame:
"""Calculate relevance table with p-values from tsfresh.
Parameters
----------
df:
Expand Down
2 changes: 2 additions & 0 deletions etna/models/nn/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from etna.models.nn.deepar import DeepARModel
from etna.models.nn.deepstate.deepstate import DeepStateModel
from etna.models.nn.mlp import MLPModel
from etna.models.nn.nbeats import NBeatsGenericModel
from etna.models.nn.nbeats import NBeatsInterpretableModel
from etna.models.nn.rnn import RNNModel
from etna.models.nn.tft import TFTModel
from etna.models.nn.utils import PytorchForecastingDatasetBuilder
5 changes: 5 additions & 0 deletions etna/models/nn/nbeats/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from etna import SETTINGS

if SETTINGS.torch_required:
from etna.models.nn.nbeats.nbeats import NBeatsGenericModel
from etna.models.nn.nbeats.nbeats import NBeatsInterpretableModel
240 changes: 240 additions & 0 deletions etna/models/nn/nbeats/blocks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
from typing import Tuple

import numpy as np

from etna import SETTINGS

if SETTINGS.torch_required:
import torch
import torch.nn as nn


class NBeatsBlock(nn.Module):
"""Base N-BEATS block which takes a basis function as an argument."""

def __init__(self, input_size: int, theta_size: int, basis_function: "nn.Module", num_layers: int, layer_size: int):
"""N-BEATS block.
Parameters
----------
input_size:
In-sample size.
theta_size:
Number of parameters for the basis function.
basis_function:
Basis function which takes the parameters and produces backcast and forecast.
num_layers:
Number of layers.
layer_size
Layer size.
"""
super().__init__()

layers = [nn.Linear(in_features=input_size, out_features=layer_size), nn.ReLU()]
for _ in range(num_layers - 1):
layers.append(nn.Linear(in_features=layer_size, out_features=layer_size))
layers.append(nn.ReLU())

self.layers = nn.ModuleList(layers)

self.basis_parameters = nn.Linear(in_features=layer_size, out_features=theta_size)
self.basis_function = basis_function

def forward(self, x: "torch.Tensor") -> Tuple["torch.Tensor", "torch.Tensor"]:
"""Forward pass.
Parameters
----------
x:
Input data.
Returns
-------
:
Tuple with backcast and forecast.
"""
for layer in self.layers:
x = layer(x)

basis_parameters = self.basis_parameters(x)
return self.basis_function(basis_parameters)


class GenericBasis(nn.Module):
"""Generic basis function."""

def __init__(self, backcast_size: int, forecast_size: int):
"""Initialize generic basis function.
Parameters
----------
backcast_size:
Number of backcast values.
forecast_size:
Number of forecast values.
"""
super().__init__()
self.backcast_size = backcast_size
self.forecast_size = forecast_size

def forward(self, theta: "torch.Tensor") -> Tuple["torch.Tensor", "torch.Tensor"]:
"""Forward pass.
Parameters
----------
theta:
Basis function parameters.
Returns
-------
:
Tuple with backcast and forecast.
"""
return theta[:, : self.backcast_size], theta[:, -self.forecast_size :]


class TrendBasis(nn.Module):
"""Polynomial trend basis function."""

def __init__(self, degree: int, backcast_size: int, forecast_size: int):
"""Initialize trend basis function.
Parameters
----------
degree:
Degree of polynomial for trend modeling.
backcast_size:
Number of backcast values.
forecast_size:
Number of forecast values.
"""
super().__init__()
self.num_poly_terms = degree + 1

self.backcast_time = nn.Parameter(self._trend_tensor(size=backcast_size), requires_grad=False)
self.forecast_time = nn.Parameter(self._trend_tensor(size=forecast_size), requires_grad=False)

def _trend_tensor(self, size: int) -> "torch.Tensor":
"""Prepare trend tensor."""
time = torch.arange(size) / size
degrees = torch.arange(self.num_poly_terms)
trend_tensor = torch.transpose(time[:, None] ** degrees[None], 0, 1)
return trend_tensor

def forward(self, theta: "torch.Tensor") -> Tuple["torch.Tensor", "torch.Tensor"]:
"""Forward pass.
Parameters
----------
theta:
Basis function parameters.
Returns
-------
:
Tuple with backcast and forecast.
"""
backcast = theta[:, : self.num_poly_terms] @ self.backcast_time
forecast = theta[:, self.num_poly_terms :] @ self.forecast_time
return backcast, forecast


class SeasonalityBasis(nn.Module):
"""Harmonic seasonality basis function."""

def __init__(self, harmonics: int, backcast_size: int, forecast_size: int):
"""Initialize seasonality basis function.
Parameters
----------
harmonics:
Harmonics range.
backcast_size:
Number of backcast values.
forecast_size:
Number of forecast values.
"""
super().__init__()

freq = torch.arange(harmonics - 1, harmonics / 2 * forecast_size) / harmonics
freq[0] = 0.0
frequency = torch.unsqueeze(freq, 0)

backcast_grid = -2 * np.pi * torch.arange(backcast_size)[:, None] / forecast_size
backcast_grid = backcast_grid * frequency

forecast_grid = 2 * np.pi * torch.arange(forecast_size)[:, None] / forecast_size
forecast_grid = forecast_grid * frequency

self.backcast_cos_template = nn.Parameter(torch.transpose(torch.cos(backcast_grid), 0, 1), requires_grad=False)
self.backcast_sin_template = nn.Parameter(torch.transpose(torch.sin(backcast_grid), 0, 1), requires_grad=False)
self.forecast_cos_template = nn.Parameter(torch.transpose(torch.cos(forecast_grid), 0, 1), requires_grad=False)
self.forecast_sin_template = nn.Parameter(torch.transpose(torch.sin(forecast_grid), 0, 1), requires_grad=False)

def forward(self, theta: "torch.Tensor") -> Tuple["torch.Tensor", "torch.Tensor"]:
"""Forward pass.
Parameters
----------
theta:
Basis function parameters.
Returns
-------
:
Tuple with backcast and forecast.
"""
params_per_harmonic = theta.shape[1] // 4

backcast_harmonics_cos = theta[:, :params_per_harmonic] @ self.backcast_cos_template
backcast_harmonics_sin = theta[:, params_per_harmonic : 2 * params_per_harmonic] @ self.backcast_sin_template
backcast = backcast_harmonics_sin + backcast_harmonics_cos

forecast_harmonics_cos = (
theta[:, 2 * params_per_harmonic : 3 * params_per_harmonic] @ self.forecast_cos_template
)
forecast_harmonics_sin = theta[:, 3 * params_per_harmonic :] @ self.forecast_sin_template
forecast = forecast_harmonics_sin + forecast_harmonics_cos

return backcast, forecast


class NBeats(nn.Module):
"""N-BEATS model."""

def __init__(self, blocks: "nn.ModuleList"):
"""Initialize N-BEATS model.
Parameters
----------
blocks:
Model blocks.
"""
super().__init__()
self.blocks = blocks

def forward(self, x: "torch.Tensor", input_mask: "torch.Tensor") -> "torch.Tensor":
"""Forward pass.
Parameters
----------
x:
Input data.
input_mask:
Input mask.
Returns
-------
:
Forecast tensor.
"""
residuals = x.flip(dims=(1,))
input_mask = input_mask.flip(dims=(1,))
forecast = x[:, -1:]

for block in self.blocks:
backcast, block_forecast = block(residuals)
residuals = (residuals - backcast) * input_mask
forecast = forecast + block_forecast

return forecast
Loading

0 comments on commit 4f7f2ad

Please sign in to comment.