🌐 EN | 🇯🇵 JP | Last sync: 2025-11-16

Chapter 2: SHAP (SHapley Additive exPlanations)

Unified Feature Importance Based on Game Theory

📖 Reading Time: 35-40 minutes 📊 Difficulty: Intermediate to Advanced 💻 Code Examples: 10 📝 Exercises: 5

This chapter covers SHAP (SHapley Additive exPlanations). You will learn mathematical formulation, algorithms such as TreeSHAP, and and visualize using the SHAP library.

Learning Objectives

By reading this chapter, you will be able to:


2.1 Theory of Shapley Values

Foundations of Game Theory

Shapley values are a concept from cooperative game theory that fairly evaluates the contribution of players.

Definition of Cooperative Games

A cooperative game $(N, v)$ is defined as follows:

Conditions:

Application to Machine Learning

In machine learning interpretation problems:

graph TB GameTheory["Cooperative Game Theory
N: Players, v: Value function"] --> Shapley["Shapley Values
Fair contribution distribution"] Shapley --> ML["Machine Learning Interpretation
Feature contributions"] ML --> SHAP["SHAP
Unified interpretation framework"] style GameTheory fill:#b3e5fc style Shapley fill:#c5e1a5 style ML fill:#fff9c4 style SHAP fill:#ffab91

Definition of Shapley Values

The Shapley value of player $i$ is defined as the average of contributions to all possible coalitions:

$$ \phi_i(v) = \sum_{S \subseteq N \setminus \{i\}} \frac{|S|! \cdot (|N| - |S| - 1)!}{|N|!} \left[ v(S \cup \{i\}) - v(S) \right] $$

Where:

Properties (Axioms) of Shapley Values

Shapley values are the unique solution satisfying the following four axioms:

Axiom Mathematical Expression Meaning
Efficiency $\sum_{i=1}^{n} \phi_i(v) = v(N)$ Sum of all players' contributions = Total value
Symmetry $v(S \cup \{i\}) = v(S \cup \{j\})$ implies $\phi_i = \phi_j$ Same contribution gets same reward
Dummy $v(S \cup \{i\}) = v(S)$ implies $\phi_i = 0$ No contribution means zero reward
Additivity $\phi_i(v + w) = \phi_i(v) + \phi_i(w)$ Independent games are decomposable

Computational Complexity

Exact computation of Shapley values requires evaluating $2^n$ coalitions, resulting in exponential time complexity $O(2^n)$.

# Requirements:
# - Python 3.9+
# - numpy>=1.24.0, <2.0.0

import numpy as np
from itertools import combinations

def shapley_value_exact(n, value_function):
    """
    Exact computation of Shapley values (only for small number of features)

    Args:
        n: Number of features
        value_function: Function returning value for subset S

    Returns:
        shapley_values: (n,) Shapley values
    """
    players = list(range(n))
    shapley_values = np.zeros(n)

    # For each player
    for i in players:
        # Other players excluding player i
        others = [p for p in players if p != i]

        # For all possible coalitions S
        for r in range(len(others) + 1):
            for S in combinations(others, r):
                S = list(S)

                # Marginal contribution: v(S ∪ {i}) - v(S)
                marginal_contribution = value_function(S + [i]) - value_function(S)

                # Weight: |S|! * (n - |S| - 1)! / n!
                weight = (np.math.factorial(len(S)) *
                         np.math.factorial(n - len(S) - 1) /
                         np.math.factorial(n))

                shapley_values[i] += weight * marginal_contribution

    return shapley_values


# Simple example: 3 features
print("=== Exact Computation of Shapley Values ===")
n = 3

# Example value function (linear model)
def value_func(S):
    """Prediction value for feature subset S"""
    # Simplified: feature weights are [1, 2, 3]
    weights = np.array([1.0, 2.0, 3.0])
    if len(S) == 0:
        return 0.0
    return weights[S].sum()

# Compute Shapley values
shapley = shapley_value_exact(n, value_func)

print(f"Number of features: {n}")
print(f"Shapley values: {shapley}")
print(f"Sum: {shapley.sum()} (= v(N) = {value_func([0, 1, 2])})")
print("\n→ Satisfies efficiency axiom: sum = total value")

Computational Complexity Problem

# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - numpy>=1.24.0, <2.0.0

"""
Example: Computational Complexity Problem

Purpose: Demonstrate data visualization techniques
Target: Beginner to Intermediate
Execution time: 2-5 seconds
Dependencies: None
"""

import matplotlib.pyplot as plt
import numpy as np

# Visualization of computational complexity
n_features = np.arange(1, 21)
num_coalitions = 2 ** n_features

plt.figure(figsize=(10, 6))
plt.semilogy(n_features, num_coalitions, 'o-', linewidth=2, markersize=8)
plt.xlabel('Number of Features', fontsize=12)
plt.ylabel('Number of Coalitions (log scale)', fontsize=12)
plt.title('Computational Complexity of Exact Shapley Value', fontsize=14)
plt.grid(True, alpha=0.3)

# Reference lines
plt.axhline(y=1e6, color='r', linestyle='--', label='1 million')
plt.axhline(y=1e9, color='orange', linestyle='--', label='1 billion')

plt.legend()
plt.tight_layout()
plt.savefig('shapley_complexity.png', dpi=150, bbox_inches='tight')
print("Saved complexity figure: shapley_complexity.png")
plt.close()

print("\n=== Computational Complexity Examples ===")
for n in [5, 10, 15, 20, 30]:
    coalitions = 2 ** n
    print(f"Features {n:2d}: {coalitions:,} coalitions")

print("\n→ Computation becomes infeasible as features increase")
print("→ Approximation algorithms (KernelSHAP, TreeSHAP, etc.) are necessary")

2.2 SHAP Values

Additive Feature Attribution

SHAP (SHapley Additive exPlanations) is a framework that applies Shapley values to machine learning interpretation.

The explanation model $g$ for prediction $f(x)$ has the following form:

$$ g(z') = \phi_0 + \sum_{i=1}^{M} \phi_i z'_i $$

Where:

Definition of SHAP Values

The SHAP value of feature $i$ for input $x$ is:

$$ \phi_i(f, x) = \sum_{S \subseteq F \setminus \{i\}} \frac{|S|! \cdot (|F| - |S| - 1)!}{|F|!} \left[ f_x(S \cup \{i\}) - f_x(S) \right] $$

Where:

Computing SHAP Values

In practice, missing features are computed by conditioning on expected values:

$$ f_x(S) = \mathbb{E}[f(X) \mid X_S = x_S] $$

Different SHAP algorithms exist depending on how this expectation is computed.

graph TB SHAP["SHAP
Unified Framework"] --> TreeSHAP["TreeSHAP
Decision tree models
Polynomial time"] SHAP --> KernelSHAP["KernelSHAP
Any model
Sampling approximation"] SHAP --> DeepSHAP["DeepSHAP
Deep learning
Gradient-based"] SHAP --> LinearSHAP["LinearSHAP
Linear models
Analytical computation"] style SHAP fill:#ffab91 style TreeSHAP fill:#c5e1a5 style KernelSHAP fill:#fff9c4 style DeepSHAP fill:#b3e5fc

TreeSHAP Algorithm

TreeSHAP is an efficient algorithm for tree-based models (decision trees, random forests, XGBoost, LightGBM, etc.).

Features:

KernelSHAP

KernelSHAP is an approximation algorithm applicable to any model.

Idea:

  1. Randomly sample feature subsets
  2. Compute predictions for each subset
  3. Estimate SHAP values via weighted linear regression

Weight function:

$$ \pi_{x}(z') = \frac{(M-1)}{\binom{M}{|z'|} |z'|(M - |z'|)} $$

2.3 SHAP Visualization

Waterfall Plots

Waterfall plots display the contribution of each feature from the base value to the final prediction value for a single sample, like a waterfall.

# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - numpy>=1.24.0, <2.0.0
# - shap>=0.42.0

"""
Example: Waterfall plotsdisplay the contribution of each feature from

Purpose: Demonstrate data visualization techniques
Target: Advanced
Execution time: 30-60 seconds
Dependencies: None
"""

import shap
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer

# Load dataset
print("=== Preparing Breast Cancer Dataset ===")
X, y = load_breast_cancer(return_X_y=True, as_frame=True)
print(f"Data size: {X.shape}")
print(f"Number of features: {X.shape[1]}")

# Train model
print("\n=== Training Random Forest ===")
model = RandomForestClassifier(n_estimators=100, random_state=42, max_depth=5)
model.fit(X, y)

train_acc = model.score(X, y)
print(f"Training accuracy: {train_acc:.4f}")

# Create TreeExplainer
print("\n=== Creating TreeExplainer ===")
explainer = shap.TreeExplainer(model)

# Compute SHAP values for one sample
sample_idx = 0
shap_values = explainer(X.iloc[[sample_idx]])

print(f"SHAP values for sample {sample_idx}:")
print(f"  Shape: {shap_values.values.shape}")
print(f"  Base value: {shap_values.base_values[0]:.4f}")
print(f"  Prediction: {shap_values.base_values[0] + shap_values.values[0].sum():.4f}")

# Create waterfall plot
print("\n=== Creating Waterfall Plot ===")
shap.plots.waterfall(shap_values[0], show=False)
import matplotlib.pyplot as plt
plt.tight_layout()
plt.savefig('shap_waterfall.png', dpi=150, bbox_inches='tight')
print("Saved waterfall plot: shap_waterfall.png")
plt.close()

Force Plots

Force plots visualize single-sample explanations similar to waterfall plots, but display positive and negative contributions side by side.

# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - shap>=0.42.0

"""
Example: Force plotsvisualize single-sample explanations similar to w

Purpose: Demonstrate data visualization techniques
Target: Beginner to Intermediate
Execution time: 2-5 seconds
Dependencies: None
"""

import shap
import matplotlib.pyplot as plt

print("\n=== Creating Force Plot ===")

# Force plot (static version)
shap.plots.force(
    shap_values.base_values[0],
    shap_values.values[0],
    X.iloc[sample_idx],
    matplotlib=True,
    show=False
)
plt.tight_layout()
plt.savefig('shap_force.png', dpi=150, bbox_inches='tight')
print("Saved force plot: shap_force.png")
plt.close()

print("\n→ Red: Features increasing prediction")
print("→ Blue: Features decreasing prediction")

Summary Plots

Summary plots aggregate SHAP values across all samples to visualize the importance and effects of each feature.

# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - shap>=0.42.0

"""
Example: Summary plotsaggregate SHAP values across all samples to vis

Purpose: Demonstrate data visualization techniques
Target: Beginner to Intermediate
Execution time: 2-5 seconds
Dependencies: None
"""

import shap
import matplotlib.pyplot as plt

print("\n=== Creating Summary Plot ===")

# Compute SHAP values for all samples (use subset if time-consuming)
shap_values_all = explainer(X[:100])  # First 100 samples

# Summary plot (bee swarm plot)
shap.summary_plot(shap_values_all, X[:100], show=False)
plt.tight_layout()
plt.savefig('shap_summary.png', dpi=150, bbox_inches='tight')
print("Saved summary plot: shap_summary.png")
plt.close()

print("\n→ Each point is one sample")
print("→ X-axis: SHAP value (contribution)")
print("→ Color: Feature value (red=high, blue=low)")

Bar Plots (Feature Importance)

# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - shap>=0.42.0

"""
Example: Bar Plots (Feature Importance)

Purpose: Demonstrate data visualization techniques
Target: Beginner to Intermediate
Execution time: 2-5 seconds
Dependencies: None
"""

import shap
import matplotlib.pyplot as plt

print("\n=== Bar Plot (Feature Importance) ===")

# Sort by mean absolute SHAP value
shap.plots.bar(shap_values_all, show=False)
plt.tight_layout()
plt.savefig('shap_bar.png', dpi=150, bbox_inches='tight')
print("Saved bar plot: shap_bar.png")
plt.close()

# Manual computation
mean_abs_shap = np.abs(shap_values_all.values).mean(axis=0)
feature_importance = sorted(
    zip(X.columns, mean_abs_shap),
    key=lambda x: x[1],
    reverse=True
)

print("\nFeature importance (top 10):")
for i, (feature, importance) in enumerate(feature_importance[:10]):
    print(f"{i+1:2d}. {feature:25s}: {importance:.4f}")

Dependence Plots

Dependence plots show the relationship between a feature's value and its SHAP value in a scatter plot.

# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - shap>=0.42.0

"""
Example: Dependence plotsshow the relationship between a feature's va

Purpose: Demonstrate data visualization techniques
Target: Beginner to Intermediate
Execution time: 2-5 seconds
Dependencies: None
"""

import shap
import matplotlib.pyplot as plt

print("\n=== Creating Dependence Plot ===")

# Select the most important feature
top_feature = feature_importance[0][0]
print(f"Analysis target: {top_feature}")

# Dependence plot
shap.dependence_plot(
    top_feature,
    shap_values_all.values,
    X[:100],
    show=False
)
plt.tight_layout()
plt.savefig('shap_dependence.png', dpi=150, bbox_inches='tight')
print("Saved dependence plot: shap_dependence.png")
plt.close()

print("\n→ X-axis: Feature value")
print("→ Y-axis: SHAP value (contribution of that feature)")
print("→ Can discover non-linear relationships and interactions")

2.4 Practical Use of SHAP Library

TreeExplainer (Tree-based Models)

TreeExplainer is the most efficient explainer for tree-based models (RandomForest, XGBoost, LightGBM, CatBoost, etc.).

# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - numpy>=1.24.0, <2.0.0
# - shap>=0.42.0
# - xgboost>=2.0.0

"""
Example: TreeExplaineris the most efficient explainer for tree-based 

Purpose: Demonstrate data visualization techniques
Target: Beginner to Intermediate
Execution time: 30-60 seconds
Dependencies: None
"""

import shap
import xgboost as xgb
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
import numpy as np

# Data preparation (Boston Housing - regression task)
print("=== Boston Housing Dataset ===")
# Note: load_boston is deprecated, using California housing as alternative
from sklearn.datasets import fetch_california_housing
data = fetch_california_housing(as_frame=True)
X, y = data.data, data.target

print(f"Data size: {X.shape}")
print(f"Features: {list(X.columns)}")

# Train/test split
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Train XGBoost model
print("\n=== Training XGBoost Model ===")
model = xgb.XGBRegressor(
    n_estimators=100,
    max_depth=5,
    learning_rate=0.1,
    random_state=42
)
model.fit(X_train, y_train)

train_score = model.score(X_train, y_train)
test_score = model.score(X_test, y_test)
print(f"Train R²: {train_score:.4f}")
print(f"Test R²: {test_score:.4f}")

# Using TreeExplainer
print("\n=== Using TreeExplainer ===")
explainer = shap.TreeExplainer(model)

# Compute SHAP values
shap_values = explainer(X_test)

print(f"SHAP values shape: {shap_values.values.shape}")
print(f"Base value: {shap_values.base_values[0]:.4f}")

# Summary plot
shap.summary_plot(shap_values, X_test, show=False)
import matplotlib.pyplot as plt
plt.tight_layout()
plt.savefig('xgboost_shap_summary.png', dpi=150, bbox_inches='tight')
print("Saved summary plot: xgboost_shap_summary.png")
plt.close()

LinearExplainer (Linear Models)

LinearExplainer is an analytical explainer for linear models (linear regression, logistic regression, etc.).

# Requirements:
# - Python 3.9+
# - shap>=0.42.0

"""
Example: LinearExplaineris an analytical explainer for linear models 

Purpose: Demonstrate data visualization techniques
Target: Beginner
Execution time: 30-60 seconds
Dependencies: None
"""

import shap
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

print("\n=== LinearExplainer (Logistic Regression) ===")

# Iris dataset
X, y = load_iris(return_X_y=True, as_frame=True)
# Simplify to binary classification
X = X[y != 2]
y = y[y != 2]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42
)

# Logistic regression
model = LogisticRegression()
model.fit(X_train, y_train)

print(f"Test accuracy: {model.score(X_test, y_test):.4f}")

# LinearExplainer
explainer = shap.LinearExplainer(model, X_train)
shap_values = explainer(X_test)

print(f"SHAP values shape: {shap_values.values.shape}")

# Waterfall plot (1 sample)
shap.plots.waterfall(shap_values[0], show=False)
plt.tight_layout()
plt.savefig('linear_shap_waterfall.png', dpi=150, bbox_inches='tight')
print("Saved waterfall plot: linear_shap_waterfall.png")
plt.close()

KernelExplainer (Any Model)

KernelExplainer can be applied to any model (including black-box models) but has high computational cost.

# Requirements:
# - Python 3.9+
# - numpy>=1.24.0, <2.0.0
# - shap>=0.42.0

"""
Example: KernelExplainercan be applied to any model (including black-

Purpose: Demonstrate data visualization techniques
Target: Advanced
Execution time: 30-60 seconds
Dependencies: None
"""

import shap
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.datasets import load_wine
import numpy as np

print("\n=== KernelExplainer (Any Model) ===")

# Wine dataset
X, y = load_wine(return_X_y=True, as_frame=True)

# Train model
model = GradientBoostingClassifier(n_estimators=50, random_state=42)
model.fit(X, y)

print(f"Training accuracy: {model.score(X, y):.4f}")

# KernelExplainer (using small sample due to high computational cost)
print("\n=== Creating KernelExplainer (background data: 50 samples) ===")
background = shap.sample(X, 50)  # Background data
explainer = shap.KernelExplainer(model.predict_proba, background)

# Compute SHAP values (only 3 samples)
print("Computing SHAP values (this may take time)...")
test_samples = X.iloc[:3]
shap_values = explainer.shap_values(test_samples)

print(f"SHAP values shape: {np.array(shap_values).shape}")
print("→ (number of classes, samples, features)")

# Force plot (class 0)
shap.force_plot(
    explainer.expected_value[0],
    shap_values[0][0],
    test_samples.iloc[0],
    matplotlib=True,
    show=False
)
plt.tight_layout()
plt.savefig('kernel_shap_force.png', dpi=150, bbox_inches='tight')
print("Saved force plot: kernel_shap_force.png")
plt.close()

print("\n→ KernelSHAP is slow but applicable to any model")

DeepExplainer (Neural Networks)

DeepExplainer is an efficient explainer for deep learning models (TensorFlow, PyTorch, etc.).

# Requirements:
# - Python 3.9+
# - numpy>=1.24.0, <2.0.0
# - shap>=0.42.0
# - torch>=2.0.0, <2.3.0

"""
Example: DeepExplaineris an efficient explainer for deep learning mod

Purpose: Demonstrate data visualization techniques
Target: Advanced
Execution time: 1-5 minutes
Dependencies: None
"""

import shap
import torch
import torch.nn as nn
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
import numpy as np

print("\n=== DeepExplainer (PyTorch Neural Network) ===")

# Generate data
X, y = make_classification(
    n_samples=1000,
    n_features=20,
    n_informative=15,
    n_classes=2,
    random_state=42
)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Convert to PyTorch data
X_train_t = torch.FloatTensor(X_train)
y_train_t = torch.LongTensor(y_train)
X_test_t = torch.FloatTensor(X_test)

# Simple neural network
class SimpleNN(nn.Module):
    def __init__(self, input_dim):
        super(SimpleNN, self).__init__()
        self.fc1 = nn.Linear(input_dim, 64)
        self.fc2 = nn.Linear(64, 32)
        self.fc3 = nn.Linear(32, 2)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

model = SimpleNN(input_dim=20)

# Training (simplified)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

for epoch in range(50):
    optimizer.zero_grad()
    outputs = model(X_train_t)
    loss = criterion(outputs, y_train_t)
    loss.backward()
    optimizer.step()

# Evaluation
model.eval()
with torch.no_grad():
    outputs = model(X_test_t)
    _, predicted = torch.max(outputs, 1)
    accuracy = (predicted == torch.LongTensor(y_test)).float().mean()
    print(f"Test accuracy: {accuracy:.4f}")

# DeepExplainer
print("\n=== Using DeepExplainer ===")
background = X_train_t[:100]  # Background data
explainer = shap.DeepExplainer(model, background)

# Compute SHAP values
test_samples = X_test_t[:10]
shap_values = explainer.shap_values(test_samples)

print(f"SHAP values shape: {np.array(shap_values).shape}")

# Summary plot (class 1)
shap.summary_plot(
    shap_values[1],
    test_samples.numpy(),
    show=False
)
plt.tight_layout()
plt.savefig('deep_shap_summary.png', dpi=150, bbox_inches='tight')
print("Saved summary plot: deep_shap_summary.png")
plt.close()

2.5 Applications and Limitations of SHAP

Model Diagnosis

SHAP can be used to diagnose model problems:

# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - numpy>=1.24.0, <2.0.0
# - shap>=0.42.0

"""
Example: SHAP can be used to diagnose model problems:

Purpose: Demonstrate data visualization techniques
Target: Advanced
Execution time: 30-60 seconds
Dependencies: None
"""

import shap
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification

print("=== Model Diagnosis with SHAP ===")

# Create data with intentional bias
np.random.seed(42)
n_samples = 1000
X = np.random.randn(n_samples, 10)

# Only features 0 and 1 are truly important
y = (X[:, 0] + X[:, 1] > 0).astype(int)

# Add noise feature with high correlation
X[:, 2] = X[:, 0] + np.random.randn(n_samples) * 0.1  # Correlated with feature 0

# Train model
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X, y)

print(f"Training accuracy: {model.score(X, y):.4f}")

# SHAP analysis
explainer = shap.TreeExplainer(model)
shap_values = explainer(X)

# Feature importance
mean_abs_shap = np.abs(shap_values.values).mean(axis=0)

plt.figure(figsize=(10, 6))
plt.bar(range(10), mean_abs_shap)
plt.xlabel('Feature Index')
plt.ylabel('Mean |SHAP value|')
plt.title('Feature Importance via SHAP')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('shap_diagnosis.png', dpi=150, bbox_inches='tight')
print("Saved diagnosis result: shap_diagnosis.png")
plt.close()

print("\nFeature importance:")
for i, importance in enumerate(mean_abs_shap):
    print(f"  Feature {i}: {importance:.4f}")

print("\n→ Feature 2 (correlated noise) may be misidentified as important")
print("→ SHAP discovers multicollinearity problems")

Feature Selection

SHAP can be used to select truly important features:

# Requirements:
# - Python 3.9+
# - numpy>=1.24.0, <2.0.0
# - shap>=0.42.0

"""
Example: SHAP can be used to select truly important features:

Purpose: Demonstrate machine learning model training and evaluation
Target: Advanced
Execution time: 30-60 seconds
Dependencies: None
"""

import shap
import numpy as np
from sklearn.ensemble import RandomForestRegressor
from sklearn.datasets import make_regression
from sklearn.model_selection import cross_val_score

print("\n=== Feature Selection with SHAP ===")

# Generate data (20 features, only 5 are important)
X, y = make_regression(
    n_samples=500,
    n_features=20,
    n_informative=5,
    noise=10.0,
    random_state=42
)

# Initial model
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X, y)

# Compute feature importance with SHAP values
explainer = shap.TreeExplainer(model)
shap_values = explainer(X)
mean_abs_shap = np.abs(shap_values.values).mean(axis=0)

# Sort by importance
feature_importance = sorted(
    enumerate(mean_abs_shap),
    key=lambda x: x[1],
    reverse=True
)

print("Feature importance (SHAP):")
for i, (idx, importance) in enumerate(feature_importance[:10]):
    print(f"{i+1:2d}. Feature {idx:2d}: {importance:.4f}")

# Retrain with top k features
for k in [5, 10, 15, 20]:
    top_features = [idx for idx, _ in feature_importance[:k]]
    X_selected = X[:, top_features]

    scores = cross_val_score(
        RandomForestRegressor(n_estimators=100, random_state=42),
        X_selected, y, cv=5, scoring='r2'
    )

    print(f"\nTop {k:2d} features: CV R² = {scores.mean():.4f} ± {scores.std():.4f}")

print("\n→ Top 5 features are sufficient for good performance")
print("→ Efficient feature selection with SHAP")

Computational Cost

The computational cost of SHAP varies greatly depending on the algorithm and model:

# Requirements:
# - Python 3.9+
# - numpy>=1.24.0, <2.0.0
# - shap>=0.42.0

"""
Example: The computational cost of SHAP varies greatly depending on t

Purpose: Demonstrate machine learning model training and evaluation
Target: Advanced
Execution time: 30-60 seconds
Dependencies: None
"""

import shap
import numpy as np
import time
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification

print("\n=== SHAP Computational Cost Comparison ===")

# Measure computation time with varying data sizes
results = []

for n_samples in [100, 500, 1000, 2000]:
    X, y = make_classification(
        n_samples=n_samples,
        n_features=20,
        n_informative=15,
        random_state=42
    )

    # Train model
    model = RandomForestClassifier(n_estimators=50, max_depth=5, random_state=42)
    model.fit(X, y)

    # TreeExplainer
    explainer_tree = shap.TreeExplainer(model)
    start = time.time()
    shap_values_tree = explainer_tree(X)
    time_tree = time.time() - start

    # KernelExplainer (only for small samples)
    if n_samples <= 500:
        background = shap.sample(X, 50)
        explainer_kernel = shap.KernelExplainer(model.predict_proba, background)
        test_samples = X[:10]  # Only 10 samples
        start = time.time()
        shap_values_kernel = explainer_kernel.shap_values(test_samples)
        time_kernel = time.time() - start
    else:
        time_kernel = np.nan

    results.append((n_samples, time_tree, time_kernel))
    print(f"Samples {n_samples:4d}: TreeSHAP={time_tree:.3f}s, KernelSHAP={time_kernel:.3f}s")

print("\n→ TreeSHAP is fast even for large-scale data")
print("→ KernelSHAP is practical only for small-scale data")

Interpretation Caveats

Caveat Explanation Mitigation
Multicollinearity SHAP values unstable among correlated features Use with correlation analysis, pre-select features
Background data selection KernelSHAP results depend on background data Select representative samples, validate with multiple backgrounds
Extrapolation Reliability decreases outside training data range Use with prediction confidence intervals
Causality SHAP values indicate correlation, not causation Use with causal inference methods

Exercises

Exercise 1: Comparing TreeSHAP and KernelSHAP

Compute SHAP values for the same dataset and model using both TreeSHAP and KernelSHAP, and compare the results. How closely do they match?

# Requirements:
# - Python 3.9+
# - shap>=0.42.0

"""
Example: Compute SHAP values for the same dataset and model using bot

Purpose: Demonstrate data visualization techniques
Target: Advanced
Execution time: 2-5 seconds
Dependencies: None
"""

import shap
from sklearn.ensemble import RandomForestClassifier

# Exercise: Compare with scatter plot
# Expected: Nearly identical, but KernelSHAP slightly differs due to approximation
Exercise 2: Investigating Impact of Multicollinearity

Create intentionally correlated features and investigate how they are interpreted by SHAP.

# Requirements:
# - Python 3.9+
# - numpy>=1.24.0, <2.0.0
# - shap>=0.42.0

"""
Example: Create intentionally correlated features and investigate how

Purpose: Demonstrate core concepts and implementation patterns
Target: Advanced
Execution time: 1-5 minutes
Dependencies: None
"""

import numpy as np
import shap

# Exercise: Create X1 and X2 (X2 = X1 + noise)
# Exercise: Train model and analyze with SHAP
# Exercise: Compare SHAP values of X1 and X2
# Analysis: SHAP values are distributed among correlated features
Exercise 3: Anomaly Detection with SHAP Values

Compare the distribution of SHAP values between normal and anomalous samples to identify features causing anomalies.

# Requirements:
# - Python 3.9+
# - shap>=0.42.0

"""
Example: Compare the distribution of SHAP values between normal and a

Purpose: Demonstrate data visualization techniques
Target: Advanced
Execution time: 2-5 seconds
Dependencies: None
"""

import shap
from sklearn.ensemble import IsolationForest

# Exercise: Create normal and anomalous data
# Exercise: Analyze SHAP values of anomalous samples
# Hint: Utilize summary plots and dependence plots
Exercise 4: SHAP Application on Time Series Data

Apply SHAP to time series data (e.g., predicting next day from past N days) and analyze which time points are important.

# Requirements:
# - Python 3.9+
# - numpy>=1.24.0, <2.0.0
# - shap>=0.42.0

"""
Example: Apply SHAP to time series data (e.g., predicting next day fr

Purpose: Demonstrate core concepts and implementation patterns
Target: Beginner to Intermediate
Execution time: ~5 seconds
Dependencies: None
"""

import numpy as np
import shap

# Exercise: Create lag features (t-1, t-2, ..., t-N)
# Exercise: Analyze importance of each time point with SHAP
# Expected: Recent time points are generally more important
Exercise 5: Comparing SHAP and PFI (Permutation Feature Importance)

Compare feature importance by SHAP with Permutation Feature Importance and analyze the differences.

# Requirements:
# - Python 3.9+
# - shap>=0.42.0

"""
Example: Compare feature importance by SHAP with Permutation Feature 

Purpose: Demonstrate core concepts and implementation patterns
Target: Beginner to Intermediate
Execution time: 1-5 minutes
Dependencies: None
"""

import shap
from sklearn.inspection import permutation_importance

# Exercise: Train model
# Exercise: Analyze correlation between the two importances
# Exercise: Investigate features with large differences
# Analysis: SHAP is local, PFI is global importance

Summary

In this chapter, we learned the theory and practice of SHAP (SHapley Additive exPlanations).

Key Points

Advantages and Limitations of SHAP

Aspect Advantages Limitations
Theoretical Foundation Solid game theory basis Computational complexity problem (exact computation difficult)
Consistency Consistent interpretation through axiomatic properties Unstable with multicollinearity
Application Scope Applicable to any model Computational cost varies greatly by model
Interpretability Both local explanation and global importance Risk of confusing correlation with causation

Next Steps

In the next chapter, we will learn about Integrated Gradients and Attention mechanisms. We will acquire more advanced interpretation techniques, including deep learning model interpretation methods, gradient-based attribution analysis, and visualization of attention mechanisms in Transformer models.

References

Disclaimer