从代码实践理解SHAP值:主效应值与交互效应值的构成解析

大模型机器学习数据库

picture.image

背景

SHAP值分解公式

SHAP值可以分解为两部分:

主效应值交互效应值

主效应值:表示每个特征单独作用时对模型预测结果的贡献,交互效应值:表示特征之间协同作用对模型预测结果的纯交互贡献,不包括单独作用的部分

主效应值的含义

特征的主效应值

主效应值 是特征 的独立作用贡献,表示特征 在不同背景下对模型预测值的边际平均贡献,它反应了特征 的独立影响,独立于其它特征的协同作用

交互效应值的含义

特征和之间的交互效应值

交互效应值 仅表示两个特征协同作用的纯贡献,排除了它们的主效应,通过 排除单独作用的贡献,这部分值纯粹反映了协同作用的增益或减益

SHAP值的完整解释

SHAP 值不仅可以分解为特征的独立作用(主效应),还可以分解为特征之间的相互影响(交互效应)。对于任意模型预测值 ,SHAP 保证如下的加性分解成立:

其中, 为基准值,即没有任何特征时的模型预测值, 主效应值, 交互效应值,当然SHAP值的计算实际上涉及深层的数学原理,尤其是基于Shapley值的博弈论方法,其核心思想是分配模型输出中的特征贡献,这远比简单的分解要复杂,这里主要是为了理解它的构成且接下来代码实现不会过多探讨 的实现,着重于通过代码实现SHAP值=主效应值+交互效应值

代码实现

模型构建


          
import pandas as pd
          
import numpy as np
          
import matplotlib.pyplot as plt 
          
import warnings
          
warnings.filterwarnings("ignore")
          

          
plt.rcParams['font.family'] = 'Times New Roman'
          
plt.rcParams['axes.unicode_minus'] = False
          
df = pd.read_excel('2024-11-27-公众号Python机器学习AI.xlsx')
          
from sklearn.model_selection import train_test_split, KFold
          

          
X = df.drop(['Y'],axis=1)
          
y = df['Y']
          

          
# 划分训练集和测试集
          
X_temp, X_test, y_temp, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
          
# 然后将训练集进一步划分为训练集和验证集
          
X_train, X_val, y_train, y_val = train_test_split(X_temp, y_temp, test_size=0.125, random_state=42)  # 0.125 x 0.8 = 0.1
          

          
import optuna  
          
from xgboost import XGBRegressor  
          
from sklearn.metrics import mean_squared_error
          
def objective(trial):
          
    params = {
          
        'n_estimators': trial.suggest_categorical('n_estimators', [50, 100, 200, 300]), 
          
        'max_depth': trial.suggest_int('max_depth', 3, 15, step=1), 
          
        'learning_rate': trial.suggest_loguniform('learning_rate', 0.01, 0.3), 
          
        'subsample': trial.suggest_uniform('subsample', 0.5, 1.0), 
          
        'colsample_bytree': trial.suggest_uniform('colsample_bytree', 0.5, 1.0),
          
        'gamma': trial.suggest_uniform('gamma', 0, 5)
          
    }
          

          
    model = XGBRegressor(**params, random_state=42)
          
    model.fit(X_train, y_train)
          
    y_pred = model.predict(X_val)
          
    return mean_squared_error(y_val, y_pred)
          

          
study = optuna.create_study(direction="minimize")
          
study.optimize(objective, n_trials=100)
          
print("Best parameters:", study.best_params)
          
best_model = XGBRegressor(**study.best_params, random_state=42)
          
best_model.fit(X_train, y_train)
      

picture.image

使用Optuna优化XGBoost回归模型的超参数,选择最佳模型参数后训练最终模型,为后续计算SHAP值提供一个已训练好的基准模型

计算SHAP值及其交互效应值


          
import shap
          
explainer = shap.TreeExplainer(best_model)
          
# 计算 SHAP 值
          
shap_values = explainer.shap_values(X_test)
          
# 计算 SHAP 交互值
          
shap_interaction_values = explainer.shap_interaction_values(X_test) 
      

使用SHAP库的TreeExplainer对已训练的模型计算了测试集特征的SHAP值和特征间的SHAP交互值,用于解释模型预测的特征贡献及交互效应

数据整理


        
            

          main\_effects\_df
        
      

picture.image

提取SHAP交互值矩阵的对角线元素,生成每个特征对应的主效应值,并将其存储为DataFrame


        
            

          df\_1
        
      

picture.image

提取每个特征与其他所有特征的交互效应值,并将其分别存储为多个DataFrame,每个特征都有与其余特征的交互效应值记录,代码与数据集获取:如需获取本文的源代码和数据集,请添加作者微信联系

以特征X_1为例验证SHAP值=主效应值+交互效应值


          
x1_shap_values = shap_values[:, 0] # 提取x_1的shap值
          
x1_main_effect = shap_interaction_values[:, 0, 0] # 提取x_1的主效应
          
x1_shap_total = x1_main_effect + df_1.sum(axis=1) # 根据shap值=主效应 + 交互效应 计算shap值
          
if np.allclose(shap_values[:,0], x1_main_effect + df_1.sum(axis=1)):
          
    print("验证成功:X_1 的总 SHAP 值等于主效应与交互效应之和!")
          
else:
          
    print("验证失败:X_1 的总 SHAP 值与主效应及交互效应之和不一致,请检查计算过程。")
      

picture.image

从shap_values中提取特征X_1的总SHAP 值(包含主效应和交互效应),并验证其是否等于主效应值与交互效应值之和

以特征X_1为例从可视化上展示 SHAP值=主效应值+交互效应值

X_1特征与其它特征的交互效应图


          
fig, axes = plt.subplots(2, 4, figsize=(20, 10), dpi=1200)
          
axes = axes.flatten()
          

          
# 提取 df_1 中的交互列
          
interaction_columns = df_1.columns
          
num_interactions = len(interaction_columns)
          

          
# 遍历交互列,绘制每个子图
          
for i, col in enumerate(interaction_columns):
          
    if i < len(axes):  # 确保不超出子图范围
          
        # 提取对应的交互特征名称
          
        interaction_feature = 'X_' + col.split('_')[-1]  # 修正特征名称格式
          
        
          
        if interaction_feature in X_test.columns:  # 检查特征是否存在
          
            # 绘制散点图
          
            sc = axes[i].scatter(X_test["X_1"], df_1[col], 
          
                                 s=10, c=X_test[interaction_feature], cmap='coolwarm')
          
            # 添加颜色条
          
            cbar = fig.colorbar(sc, ax=axes[i], aspect=30, shrink=0.8)
          
            cbar.set_label(interaction_feature, fontsize=10)
          
            cbar.outline.set_visible(False)
          

          
            # 添加水平参考线和标签
          
            axes[i].axhline(y=0, color='black', linestyle='-.', linewidth=1)
          
            axes[i].set_xlabel('X_1', fontsize=10)
          
            axes[i].set_ylabel(f'SHAP interaction\nfor X_1 and {interaction_feature}', fontsize=10)
          
            axes[i].spines['top'].set_visible(False)
          
            axes[i].spines['right'].set_visible(False)
          
        else:
          
            # 如果特征不存在,标注信息
          
            axes[i].text(0.5, 0.5, f"Feature {interaction_feature} not found", 
          
                         fontsize=12, ha='center', va='center')
          
            axes[i].set_axis_off()
          

          
# 移除未使用的子图
          
for j in range(num_interactions, len(axes)):
          
    fig.delaxes(axes[j])
          

          
plt.tight_layout()
          
plt.savefig("X1_SHAP_interaction_subplots_fixed.pdf", format='pdf', bbox_inches='tight', dpi=1200)
          
plt.show()
      

picture.image

X_1的主效应图


          
plt.figure(figsize=(6, 4), dpi=1200)
          
sc = plt.scatter(X_test["X_1"], main_effects_df['X_1'], 
          
                 s=10)
          
plt.axhline(y=0, color='black', linestyle='-.', linewidth=1)  
          
plt.xlabel('X_1', fontsize=12)
          
plt.ylabel('SHAP main effect value', fontsize=12)  
          
ax = plt.gca()
          
ax.spines['top'].set_visible(False)
          
ax.spines['right'].set_visible(False)
          
plt.savefig("SHAP main effect.pdf", format='pdf', bbox_inches='tight', dpi=1200)
          
plt.show()
      

picture.image

主效应值+交互效应值


          
plt.figure(figsize=(6, 4), dpi=1200)
          
sc = plt.scatter(X_test["X_1"], x1_shap_total, 
          
                 s=10)
          
plt.axhline(y=0, color='black', linestyle='-.', linewidth=1)  
          
plt.xlabel('X_1', fontsize=12)
          
plt.ylabel('SHAP value for\nX_1', fontsize=12)  
          
ax = plt.gca()
          
ax.spines['top'].set_visible(False)
          
ax.spines['right'].set_visible(False)
          
plt.savefig("SHAP——1.pdf", format='pdf', bbox_inches='tight', dpi=1200)
          
plt.show()
      

picture.image

对SHAP值


          
# 利用shap库自带函数绘制 SHAP 依赖图,不显示颜色条
          
# 计算shap值为Explanation格式
          
shap_values_Explanation = explainer(X_test)
          
shap.dependence_plot('X_1', shap_values_Explanation.values, X_test, interaction_index=None, show=False)
          
# 添加 SHAP=0 的横线
          
plt.axhline(y=0, color='black', linestyle='-.', linewidth=1)
          
plt.savefig("SHAP——2.pdf", format='pdf',bbox_inches='tight',dpi=1200)
          
plt.show()
      

picture.image

图三是利用公式 x1_shap_total = x1_main_effect + df_1.sum(axis=1) 计算得到的总 SHAP 值可视化,显示了特征 的整体贡献,既包括其主效应值(x1_main_effect)即特征单独作用的贡献,也包括交互效应值(df_1.sum(axis=1),该特征与其它所有特征交互效应值之和)即特征与其他特征协同作用的贡献;图四则直接通过 explainer.shap_values(X_test) 计算 SHAP 值,绘制了常见的 SHAP 依赖图也就是文献中最常见的一种SHAP可视化图,表示 对模型预测的整体影响,包含了单独作用和协同作用,且图三和图四的分布完全一致,进一步通过代码验证分解公式SHAP值=主效应值+交互效应值,此外,图一单独可视化了 的交互效应(df_1),表示特征协同作用的纯贡献;图二可视化 的主效应值(main_effects_df['X_1']),表示特征单独作用的贡献,综合这些图表,读者能够清晰地理解 SHAP 值的来源及其可视化在解释特征对模型预测影响中的作用,也就可以对前两篇文章有更清晰的理解——期刊配图:从SHAP依赖图到交互图优化,解析特征协同作用期刊配图:SHAP主效应图绘制解释单个特征在独立作用时对模型预测的贡献

🎁 赠书活动来啦!🎁

picture.image

picture.image

支持知识分享,畅享学习乐趣!特别感谢清华出版社 对本次赠书活动的鼎力支持!即日起,只需

点赞、在看、转发 此文章,作者将从后台随机抽取一位幸运儿,免费包邮赠送清华出版社提供的《ChatGLM3大模型本地化部署、应用开发与微调》这本精彩书籍📚!

💡 赶快参与,一键三连,说不定你就是那位幸运读者哦!

往期推荐

SCI图表复现:整合数据分布与相关系数的高级可视化策略

SCI图表:基于相关性和标准差的多模型评价——泰勒图解析

期刊文章配图:基于分组折线图的多机器学习模型表现评估对比

复现SCI文章 SHAP 依赖图可视化以增强机器学习模型的可解释性

SCI图表复现:优化SHAP特征贡献图展示更多模型细节

复现 Nature 图表——基于PCA的高维数据降维与可视化实践及其扩展

复现Nature图表——基于PCA降维与模型预测概率的分类效果可视化

SCI图表复现:特征相关性气泡热图展示

一图胜千言:回归预测模型训练集与测试集的进阶可视化

期刊文章配图:基于雷达图的多机器学习模型表现评估对比

期刊文章配图:斯皮尔曼相关系数热图反应非线性变量相关性

picture.image

如果你对类似于这样的文章感兴趣。

欢迎关注、点赞、转发~

个人观点,仅供参考

0
0
0
0
关于作者

文章

0

获赞

0

收藏

0

相关资源
字节跳动 XR 技术的探索与实践
火山引擎开发者社区技术大讲堂第二期邀请到了火山引擎 XR 技术负责人和火山引擎创作 CV 技术负责人,为大家分享字节跳动积累的前沿视觉技术及内外部的应用实践,揭秘现代炫酷的视觉效果背后的技术实现。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论