深度解析SHAP:机器学习与深度学习模型解释的全面指南

文摘   2024-06-27 20:02   江西  

什么是SHAP解释?

在机器学习和深度学习领域,当面对那些具有出色预测性能但内部决策过程不透明的复杂模型时,如深度神经网络和集成学习模型(如XGBoost、LightGBM),模型的可解释性成为一个关键议题。这些模型通常被称为“黑箱”模型,因为它们的工作机制对于非专业人士来说往往难以理解。为了揭开这些黑箱的面纱,研究者们开发了各种工具来尝试解释这些模型的预测。

SHAP(SHapley Additive exPlanations)就是这样一种工具,它提供了一种方法,通过为模型的每个特征分配一个重要性值(即SHAP值),来解释模型输出(即预测结果)的具体原因。这些SHAP值可以揭示出哪些特征对模型的预测结果产生了显著影响,以及这些影响是正面的还是负面的。

SHAP的核心理念
SHAP(SHapley Additive exPlanations)的核心思想确实源自于合作博弈论中的Shapley值。在合作博弈论中,Shapley值被用来衡量每个参与者在联盟(或合作)中的贡献,从而确保收益的公平分配。SHAP将这种概念巧妙地引入到机器学习模型的解释中,用于量化每个特征对模型预测结果的贡献。
SHAP的特点

一致性:在评估机器学习模型的特征贡献时,SHAP方法遵循一个重要的原则,即如果某个特征对模型预测结果的贡献增加,那么其对应的SHAP值也会相应地增加。这一性质确保了SHAP值在衡量特征重要性时的一致性和可靠性。

局部解释:SHAP值不仅限于对模型整体行为的解释,它还能为单个预测结果提供详细的解释。这意味着,对于模型给出的每一个预测结果,我们都可以使用SHAP值来揭示哪些特征对得出这个预测结果产生了重要影响,以及它们是如何贡献的。这种解释方法对于理解模型在特定情境下的决策过程非常有用。

全局解释:虽然SHAP值主要用于局部解释,但通过对多个数据点的SHAP值进行汇总和分析,我们也可以获得模型的全局解释。

SHAP值的计算

穷举特征组合:SHAP 首先会生成所有可能的特征组合,即考虑数据集中的每个特征在不同组合下的表现。这样做是为了模拟特征之间的交互效应,从而更准确地评估每个特征对预测结果的独立贡献。

计算边际贡献:接着,对于数据集中的每个特征,SHAP 会计算它在不同特征组合中的边际贡献。边际贡献指的是当一个特征被添加到某个特征组合中时,模型预测结果的变化量。这个变化量反映了该特征对预测结果的直接影响。

加权平均:最后,SHAP 会对每个特征在不同组合中的边际贡献进行加权平均,从而得到该特征的SHAP值。这个加权平均过程考虑了特征组合的出现概率(即它们在训练数据中的频率),确保SHAP值能够准确地反映特征在整个数据集上的平均贡献。

SHAP的应用

特征重要性排序:通过计算和分析SHAP值,我们可以对特征进行重要性排序,从而直观地识别出哪些特征对模型预测结果的影响最为显著。这有助于我们更好地理解模型的决策过程,并可能指导我们进行特征选择或特征工程。

解释个体预测:SHAP值提供了一种有效的方式来解释单个数据点的预测结果。对于每个数据点,SHAP值可以揭示出哪些特征对模型的预测产生了重要影响,以及这些影响是正面的还是负面的。这有助于我们理解模型为何做出特定的预测,并增强模型的可解释性。

异常检测:通过分析SHAP值,我们可以发现异常数据点和潜在的问题特征。如果某个数据点的SHAP值与其他数据点存在显著差异,这可能意味着该数据点是一个异常点。同样,如果某个特征的SHAP值在所有数据点上都非常大或非常小,这可能意味着该特征存在潜在问题,需要进一步调查。通过这种方法,SHAP值不仅可以帮助我们解释模型预测,还可以作为异常检测和特征工程的有力工具。

SHAP在实践中的使用
SHAP支持多种机器学习框架,如XGBoost、LightGBM、sklearn、TensorFlow等,使用SHAP时,通常包括以下步骤:

训练模型:使用数据集训练机器学习模型

计算SHAP值:使用SHAP库计算每个特征的SHAP值

可视化与解释:通过可视化方法展示SHAP值,进行模型解释

代码实现

搭建LightGBM模型

import pandas as pdimport numpy as npimport matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = 'SimHei'plt.rcParams['axes.unicode_minus'] = Falsedf = pd.read_excel('california.xlsx')
from sklearn.model_selection import train_test_split
X = df.drop(['price'],axis=1)y = df['price']
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
# 数据集标准化x_mean = X_train.mean()x_std = X_train.std()y_mean = y.mean()y_std = y.std()X_train = (X_train - x_mean)/x_stdy_train = (y_train-y_mean)/y_stdX_val = (X_val - x_mean)/x_stdy_val = (y_val - y_mean)/y_stdX_test = (X_test - x_mean)/x_stdy_test = (y_test - y_mean)/y_std
import lightgbm as lgb
# LightGBM模型参数params_lgb = { 'learning_rate': 0.02, # 学习率,控制每一步的步长,用于防止过拟合。典型值范围:0.01 - 0.1 'boosting_type': 'gbdt', # 提升方法,这里使用梯度提升树(Gradient Boosting Decision Tree,简称GBDT) 'objective': 'mse', # 损失函数 'metric': 'rmse', # 评估指标 'num_leaves': 127, # 每棵树的叶子节点数量,控制模型复杂度。较大值可以提高模型复杂度但可能导致过拟合 'verbose': -1, # 控制 LightGBM 输出信息的详细程度,-1表示无输出,0表示最少输出,正数表示输出更多信息 'seed': 42, # 随机种子,用于重现模型的结果 'n_jobs': -1, # 并行运算的线程数量,-1表示使用所有可用的CPU核心 'feature_fraction': 0.8, # 每棵树随机选择的特征比例,用于增加模型的泛化能力 'bagging_fraction': 0.9, # 每次迭代时随机选择的样本比例,用于增加模型的泛化能力 'bagging_freq': 4 # 每隔多少次迭代进行一次bagging操作,用于增加模型的泛化能力}
model_lgb = lgb.LGBMRegressor(**params_lgb)model_lgb.fit(X_train, y_train, eval_set=[(X_val, y_val)],               eval_metric='rmse')              

将数据集划分为训练集、验证集和测试集,并对其进行标准化处理,然后使用LightGBM模型进行训练,设置参数以防止过拟合和提高泛化能力,到目前为止就构建了一个LighGBM模型,对这个预测结果进行一个简单的可视化

pred_train = model_lgb.predict(X_train)pred_val = model_lgb.predict(X_val)pred_test = model_lgb.predict(X_test)
y_train_h = y_train*y_std+y_meanpred_train_h = pred_train*y_std+y_mean
y_val_h = y_val*y_std+y_meanpred_val_h = pred_val*y_std+y_mean
y_test_h = y_test*y_std+y_meanpred_test_h = pred_test*y_std+y_mean
import seaborn as snscolors = sns.color_palette("husl", 3)
plt.figure(figsize=(15,5),dpi=300)plt.subplot(3,1,1)plt.scatter(y_train_h, pred_train_h, label='训练集', alpha=0.3, color=colors[0])plt.xlabel('真实值')plt.ylabel('预测值')plt.legend()plt.subplot(3,1,2)plt.scatter(y_val_h, pred_val_h, label='验证集', alpha=0.3, color=colors[1])plt.xlabel('真实值')plt.ylabel('预测值')plt.legend()plt.subplot(3,1,3)plt.scatter(y_test_h, pred_test_h, label='测试集', alpha=0.3, color=colors[2])plt.xlabel('真实值')plt.ylabel('预测值')plt.legend()plt.tight_layout()plt.show()

将模型预测结果与真实值进行比较并可视化,但这种简单的可视化无法深入解释模型的决策过程,但是我们可以借助SHAP值进一步分析模型特征的重要性和贡献度

摘要图(Summary Plot)

import shap# 构建 shap解释器explainer = shap.TreeExplainer(model_lgb)# 计算测试集的shap值shap_values = explainer.shap_values(X_train)# 特征标签labels = X_train.columnsplt.rcParams['font.family'] = 'serif'plt.rcParams['font.serif'] = 'Times new Roman'plt.rcParams['font.size'] = 13#cmap="?"配色viridis Spectral coolwar mRdYlGn RdYlBu RdBu RdGy PuOr BrBG PRGn PiYGplt.figure()shap.summary_plot(shap_values, X_train, feature_names=labels, plot_type="dot")

SHAP摘要图是一种常用的可视化工具,旨在直观展示机器学习或深度学习模型中特征的重要性和特征值的影响方向。它通过结合特征重要性和特征效应图,清晰展现了每个特征的SHAP值分布情况,从而帮助我们深入理解每个特征对模型预测结果的具体贡献。

在众多学术研究和应用中,SHAP摘要图已成为不可或缺的分析工具。为了提供更加多样化和个性化的视觉体验,用户可以通过调整cmap参数来更改配色方案,避免审美疲劳。例如,"viridis"配色方案从黄色渐变至蓝绿色,适用于多种场景;"Spectral"则从红色过渡到蓝色,特别适用于显示具有正负影响的特征;"coolwarm"从冷色调过渡到暖色调,为解读提供鲜明对比;"RdYlGn"、"RdYlBu"、"RdBu"等则通过红、绿、蓝等颜色的组合,展现了丰富的视觉效果。

接下来,我们将从不同角度对SHAP摘要图进行深度解读


在SHAP摘要图中,颜色直观地表示了特征值对模型预测结果的影响方向。具体来说:

  • 粉红色点:代表特征值在当前观测点(或样本)上对模型预测产生了正向(或积极)的影响。这意味着当该特征值增加时,模型的预测值也会倾向于增加。

  • 蓝色点:则表示特征值在当前观测点上对模型预测产生了负向(或消极)的影响。当该特征值增加时,模型的预测值可能会减少。

水平轴(SHAP值)

  • 水平轴展示了每个特征对预测结果的影响大小。观察点(或样本点)离中心线(零点)越远,表示该特征对模型输出的影响越大。正的SHAP值表示正面影响,即该特征值增加时,模型预测值也会增加;负的SHAP值则表示负面影响,即该特征值增加时,模型预测值会减少。

垂直轴(特征排列)

  • 垂直排列的特征按其对模型输出的总影响力进行排序。位于上方的特征对模型输出的影响更大,其SHAP值的分布范围也更广;而位于下方的特征则影响较小,其SHAP值多集中在零值附近。

特征影响力解释

  • 最上方特征(如MedInc):由于其在图中显示了大量的正面和负面影响点,这表明在不同的观测值下,该特征对模型预测的结果有着显著且多变的影响。这意味着该特征在模型决策过程中扮演着重要角色。

  • 中部特征(如AveRooms):尽管也显示出两种颜色的点,但点的分布更集中,表明其影响相对较小,且影响的方向较为一致。

  • 底部特征(如Population):这些特征在图中对模型的影响最小,且大部分影响点都接近零值,表明这些特征在模型预测中的贡献较小,或者可能与模型预测结果的关系不紧密。

理解SHAP值

SHAP值反映了在特定模型和特定样本下,每个特征对模型输出结果的具体影响。通过计算SHAP值,我们可以量化地评估每个特征在模型预测中的作用,从而更深入地理解模型的决策过程。同时,SHAP值也为特征选择和模型优化提供了有力的工具。

print("shap维度:",shap_values.shape)print("测试集维度:",X_train.shape)

可以看见shap_values和测试集(这里做的摘要图是对测试集进行操作)维度是一致的,也就是每一个shap值实际和原始数据每一个点是一一对应的,当然也可以对所有数据集在一张画布上展示每一个特征的排序

# 计算训练集和测试集的SHAP值shap_values_train = explainer.shap_values(X_train)shap_values_val = explainer.shap_values(X_val)shap_values_test = explainer.shap_values(X_test)
# 绘制SHAP值总结图(Summary Plot)plt.figure(figsize=(15, 5))plt.subplot(1, 3, 1)shap.summary_plot(shap_values_train, X_train, plot_type="bar", show=False)plt.title("X_train")plt.xlabel('') # 移除 x 轴标签避免x轴重叠
plt.subplot(1, 3, 2)shap.summary_plot(shap_values_val, X_val, plot_type="bar", show=False)plt.title("X_val")
plt.subplot(1, 3, 3)shap.summary_plot(shap_values_test, X_test, plot_type="bar", show=False)plt.title("X_test")plt.xlabel('')
plt.tight_layout()plt.show()

这里计算的就不在是每一个点的shap值,而是计算的每一个特征的重要程度,特征按影响力从大到小进行排序,如何进行计算的呢?

np.abs(shap_values_train).mean(axis=0) # 利用测试集进行演示

就是把SHAP_value样本取绝对值的平均值从而得到每个特征的重要程度

依赖图(Dependence Plot)

shap.dependence_plot('MedInc', shap_values, X_train, interaction_index='AveOccup')

依赖图用于显示一个特征的SHAP值与该特征值之间的关系,并可以展示特征之间的交互作用

在这里shap.dependence_plot函数的参数解释如下:

'MedInc':这是你想要研究的特征名

shap_values:这是通过SHAP计算得到的特征重要性值

X_train:这是用于生成SHAP值的训练数据

interaction_index='AveOccup':这是指定与主特征(MedInc)交互的特征,SHAP会在图中显示两者的交互效果
最后对这个依赖图进行解读:
正相关关系:在这里随着MedInc的增加,目标变量AveOccup也随之增加 
影响的程度:MedInc的值在 3 到 6 的范围内,SHAP 值变化并不明显,表明,在这个范围内,MedInc对模型预测的目标变量影响并不大,反之成立

交互效果:通过观察图中的颜色变化,可以了解主特征与交互特征之间的关系以及它们对模型预测的共同影响

力图(Force Plot)

# 绘制单个样本的SHAP解释(Force Plot)sample_index = 7  # 选择一个样本索引进行解释shap.force_plot(explainer.expected_value, shap_values_test[sample_index], X_test.iloc[sample_index], matplotlib=True)

力图用于直观地展示单个样本的SHAP值及其对模型预测结果的影响,通过力图,可以清晰地看到每个特征对该样本预测值的贡献

力图可视化解释:

基线值:图中的起点表示模型的基线值(expected_value)也就是可视化当中的base valuc

特征贡献:每个特征的贡献通过带颜色的条表示,条的长度表示该特征对最终预测值的影响大小,红色条表示正向贡献,即该特征使预测值增加,蓝色条表示负向贡献,即该特征使预测值减少

预测值:终点表示模型对该样本的最终预测值,这是基线值加上所有特征贡献的总和,在这里为-0.31
交互作用摘要图(Interaction Summary Plot)
shap_interaction_values = explainer.shap_interaction_values(X_train)shap.summary_plot(shap_interaction_values, X_train)

用于展示特征之间交互作用的重要性和影响的一种可视化方法

特征排序:在SHAP摘要图中,特征被按照其重要性进行排序,最顶部展示的是对模型预测结果影响最大的特征。这里的重要性值是每个特征与其他所有特征交互作用的重要性值之和,因此它全面反映了该特征在模型决策过程中的总体贡献。

SHAP交互作用值分布:图中的每个点代表了一个样本的交互作用值。这个值量化了一个特征与另一个特征的交互对模型预测结果的影响程度。交互作用值越大,意味着这种交互对预测结果的影响也越大。

颜色标识特征交互:在SHAP摘要图中,颜色被用来标识与主特征发生交互的其他特征。通过颜色的渐变,我们可以直观地看出不同特征之间的交互效果。颜色的深浅或变化反映了交互作用的强弱或类型,为我们提供了关于特征间复杂关系的深入洞察。

SHAP热图(Heatmap)
# 创建 shap.Explanation 对象shap_explanation = shap.Explanation(values=shap_values_test[0:500,:],                                     base_values=explainer.expected_value,                                     data=X_test.iloc[0:500,:], feature_names=X_test.columns)# 绘制热图shap.plots.heatmap(shap_explanation)

热图解读:

左侧y轴为重要性特征排名,特征按影响力从大到小进行排序,右侧y轴为其可视化,图像中颜色深浅表示SHAP值的大小,也就是该特征下值对模型的影响,颜色越深SHAP值绝对值越大越影响模型,顶部为模型在这些数值下的预测结果可视化,这里只从测试集中选择了500个数据进行可视化,太多的数据会影响运行时间,以及可视化的精美,读者自行取舍

通过热图,可以有效地探索和理解复杂模型的特征重要性和特征之间的相互关系,从而提高模型的解释性和可解释性





本文仅限转载,不代表本公众号观点


科普与科研
本公众号主要发布科普知识以及国内外一些科研进展。本公众号前身为睡眠障碍与脑科学,着重健康科普及脑科学领域。
 推荐账号,扫码关注
推荐账号二维码
 最新文章