timeseriesAI / tsai

Time series Timeseries Deep Learning Machine Learning Python Pytorch fastai | State-of-the-art Deep Learning library for Time Series and Sequences in Pytorch / fastai
https://timeseriesai.github.io/tsai/
Apache License 2.0
5.16k stars 644 forks source link

multi-horizon forecasting #591

Closed CHonChou closed 1 year ago

CHonChou commented 2 years ago

Hello, I get an error when I use many-Plus models for multi-horizon forecasting: TypeError: init() got an unexpected keyword argument 'custom_head' my code as follows:

image

those models include: (For multi-horizon forecast, have no custom_head)  FCN  FCNPlus  InceptionTime  MLP  RNN  LSTM  GRU  RNN_FCN  MRNN_FCN  LSTM_FCN  MLSTM_FCN  GRU_FCN  MGRU_FCN  ResCNN  ResNet  ResNetPlus  TCN  XceptionTime  XceptionTimePlus  ResNetPlus - need seq_len  RNN_FCNPlus - cannot multiply  MRNN_FCNPlus - cannot multiply  LSTM_FCNPlus - cannot multiply  MLSTM_FCNPlus - cannot multiply  GRU_FCNPlus - cannot multiply  MGRU_FCNPlus - cannot multiply

bale-go commented 1 year ago

Unfortunately, TSiTPlus does not work for multi-step forecasting either. Despite being mentioned as a working model in the description.

MOREDataset commented 1 year ago

TSTPlus works fine for me. The only problems I get are with mWDNPlus and XceptionTimePlus..

bale-go commented 1 year ago

TSTPlus works for me too. It is the TSiTPlus that does not run.

bale-go commented 1 year ago

I solved the issue with TSiTPlus. The function "forward" in class "_TSiTBackbone" needed an extra line (x = x.contiguous()) after transposing and before returning the tensor x. I can make a PR with the fix if you'd like.

oguiza commented 1 year ago

Hi @CHonChou, @MOREDataset, @bale-go, could any of you please post the full stack trace of this type of error?

bale-go commented 1 year ago

Minimal reproducible example for TSiTPlus using tsai-0.3.2:

from tsai.all import *
ts = get_forecasting_time_series("Sunspots").values
X, y = SlidingWindow(60, horizon=3)(ts)
splits = TimeSplitter(235)(y) 
batch_tfms = TSStandardize()
fcst = TSForecaster(X, y, splits=splits, path='models', batch_tfms=batch_tfms, bs=512, arch=TSiTPlus, metrics=mae, cbs=ShowGraph())
fcst.fit_one_cycle(50, 1e-3)
fcst.export("fcst.pkl")

Output:

Dataset: Sunspots downloading data... ...data downloaded. Path = data/forecasting/Sunspots.csv use_token set to False as c_out == 1 epoch train_loss valid_loss mae time
Traceback (most recent call last):--------------------------------------------------------------------------------| 0.00% [0/5 00:00<?] File "mre.py", line 7, in fcst.fit_one_cycle(50, 1e-3) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastai/callback/schedule.py", line 119, in fit_one_cycle self.fit(n_epoch, cbs=ParamScheduler(scheds)+L(cbs), reset_opt=reset_opt, wd=wd, start_epoch=start_epoch) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastai/learner.py", line 256, in fit self._with_events(self._do_fit, 'fit', CancelFitException, self._end_cleanup) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastai/learner.py", line 193, in _withevents try: self(f'before{event_type}'); f() File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastai/learner.py", line 245, in _do_fit self._with_events(self._do_epoch, 'epoch', CancelEpochException) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastai/learner.py", line 193, in _withevents try: self(f'before{event_type}'); f() File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastai/learner.py", line 239, in _do_epoch self._do_epoch_train() File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastai/learner.py", line 231, in _do_epoch_train self._with_events(self.all_batches, 'train', CancelTrainException) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastai/learner.py", line 193, in _withevents try: self(f'before{event_type}'); f() File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastai/learner.py", line 199, in all_batches for o in enumerate(self.dl): self.one_batch(o) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/tsai/learner.py", line 39, in one_batch self._with_events(self._do_one_batch, 'batch', CancelBatchException) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastai/learner.py", line 193, in _withevents try: self(f'before{event_type}'); f() File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastai/learner.py", line 205, in _do_one_batch self.pred = self.model(self.xb) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1110, in _call_impl return forward_call(*input, kwargs) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/torch/nn/modules/container.py", line 141, in forward input = module(input) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1110, in _call_impl return forward_call(*input, *kwargs) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/torch/nn/modules/container.py", line 141, in forward input = module(input) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1110, in _call_impl return forward_call(input, kwargs) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastai/layers.py", line 71, in Flatten return TensorBase(x.view(-1) if self.full else x.view(x.size(0), -1)) RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.

bale-go commented 1 year ago

With XceptionTimePlus:

from tsai.all import *
ts = get_forecasting_time_series("Sunspots").values
X, y = SlidingWindow(60, horizon=3)(ts)
splits = TimeSplitter(235)(y) 
batch_tfms = TSStandardize()
fcst = TSForecaster(X, y, splits=splits, path='models', batch_tfms=batch_tfms, bs=512, arch=XceptionTimePlus, metrics=mae, cbs=ShowGraph())
fcst.fit_one_cycle(50, 1e-3)
fcst.export("fcst.pkl")

Dataset: Sunspots downloading data... ...data downloaded. Path = data/forecasting/Sunspots.csv Traceback (most recent call last): File "mre.py", line 6, in fcst = TSForecaster(X, y, splits=splits, path='models', batch_tfms=batch_tfms, bs=512, arch=XceptionTimePlus, metrics=mae, cbs=ShowGraph()) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/tsai/tslearner.py", line 175, in init model = build_ts_model(arch, dls=dls, device=device, verbose=verbose, pretrained=pretrained, weights_path=weights_path, File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/tsai/models/utils.py", line 172, in build_ts_model model = arch(c_in, c_out, arch_config, kwargs).to(device=device) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/tsai/models/XceptionTimePlus.py", line 77, in init backbone = XceptionBlockPlus(c_in, nf, coord=coord, norm=norm, kwargs) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastcore/meta.py", line 40, in call res.init(*args,*kwargs) File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/tsai/models/XceptionTimePlus.py", line 59, in init self.xception.append(XceptionModulePlus(n_in, n_out, coord=coord, File "....../miniconda3/envs/tsais/lib/python3.8/site-packages/fastcore/meta.py", line 40, in call res.init(args,kwargs) TypeError: init() got an unexpected keyword argument 'custom_head'

MOREDataset commented 1 year ago

Hi @oguiza, I was wondering if there are any updates on these issues..

oguiza commented 1 year ago

No, not yet. But I'll prioritize it and will try to fix it within the next few days.

oguiza commented 1 year ago

Hi @CHonChou, @bale-go, @MOREDataset, I have an update on this issue. The models that currently support multi-horizon forecasting are:

pip install git+https://github.com/timeseriesAI/tsai.git

All of them work with this code for training:

from tsai.basics import *

fcst_history = 60
fcst_horizon = 3

df = get_forecasting_time_series("Sunspots")
X, y = prepare_forecasting_data(df, fcst_history=fcst_history, fcst_horizon=fcst_horizon, dtype=np.float32)
splits = get_forecasting_splits(df, fcst_history=fcst_history, fcst_horizon=fcst_horizon, test_size=235)
batch_tfms = TSStandardize()
fcst = TSForecaster(X, y, splits=splits, path='models', batch_tfms=batch_tfms, bs=512, arch="TSiTPlus", metrics=mae, cbs=ShowGraph())
fcst.fit_one_cycle(50, 1e-3)
fcst.export("fcst.pkl")

(the advantage of passing the arch as a string is that you don't need to import it beforehand).

And this one for inference:

from tsai.inference import load_learner

fcst = load_learner("models/fcst.pkl")
preds, *_ = fcst.get_X_preds(X[splits[1]])

If you try them let me know if you find any issues.

MOREDataset commented 1 year ago

Hi @oguiza, THANK YOU VERY MUCH for your continuous hard work on this amazing library! I can confirm that the models TSTPlus + LSTMPlus + MiniRocketPlus + mWDNPlus + XCMPlus + GRUPlus + XceptionTimePlus + TSiTPlus work now for multivariate multihorizon forecasting! However, there is a problem with PatchTST yielding the following error: PatchTST image

oguiza commented 1 year ago

Hi @MOREDataset, I would need a code snippet to reproduce the issue. This code works for me:

from tsai.basics import *

fcst_history = 60
fcst_horizon = 3

df = get_forecasting_time_series("Sunspots")
X, y = prepare_forecasting_data(df, fcst_history=fcst_history, fcst_horizon=fcst_horizon, dtype=np.float32)
splits = get_forecasting_splits(df, fcst_history=fcst_history, fcst_horizon=fcst_horizon, test_size=235)
batch_tfms = TSStandardize()
fcst = TSForecaster(X, y, splits=splits, path='models', batch_tfms=batch_tfms, bs=512, arch="PatchTST", metrics=mae, cbs=ShowGraph())
fcst.fit_one_cycle(50, 1e-3)
fcst.export("fcst.pkl")
MOREDataset commented 1 year ago

HI @oguiza,

Here is the snippet of the code I used:

models = {'PatchTST'}#'TSTPlus','XCMPlus','LSTMPlus','GRUPlus','MiniRocketPlus'}
for key in models:
    exec('model_name = {}'.format(key))  
    splits = get_splits(y_train, valid_size=.2, stratify=False, random_state=42, shuffle=True)
    tfms  = [None, [ToFloat(), ToNumpyTensor()]]
    dsets = TSDatasets(X_train, y_train, tfms=tfms, splits=splits, inplace=True)
    dls   = TSDataLoaders.from_dsets(dsets.train, dsets.valid, bs=[64, 128])    
    par = {}
    #Model learning
    learn = TSForecaster(X_train, y_train, splits=splits, tfms=tfms, bs=1042, arch=model_name, arch_config=par, metrics=[mae, rmse], cbs=[ShowGraph(),SaveModel()])
    lr = learn.lr_find()   
    learning_rate = lr.valley
    learn.fit_one_cycle(freeze_epoch, learning_rate)

I don't see any difference between both codes though!

oguiza commented 1 year ago

Hi @MOREDataset, I've tested your code with some dummy data and it works AS LONG AS the input and output have the same number of features/ channels:

from tsai.models.PatchTST import PatchTST
X_train = np.random.rand(1000, 3, 100)
y_train = np.random.rand(1000, 3, 20)
freeze_epoch = 1
models = {'PatchTST'}#'TSTPlus','XCMPlus','LSTMPlus','GRUPlus','MiniRocketPlus'}
for key in models:
    exec('model_name = {}'.format(key))  
    splits = get_splits(y_train, valid_size=.2, stratify=False, random_state=42, shuffle=True)
    tfms  = [None, [ToFloat(), ToNumpyTensor()]]
    dsets = TSDatasets(X_train, y_train, tfms=tfms, splits=splits, inplace=True)
    dls   = TSDataLoaders.from_dsets(dsets.train, dsets.valid, bs=[64, 128])    
    par = {}
    #Model learning
    learn = TSForecaster(X_train, y_train, splits=splits, tfms=tfms, bs=1042, arch=model_name, arch_config=par, metrics=[mae, rmse], cbs=[ShowGraph(),SaveModel()])
    lr = learn.lr_find()   
    learning_rate = lr.valley
    learn.fit_one_cycle(freeze_epoch, learning_rate)

However, it will fail if you use something like this:

X_train = np.random.rand(1000, 3, 100)
y_train = np.random.rand(1000, 1, 20)

The reason for that is that this is the way the original model works. I have in mind a PatchTSTPlus that will allow the option to predict time series with different # features. But it still is not available. So I don't consider this a bug. This works as designed. There's a separate issue (#713) to address this enhancement. Based on that, I'd propose that this issue is closed.

Note: @MOREDataset , your code could be simplified if you use something like this:

X_train = np.random.rand(1000, 3, 100)
y_train = np.random.rand(1000, 3, 20)
splits = get_splits(y_train, valid_size=.2, stratify=False, random_state=42, shuffle=True)
tfms  = [None, [ToFloat(), ToNumpyTensor()]]
learn = TSForecaster(X_train, y_train, splits=splits, tfms=tfms, bs=1042, arch="PatchTST", metrics=[mae, rmse], cbs=[ShowGraph(),SaveModel()])
lr = learn.lr_find()   
learning_rate = lr.valley
learn.fit_one_cycle(1, learning_rate)

You can pass a string as an arch to TSClassifier, TSRegressor, and TSForecaster.

MOREDataset commented 1 year ago

Hi @oguiza,

Thank you for such a detailed response. Yes, I doubled checked the size of the input, and the X_train and y_train have different features. image l will make sure to follow your highlighted issue. Thank you again.

oguiza commented 1 year ago

Closed as the initial issue is resolved.