Skip to content
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

Refactor/backtesting #125

Merged
merged 91 commits into from
Sep 17, 2020
Merged

Refactor/backtesting #125

merged 91 commits into from
Sep 17, 2020

Conversation

guillaumeraille
Copy link
Contributor

Fixes #DARTS-133.

Summary

Contain a proposal to refactor backtesting.

Add reviews and comments directly to PROPOSAL.md

Other Information

@hrzn
Copy link
Contributor

hrzn commented Jul 6, 2020

That looks very good @guillaumeraille
+1 for having model.backtest() methods and +1 for the submodule darts.model_selection
Some very minor comments that I think of now: I think we could find better names for fcast_horizon_n and explore_models().

PROPOSAL.md Outdated

```python
model = ExponentialSmoothing()
historical_forecast = model.backtest(series, pd.Timestamp('19550101'), fcast_horizon_n=3, verbose=True))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be returned here? A pandas series or a new "Backtesting" object with some properties?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A time series containing the forecast values for series, when successively applying the specified model with the specified forecast horizon

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But my favorite is the next use case example where it also directly return the residuals so that we can remove the forecast_residual method

PROPOSAL.md Outdated
We could also add the gridSearch logic directly on the model class as a `classmethod`:

```python
best_model = ExponentialSmoothing.gridsearch(series, fcast_horizon...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this idea, as for some models, we must perform this search to find the best model (theta and ETS are good example, and I had to do one for 4Theta)
One other option to do the gridsearch would be to compare the fittedvalues when it exists, instead of the results of a backtest (faster and should assess the performance of the model as well)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in that case we could create a dedicated method for that on the model as well (cant be a classmethod if it needs some fitted value) what do you think ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it seems more appropriate. Moreover that the fittedvalues call is not necessarily consistent between models

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that's a possible solution to https://github.com/unit8co/darts/pull/123/files#r453315099

PROPOSAL.md Outdated
forcasting anyway:

```python
historical_forecast, residuals = model.backtest(series, pd.Timestamp('19550101'), fcast_horizon_n=3, verbose=True))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it, definitely more elegant. My only concern is whether this is an intuitive way to get to the residuals from the perspective of a new user.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an other possibility would be to store residuals on the model and retrieve it on request but I believe residuals shouldn't belong to the model

PROPOSAL.md Outdated
The main idea behind this proposal is to refactor `backtesting.py` by implementing it directly in the model
class. Here would be a quick list of changes proposed:

- `backtest_forecasting`, `backtest_regression`, `backtest_gridsearch` -> moved in corresping model methods
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think backtest_gridsearch could be made to a static method of ForecastingModel? (Since it takes a class, and not an instance as argument)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about having an abstract method ForecastingModel.hyperparams() that returns the params dict that backtest_gridsearch() would iterate over? This way model implementers would just have to implement this one method for the model to be grid-searchable. @guillaumeraille @pennfranc wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a bad idea but I think the params dict is more dataset / application dependent hence unkown at model implementation time dont you think ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right that's true. What I had in mind was more for this method to return decent default hyper-params, but we would still need to accept user-defined parameters.

PROPOSAL.md Outdated
---
## Progress

- [x] backtest_forcasting moved in model methods
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1


def _backtest_build_fit_and_predict_kwargs(self,
target_indices: Optional[List[int]],
component_index: Optional[int],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't seem to be used

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean because it is overriden? It is used as default and not rewritten in the multivariate case so used

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed, I was talking about component_index that is apparently not used in this method.

@pennfranc pennfranc marked this pull request as ready for review September 8, 2020 08:22
darts/models/forecasting_model.py Outdated Show resolved Hide resolved
darts/models/forecasting_model.py Show resolved Hide resolved
darts/models/forecasting_model.py Outdated Show resolved Hide resolved
darts/models/forecasting_model.py Outdated Show resolved Hide resolved
darts/models/forecasting_model.py Outdated Show resolved Hide resolved
darts/models/forecasting_model.py Outdated Show resolved Hide resolved
darts/models/regression_model.py Outdated Show resolved Hide resolved
darts/models/regression_model.py Outdated Show resolved Hide resolved
@@ -337,7 +337,7 @@ def select_best_model(ts: TimeSeries, thetas: Optional[List[int]] = None,
using the fitted values on the training series `ts`.


Uses 'backtesting.backtest_gridsearch' with 'use_fitted_values=True' and 'metric=metrics.mae`.
Uses 'ForecastingModel.gridsearch' with 'use_fitted_values=True' and 'metric=metrics.mae`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function select_best_model is basically doing a gridsearch, but it's misplaced IMO, and I think we should remove it if we can.
Instead one option could be to have each model implement a method default_hyperparams returning some good ranges of hyper parameters to be iterated on when grid-searching. This way afterwards in ForecastingModel we could have only one method auto_select_model doing the gridsearch on reasonable hyper-params.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, some standardization may be good here. But should we leave this for a separate PR?

darts/timeseries.py Show resolved Hide resolved
Copy link
Contributor

@hrzn hrzn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice improvement :)

@pennfranc pennfranc merged commit c75c9c4 into refactor/fit-args Sep 17, 2020
pennfranc added a commit that referenced this pull request Sep 17, 2020
* add support for columns to the TimeSeries object

* add colum support indexing to timeseries

* fix wrong docstring

* refactor indexing, fix docstring, columns as last arg

* clean indexing method

* refactor indexing only based on loc and iloc

* Update darts/timeseries.py

Co-authored-by: Julien Herzen <julien.herzen@unit8.co>

* use underlying columns by default

* fix column added on intern _df and use self.freq_str

* fix parameter position in from_times_and_values

* fix the tests to use str columns

* fix docstring timeseries

* remove None check on df that should exists

* add comment for clarifying that _df is a copy

* add separate function to process columns

* adapt map with str col indexing

* univariate fcast model only support univariate ts

* MultivariateFcasModel fits on the whole training ts

* refactor torch forcasting model to use covariate_series

* fix unused imports

* allow to specify only covaraite_series

* enforce covariate_series and target_series inputs for multivariate model

* adapt torch datasets to use covariate / target series

* adapt validation series provided as a Tuple

* fix typo

* adapt create_dataset on tcn model

* remove component index from fit function

* adapt tests to new syntax

* refacotr metaclasses

* abstract a new method make fitable series

* adapt torchforcastingmodel to parent class changes

* keep covariate/target seires for Multivariate models only

* fix typos with new implementation

* move series length check in forcasting model

* rename covariate into training series

* adapt old backtesting to support the new fit args syntax

* Refactor/backtesting (#125)

* add .DS_Store to .gitignore

* add proposal.md

* add draft version of backtest forcasting

* add backtest to model (simple refactoring)

* extract backtest sanity checks in a method

* extract building fit_kwargs and predict_kwargs in a method

* minor fix import comment and assertion

* refactor all backtest factoring tests

* update progress on proposal.md

* add coverage.sh

* fix permission on coverage.sh

* improve coverage sh script

* add coverage.xml to .igtignore

* improve doc on coverage.sh

* fix doc

* fix doc for real

* univariate fcast model only support univariate ts

* MultivariateFcasModel fits on the whole training ts

* refactor torch forcasting model to use covariate_series

* fix unused imports

* allow to specify only covaraite_series

* enforce covariate_series and target_series inputs for multivariate model

* adapt torch datasets to use covariate / target series

* adapt validation series provided as a Tuple

* fix typo

* adapt create_dataset on tcn model

* remove component index from fit function

* adapt tests to new syntax

* add proposal.md

* add draft version of backtest forcasting

* add backtest to model (simple refactoring)

* extract backtest sanity checks in a method

* extract building fit_kwargs and predict_kwargs in a method

* minor fix import comment and assertion

* refactor all backtest factoring tests

* update progress on proposal.md

* fix doc

* fix doc for real

* fix typos and remove diagram in backtest doc

* WIP add residuals

* add decorator for sanity checks

* clean forecasting_model

* add start multitype parameter support

* fix check on undefined param in sanity checks

* add comments

* fix(backtesting, tests): fixed bugs so that all forecasting backtest tests pass, corrected some typos

* feature(backtesting): changed handling of residuals (re-introduced own function instead of being by-product of backtest)

* fix(test_forecasting_model): deleted old file that was renamed due to type

* feat(backtesting): moved gridsearch to ForecastingModel, removed functions from backtesting module that have been moved to ForecastingModel class, adapted tests

* feat(backtesting): adapted docstring of gridsearch function

* fix(Theta): adapted FourTheta model to use new gridsearch function

* fix(forecasting_model, torch_forecasting_model): fixed docstrings

* feat(backtesting): moved backtest_regression to regression model class

* fix(forecasting_model): renamed covariate_series to training_series

* fix(forecasting_model): fixed residuals function

* fix(style): linter

* feat(backtesting): renamed backtest_gridsearch to gridsearch

* fix(tests): fixed residuals test case

* feat(backtesting): moved residuals plotting function to statistics module

* feat(backtesting): removed backtesting module

* fix(style): linter

* fix(style): linter

* fix(torch_forecasting_model): fixed check in predict function

* fix(forecasting_model): fixed backtest sanity check

* fix(torch_forecasting_model): removed unnecessary  (and bug-causing) sanity check method

* feat(examples): refactored notebooks to support new function signatures

* fix(style): linter

* updated PROPOSAL.md

* feat(forecasting_model): improved documentation

* fix(torch_forecasting_model): removed redundant function

* style(torch_forecasting_model): linter

* fix(torch_forecasting_model): fixed docstring typo

* fix(torch_forecasting_model, tests): clean up old comments

* fix(statistics): improved docstrings

* fix(forecasting_model, regression_model): improved variable names, fixed documentation

* fix(tests): fixed old variable name in backtesting tests

* removed PROPOSAL.md

* feat(regression_model): added stride functionality to backtest method

* fix(forecasting_model, regression_model): improved documentation

* fix(forecasting_model): improved documentation

* fix(forecasting_model): improved start parameter documentation

* fix(forecasting_model, regression_model): cleaned up code, improved docstrings, added missing checks

* feat(forecasting_model): improved backtest docstring

* fix(forecasting_model, tests): improved backtest sanity checks, added corresponding test cases

* feat(backtesting): replaced 'num_predictions' parameter by 'start' parameter in 'ForecastingModel.gridsearch'

* fix(examples): updated notebooks

Co-authored-by: Guillaume Raille <guillaume.raille@unit8.co>
Co-authored-by: pennfranc <flaessig@student.ethz.ch>

Co-authored-by: Julien Herzen <julien.herzen@unit8.co>
Co-authored-by: TheMP <marek.pasieka@gmail.com>
Co-authored-by: Francesco Lässig <42946363+pennfranc@users.noreply.github.com>
Co-authored-by: Guillaume <66320848+guillaumeraille@users.noreply.github.com>
Co-authored-by: pennfranc <flaessig@student.ethz.ch>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants