New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BUG: ETS multiplicative error models log-likelihood calculation #9137
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea but can simplify a bit.
# are replaced with 10^-32 (a very small number) and we take | ||
# the absolute of yhat to avoid computing the log of negative | ||
# numbers. | ||
yhat[yhat == 0] = 1e-32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really should leave the <=. == 0 is likely too strict, and if it really is ==0, then <=0 will catch all cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fun corner cases. FWIW, I don't think you need the clipping at all anymore. It's almost certainly not being triggered here, and if you do switch it to clip the negatives, then it becomes unstable again.
# the absolute of yhat to avoid computing the log of negative | ||
# numbers. | ||
yhat[yhat == 0] = 1e-32 | ||
logL -= np.sum(np.log(np.abs(yhat))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think reflecting negatives is a good idea. Better to replace with small numbers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please note that the absolute value is not there to avoid negative numbers, but instead arises mathematically:
If
So, it is necessary to have it there for the correct calculation of the log-likelihood. The derivations are based on the Section 11.1 here: https://openforecast.org/adam/ADAMETSEstimationLikelihood.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for reviewing the PR @bashtage and thanks for the additional explanation on the likelihood derivation @config-i1.
Based on @config-i1's explanation and the other implementations, I think it makes sense to keep the np.abs(yhat)
do you agree @bashtage?
Hello @ltsaprounis! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found: There are currently no PEP 8 issues detected in this Pull Request. Cheers! 🍻 Comment last updated at 2024-03-19 11:20:34 UTC |
Made some changes to the comment for the log-likelihood calculation based on Ivan's comments above. Let me know what you think @bashtage. |
Related to #7331
tl;dr: This PR fixes a small issue in the likelihood calculation of ETS models with multiplicative errors, that caused them to have low ICs and unreasonable forecasts.
It appears that the problem raised in #7331 (ETS(MAM) giving unreasonable forecasts) is still there. The fix for the issue had the desired effect for the example time series in the issue but unfortunately the exploding forecasts can still appear for different time series like the one below:
Code to reproduce the plots
Moreover, the log-likelihood of the unreasonable forecasts is unreasonably high making the AutoETS implementations (e.g. sktime, aeon) that are based on ETS model selection using information criteria (e.g. AIC) select the unreasonable forecasts.
The problem comes from the Log-Likelihood calculation for ETS models with multiplicative errors, and more specifically the fail-safe mechanism for negative or zero yhat values here:
statsmodels/statsmodels/tsa/exponential_smoothing/ets.py
Lines 1162 to 1169 in 23faea3
This way is not consistent with the literature (see screenshot below):
screenshot from the online version of Svetunkov, I. (2023). Forecasting and Analytics with the Augmented Dynamic Adaptive Model (ADAM) (1st ed.). Chapman and Hall/CRC. https://doi.org/10.1201/9781003452652
In addition, it's not consistent with any other ETS implementations, where they all seem to follow the formula above for the last term of the log-likelihood, namely log(| yhat |). Below is the relevant code from the other OSS implementations I checked:
All the implementations listed above give similar Log-Likelihood values and AIC as well as reasonable forecasts. The AIC for ETS and this time series is best for the EST(ANN) model hence all the automatic model selection methods from the packages above select this one. Below are the forecasts from these different packages for ETS(MAM) for the same time "problematic" series:
(R) forecast
(R) smooth
(Python) nixtla - statsforecast
This PR changes the code for the log-likelihood for multiplicative error models so that it's consistent with the literature and the other ETS implementations. This results in similar forecasts like above:
Here are the values for each the log-likelihood and the AIC for ETS(MAM) and that series for each implementation, including the change in statsmodels proposed in this PR.
PS: Many many many thanks to @config-i1 for helping me to figure this one out! 🙏