facebook / prophet

Tool for producing high quality forecasts for time series data that has multiple seasonality with linear or non-linear growth.
https://facebook.github.io/prophet
MIT License
18.54k stars 4.54k forks source link

Hyperparameter Tuning for additional_seasonality #2060

Open sccrbrg opened 3 years ago

sccrbrg commented 3 years ago

I have a feeling this is more of a basic python question, but I'm struggling to find an answer on how to reference these additive regression components.

The FB Prophet documentation says to build a parameter grid with all of the setting configurations that you want to pass iteratively into the model to determine the combination that generates the lowest amount of error, which I have done (partially):

param_grid = {  
                'changepoint_prior_scale': [[0.005, .01, 0.05, 0.5, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80 ,90, 100],
                'changepoint_range': [0.8, 0.9],
                'holidays_prior_scale':[[0.005, .01, 0.05, 0.5, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80 ,90, 100]],
                'seasonality_mode': ['multiplicative', 'additive'],
                'growth': ['linear', 'logistic'],
               }

The documentation only explains how to hyperparameter tune the standard python model features, there are no examples for how to pass iterative parameters for "added" regression features that the Prophet model supports.

Here's an example of my relevant code:

M = Prophet(
    growth='linear',
    #interval_width=0.80,
    seasonality_mode= 'multiplicative',
    daily_seasonality=False,
    weekly_seasonality=False,
    yearly_seasonality=False,
    holidays=Holidays
    ).add_seasonality(
        name='monthly',
        period=30.5,
        fourier_order=50,#25
        prior_scale=20
    ).add_seasonality(
        name='daily',
        period=1,
        fourier_order=70,#25
        prior_scale=20
    ).add_seasonality(
        name='weekly',
        period=7,
        fourier_order=50,
        prior_scale=60
    ).add_seasonality(
        name='yearly',
        period=365.25,
        fourier_order= 30)

I'm wondering how to appropriately refer to something like "Monthly Fourier_Order" and "Monthly Prior_Scale" in my parameter grid. I tried Monthly.fourier_order and it didn't work.

Any help would be appreciated.

sccrbrg commented 3 years ago

Well, I'm going to try something like this and see if it works. I ran something similar but took over 12 hours to run and wanted to make some changes... so rerunning now.


param_grid = {  

                'changepoint_prior_scale': [0.005, .01, 0.05, 0.5, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80 ,90, 100],
                'changepoint_range': [0.8, 0.9],
                'seasonality_mode': ['multiplicative', 'additive'],
                'growth': ['linear', 'logistic'],
                'monthy_fourier': [0.005, .01, 0.05, 0.5, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80 ,90, 100],
                'monthy_prior_scale': [0.005, .01, 0.05, 0.5, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80 ,90, 100],
                'daily_fourier': [0.005, .01, 0.05, 0.5, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80 ,90, 100],
                'daily_prior_scale': [0.005, .01, 0.05, 0.5, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80 ,90, 100],
                'weekly_fourier': [0.005, .01, 0.05, 0.5, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80 ,90, 100],
                'weekly_prior_scale': [0.005, .01, 0.05, 0.5, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80 ,90, 100],
                'yearly_fourier': [0.005, .01, 0.05, 0.5, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80 ,90, 100],
                'yearly_prior_scale': [0.005, .01, 0.05, 0.5, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80 ,90, 100]
              }

# Generate all combinations of parameters
all_params = [dict(zip(param_grid.keys(), v)) for v in itertools.product(*param_grid.values())]

mape = []  # Store the MAPEs for each params here

# Use cross validation to evaluate all parameters
for params in all_params:
    m = Prophet(
        changepoint_prior_scale = all_params['changepoint_prior_scale'],
        changepoint_range = all_params['changepoint_range'],
        seasonality_mode = all_params['seasonality_mode'],
        growth = all_params['growth'],
        holidays=Holidays,
        ).add_seasonality(
            name='monthly',
            period=30.5,
            fourier_order = all_params['monthly_fourier'],
            prior_scale = all_params['monthly_prior_scale']
        ).add_seasonality(
            name='daily',
            period=1,
            fourier_order = all_params['daily_fourier'],
            prior_scale = all_params['daily_prior_scale']
        ).add_seasonality(
            name='weekly',
            period=7,
            fourier_order = all_params['weekly_fourier'],
            prior_scale = all_params['weekly_prior_scale']
        ).add_seasonality(
            name='yearly',
            period=365.25,
            fourier_order = all_params['yearly_fourier'],
            prior_scale = all_params['yearly_prior_scale']).fit(cv_data) 

Any feedback from someone that knows the answer in the meantime would be great.

tcuongd commented 3 years ago

I think what you've done is essentially it.

Perhaps a nicer way to code it would be to wrap the creation of the Prophet model into a function, but it'll do exactly the same thing as you've got there anyways.

For your hyperparameter optimisation, if you're just using yhat - y (or something similar) as the objective, a way to speed things up would be to set uncertainty_samples=0 when creating the Prophet model (this is used to output yhat_lower and yhat_upper, but you won't need to use them).