Explaining Forecasting Models with SHAP#
Why did the model predict a spike tomorrow morning? Which lagged values mattered most? How do covariates shape the forecast?
Darts’ ShapExplainer answers these questions using SHAP (SHapley Additive exPlanations) [1], a game-theoretic framework that attributes each input feature’s contribution to a prediction. It works with any of Darts’ scikit-learn-like [2] and PyTorch [3] models through a single, unified API.
What you will learn:
3. Global Explainability : which features matter most overall
4. Local Explainability : why the model made a specific prediction
5. Probabilistic explainability : explaining quantile forecasts
We use the ElectricityConsumptionZurichDataset throughout, keeping the data small for fast computation.
[1] Lundberg & Lee, A Unified Approach to Interpreting Model Predictions, NeurIPS 2017.
[2] Models such as
SKLearnModel,LinearRegressionModel,LightGBMModel, …[3] Regular neural network models such as
TiDEModeland foundation models such asChronos2Model, …
1. Setup and Data Preparation#
[1]:
%load_ext autoreload
%autoreload 2
%matplotlib inline
[2]:
import logging
import warnings
import numpy as np
import plotly
import shap
from darts import concatenate, set_option
from darts.datasets import ElectricityConsumptionZurichDataset
from darts.explainability import ShapExplainer
from darts.metrics import mae, mic, miw
from darts.models import DLinearModel, LinearRegressionModel
from darts.utils.likelihood_models import QuantileRegression
from darts.utils.timeseries_generation import datetime_attribute_timeseries
warnings.filterwarnings("ignore")
logging.disable(logging.CRITICAL)
set_option("plotting.use_darts_style", True)
plotly.offline.init_notebook_mode()
shap.initjs()
We load hourly electricity consumption for Zurich households & SMEs, using the last four weeks (three for training, one for testing). Also, let’s use calendar features “hour” and “dayofweek” as future covariates.
[3]:
data = ElectricityConsumptionZurichDataset().load().astype("float32")
data = data.resample("h", method="sum")[-28 * 24 - 1 : -1]
series = data["Value_NE5"].with_columns_renamed("Value_NE5", "consumption")
future_covariates = (
datetime_attribute_timeseries(
time_index=series,
attribute="hour",
add_length=24,
)
.add_datetime_attribute("dayofweek")
.astype("float32")
)
train, test = series[: -24 * 7], series[-24 * 7 :]
[4]:
fig = train.plotly(label="train")
test.plotly(label="test", fig=fig)
fig.update_layout(yaxis_title="Consumption (MWh)", autosize=True)