From c8e84576e348f873b89517bddb25f610aaf9a774 Mon Sep 17 00:00:00 2001 From: dennisbader Date: Fri, 3 Nov 2023 12:12:48 +0100 Subject: [PATCH 1/3] use native quantile regression for xgb 2.0.0 and above --- darts/models/forecasting/xgboost.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/darts/models/forecasting/xgboost.py b/darts/models/forecasting/xgboost.py index 99e2df3ff9..7e634438e6 100644 --- a/darts/models/forecasting/xgboost.py +++ b/darts/models/forecasting/xgboost.py @@ -25,6 +25,10 @@ logger = get_logger(__name__) +# Check whether we are running xgboost >= 2.0.0 for quantile regression +tokens = xgb.__version__.split(".") +xgb_200_or_above = int(tokens[0]) >= 2 + def xgb_quantile_loss(labels: np.ndarray, preds: np.ndarray, quantile: float): """Custom loss function for XGBoost to compute quantile loss gradient. @@ -183,8 +187,12 @@ def encode_year(idx): if likelihood in {"poisson"}: self.kwargs["objective"] = f"count:{likelihood}" elif likelihood == "quantile": + if xgb_200_or_above: + # leverage built-in Quantile Regression + self.kwargs["objective"] = "reg:quantileerror" self.quantiles, self._median_idx = self._prepare_quantiles(quantiles) self._model_container = self._get_model_container() + self._rng = np.random.default_rng(seed=random_state) # seed for sampling super().__init__( @@ -249,12 +257,18 @@ def fit( ) ] + # TODO: XGBRegressor supports multi quantile reqression which we could leverage in the future + # see https://xgboost.readthedocs.io/en/latest/python/examples/quantile_regression.html if self.likelihood == "quantile": # empty model container in case of multiple calls to fit, e.g. when backtesting self._model_container.clear() for quantile in self.quantiles: - obj_func = partial(xgb_quantile_loss, quantile=quantile) - self.kwargs["objective"] = obj_func + if xgb_200_or_above: + self.kwargs["quantile_alpha"] = quantile + else: + self.kwargs["objective"] = partial( + xgb_quantile_loss, quantile=quantile + ) self.model = xgb.XGBRegressor(**self.kwargs) super().fit( From 2bd75e914dae23f2eab5f512ebe2e5fadb76c13f Mon Sep 17 00:00:00 2001 From: dennisbader Date: Fri, 3 Nov 2023 12:24:58 +0100 Subject: [PATCH 2/3] update changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a6127fabb..11bc0b42d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,9 @@ but cannot always guarantee backwards compatibility. Changes that may **break co - 🚀🚀 Optimized `historical_forecasts()` for pre-trained `TorchForecastingModel` running up to 20 times faster than before!. [#2013](https://github.com/unit8co/darts/pull/2013) by [Dennis Bader](https://github.com/dennisbader). - Added callback `darts.utils.callbacks.TFMProgressBar` to customize at which model stages to display the progress bar. [#2020](https://github.com/unit8co/darts/pull/2020) by [Dennis Bader](https://github.com/dennisbader). - Improvements to documentation: - - Adapted the example notebooks to properly apply data transformers and avoid look-ahead bias. [#2020](https://github.com/unit8co/darts/pull/2020) by [Samriddhi Singh](https://github.com/SimTheGreat). + - Adapted the example notebooks to properly apply data transformers and avoid look-ahead bias. [#2020](https://github.com/unit8co/darts/pull/2020) by [Samriddhi Singh](https://github.com/SimTheGreat). +- Improvements to Regression Models: + - `XGBModel` now leverages XGBoost's native Quantile Regression support that was released in version 2.0.0 for improved probabilistic forecasts. [#2051](https://github.com/unit8co/darts/pull/2051) by [Dennis Bader](https://github.com/dennisbader). **Fixed** - Fixed a bug when calling optimized `historical_forecasts()` for a `RegressionModel` trained with unequal component-specific lags. [#2040](https://github.com/unit8co/darts/pull/2040) by [Antoine Madrona](https://github.com/madtoinou). From 3aab3b15a6554967f113b7079cb0aa206e5fb03b Mon Sep 17 00:00:00 2001 From: dennisbader Date: Fri, 3 Nov 2023 12:26:58 +0100 Subject: [PATCH 3/3] revert change to objective --- darts/models/forecasting/xgboost.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/darts/models/forecasting/xgboost.py b/darts/models/forecasting/xgboost.py index 7e634438e6..3b694c502f 100644 --- a/darts/models/forecasting/xgboost.py +++ b/darts/models/forecasting/xgboost.py @@ -266,9 +266,8 @@ def fit( if xgb_200_or_above: self.kwargs["quantile_alpha"] = quantile else: - self.kwargs["objective"] = partial( - xgb_quantile_loss, quantile=quantile - ) + objective = partial(xgb_quantile_loss, quantile=quantile) + self.kwargs["objective"] = objective self.model = xgb.XGBRegressor(**self.kwargs) super().fit(