Nixtla / neuralforecast

Scalable and user friendly neural :brain: forecasting algorithms.
https://nixtlaverse.nixtla.io/neuralforecast
Apache License 2.0
2.98k stars 342 forks source link

AutoTFT Error with Exogenous Features #943

Closed PotosnakW closed 5 months ago

PotosnakW commented 6 months ago

What happened + What you expected to happen

When training TFT model with exogenous features, an error occurs (below). This error does not occur if exogenous variables are 'None' (eg., 'hist_exog_list'=None). Other models (NHITS, LSTM, MLP, TCN, RNN) do not have this issue.

File "/neuralforecast/neuralforecast/common/_base_model.py", line 55, in _get_temporal_exogenous_cols set(temporal_cols.tolist()) & set(self.hist_exog_list + self.futr_exog_list) TypeError: can only concatenate tuple (not "list") to tuple

Code is provided to replicate the error.

Versions / Dependencies

neuralforecast==1.7.0 (all recent updates pulled)

OS: Springdale Open Enterprise Linux (--> based on Red Hat Enterprise Linux) Browser: N/A Version: 8.7 Moderna

Reproduction script

import torch
import pandas as pd
from datasetsforecast.long_horizon import LongHorizon

from ray import tune
from ray.tune.search.hyperopt import HyperOptSearch

from neuralforecast.auto import AutoRNN, AutoLSTM, AutoTCN, AutoTFT
from neuralforecast.core import NeuralForecast
from neuralforecast.losses.pytorch import MAE

import logging
logging.getLogger("pytorch_lightning").setLevel(logging.WARNING)

def main():
    Y_df = pd.read_csv('/longhorizon/datasets/ili/M/df_y.csv)
    Y_df['ds'] = pd.to_datetime(Y_df['ds'])

    # add exogenous variable to test
    Y_df['exog1'] = 3

    n_time = len(Y_df.ds.unique())
    val_size = int(.2 * n_time)
    test_size = int(.2 * n_time)
    horizon = 96

    # compare tft to lstm baseline
    lstm_config = {
        "hist_exog_list":['exog1'],
        "learning_rate": tune.choice([1e-3]),
        "max_steps": tune.choice([4]),   
        "val_check_steps": tune.choice([2]),
        "input_size": tune.choice([2 * horizon]),
        "encoder_n_layers":tune.choice([2]),
        "encoder_hidden_size":tune.choice([128]),
        "context_size":tune.choice([10]),
        "decoder_hidden_size":tune.choice([128]),
        "decoder_layers":tune.choice([2]),
        "random_seed": tune.choice([1, 2, 3, 4, 5]),
    }

    tft_config = {
        "hist_exog_list":['exog1'],
        "learning_rate": tune.choice([1e-3]),
        "max_steps": tune.choice([4]),   
        "val_check_steps": tune.choice([2]),
        "input_size": tune.choice([2 * horizon]),                                 
        'hidden_size': tune.choice([256, 512]),
        'dropout': tune.choice([0]),
        'attn_dropout': tune.choice([0]),
        "random_seed": tune.choice([1, 2, 3, 4, 5]),
    }

    models = [AutoLSTM(h=horizon,
                       loss=MAE(),
                           config=lstm_config,
                           search_alg=HyperOptSearch(),
                           num_samples=1),
              AutoTFT(h=horizon,
                      loss=MAE(),
                      config=tft_config,
                      search_alg=HyperOptSearch(),
                      num_samples=1),
             ]

    fcst = NeuralForecast(models=models, freq='7D') #freq='15min'

    fcst_df = fcst.cross_validation(df=Y_df, val_size=val_size,test_size=test_size, n_windows=None)

if __name__=='__main__':
    print('testing')
    main()

Issue Severity

High: It blocks me from completing my task.

jmoralez commented 6 months ago

Thanks for the excellent report. If anyone wants to pick this up the fix is to provide those values to the parent class init (BaseWindows) as is done for NHITS: https://github.com/Nixtla/neuralforecast/blob/37717f43fabcd9c502dc5f0ef089013c6adcc265/neuralforecast/models/nhits.py#L285-L287

which makes sure the inputs are actually lists: https://github.com/Nixtla/neuralforecast/blob/37717f43fabcd9c502dc5f0ef089013c6adcc265/neuralforecast/common/_base_windows.py#L119-L121

instead of manually doing it here https://github.com/Nixtla/neuralforecast/blob/37717f43fabcd9c502dc5f0ef089013c6adcc265/neuralforecast/models/tft.py#L496-L498