Prophet: Limiting PyStan's max iterations

Created on 16 Sep 2019  路  7Comments  路  Source: facebook/prophet

Hi,

I am forecasting several thousand time series each of the same size. Most of them look "clean" with proper seasonality and trend, but some few are outliers and have really small values like this:
Acct Product y ds
184162 559394010 2DA 0 2019-08-21
184163 559394010 2DA 0 2019-08-22
184164 559394010 2DA 0 2019-08-23
184165 559394010 2DA 0 2019-08-24
184166 559394010 2DA 0 2019-08-25
184167 559394010 2DA 0 2019-08-26
184168 559394010 2DA 0 2019-08-27
184169 559394010 2DA 0 2019-08-28
184170 559394010 2DA 0 2019-08-29
184171 559394010 2DA 0 2019-08-30
184172 559394010 2DA 0 2019-08-31
184173 559394010 2DA 0 2019-09-01
184174 559394010 2DA 0 2019-09-02
184175 559394010 2DA 0 2019-09-03
184176 559394010 2DA 0 2019-09-04
184177 559394010 2DA 0 2019-09-05
184178 559394010 2DA 0 2019-09-06
184179 559394010 2DA 0 2019-09-07
184180 559394010 2DA 0 2019-09-08
184181 559394010 2DA 0 2019-09-09
184182 559394010 2DA 1 2019-09-10
184183 559394010 2DA 0 2019-09-11

where there are several zeroes and a single 1, or something similar. When fitting this time series, PyStan will iterate for over a minute, going all the way to Iteration 10000, then stopping.

Iteration 9987. Log joint probability = 1480.24. Improved by 0.0011447.
Iteration 9988. Log joint probability = 1480.24. Improved by 0.000244781.
Iteration 9989. Log joint probability = 1480.25. Improved by 0.000180848.
Iteration 9990. Log joint probability = 1480.25. Improved by 0.000175787.
Iteration 9991. Log joint probability = 1480.25. Improved by 0.000149703.
Iteration 9992. Log joint probability = 1480.25. Improved by 0.000208052.
Iteration 9993. Log joint probability = 1480.25. Improved by 0.00014427.
Iteration 9994. Log joint probability = 1480.25. Improved by 0.00021152.
Iteration 9995. Log joint probability = 1480.25. Improved by 0.000210965.
Iteration 9996. Log joint probability = 1480.25. Improved by 5.59633e-005.
Iteration 9997. Log joint probability = 1480.25. Improved by 0.00010602.
Iteration 9998. Log joint probability = 1480.25. Improved by 9.73031e-005.
Iteration 9999. Log joint probability = 1480.25. Improved by 5.48104e-005.
Iteration 10000. Log joint probability = 1480.25. Improved by 6.20919e-005.

After fitting, the model predicts 0 (as expected). Is there some parameter I can pass into the PyStan model to cap it at, say, 300? Nearly all of my models take 200 iterations at most to fit.

Most helpful comment

Passing iter as keyword argument when calling fit() like

Prophet(...).fit(df, iter=250)

will update iter in the mentioned args dict used for model sampling and optimizing:

def fit(self, df, **kwargs):
    """Fit the Prophet model.
    ...
    kwargs: Additional arguments passed to the optimizing or sampling
            functions in Stan.
    Returns
    -------
    The fitted Prophet object.
    """
    ...
    elif self.mcmc_samples > 0:
            args = dict(
                data=dat,
                init=stan_init,
                iter=self.mcmc_samples,
            )
    args.update(kwargs)
    self.stan_fit = model.sampling(**args)
    ...
    else:
            args = dict(
                data=dat,
                init=stan_init,
                algorithm='Newton' if dat['T'] < 100 else 'LBFGS',
                iter=1e4,
            )
    args.update(kwargs)
    try:
        self.stan_fit = model.optimizing(**args)
    ...

https://github.com/facebook/prophet/blob/master/python/fbprophet/forecaster.py#L1022-L1046
https://github.com/facebook/prophet/blob/master/python/fbprophet/forecaster.py#L1112-L1134

Still wondering whether this is the intended & correct way of handling this...

All 7 comments

I've found the following in forecaster.py:

args = dict(
                data=dat,
                init=stan_init,
                algorithm='Newton' if dat['T'] < 100 else 'LBFGS',
                iter=250,
            )

Changing iterto 250 seems to cap it, but I am curious what this value actually does. Are there any consequences of clipping iterations, assuming they are outlier cases like the above? From the PyStan docs I found that iter is a "Positive integer specifying how many iterations for each chain including warmup." What exactly does that mean?

This is for the multiple-try Metropolis solver. iter would be the number of iterations the Metropolis solver will have to do before a proposal get accepted (convergence) or rejected. This number will be applied to each chain (4 by default). My guess if you set this too low you'll run into non-convergence issues. I've been using 300 and never ran into an issue.

Thanks!

I've found the following in forecaster.py:

args = dict(
                data=dat,
                init=stan_init,
                algorithm='Newton' if dat['T'] < 100 else 'LBFGS',
                iter=250,
            )

Changing iterto 250 seems to cap it, but I am curious what this value actually does. Are there any consequences of clipping iterations, assuming they are outlier cases like the above? From the PyStan docs I found that iter is a "Positive integer specifying how many iterations for each chain including warmup." What exactly does that mean?

How do you pass this args to fbprophet?

It is not implemented currently. I just manually edited forecaster.py and set the value to 250, but if you wanted to pass this value in as an arg, you could add a new variable iter into the __init__ method of Prophet (in forecaster.py), and change the above code to

args = dict(
                data=dat,
                init=stan_init,
                algorithm='Newton' if dat['T'] < 100 else 'LBFGS',
                iter=self.iter,
            )

Passing iter as keyword argument when calling fit() like

Prophet(...).fit(df, iter=250)

will update iter in the mentioned args dict used for model sampling and optimizing:

def fit(self, df, **kwargs):
    """Fit the Prophet model.
    ...
    kwargs: Additional arguments passed to the optimizing or sampling
            functions in Stan.
    Returns
    -------
    The fitted Prophet object.
    """
    ...
    elif self.mcmc_samples > 0:
            args = dict(
                data=dat,
                init=stan_init,
                iter=self.mcmc_samples,
            )
    args.update(kwargs)
    self.stan_fit = model.sampling(**args)
    ...
    else:
            args = dict(
                data=dat,
                init=stan_init,
                algorithm='Newton' if dat['T'] < 100 else 'LBFGS',
                iter=1e4,
            )
    args.update(kwargs)
    try:
        self.stan_fit = model.optimizing(**args)
    ...

https://github.com/facebook/prophet/blob/master/python/fbprophet/forecaster.py#L1022-L1046
https://github.com/facebook/prophet/blob/master/python/fbprophet/forecaster.py#L1112-L1134

Still wondering whether this is the intended & correct way of handling this...

Passing iter as keyword argument when calling fit() like

Prophet(...).fit(df, iter=250)

will update iter in the mentioned args dict used for model sampling and optimizing:

def fit(self, df, **kwargs):
    """Fit the Prophet model.
    ...
    kwargs: Additional arguments passed to the optimizing or sampling
            functions in Stan.
    Returns
    -------
    The fitted Prophet object.
    """
    ...
    elif self.mcmc_samples > 0:
            args = dict(
                data=dat,
                init=stan_init,
                iter=self.mcmc_samples,
            )
    args.update(kwargs)
    self.stan_fit = model.sampling(**args)
    ...
    else:
            args = dict(
                data=dat,
                init=stan_init,
                algorithm='Newton' if dat['T'] < 100 else 'LBFGS',
                iter=1e4,
            )
    args.update(kwargs)
    try:
        self.stan_fit = model.optimizing(**args)
    ...

https://github.com/facebook/prophet/blob/master/python/fbprophet/forecaster.py#L1022-L1046
https://github.com/facebook/prophet/blob/master/python/fbprophet/forecaster.py#L1112-L1134

Still wondering whether this is the intended & correct way of handling this...

thanks man!

Was this page helpful?
0 / 5 - 0 ratings