SHAP (SHapley Additive exPlanations, [1]) is an ingenious way to study black box models. SHAP values decompose - as fair as possible - predictions into additive feature contributions. Crunching SHAP values requires clever algorithms by clever people. Analyzing them, however, is super easy with the right visualizations. The “shapviz” package offers the latter:
sv_dependence()
: Dependence plots to study feature
effects and interactions.sv_importance()
: Importance plots (bar plots and/or
beeswarm “summary” plots) to study variable importance.sv_interaction()
: Interaction plots.sv_waterfall()
: Waterfall plots to study single
predictions.sv_force()
: Force plots as an alternative to waterfall
plots.These plots require a “shapviz” object, which is built from two things only:
S
: Matrix of SHAP valuesX
: Dataset that includes the corresponding feature
valuesOptionally, a baseline
can be passed to represent an
average prediction on the scale of the SHAP values. Also a 3D array of
SHAP interaction values can be passed as S_inter
.
A key feature of “shapviz” is that X
is used for
visualization only. Thus it is perfectly fine to use factor variables,
even if the underlying model would not accept these. Additionally, in
order to improve visualization, it can sometimes make sense to clip
gross outliers, take logarithms for certain columns, or replace missing
values by some explicit value.
To further simplify the use of “shapviz”, we added direct connectors to these R packages:
For XGBoost, LightGBM, and H2O, the SHAP values are directly calculated from the fitted model.
CatBoost
is
not included, but see the vignette how to use its SHAP calculation
backend with “shapviz”.
# From CRAN
install.packages("shapviz")
# Or the newest version from GitHub:
# install.packages("devtools")
::install_github("mayer79/shapviz") devtools
Shiny diamonds… let’s model their prices by four “c” variables with XGBoost:
library(shapviz)
library(ggplot2)
library(xgboost)
set.seed(3653)
<- c("carat", "cut", "color", "clarity")
x <- xgb.DMatrix(data.matrix(diamonds[x]), label = diamonds$price)
dtrain
<- xgb.train(
fit params = list(learning_rate = 0.1, objective = "reg:squarederror"),
data = dtrain,
nrounds = 65L
)
One line of code creates a “shapviz” object. It contains SHAP values
and feature values for the set of observations we are interested in.
Note again that X
is solely used as explanation dataset,
not for calculating SHAP values.
In this example, we construct the “shapviz” object directly from the
fitted XGBoost model. Thus we also need to pass a corresponding
prediction dataset X_pred
used for calculating SHAP values
by XGBoost.
<- diamonds[sample(nrow(diamonds), 2000L), ]
dia_small
<- shapviz(fit, X_pred = data.matrix(dia_small[x]), X = dia_small) shp
Note: If X_pred
would contain one-hot-encoded dummy
variables, their SHAP values (and also SHAP interaction values) could be
collapsed by the collapse
argument of
shapviz()
.
Let’s explain the first prediction by a waterfall plot:
sv_waterfall(shp, row_id = 1)
Or alternatively, by a force plot:
sv_force(shp, row_id = 1)
We have decomposed 2000 predictions, not just one. This allows us to study variable importance at a global model level by studying average absolute SHAP values or by looking at beeswarm “summary” plots of SHAP values.
sv_importance(shp)
sv_importance(shp, kind = "beeswarm")
sv_importance(shp, kind = "both", show_numbers = TRUE, bee_width = 0.2)
A scatterplot of SHAP values of a feature like color
against its observed values gives a great impression on the feature
effect on the response. Vertical scatter gives additional info on
interaction effects (using a heuristic to select the feature on the
color axis).
sv_dependence(shp, v = "color")
If SHAP interaction values have been computed (via XGBoost or “treeshap”), the dependence plot can focus on main effects or SHAP interaction effects (multiplied by two due to symmetry):
<- shapviz(
shp_with_inter X_pred = data.matrix(dia_small[x]), X = dia_small, interactions = TRUE
fit,
)
sv_dependence(shp_with_inter, v = "color", color_var = "cut", interactions = TRUE)
We can also study all interactions and main effects together using the following beeswarm visualization:
sv_interaction(shp_with_inter) +
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1))
Check out the package help and the vignette for further information.
[1] Scott M. Lundberg and Su-In Lee. A Unified Approach to Interpreting Model Predictions. Advances in Neural Information Processing Systems 30 (2017).