找回密码
 会员注册
查看: 23|回复: 0

Python中的SHAP简介

[复制链接]

8

主题

0

回帖

25

积分

新手上路

积分
25
发表于 2024-9-6 12:19:09 | 显示全部楼层 |阅读模式
本文中有多篇计划文章,后期会补充相关链接。鉴于公众号内无法后期修改文章,请关注原文链接。如何创建和解释SHAP图:瀑布图、力图、平均SHAP图、蜂群图和依赖图可直接在橱窗里购买,或者到文末领取优惠后购买:SHAP是用于理解和调试模型的最强大的Python包。它可以告诉我们每个模型特征对单个预测的贡献。通过汇总SHAP值,我们还可以了解多个预测的趋势。只需几行代码,我们就能识别和可视化模型中的重要关系。我们将介绍用于计算和显示SHAP值的代码。其中包括对以下SHAP图的解释:WaterfallPlotForcePlotMeanSHAPPlotBeeswarmPlotDependencePlot我们针对预测连续目标变量的模型执行此操作。如果你需要分类的解释,日后会专门写相关文章,届时可以参阅[[二元和多类目标变量的SHAP]],你可以在GitHub1上找到完整的项目。数据集为了演示SHAP包,我们将使用包含4,177个观测值的「鲍鱼数据集」2。下面,你可以看到我们的数据集的快照。鲍鱼是一种贝类美食。我们想使用数据集来预测它们的年龄。更具体地说,我们的目标变量是鲍鱼壳中的环数。我们将使用shelllength、shelldiameter和鲍鱼的wholeweight等特征。在下图中,我们直观地展示了一些特征与目标变量之间的关系。Shuckedweight是鲍鱼肉的重量(即从壳中取出后的重量)。我们可以看到,去壳重量增加时,环的数量往往会增加。这是有道理的,因为我们认为年龄较大的鲍鱼会更大,肉也更多。我们还可视化了鲍鱼的性别,这是一个分类特征。在模型中使用此功能之前,我们需要使用One-Hot编码对其进行转换。正如你所看到的,这会导致每个生成的二进制特征都有一个单独的SHAP值。这使得很难理解原始分类特征的整体贡献。我们将在后面专门写一篇文章「[[分类特征的SHAP]]」探讨一种解决方案。对于最后一点数据探索,我们为连续特征创建一个相关矩阵。可以看到,我们正在处理高度相关的特征。长度和直径完全相关。同样,全重与其他重量测量值也高度相关。例如肉的重量(去壳重量)和除去肉的外壳重量(外壳重量)。包我们在下面导入必要的Python包。我们有一些用于管理和可视化数据的标准库(第4-9行)。XGBoost用于对目标变量进行建模(第13行),我们导入一些包来评估我们的模型(第14行)。最后,我们导入SHAP包(第16行)。我们初始化包(第17行)。这样我们就可以在笔记本中显示一些SHAP图。importwarningswarnings.filterwarnings("ignore")importpandasaspdimportnumpyasnpimportmatplotlib.pyplotasplt%matplotlibinlineplt.style.use('default')importseabornassnsimportxgboostasxgbfromsklearn.metricsimportaccuracy_score,confusion_matriximportshapshap.initjs()importosdp=os.environ.get('pub_data')1234567891011121314151617181920建模我们使用数据探索来指导特征工程。首先,我们从X特征矩阵中删除长度和整体重量(第10-11行)。我们发现这些与其他特征完美相关。#importdatasetdf=pd.read_csv(dp+'Abalonedataset/abalone.data',names=["sex","length","diameter","height","wholeweight","shuckedweight","visceraweight","shellweight","rings",],)#Getfeaturesy=df["rings"]X=df[["sex","length","height","shuckedweight","visceraweight","shellweight"]]12345678910111213141516171819我们看到性别是一个分类特征。在将其用于模型之前,我们需要将其转换为3个虚拟变量(第2-4行)。然后,我们从特征矩阵中删除原始特征(第5行)。#createdummyvariablesy=y.copy()X=X.copy()X.loc[:,'sex.M']=X['sex'].apply(lambdas:1ifs=='M'else0)X.loc[:,'sex.F']=X['sex'].apply(lambdas:1ifs=='F'else0)X.loc[:,'sex.I']=X['sex'].apply(lambdas:1ifs=='I'else0)X=X.drop("sex",axis=1)12345678我们现在有8个模型特征。你可以在下面看到这些特征的快照。现在,我们可以训练一个用于预测鲍鱼壳环数的模型。由于我们的目标变量是连续的,因此我们使用XGBRegressor(第2行)。我们在整个特征集上训练模型(第3行)。#trainmodelmodel=xgb.XGBRegressor(objective="reg:squarederror")model.fit(X,y)123这个模型应该足以演示SHAP包。我们可以通过使用图3中的散点图对其进行评估来看到这一点。我们正在将模型的预测(第2行)与实际环数进行比较。红线给出了如果我们有完美预测的值。**注意:**我们没有在模型上投入太多精力。除非你使用SHAP进行数据探索,否则你应该始终使用最佳实践。你的模型越好,你的SHAP分析就越可靠。SHAP图最后,我们可以使用SHAP值来解释这个模型。为此,我们将模型传递给shap.Explainer器函数(第2行)。这将创建一个解释器对象。我们使用它来计算特征矩阵中每个观测值的SHAP值(第3行)。#getshapvaluesexplainer=shap.Explainer(model)shap_values=explainer(X)123图1:Waterfall特征矩阵中的4,177个观测值中的每一个都有8个SHAP值。也就是说,我们模型中的每个特征都有一个SHAP值。我们可以使用waterfall函数来可视化第一个观测值的SHAP值(第2行)。#Waterfallplotforfirstobservationshap.plots.waterfall(shap_values[0])12E[f(x)]=9.933给出了所有4,177只鲍鱼的平均预测环数。f(x)=12.668是这只鲍鱼的预测环数。SHAP值是介于两者之间的所有值。例如,去壳重量使预测环数增加了1.68我们数据集中的每个观测/鲍鱼都会有一个独特的瀑布图。它们都可以用与上述相同的方式进行解释。在每种情况下,SHAP值告诉我们与平均预测相比,特征对预测的贡献如何。较大的正/负值表示该特征对模型的预测有显著影响。图2:ForcePlot另一种可视化方法是使用力图。你可以将其视为浓缩的瀑布图。我们从相同的基值9.933开始,你可以看到每个特征对最终预测13.04的贡献。#Forceplotforfirstobservationshap.plots.force(shap_values[0])12图3:叠加ForcePlot瀑布图和力图非常适合解释单个预测。要了解我们的模型如何进行总体预测,我们需要汇总SHAP值。一种方法是使用堆叠力图我们可以将多个力图组合在一起以创建堆叠力图。在这里,我们在力图函数中传递前100个观测值的SHAP值。每个单独的力图现在都是垂直的并且并排堆叠。shap.plots.force(shap_values[0:100])1你可以在下面看到此图是交互式的。我们还可以更改图的顺序并选择要显示的特征贡献。例如,在下面的图中我们有:仅显示壳重量的SHAP值(y轴=**shellweighteffects)按壳重量特征值对力图进行排序(x轴=**shellweight)从该图中我们可以看出,随着壳重的增加,SHAP值也会增加。换句话说,年龄较大的鲍鱼壳往往更重。这是了解我们的模型所捕获关系的性质的一种方法。我们将看到蜂群图和依赖图也可以以这种方式使用。图4:MeanSHAP下一个图将告诉我们哪些特征最重要。对于每个特征,我们计算所有观测值的平均SHAP值。具体来说,我们取绝对值的平均值,因为我们不希望正值和负值相互抵消。最后,我们得到下面的条形图。每个特征都有一个条形图。例如,我们可以看到壳重量具有最大的平均SHAP值。#MeanSHAPshap.plots.bar(shap_values)12具有较大正/负贡献的特征将具有较大的平均SHAP值。换句话说,这些特征对模型的预测产生了重大影响。从这个意义上讲,此图的使用方式与特征重要性图相同。图5:Beeswarm接下来,我们得到了最有用的图表。蜂群将所有SHAP值可视化。在y轴上,这些值按特征分组。对于每个组,点的颜色由特征值决定(即,特征值越高,颜色越红)。#Beeswarmplotshap.plots.beeswarm(shap_values)12与平均SHAP一样,蜂群可用于突出重要关系。事实上,上图中的特征是按平均SHAP排序的。我们也可以开始理解这些关系的性质。对于壳重,请注意SHAP值如何随着特征值的增加而增加。我们在堆叠力图中看到了类似的关系。它告诉我们,壳重值越大,预测的环数也就越多。你可能已经注意到,去壳重量具有相反的关系。查看蜂群图,我们可以看到此特征的较大值与较小的SHAP值相关。我们可以使用依赖图仔细研究这些关系。图6:DependencePlot依赖图是单个特征的SHAP值与特征值的散点图。如果特征与目标变量具有非线性关系,则它们特别有用。例如,以壳重的依赖关系图为例。查看蜂群图,我们可能假设SHAP值随特征值线性增加。依赖关系图告诉我们,这种关系并不是完全线性的。#Plot1:shellweightshap.plots.scatter(shap_values[:,"shellweight"])12我们还可以使用第二个特征的值来为散点图着色。现在我们有了相同的图,去壳重量越大,点越红。我们可以看到,当壳重和去壳重量都很大时,SHAP值也很大。shap.plots.scatter(shap_values[:,"shellweight"],color=shap_values[:,"shuckedweight"])12这些图可用于可视化特征之间的相互作用,但要小心!在我们的例子中,该图是两个特征之间相关性的结果。我们还有去壳重量(即鲍鱼肉的重量)的依赖关系图。使用此图,我们可以确认我们在蜂群图中看到的关系。SHAP值确实会随着去壳重量的增加而下降。#Plot2:shuckedweightshap.plots.scatter(shap_values[:,"shuckedweight"])12直观地看,这种关系似乎很奇怪。我们不是希望年龄较大的鲍鱼更大、肉更多吗?事实上,这是壳重和去壳重量相互作用的结果。由于相关性,我们在依赖图中看不到它。在文章《[[分析与SHAP的相互作用]]》中,我们探讨了如何使用SHAP交互值来识别此类相互作用。SHAP用于分类你会很高兴地知道,分类问题的SHAP图与上面的非常相似。除了二元目标变量之外,我们根据对数几率来解释SHAP值。对于多类目标,我们使用softmax。一个缺点是我们最终会为多类目标中的每个类绘制单独的SHAP图。我们将在后面一篇文章中讨论了一些更好的聚合值的方法,暂定为[[二元和多类目标变量的SHAP]],当你的模型预测分类目标变量时,代码和SHAP图的解释指南。我们可以看到,SHAP值是一种有用的工具,可用于了解我们的模型如何进行预测。然而,我们只是触及了该软件包所能提供内容的表面。如果你想了解更多信息,我日后将会写一些有关SHAP的文章,用于更深入地探讨了SHAP和Shapley值的某些方面,包括:[[从Shapley到SHAP—数学理解]],如何计算SHAP特征贡献的概述[[KernelSHAP与TreeSHAP]],根据速度、复杂性和其他考虑因素比较SHAP近似方法[[使用SHAP调试PyTorch图像回归模型]],使用DeepShap理解和改进自动驾驶汽车模型由于本人常年使用双向链接软件写文,在粘贴的时候会时常有类似[[XXX文章名]]的样式,这只是我文章相互连接的格式,如若不能链接到别处大概率就是还未成文发布。AI进阶:企业项目实战3参考S.Lundberg,SHAPPythonpackage(2021),https://github.com/slundberg/shapS.Lundberg&S.Lee,AUnifiedApproachtoInterpretingModelPredictions(2017),https://arxiv.org/pdf/1705.07874.pdfC.Molnar,InterpretableMachineLearning(2021)https://christophm.github.io/interpretable-ml-book/shap.html希望这篇文章对你有所帮助!你还可以阅读我的其他文章,或者查看有关企业AI实战项目的教程,相信会让你拥有更多收获。「AI秘籍」系列课程:人工智能应用数学基础人工智能Python基础人工智能基础核心知识人工智能BI核心知识人工智能CV核心知识Github,https://github.com/hivandu/public_articles/blob/main/src/SHAP/shap_tutorial.ipynb↩︎AbaloneDataset,https://archive.ics.uci.edu/dataset/1/abalone↩︎AI企业项目实战,https://www.sanjieke.cn/course/detail/sjk/8005780↩︎
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-10 05:03 , Processed in 0.439671 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表