Code Monkey home page Code Monkey logo

n-beats's Introduction

ServiceNow completed its acquisition of Element AI on January 8, 2021. All references to Element AI in the materials that are part of this project should refer to ServiceNow.

N-BEATS

This repo provides an implementation of the N-BEATS algorithm introduced in https://arxiv.org/abs/1905.10437 and enables reproducing the experimental results presented in the paper.

N-BEATS is a neural-network based model for univariate timeseries forecasting.

N-BEATS Architecture

Repository Structure

Model

PyTorch implementation of N-BEATS can be found in models/nbeats.py

Datasets

The loaders for each dataset used in the paper are in datasets/*.py

Experiments

Experiments to reproduce the paper results are located in experiments/*, where each experiment package contains main.py with the training and forecasting logic along with two configuration files for both generic and interpretable models.

Results

The notebooks directory contains a notebook per experiment with the final scores. We also included the results for 10 times smaller ensembles, you can see that the difference is not significant. Note: This is a "lighter" version of original N-BEATS which performs slightly different, sometimes better sometimes worse but overall on the same SOTA level.

How to reproduce the results

Make sure you have docker installed. Using NVidia GPUs is preferable, but not required. Depending on your environment you may need to adjust Makefile's docker image name and write additional logic to train models in parallel.

The default configuration is using 10 repeats what produces up to 180 models per experiment, as you can see in notebooks the difference between big and small ensembles is not significant. To switch to small ensemble set build.repeats = 1 in *.gin files for experiments you want to run (they are located in experiments/*/*.gin).

  1. Build docker image

    make init
  2. Download datasets

    make dataset

    This command will download dataset into ./storage/datasets directory

  3. (Optional) Test metrics. To make sure that all datasets are correct and the metrics calculation works as expected you can run test.

    make test
  4. Build an experiment

    make build config=experiments/m4/interpretable.gin

    This will generate directories with configurations and command for each model of ensemble in ./storage/experiments/m4_interpretable. Note that the config parameter takes the relative path to actual configuration.

  5. Run experiments. Substitute different values for repeat and lookback in the command lines below to run other configurations of a model.

    CPU

    make run command=storage/experiments/m4_interpretable/repeat=0,lookback=2,loss=MAPE/command

    GPU

     make run command=storage/experiments/m4_interpretable/repeat=0,lookback=2,loss=MAPE/command gpu=<gpu-id>

    If you have multiple GPUs on the same machine then run this command in parallel for each gpu-id.

    The logs, losses, snapshots and final forecasts will be stored in storage/experiments/m4_interpretable/repeat=0,lookback=2,loss=MAPE directory.

    You can of course automate running across all experiments with the following example (assuming BASH):

    for instance in `/bin/ls -d storage/experiments/m4_interpretable/*`; do 
        echo $instance
        make run command=${instance}/command
    done

    If you have resources where the training can be scaled in a "cloud", then consider adding a new target to the Makefile. Below is an example in pseudo-code:

    run-all:
    rsync ${ROOT} ${network-share}
    for instance in $$(ls ${ROOT}/${experiment}); do \
    	cloud submit \
    			--image=${IMAGE} \
    			-v ${network-share}:/experiment \
    			-w /experiment \
    			-e PYTHONPATH=/experiment \
    			-e STORAGE=/experiment/storage \
    			-- \
    			bash -c "`cat ${ROOT}/${experiment}/$${instance}/command`"; \
    done
  6. Get the experiment statistics.

    Note: If the experiment was running in a cloud make sure the results are downloaded into storage/experiments.

    Start JupyterLab make notebook port=<port>

    Run a notebook in notebooks directory for the corresponding experiment to get the experiment's performance statistics.

Citation

If you use N-BEATS in any context, please cite the following paper:

@inproceedings{
  Oreshkin2020:N-BEATS,
  title={{N-BEATS}: Neural basis expansion analysis for interpretable time series forecasting},
  author={Boris N. Oreshkin and Dmitri Carpov and Nicolas Chapados and Yoshua Bengio},
  booktitle={International Conference on Learning Representations},
  year={2020},
  url={https://openreview.net/forum?id=r1ecqn4YwB}
}

This is a collaborative work between Element AI and Mila. Please cite the paper if you use this model or the code.

n-beats's People

Contributors

dmitri-carpov avatar espenha avatar servicenowresearch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

n-beats's Issues

Questions about the MASE.

In the MASE, I find that the shapes of in_sample, out_sample, and forecasting should be time_o or time_in, instead of batch, time_i/o.
Moreover, does the MASE follow the formulation in Section 2 of the paper? The current implementation seems utilize all historical data (instead of the data from 1~T), and the denominator in current implementation does not include future data, i.e. data from T~T+H?

NBeats.forward()

Hi,
In the 'nbeats.py', the class NBeats:

class NBeats(t.nn.Module):

    def __init__(self, blocks: t.nn.ModuleList):
        super().__init__()
        self.blocks = blocks

    def forward(self, x: t.Tensor, input_mask: t.Tensor) -> t.Tensor:

        residuals = x.flip(dims=(1,))
        input_mask = input_mask.flip(dims=(1,))

        forecast = x[:, -1:]
        for i, block in enumerate(self.blocks):
            backcast, block_forecast = block(residuals)
            residuals = (residuals - backcast) * input_mask  
            forecast = forecast + block_forecast
        return forecast

in the forward funtion, why do this operation: ‘residuals = x.flip(dims=(1,))’ ???

Increase N-beats reproducibility by providing pre-computed forecast on evaluated datasets in the original paper

Hi,

I propose that the point forecast of N-BEATS be included in this repo for all the dataset evaluated in the original paper like it has been done for the M4 competition github. This would ease the comparison of the N-beats model with others and increase the visibility of the N-BEATS paper.

Beside reproducing the experimental results presented in the paper, it is often more convenient to rely on precomputed forecast to compare different model on the same dataset. For instance, the M4 competition's github repository provides the point forecast of all the models submitted in compressed csv files which permits comparing the models per individual series. We can evaluate the performance of ES-RNN and FFORMA using different loss functions but not the N-beats model. Thankfully, this is the case for ES-RNN and FFORMA given the execution time to reproduce their forecasts. It would be great if N-BEATS wouldn't fall under the reproducibility exception. The argument holds for the other dataset evaluated.

Great repo btw!

Project dependencies may have API risk issues

Hi, In N-BEATS, inappropriate dependency versioning constraints can cause risks.

Below are the dependencies and version constraints that the project is using

gin-config
fire
matplotlib
numpy
pandas
patool
torch
tqdm
xlrd

The version constraint == will introduce the risk of dependency conflicts because the scope of dependencies is too strict.
The version constraint No Upper Bound and * will introduce the risk of the missing API Error because the latest version of the dependencies may remove some APIs.

After further analysis, in this project,
The version constraint of dependency pandas can be changed to >=0.13.0,<=0.23.4.
The version constraint of dependency tqdm can be changed to >=4.42.0,<=4.64.0.

The above modification suggestions can reduce the dependency conflicts as much as possible,
and introduce the latest version as much as possible without calling Error in the projects.

The invocation of the current project includes all the following methods.

The calling methods from the pandas
pandas.DataFrame.to_csv
pandas.read_csv
pandas.read_excel
pandas.concat
The calling methods from the tqdm
itertools.product
tqdm.tqdm
The calling methods from the all methods
dates.s.datetime.strptime.strftime.map.list.np.unique.dump
layer
collections.OrderedDict
numpy.cos
pandas.concat
numpy.array
datasets.tourism.TourismDataset.download
i.permutations.np.where.raw_data.rstrip
os.stat
tqdm.tqdm
training_values.extend
group_by.forecast_file.summary_filter.experiment_path.os.path.join.glob.tqdm.file.file.pd.read_csv.pd.concat.set_index.groupby
self.build
join
x_mask.x.model.cpu.detach
cmd.write
experiments.model.interpretable
x_mask.x.model.cpu
torch.abs
self.snapshot
values.extend
models.nbeats.GenericBasis
min
os.fsync
numpy.sin
optimizer.state_dict
numpy.where
iter
summary.utils.group_values
enumerate
torch.no_grad
__loss_fn
URL_TEMPLATE.format
test.reset_index.reset_index
i.i.data.sum
model.to.parameters
numpy.mean
permutations.rstrip.split.np.array.astype.rstrip
os.chmod
patoolib.extract_archive
torch.device
value.str.replace
left_indices.append
torch.load
weighted_score.values
torch.save
super.__init__
datasets.tourism.TourismDataset.load
pandas.DataFrame.to_csv
experiments.trainer.trainer
snapshot_manager.register
float
common.sampler.TimeseriesSampler
dir_path.Path.mkdir
pandas.DataFrame
str
os.getenv
groups.extend
torch.mean
row_vector.split.np.array.astype
i.permutations.np.where.raw_data.rstrip.split
x.flip
models.nbeats.TrendBasis
numpy.random.randint
group.lower
datasets.traffic.TrafficDataset.download
common.metrics.mase
shutil.copy
torch.cuda.is_available
training_loss_fn.backward
metric
d.items
model.load_state_dict
datasets.m4.NAIVE2_FORECAST_FILE_PATH.pd.read_csv.values.astype
range
common.torch.losses.smape_2_loss
parsed_values.np.array.astype
numpy.array.dump
datetime.timedelta
i.timedelta.current_date.strftime
itertools.product
os.path.dirname
os.walk
time.time
torch.nn.ModuleList
optimizer.load_state_dict
row_vector.split
os.rename
common.http_utils.download
dataset.dump
urllib.request.urlretrieve
numpy.isnan
numpy.load
snapshot_manager.restore
Exception
self.basis_parameters
training_loss_fn
super
url.split
numpy.abs
snapshot_manager.enable_time_tracking
int
numpy.power
forecasts.extend
test_values.extend
datasets.m4.M4Dataset.download
pandas.read_csv.iterrows
common.sampler.TimeseriesSampler.last_insample_window
list
common.metrics.mape
success_flag.Path.touch
dict.items
pandas.read_csv.set_index
cfg.write
ids.extend
TourismDataset
model.to.to
datasets.traffic.TrafficDataset.load.split_by_date
file_path.os.path.dirname.pathlib.Path.mkdir
torch.nn.Linear
zip
numpy.concatenate
models.nbeats.NBeats
right_indices.append
fire.Fire
torch.optim.Adam
M3Dataset
logging.root.setLevel
raw_line.replace.strip.split
timeseries_dict.values.list.np.array.dump
collections.OrderedDict.values
gin.configurable
models.nbeats.NBeatsBlock
max
s.datetime.strptime.strftime
permutations.rstrip.split.np.array.astype
numpy.append
datasets.m3.M3Dataset.load
pandas.read_csv
len
pandas.read_excel
default_device
TrafficDataset
numpy.prod
test.iloc.astype
dict
datasets.electricity.ElectricityDataset.load
common.torch.ops.default_device
common.torch.ops.divide_no_nan
models.nbeats.SeasonalityBasis
numpy.zeros
datasets.electricity.ElectricityDataset.load.split_by_date
torch.load.items
torch.nn.Parameter
isinstance
torch.nn.utils.clip_grad_norm_
numpy.transpose
common.torch.snapshots.SnapshotManager
sys.stdout.flush
torch.load.keys
numpy.max
os.path.isdir
numpy.sum
input_mask.flip.flip
tempfile.NamedTemporaryFile
M4Dataset
torch.optim.Adam.zero_grad
datasets.m3.M3Dataset.download
numpy.round
train_meta.iloc.astype
numpy.unique
torch.tensor
dataclasses.dataclass
dates.np.array.dump
tempfile.NamedTemporaryFile.flush
self.instance
f.readlines
os.path.basename
open
permutations.rstrip.split
common.torch.losses.mape_loss
common.metrics.smape_1
forecast_file.summary_filter.experiment_path.os.path.join.glob.tqdm.file.file.pd.read_csv.pd.concat.set_index
raw_line.replace.strip
urllib.request.install_opener
sys.stdout.write
horizons.extend
group_by.group_by.forecast_file.summary_filter.experiment_path.os.path.join.glob.tqdm.file.file.pd.read_csv.pd.concat.set_index.groupby.median
round
group_count
logging.info
gin.parse_config_file
sorted
common.torch.losses.mase_loss
urllib.request.build_opener
torch.relu
common.http_utils.url_file_name
glob.glob
x_mask.x.model.cpu.detach.numpy
model.state_dict
round_all
torch.float32.array.t.tensor.to
format
datetime.datetime.strptime
os.path.join
numpy.save
datasets.m4.M4Dataset.load
self.summarize_groups.keys
dates.extend
torch.optim.Adam.step
self.summarize_groups
pathlib.Path
torch.einsum
instance_path.Path.mkdir
self.basis_function
numpy.ceil
map
datasets.traffic.TrafficDataset.load
os.path.isfile
model.to.train
block
experiments.model.generic
datasets.electricity.ElectricityDataset.download
experiments.trainer.trainer.eval
model
raw_line.replace
build_cache
splits.items
tempfile.NamedTemporaryFile.fileno
train.reset_index.reset_index
numpy.array.append
ElectricityDataset
common.metrics.smape_2
numpy.arange
next
numpy.sqrt

@developer
Could please help me check this issue?
May I pull a request to fix it?
Thank you very much.

history_size/window_sampling_limit is shorter than input size

For certain datasets, e.g. Yearly/Quarterly/Monthly M4 datasets, the quantity history_size is set to 1.5, leading window_sampling_limit to be 1.5 times of the horizon length. Yet, the input size could be up to 7 times the horizon length, meaning that during training phase the model mostly observes padding. Is this an issue, and possibly leading to a degradation in performance in these dataset?

Reproducing M4 Results

I am having trouble reproducing the results on the M4 dataset. I am getting the following error when running the notebook for the m4:


ModuleNotFoundError Traceback (most recent call last)
in
1 import pandas as pd
2
----> 3 from summary.m4 import M4Summary
4 from summary.utils import median_ensemble
5

ModuleNotFoundError: No module named 'summary'

When I follow the reproduction steps I run into this error.

Here is my build script.

#!/bin/bash
make init
make dataset
make build config=experiments/m4/interpretable.gin
make run command=storage/experiments/m4_interpretable/repeat=0,lookback=2,loss=MAPE/command

The only variable as input should be the target which is part of time_varying_unknown_reals

My data set has : time_idx, Date, Ticker, Open, high, low, close, Stock split and Dividends.
My Time series data set is:
training = TimeSeriesDataSet(
combined_data[lambda x: x.time_idx <= training_cutoff],
time_idx="time_idx",
target="Close",
group_ids=["Ticker"],
max_encoder_length=60,
max_prediction_length=7,
static_categoricals=["Ticker"],
time_varying_known_reals=["time_idx", "Open", "High", "Low", "Volume", "Stock Splits", "Dividends"],
time_varying_unknown_reals=["Close"],
allow_missing_timesteps=True,
target_normalizer=GroupNormalizer(
groups=["Ticker"], transformation="softplus"
),
add_relative_time_idx=False,
add_target_scales=True,
add_encoder_length=True,
)

I get this error, when I run this :

Define the model with the suggested hyperparameters

model = NBeats.from_dataset(
    training,
    learning_rate=learning_rate,
    hidden_size=hidden_size,
    widths=widths,
    backcast_loss_ratio=backcast_loss_ratio,
    dropout=dropout
)

I see the data set has reals like encoder_length, Close_min, close_scaled and all time varying known reals I gave in the data set.
I tried removing all the reals, still I get the error as number of reals are encoder, and Close related variables. I cannot not give encoder length. Issue arise from this piece of code:

assert (
len(dataset.flat_categoricals) == 0
and len(dataset.reals) == 1
and len(dataset.time_varying_unknown_reals) == 1
and dataset.time_varying_unknown_reals[0] == dataset.target
), "The only variable as input should be the target which is part of time_varying_unknown_reals"

a question abouts the ’generic.gin ‘

In experiments/m4/generic.gin,
`
instance.history_size = {
'Yearly': 1.5,
'Quarterly': 1.5,
'Monthly': 1.5,
'Weekly': 10,
'Daily': 10,
'Hourly': 10
}

instance.iterations = {
'Yearly': 15000,
'Quarterly': 15000,
'Monthly': 15000,
'Weekly': 5000,
'Daily': 5000,
'Hourly': 5000
}
`
How are the above parameters determined?

Cannot run on gpu

I can successfully make run the experiment on tourism dataset on cpu. However, when I use
make run command=storage/experiments/tourism_interpretable/repeat=0,lookback=2,loss=MAPE/command gpu=0

I have the following feedback:
the input device is not a TTY
Makefile:30: recipe for target 'run' failed
make: *** [run] Error 1

Question about hyperparameters

Are there any hyperparameters that are more important than others and do you have recommended ranges? I'm thinking of something similar to this, but for N-BEATS:

image

why does forward of GenericBasis Class in models/nbeats.py return theta instead of FC projection of theta?

class GenericBasis(t.nn.Module):
"""
Generic basis function.
"""
def init(self, backcast_size: int, forecast_size: int):
super().init()
self.backcast_size = backcast_size
self.forecast_size = forecast_size

def forward(self, theta: t.Tensor):
    return theta[:, :self.backcast_size], theta[:, -self.forecast_size:]

is it more reasonable to return a function of theta just like trendBasis and seasonalityBasis?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.