Skip to content

Commit 9ba3258

Browse files
Devops/slim flavour requirements (#2906)
* small fix on assert condition * Moved XGB, SF to notorch flavor * Update CHANGELOG.md * minor updates * PR fixes * minor updates * removed core tests from develop workflow --------- Co-authored-by: dennisbader <[email protected]>
1 parent 70068f1 commit 9ba3258

File tree

14 files changed

+542
-420
lines changed

14 files changed

+542
-420
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ but cannot always guarantee backwards compatibility. Changes that may **break co
1717
- Added `add_regressor_configs` parameter to the `Prophet` model, enabling component-specific control over `prior_scale`, `mode`, and `standardize` for the future covariates. [#2882](https://github.com/unit8co/darts/issues/2882) by [Ramsay Davis](https://github.com/RamsayDavisWL).
1818
- 🔴 Increased the decimal places for quantile component names from 2 to 3 for more precise quantiles. (e.g. `component_name_q0.500` for quantile 0.5). This affects quantile forecasts as well as quantiles computed with `TimeSeries.quantile()`. [#2887](https://github.com/unit8co/darts/pull/2786) by [He Weilin](https://github.com/cnhwl).
1919
- Added parameter `load_best` to `TorchForecastingModel.fit()` and `fit_from_dataset()` which, when `True`, will automatically load (and use) the best model on the validation set at the end of the training process. [#2903](https://github.com/unit8co/darts/pull/2903) by [He Weilin](https://github.com/cnhwl).
20-
- Added model creation parameters `random_errors` and `error` to `ExponentialSmoothing` that give control over how probabilistic forecasts are generated. [#2290491](https://github.com/unit8co/darts/pull/2904) by [Jakub Chłapek](https://github.com/jakubchlapek)
20+
- Added model creation parameters `random_errors` and `error` to `ExponentialSmoothing` that give control over how probabilistic forecasts are generated. [#2904](https://github.com/unit8co/darts/pull/2904) by [Jakub Chłapek](https://github.com/jakubchlapek)
2121
- Added parameter `val_length` to `ForecastingModel.historical_forecasts()`, `backtest()` and `residuals()` which will extract a validation set of length `val_length` after the end of each training set when `retrain=True`. The validation set is then used to fit the underlying forecasting model if it supports it. This is especially useful for early stopping mechanisms to reduce overfitting and / or training times. [#2894](https://github.com/unit8co/darts/pull/2894) by [Dennis Bader](https://github.com/dennisbader).
2222
- 🔴 Renamed the `RegressionEnsembleModel` ensemble model attribute from `regression_model` to `ensemble_model` to make it more clear that this model is used to combine the predictions of the base models. [#2894](https://github.com/unit8co/darts/pull/2894) by [Dennis Bader](https://github.com/dennisbader).
2323

@@ -29,6 +29,7 @@ but cannot always guarantee backwards compatibility. Changes that may **break co
2929
**Dependencies**
3030

3131
- We raised the minimum pytorch-lightning version to `pytorch-lightning>=2.0.0`. [#2888](https://github.com/unit8co/darts/pull/2888) by [Dennis Bader](https://github.com/dennisbader).
32+
- We made the Darts core and `torch` packages lighter by removing XGBoost and StatsForecast from the dependencies. All our forecasting models wrapping around these libraries will still be supported. To use them simply install the packages manually or via `u8darts[notorch]` and `u8darts[all]`. [#2906](https://github.com/unit8co/darts/pull/2906) by [Jakub Chłapek](https://github.com/jakubchlapek)
3233

3334
### For developers of the library:
3435

INSTALL.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Below, we detail how to install Darts using either `conda` or `pip`.
44

55
## From PyPI
6-
Install Darts with all models except the ones from optional dependencies (Prophet, LightGBM, CatBoost, see more on that [here](#enabling-optional-dependencies)): `pip install darts`.
6+
Install Darts with all models except the ones from optional dependencies (Prophet, LightGBM, CatBoost, XGBoost, StatsForecast see more on that [here](#enabling-optional-dependencies)): `pip install darts`.
77

88
If this fails on your platform, please follow the official installation
99
guide for [PyTorch](https://pytorch.org/get-started/locally/), then try installing Darts again.
@@ -12,8 +12,8 @@ As some dependencies are relatively big or involve non-Python dependencies,
1212
we also maintain the `u8darts` package, which provides the following alternate lighter install options:
1313

1414
* Install Darts with all available models: `pip install "u8darts[all]"`
15-
* Install core only (without neural networks, Prophet, LightGBM and Catboost): `pip install u8darts`
16-
* Install core + Prophet + LightGBM + CatBoost: `pip install "u8darts[notorch]"`
15+
* Install core only (without neural networks, Prophet, LightGBM, Catboost, XGBoost and StatsForecast): `pip install u8darts`
16+
* Install core + Prophet + LightGBM + CatBoost + XGBoost + StatsForecast: `pip install "u8darts[notorch]"`
1717
* Install core + neural networks (PyTorch): `pip install "u8darts[torch]"` (equivalent to `pip install darts`)
1818

1919
## From conda-forge
@@ -29,16 +29,15 @@ Activate the environment
2929
As some models have relatively heavy dependencies, we provide four conda-forge packages:
3030

3131
* Install Darts with all available models: `conda install -c conda-forge -c pytorch u8darts-all`
32-
* Install core only (without neural networks, Prophet, LightGBM and Catboost): `conda install -c conda-forge u8darts`
33-
* Install core + Prophet + LightGBM + CatBoost: `conda install -c conda-forge u8darts-notorch`
32+
* Install core only (without neural networks, Prophet, LightGBM, Catboost, XGBoost and StatsForecast): `conda install -c conda-forge u8darts`
33+
* Install core + Prophet + LightGBM + CatBoost + XGBoost + StatsForecast: `conda install -c conda-forge u8darts-notorch`
3434
* Install core + neural networks (PyTorch): `conda install -c conda-forge -c pytorch u8darts-torch`
3535

3636

3737
## Other Information
3838

3939
### Enabling Optional Dependencies
40-
As of version 0.25.0, the default `darts` package does not install Prophet, CatBoost, and LightGBM dependencies anymore, because their
41-
build processes were too often causing issues. We continue supporting the model wrappers `Prophet`, `CatBoostModel`, and `LightGBMModel` in Darts though. If you want to use any of them, you will need to manually install the corresponding packages (or install a Darts flavor as described above).
40+
As of version 0.38.0, we made the default `darts` package more lightweight. Packages Prophet, CatBoost, LightGBM, XGBoost and StatsForecast will not be installed anymore. Don't worry though, we keep supporting our model wrappers `Prophet`, `CatBoostModel`, `LightGBMModel`, `XGBoost` and `StatsForecast` in Darts. If you want to use any of them, you will need to manually install the corresponding packages (or install a Darts flavor as described above).
4241

4342
#### Prophet
4443
Install the `prophet` package (version 1.1.1 or more recent) using the [Prophet install guide](https://facebook.github.io/prophet/docs/installation.html#python)
@@ -49,6 +48,12 @@ Install the `catboost` package (version 1.0.6 or more recent) using the [CatBoos
4948
#### LightGBMModel
5049
Install the `lightgbm` package (version 3.2.0 or more recent) using the [LightGBM install guide](https://lightgbm.readthedocs.io/en/latest/Installation-Guide.html)
5150

51+
#### XGBoost
52+
Install the `xgboost` package (version 2.1.4 or more recent) using the [XGBoost install guide](https://xgboost.readthedocs.io/en/stable/install.html)
53+
54+
#### StatsForecast
55+
Install the `statsforecast` package (version 1.4 or more recent) using the [StatsForecast install guide](https://nixtlaverse.nixtla.io/statsforecast/index.html#installation)
56+
5257
### Enabling GPU support
5358
Darts relies on PyTorch for the neural network models.
5459
For GPU support, please follow the instructions to install CUDA in the [PyTorch installation guide](https://pytorch.org/get-started/locally/).

darts/tests/conftest.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,58 @@
1717
logger.warning("Torch not installed - Some tests will be skipped.")
1818
TORCH_AVAILABLE = False
1919

20+
try:
21+
import catboost
22+
import lightgbm
23+
import xgboost # noqa: F401
24+
25+
GBM_AVAILABLE = True
26+
except ImportError:
27+
logger.warning(
28+
"Gradient Boosting Models not installed - Some tests will be skipped."
29+
)
30+
GBM_AVAILABLE = False
31+
32+
try:
33+
import xgboost # noqa: F401
34+
35+
XGB_AVAILABLE = True
36+
except ImportError:
37+
logger.warning("XGBoost not installed - Some tests will be skipped.")
38+
XGB_AVAILABLE = False
39+
40+
try:
41+
import lightgbm # noqa: F401
42+
43+
LGBM_AVAILABLE = True
44+
except ImportError:
45+
logger.warning("LightGBM not installed - Some tests will be skipped.")
46+
LGBM_AVAILABLE = False
47+
48+
try:
49+
import catboost # noqa: F401
50+
51+
CB_AVAILABLE = True
52+
except ImportError:
53+
logger.warning("CatBoost not installed - Some tests will be skipped.")
54+
CB_AVAILABLE = False
55+
56+
try:
57+
import prophet # noqa: F401
58+
59+
PROPHET_AVAILABLE = True
60+
except ImportError:
61+
logger.warning("Prophet not installed - Some tests will be skipped.")
62+
PROPHET_AVAILABLE = False
63+
64+
try:
65+
import statsforecast # noqa: F401
66+
67+
SF_AVAILABLE = True
68+
except ImportError:
69+
logger.warning("StatsForecast not installed - Some tests will be skipped.")
70+
SF_AVAILABLE = False
71+
2072
try:
2173
import onnx # noqa: F401
2274
import onnxruntime # noqa: F401

darts/tests/explainability/test_shap_explainer.py

Lines changed: 84 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@
2323
SKLearnModel,
2424
XGBModel,
2525
)
26+
from darts.tests.conftest import (
27+
GBM_AVAILABLE,
28+
LGBM_AVAILABLE,
29+
XGB_AVAILABLE,
30+
)
2631
from darts.utils.timeseries_generation import linear_timeseries
27-
from darts.utils.utils import NotImportedModule
28-
29-
lgbm_available = not isinstance(LightGBMModel, NotImportedModule)
30-
cb_available = not isinstance(CatBoostModel, NotImportedModule)
3132

3233

3334
def extract_year(index):
@@ -138,43 +139,49 @@ class TestShapExplainer:
138139
np.concatenate([fut_cov_1.reshape(-1, 1), fut_cov_2.reshape(-1, 1)], axis=1),
139140
)
140141

141-
def test_creation(self):
142-
model_cls = LightGBMModel if lgbm_available else XGBModel
142+
@pytest.mark.skipif(not GBM_AVAILABLE, reason="requires gradient boosting models")
143+
@pytest.mark.parametrize(
144+
"model",
145+
[
146+
{
147+
"model_cls": LightGBMModel,
148+
"config": {
149+
"lags": 4,
150+
"lags_past_covariates": [-1, -2, -3],
151+
"lags_future_covariates": [0],
152+
"output_chunk_length": 4,
153+
"add_encoders": add_encoders,
154+
},
155+
},
156+
{
157+
"model_cls": CatBoostModel,
158+
"config": {
159+
"lags": 4,
160+
"lags_past_covariates": [-1, -2, -6],
161+
"lags_future_covariates": [0],
162+
"output_chunk_length": 4,
163+
},
164+
},
165+
{
166+
"model_cls": XGBModel,
167+
"config": {
168+
"lags": 4,
169+
"lags_past_covariates": [-1, -2, -3],
170+
"lags_future_covariates": [0],
171+
"output_chunk_length": 4,
172+
"add_encoders": add_encoders,
173+
},
174+
},
175+
],
176+
)
177+
def test_gbm_creation(self, model):
178+
model_cls = model["model_cls"]
179+
config = model["config"]
143180
# Model should be fitted first
144-
m = model_cls(
145-
lags=4,
146-
lags_past_covariates=[-1, -2, -3],
147-
lags_future_covariates=[0],
148-
output_chunk_length=4,
149-
add_encoders=self.add_encoders,
150-
)
151-
with pytest.raises(ValueError):
152-
ShapExplainer(m, self.target_ts, self.past_cov_ts, self.fut_cov_ts)
153-
154-
# Model should be a SKLearnModel
155-
m = ExponentialSmoothing()
156-
m.fit(self.target_ts["price"])
157-
with pytest.raises(ValueError):
158-
ShapExplainer(m)
181+
m = model_cls(**config)
159182

160-
# For now, multi_models=False not allowed
161-
m = LinearRegressionModel(lags=1, output_chunk_length=2, multi_models=False)
162-
m.fit(
163-
series=self.target_ts,
164-
)
165183
with pytest.raises(ValueError):
166-
ShapExplainer(
167-
m,
168-
self.target_ts,
169-
)
170-
171-
m = model_cls(
172-
lags=4,
173-
lags_past_covariates=[-1, -2, -3],
174-
lags_future_covariates=[0],
175-
output_chunk_length=4,
176-
add_encoders=self.add_encoders,
177-
)
184+
ShapExplainer(m, self.target_ts, self.past_cov_ts, self.fut_cov_ts)
178185

179186
m.fit(
180187
series=self.target_ts,
@@ -214,6 +221,28 @@ def test_creation(self):
214221
shap_explain.explainers.explainers[0][0], shap.explainers.Tree
215222
)
216223

224+
# Bad choice of shap explainer
225+
with pytest.raises(ValueError):
226+
ShapExplainer(m, shap_method="bad_choice")
227+
228+
def test_creation(self):
229+
# Model should be a SKLearnModel
230+
m = ExponentialSmoothing()
231+
m.fit(self.target_ts["price"])
232+
with pytest.raises(ValueError):
233+
ShapExplainer(m)
234+
235+
# For now, multi_models=False not allowed
236+
m = LinearRegressionModel(lags=1, output_chunk_length=2, multi_models=False)
237+
m.fit(
238+
series=self.target_ts,
239+
)
240+
with pytest.raises(ValueError):
241+
ShapExplainer(
242+
m,
243+
self.target_ts,
244+
)
245+
217246
# Linear model - also not a MultiOutputRegressor
218247
m = LinearRegressionModel(
219248
lags=1,
@@ -257,33 +286,8 @@ def test_creation(self):
257286
shap_explain = ShapExplainer(m)
258287
assert isinstance(shap_explain.explainers.explainers, shap.explainers.Linear)
259288

260-
# CatBoost
261-
model_cls = CatBoostModel if cb_available else XGBModel
262-
m = model_cls(
263-
lags=4,
264-
lags_past_covariates=[-1, -2, -6],
265-
lags_future_covariates=[0],
266-
output_chunk_length=4,
267-
)
268-
m.fit(
269-
series=self.target_ts,
270-
past_covariates=self.past_cov_ts,
271-
future_covariates=self.fut_cov_ts,
272-
)
273-
shap_explain = ShapExplainer(m)
274-
if m._supports_native_multioutput:
275-
assert isinstance(shap_explain.explainers.explainers, shap.explainers.Tree)
276-
else:
277-
assert isinstance(
278-
shap_explain.explainers.explainers[0][0], shap.explainers.Tree
279-
)
280-
281-
# Bad choice of shap explainer
282-
with pytest.raises(ValueError):
283-
ShapExplainer(m, shap_method="bad_choice")
284-
285289
def test_explain(self):
286-
model_cls = LightGBMModel if lgbm_available else XGBModel
290+
model_cls = LightGBMModel if LGBM_AVAILABLE else LinearRegressionModel
287291
m = model_cls(
288292
lags=4,
289293
lags_past_covariates=[-1, -2, -3],
@@ -440,7 +444,7 @@ def test_explain(self):
440444
assert isinstance(shap_explain.explain(), ShapExplainabilityResult)
441445

442446
def test_explain_with_lags_future_covariates_series_of_same_length_as_target(self):
443-
model_cls = LightGBMModel if lgbm_available else XGBModel
447+
model_cls = LightGBMModel if LGBM_AVAILABLE else LinearRegressionModel
444448
model = model_cls(
445449
lags=4,
446450
lags_past_covariates=[-1, -2, -3],
@@ -476,7 +480,7 @@ def test_explain_with_lags_future_covariates_series_extending_into_future(self):
476480
fut_cov = np.random.normal(0, 1, len(days)).astype("float32")
477481
fut_cov_ts = TimeSeries.from_times_and_values(days, fut_cov.reshape(-1, 1))
478482

479-
model_cls = LightGBMModel if lgbm_available else XGBModel
483+
model_cls = LightGBMModel if LGBM_AVAILABLE else LinearRegressionModel
480484
model = model_cls(
481485
lags=4,
482486
lags_past_covariates=[-1, -2, -3],
@@ -512,7 +516,7 @@ def test_explain_with_lags_covariates_series_older_timestamps_than_target(self):
512516
past_cov = np.random.normal(0, 1, len(days)).astype("float32")
513517
past_cov_ts = TimeSeries.from_times_and_values(days, past_cov.reshape(-1, 1))
514518

515-
model_cls = LightGBMModel if lgbm_available else XGBModel
519+
model_cls = LightGBMModel if LGBM_AVAILABLE else LinearRegressionModel
516520
model = model_cls(
517521
lags=None,
518522
lags_past_covariates=[-1, -2],
@@ -539,7 +543,7 @@ def test_explain_with_lags_covariates_series_older_timestamps_than_target(self):
539543
assert explanation.start_time() == self.target_ts.start_time()
540544

541545
def test_plot(self):
542-
model_cls = LightGBMModel if lgbm_available else XGBModel
546+
model_cls = LightGBMModel if LGBM_AVAILABLE else LinearRegressionModel
543547
m_0 = model_cls(
544548
lags=4,
545549
lags_past_covariates=[-1, -2, -3],
@@ -641,7 +645,7 @@ def test_plot(self):
641645
plt.close()
642646

643647
def test_feature_values_align_with_input(self):
644-
model_cls = LightGBMModel if lgbm_available else XGBModel
648+
model_cls = LightGBMModel if LGBM_AVAILABLE else LinearRegressionModel
645649
model = model_cls(
646650
lags=4,
647651
output_chunk_length=1,
@@ -668,7 +672,7 @@ def test_feature_values_align_with_input(self):
668672
)
669673

670674
def test_feature_values_align_with_raw_output_shap(self):
671-
model_cls = LightGBMModel if lgbm_available else XGBModel
675+
model_cls = LightGBMModel if LGBM_AVAILABLE else LinearRegressionModel
672676
model = model_cls(
673677
lags=4,
674678
output_chunk_length=1,
@@ -695,7 +699,7 @@ def test_feature_values_align_with_raw_output_shap(self):
695699
), "The shape of the feature values should be the same as the shap values"
696700

697701
def test_shap_explanation_object_validity(self):
698-
model_cls = LightGBMModel if lgbm_available else XGBModel
702+
model_cls = LightGBMModel if LGBM_AVAILABLE else LinearRegressionModel
699703
model = model_cls(
700704
lags=4,
701705
lags_past_covariates=2,
@@ -719,13 +723,13 @@ def test_shap_explanation_object_validity(self):
719723

720724
@pytest.mark.parametrize(
721725
"config",
722-
[
723-
(XGBModel, {}),
724-
(
725-
LightGBMModel if lgbm_available else XGBModel,
726-
{"likelihood": "quantile", "quantiles": [0.5]},
727-
),
728-
],
726+
[(LinearRegressionModel, {})]
727+
+ ([(XGBModel, {})] if XGB_AVAILABLE else [])
728+
+ (
729+
[(LightGBMModel, {"likelihood": "quantile", "quantiles": [0.5]})]
730+
if LGBM_AVAILABLE
731+
else []
732+
),
729733
)
730734
def test_shap_selected_components(self, config):
731735
"""Test selected components with and without Darts' MultiOutputRegressor"""
@@ -768,7 +772,7 @@ def test_shap_selected_components(self, config):
768772

769773
def test_shapley_with_static_cov(self):
770774
ts = self.target_ts_with_static_covs
771-
model_cls = LightGBMModel if lgbm_available else XGBModel
775+
model_cls = LightGBMModel if LGBM_AVAILABLE else LinearRegressionModel
772776
model = model_cls(
773777
lags=4,
774778
output_chunk_length=1,
@@ -813,7 +817,7 @@ def test_shapley_with_static_cov(self):
813817
]
814818

815819
def test_shapley_multiple_series_with_different_static_covs(self):
816-
model_cls = LightGBMModel if lgbm_available else XGBModel
820+
model_cls = LightGBMModel if LGBM_AVAILABLE else LinearRegressionModel
817821
model = model_cls(
818822
lags=4,
819823
output_chunk_length=1,

0 commit comments

Comments
 (0)