Support XGBoost categorical splits in CPU TreeExplainer with background - #3
Closed
04pallav wants to merge 2 commits into
Closed
Support XGBoost categorical splits in CPU TreeExplainer with background#304pallav wants to merge 2 commits into
04pallav wants to merge 2 commits into
Conversation
When background data forces the _cext path (tree_path_dependent), XGBoost models trained with enable_categorical=True need cat splits tagged in the loader, XGB-specific routing in tree_shap.h (threshold_types=2), and pandas category columns converted to integer codes for both explain and background rows. Includes tests and XGBOOST_CATEGORICAL_README.md walkthrough.
04pallav
force-pushed
the
support/xgboost-categorical-cpu
branch
from
August 3, 2026 00:08
adbc6df to
bb029e1
Compare
for more information, see https://pre-commit.ci
Owner
Author
|
Superseded by upstream draft PR: shap#5107 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issues: shap#2662, shap#5068
Upstream draft PR: shap#5107
Summary
TreeExplaineron an XGBoost model with native categoricals (enable_categorical=True) works today without a background dataset: SHAP delegates to XGBoost’s ownpred_contribspath. It breaks when you pass background data withfeature_perturbation="tree_path_dependent"— that configuration forces SHAP’s CPU_cextimplementation, which did not support XGB categorical trees end-to-end.On master, that
_cextpath either raisesNotImplementedErrorfor categorical XGB models, or (if reached) cannot traverse the trees correctly: the loader never marks categorical nodes or builds their bitmask thresholds, the C++ core has no XGB split type (only numeric and LightGBM), and pandascategorycolumns are passed throughto_numpy()as labels instead of the integer codes the booster trained on.This PR connects the full pipeline for that path:
XGBTreeModelLoader): mark categorical split nodes asthreshold_types=2and set threshold bitmasks from category codes.tree_shap.h): add XGB routing (in-set → right,2^codebitmask) via a shared split dispatcher used in predict, weight update, and SHAP recursion._tree.py): convert categorical columns to codes throughtransform_input/_to_input_array; allow_cextwhen type-2 nodes are present; keep background and explain rows in sync (TreeExplainer.data← encodedTreeEnsemble.data).Testing
Success criterion: with background +
tree_path_dependent, row-level SHAP values satisfy additivity —sum(shap_values) + expected_value ≈ predict(..., output_margin=True).Automated (
TestExplainerXGBoostCategorical):Workclassas pandascategory,XGBClassifier(enable_categorical=True, tree_method="hist"), 100-row background + 20 explain rows.test_tree_path_dependent_background_additivity: background forces_cext(notpred_contribs); assertsthreshold_types == 2nodes exist; runsshap_values(..., check_additivity=True); explicitly checksshap.sum(1) + expected_valuevsoutput_margin(rtol=atol=1e-5).test_interventional_raises: same model/background withfeature_perturbation="interventional"raisesNotImplementedError(expected gap).CI: full upstream
testsworkflow green on this branch (all OS/Python matrix jobs, includingoldest supported numpy).Not tested: per-feature agreement with XGB
pred_contribson the no-background shortcut path.Not in this PR
NotImplementedError_cext_gpu)pred_contribs