treeig 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
treeig-0.1.1/LICENSE ADDED
@@ -0,0 +1,11 @@
1
+ Copyright 2026 Ludger Hentschel
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4
+
5
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6
+
7
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8
+
9
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10
+
11
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
treeig-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,389 @@
1
+ Metadata-Version: 2.4
2
+ Name: treeig
3
+ Version: 0.1.1
4
+ Summary: Exact Integrated Gradients for tree ensembles.
5
+ Author: Ludger Hentschel
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/lhentschel/treeig
8
+ Project-URL: Repository, https://github.com/lhentschel/treeig
9
+ Project-URL: Issues, https://github.com/lhentschel/treeig/issues
10
+ Keywords: machine-learning,interpretability,integrated-gradients,feature-attribution,xai,trees,xgboost,lightgbm
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: numpy>=1.24
24
+ Requires-Dist: numba>=0.58
25
+ Provides-Extra: sklearn
26
+ Requires-Dist: scikit-learn>=1.3; extra == "sklearn"
27
+ Provides-Extra: xgboost
28
+ Requires-Dist: xgboost>=2.0; extra == "xgboost"
29
+ Provides-Extra: lightgbm
30
+ Requires-Dist: lightgbm>=4.0; extra == "lightgbm"
31
+ Provides-Extra: all
32
+ Requires-Dist: scikit-learn>=1.3; extra == "all"
33
+ Requires-Dist: xgboost>=2.0; extra == "all"
34
+ Requires-Dist: lightgbm>=4.0; extra == "all"
35
+ Provides-Extra: dev
36
+ Requires-Dist: pytest>=8.0; extra == "dev"
37
+ Requires-Dist: build>=1.0; extra == "dev"
38
+ Requires-Dist: twine>=5.0; extra == "dev"
39
+ Requires-Dist: scikit-learn>=1.3; extra == "dev"
40
+ Requires-Dist: xgboost>=2.0; extra == "dev"
41
+ Requires-Dist: lightgbm>=4.0; extra == "dev"
42
+ Dynamic: license-file
43
+
44
+ # TreeIG
45
+
46
+ TreeIG computes exact Integrated Gradients for tree ensembles. It decomposes the change in a fitted tree model's scalar output between a baseline input $x_0$ and an observation $x$ into additive feature contributions.
47
+
48
+ For each observation, TreeIG returns feature attributions $\phi_j$ satisfying
49
+
50
+ ```text
51
+ sum_j phi_j = F(x) - F(x0)
52
+ ```
53
+
54
+ where $F$ is the scalar model output being explained. For regression models, $F$ is the prediction. For supported classifiers, $F$ is the raw margin/logit, not the predicted probability.
55
+
56
+ TreeIG extends the Integrated Gradients framework of Sundararajan, Taly, and Yan (2017) to tree ensembles by exploiting the piecewise-constant structure of tree models.
57
+
58
+ TreeIG uses generalized gradients to extend Integrated Gradients to tree-based models. The integrals of the generalized gradients are exactly equal to the sum of the prediction steps along the input path. TreeIG uses this equivalence to efficiently compute Integrated Gradients for tree models.
59
+
60
+ ## References
61
+
62
+ TreeIG:
63
+
64
+ - Hentschel, Ludger. 2026.
65
+ "TreeIG: Exact Integrated Gradients for Tree-Based Models."
66
+ *www.ludgerhentschel.com/Research.html*
67
+
68
+ Integrated Gradients:
69
+
70
+ - Sundararajan, Mukund, Ankur Taly, and Qiqi Yan. 2017.
71
+ "Axiomatic Attribution for Deep Networks."
72
+ *International Conference on Machine Learning (ICML)*.
73
+
74
+ SHAP and TreeSHAP:
75
+
76
+ - Lundberg, Scott M., and Su-In Lee. 2017.
77
+ "A Unified Approach to Interpreting Model Predictions."
78
+ *Advances in Neural Information Processing Systems (NeurIPS)*.
79
+
80
+ - Lundberg, Scott M., Gabriel Erion, and Su-In Lee. 2020.
81
+ "From Local Explanations to Global Understanding with Explainable AI for Trees."
82
+ *Nature Machine Intelligence*.
83
+
84
+ Popular implementations of Integrated Gradients for smooth models include:
85
+
86
+ - Captum for PyTorch:
87
+ https://captum.ai/
88
+
89
+ - TensorFlow Integrated Gradients tutorials:
90
+ https://www.tensorflow.org/tutorials/interpretability/integrated_gradients
91
+
92
+ ## Why TreeIG?
93
+
94
+ Standard Integrated Gradients defines feature contributions by integrating
95
+ model gradients along a path from a baseline input to the observation.
96
+ Tree models are piecewise constant, so ordinary gradients are zero almost
97
+ everywhere and undefined at split boundaries.
98
+
99
+ TreeIG uses the tree structure directly. Along the straight-line path
100
+
101
+ ```text
102
+ x(t) = x0 + t * (x - x0), 0 <= t <= 1,
103
+ ```
104
+
105
+ a tree prediction changes only when the path crosses a split threshold.
106
+ TreeIG finds those crossings exactly and assigns each jump in prediction
107
+ to the feature responsible for the crossing. For ensembles, contributions
108
+ are summed across trees.
109
+
110
+ This gives an exact additive decomposition for tree models without
111
+ numerical quadrature.
112
+
113
+ ## Relation to SHAP and TreeSHAP
114
+
115
+ TreeIG and TreeSHAP answer different attribution questions.
116
+
117
+ TreeSHAP computes Shapley-value attributions based on conditional or
118
+ interventional feature perturbations. Its contributions measure how
119
+ features contribute to the model prediction relative to a reference
120
+ distribution over feature subsets.
121
+
122
+ TreeIG instead explains the realized change in model output along a
123
+ specific path from a baseline input $x_0$ to an observation $x$.
124
+ The attribution is therefore path-based rather than subset-based.
125
+
126
+ For smooth models, TreeIG reduces to ordinary Integrated Gradients.
127
+ For tree models, TreeIG computes the exact path decomposition implied
128
+ by split crossings.
129
+
130
+ Neither framework dominates the other. They address different
131
+ counterfactual questions and therefore produce different decompositions.
132
+
133
+ ## Supported models
134
+
135
+ TreeIG currently supports finite numeric inputs for these model classes.
136
+
137
+ ### Regression
138
+
139
+ - `sklearn.tree.DecisionTreeRegressor`
140
+ - `sklearn.ensemble.RandomForestRegressor`
141
+ - `sklearn.ensemble.ExtraTreesRegressor`
142
+ - `sklearn.ensemble.GradientBoostingRegressor`
143
+ - `xgboost.XGBRegressor`
144
+ - `xgboost.Booster`
145
+ - `lightgbm.LGBMRegressor`
146
+ - `lightgbm.Booster`
147
+
148
+ ### Classification, raw margins only
149
+
150
+ - `sklearn.ensemble.GradientBoostingClassifier`
151
+ - `xgboost.XGBClassifier`
152
+ - `lightgbm.LGBMClassifier`
153
+
154
+ For classification models, TreeIG attributes raw scores, margins, or
155
+ logits. It does not currently attribute predicted probabilities.
156
+
157
+ ## Not currently supported
158
+
159
+ TreeIG deliberately does not yet support:
160
+
161
+ - probability-output attribution;
162
+ - missing-value routing;
163
+ - categorical splits;
164
+ - CatBoost;
165
+ - probability-averaging or vote-share classifiers such as
166
+ `DecisionTreeClassifier`, `RandomForestClassifier`, and
167
+ `ExtraTreesClassifier`.
168
+
169
+ ## Installation
170
+
171
+ ```bash
172
+ pip install treeig
173
+ ```
174
+
175
+ Or locally:
176
+
177
+ ```bash
178
+ pip install -e .
179
+ ```
180
+
181
+ ## Basic usage
182
+
183
+ ```python
184
+ import numpy as np
185
+ import treeig as tig
186
+
187
+ # model is a fitted supported tree model
188
+ x0 = X_train.mean(axis=0)
189
+ X_eval = X_test[:100]
190
+
191
+ ig = tig.TreeIG(model, baseline=x0)
192
+ phi = ig.attribute(X_eval)
193
+ ```
194
+
195
+ `phi` has the same shape as `X_eval`. Row `i`, column `j`
196
+ is the contribution of feature `j` to the model-output change from
197
+ `x0` to `X_eval[i]`.
198
+
199
+ For regression models:
200
+
201
+ ```python
202
+ np.testing.assert_allclose(
203
+ phi.sum(axis=1),
204
+ model.predict(X_eval) - model.predict(x0.reshape(1, -1))[0],
205
+ )
206
+ ```
207
+
208
+ ## Diagnostics
209
+
210
+ Use `explain` when you want attributions together with completeness
211
+ diagnostics.
212
+
213
+ ```python
214
+ ig = tig.TreeIG(model, baseline=x0)
215
+ phi, infos, summary = ig.explain(X_eval)
216
+
217
+ print(summary)
218
+ ```
219
+
220
+ Each entry in `infos` contains diagnostics for one observation:
221
+
222
+ ```python
223
+ {
224
+ "n_events": ..., # number of split-crossing events
225
+ "endpoint_delta": ..., # F(x) - F(x0)
226
+ "attribution_sum": ..., # sum_j phi_j
227
+ "residual": ..., # attribution_sum - endpoint_delta
228
+ "abs_residual": ...,
229
+ }
230
+ ```
231
+
232
+ The `summary` dictionary reports aggregate residual and event-count
233
+ statistics.
234
+
235
+ ## Classification targets
236
+
237
+ For binary additive-score classifiers, `target=None` and `target=1`
238
+ both attribute the positive-class margin. `target=0` attributes the
239
+ negative margin, implemented as the negative of the positive-class
240
+ margin.
241
+
242
+ ```python
243
+ ig = tig.TreeIG(model, baseline=x0, target=1)
244
+ phi_pos = ig.attribute(X_eval)
245
+
246
+ ig = tig.TreeIG(model, baseline=x0, target=0)
247
+ phi_neg = ig.attribute(X_eval)
248
+ ```
249
+
250
+ For multiclass classifiers, pass the class index explicitly.
251
+
252
+ ```python
253
+ ig = tig.TreeIG(model, baseline=x0, target=2)
254
+ phi_class_2 = ig.attribute(X_eval)
255
+ ```
256
+
257
+ TreeIG attributes raw class margins. If probability-space explanations
258
+ are needed, users should transform or interpret the margin-level
259
+ contributions separately.
260
+
261
+ ## Warmup
262
+
263
+ TreeIG uses Numba for fast attribution kernels. The first call may
264
+ include compilation time. You can compile the kernels in advance with
265
+ `warmup`.
266
+
267
+ ```python
268
+ ig = tig.TreeIG(model, baseline=x0).warmup(X_eval[:3])
269
+ phi = ig.attribute(X_eval)
270
+ ```
271
+
272
+ ## Functional interface
273
+
274
+ TreeIG also provides a direct functional interface.
275
+
276
+ ```python
277
+ phi, infos, summary = tig.compute(
278
+ model,
279
+ baseline=x0,
280
+ X=X_eval,
281
+ )
282
+ ```
283
+
284
+ For backward compatibility, the following aliases are also available:
285
+
286
+ ```python
287
+ from treeig import (
288
+ exact_gb_ig_batch_fast,
289
+ warmup_exact_gb_ig,
290
+ timed_call,
291
+ )
292
+ ```
293
+
294
+ ## Numerical conventions
295
+
296
+ TreeIG follows each backend's split-routing convention as closely as
297
+ possible.
298
+
299
+ - scikit-learn trees route left when `x[j] <= threshold`;
300
+ - LightGBM numeric splits route left when `x[j] <= threshold`;
301
+ - XGBoost numeric splits route left when `x[j] < threshold`
302
+ using float32-style comparisons.
303
+
304
+ Inputs must be finite numeric arrays. Missing-value routing is not
305
+ currently implemented, so `NaN` and `Inf` values raise errors.
306
+
307
+ ## Baselines
308
+
309
+ The baseline `x0` defines the reference point for the decomposition.
310
+ Common choices include:
311
+
312
+ - the training-sample mean;
313
+ - a median or representative observation;
314
+ - a domain-specific neutral input;
315
+ - a fixed benchmark case.
316
+
317
+ The attribution always explains the difference between the model output
318
+ at the observation and the model output at the chosen baseline.
319
+ Different baselines answer different questions.
320
+
321
+ ## Interpretation
322
+
323
+ For an observation `x`, TreeIG reports how much each feature contributes
324
+ to moving the model output from `F(x0)` to `F(x)` along the straight-line
325
+ path from `x0` to `x`.
326
+
327
+ Positive contributions increase the scalar output relative to the
328
+ baseline. Negative contributions decrease it. The contributions are
329
+ additive by construction.
330
+
331
+ ## Example: XGBoost regression
332
+
333
+ ```python
334
+ import numpy as np
335
+ import xgboost as xgb
336
+ import treeig as tig
337
+
338
+ model = xgb.XGBRegressor(
339
+ n_estimators=100,
340
+ max_depth=3,
341
+ learning_rate=0.05,
342
+ objective="reg:squarederror",
343
+ random_state=0,
344
+ )
345
+
346
+ model.fit(X_train, y_train)
347
+
348
+ x0 = X_train.mean(axis=0)
349
+ X_eval = X_test[:100]
350
+
351
+ ig = tig.TreeIG(model, baseline=x0).warmup(X_eval[:3])
352
+
353
+ phi, infos, summary = ig.explain(X_eval)
354
+
355
+ print(phi.shape)
356
+ print(summary["max_abs_residual"])
357
+ ```
358
+
359
+ ## Example: multiclass classification margins
360
+
361
+ ```python
362
+ import lightgbm as lgb
363
+ import treeig as tig
364
+
365
+ model = lgb.LGBMClassifier(...)
366
+ model.fit(X_train, y_train)
367
+
368
+ x0 = X_train.mean(axis=0)
369
+ X_eval = X_test[:100]
370
+
371
+ # Attribute class-2 raw margin
372
+ ig = tig.TreeIG(model, baseline=x0, target=2)
373
+
374
+ phi = ig.attribute(X_eval)
375
+ ```
376
+
377
+ ## Project status
378
+
379
+ TreeIG is intended for exact additive attribution of fitted tree models
380
+ in raw-output space. The current implementation focuses on correctness,
381
+ backend-specific routing consistency, and a compact API.
382
+
383
+ Future extensions may include:
384
+
385
+ - probability-space attribution;
386
+ - missing-value routing;
387
+ - categorical splits;
388
+ - CatBoost support;
389
+ - additional attribution paths and allocation rules.