Press "Enter" to skip to content

用于二元和多类目标变量的SHAP

当你的模型预测分类目标变量时,代码和解释SHAP图的指南

Nika Benedictova在Unsplash上的照片

SHAP值给出了模型特征对预测的贡献。对于二元目标变量,我们将这些值解释为对数几率。对于多类目标,我们使用softmax。我们将会:

  • 更深入地讨论这些解释
  • 提供显示SHAP图的代码
  • 探索聚合多类目标的SHAP值的新方法

您还可以观看关于这个主题的视频:

之前的SHAP教程

我们从之前的SHAP教程中继续进行。它详细介绍了连续目标变量的SHAP图。您会发现这些图和它们的见解对于分类目标变量也是相似的。您还可以在GitHub上找到完整的项目。

用Python介绍SHAP

如何创建和解释SHAP图:瀑布、力量、平均SHAP、蜂群和依赖关系

towardsdatascience.com

总结一下,我们使用SHAP来解释使用鲍鱼数据集构建的模型。该数据集有4177个实例,您可以在下面看到特征的示例。我们使用8个特征来预测y——鲍鱼壳上的环数。环数与鲍鱼的年龄有关。在本教程中,我们将将y分为不同的组,创建二元和多类目标变量。

X特征矩阵(源自UCI机器学习库)(许可:CC0:公共领域)

二元目标变量

对于连续目标变量,我们看到每个实例都有8个SHAP值,即每个模型特征一个。如图1所示,如果我们将这些值相加,并加上平均预测值E[f(x)],我们就得到了该实例的预测值f(x)。对于二元目标变量,我们具有相同的性质,不同之处在于我们将这些值解释为正预测的对数几率。

图1:将SHAP值解释为对数几率(来源:作者)

为了理解这一点,让我们深入了解一个SHAP图。我们首先创建一个二元目标变量(第2行)。我们根据y创建两个组:

  • 如果鲍鱼环数超过平均环数,则为1
  • 否则为0
#二元目标变量y_bin = [1 if y_>10 else 0 for y_ in y]

我们使用这个目标变量和8个特征来训练一个XGBoost分类器(第2-3行)。该模型的准确率为96.6%。

#训练模型model_bin = xgb.XGBClassifier(objective="binary:logistic")model_bin.fit(X, y_bin)

现在我们计算SHAP值(第2-3行)。我们输出这个对象的形状(第5行),得到的结果是(4177, 8)。所以,就像连续目标一样,我们对于每个预测和特征都有一个SHAP值。稍后,我们将看到这对于多类目标是不同的。

#获取shap值
explainer = shap.Explainer(model_bin)
shap_values_bin = explainer(X)
print(shap_values_bin.shape) #输出: (4177, 8)

我们在第一个实例中显示一个瀑布图(第6行)。我们可以在图2中看到结果。请注意,代码与连续变量的代码相同。除了数字,瀑布图也看起来相似。

#第一个实例的瀑布图
shap.plots.waterfall(shap_values_bin[0])

现在,E[f(x)] = -0.789表示对所有4177个鲍鱼的平均预测log odds。这是一个正(1)预测的log odds。对于这个特定的鲍鱼,模型预测它有一个以上平均环数的概率为0.3958(即P = 0.3958)。这给出了我们的预测log odds为f(x) = ln(0.3958/(1–0.3958)) = -0.423

图2:带有二元目标变量的瀑布图(来源:作者)

因此,SHAP值给出了预测log odds与平均预测log odds之间的差异。正的SHAP值增加了log odds。例如,去壳重量增加了log odds1.32。换句话说,该特征增加了模型预测一个以上平均环数的概率。同样,负值减少了log odds。

我们也可以像之前一样对这些值进行聚合。好消息是像蜜蜂图或平均SHAP之类的图的解释将保持不变。只需记住我们正在处理log odds。现在让我们看看对于多类目标变量,这种解释如何改变。

多类目标变量

我们首先创建一个具有3个类别(年轻(0),VoAGI(1)和老年(2))的新目标变量(y_cat)。与之前一样,我们训练一个XGBoost分类器来预测这个目标变量(第5-6行)。

#分类目标变量
y_cat = [2 if y_ > 12 else 1 if y_ > 8 else 0 for y_ in y]
#训练模型
model_cat = xgb.XGBClassifier(objective="binary:logistic")
model_cat.fit(X, y_cat)

对于这个模型,我们无法再谈论“正预测”。我们可以通过输出第一个实例的预测概率(第2行)来看到这一点。这给出了[0.2562, 0.1571, 0.5866]。在这种情况下,第3个概率最高,因此预测鲍鱼是老年(2)。对于SHAP来说,这意味着我们不能再只考虑正类的值。

#获取概率预测
model_cat.predict_proba(X)[0]

当我们计算SHAP值时(第2-3行),我们可以看到这一点。代码与二元模型相同。然而,当我们输出形状(第5行)时,我们得到了(4177, 8, 3)。现在,我们对每个实例、特征和类别都有一个SHAP值。

#获取shap值
explainer = shap.Explainer(model_cat)
shap_values_cat= explainer(X)
print(np.shape(shap_values_cat))

因此,我们必须为每个类别单独显示SHAP值的瀑布图。我们在下面的代码中为第一个实例做了这个。

#类别0的瀑布图
shap.plots.waterfall(shap_values_cat[0,:,0])
#类别1的瀑布图
shap.plots.waterfall(shap_values_cat[0,:,1])
#类别2的瀑布图
shap.plots.waterfall(shap_values_cat[0,:,2])

图3给出了类别0的瀑布图。这些值解释了每个特征对该类别的模型预测的贡献。相对于该类别的平均预测。我们看到该类别的概率相对较低(即0.2562)。我们可以看到去壳重量特征对这个较低的概率做出了最显著的贡献。

图3:类别0的瀑布图(来源:作者)

图4显示了其他类别的输出。你会注意到类别2的f(x) = 1.211是最大的。这是有道理的,因为我们看到这个类别的概率也是最大的(0.5866)。当分析这个实例的SHAP值时,关注这个瀑布图可能是有意义的。它是这个鲍鱼的类别预测。

用于二元和多类目标变量的SHAP 四海 第6张

图4:类别1和2的瀑布图(来源:作者)

使用Softmax解释值

由于我们现在处理多类别问题,f(x)以Softmax的形式给出。我们可以使用下面的函数将Softmax值转换为概率。在上面的瀑布图中,fx给出了三个f(x)值,结果为[0.2562, 0.1571, 0.5866]。这些是我们在实例0中看到的相同预测概率!

def softmax(x):    """计算x中每个分数集合的Softmax值"""    e_x = np.exp(x - np.max(x))    return e_x / e_x.sum(axis=0)# 将Softmax转换为概率fx = [0.383,-0.106,1.211]softmax(fx)

聚合多类别SHAP值

这些SHAP值可以使用任何SHAP图进行聚合。然而,就像瀑布图一样,每个类别都会有单独的图表。分析这些图表可能很繁琐,特别是当目标变量中有很多类别时。因此,我们将讨论一些其他的聚合方法来结束。

首先是均值SHAP图的一种版本。我们分别计算每个类别的shap值的绝对均值(第2-4行)。然后创建一个柱状图,每个类别和特征对应一个柱子。

# 计算每个类别的均值SHAP值mean_0 = np.mean(np.abs(shap_values_cat.values[:,:,0]),axis=0)mean_1 = np.mean(np.abs(shap_values_cat.values[:,:,1]),axis=0)mean_2 = np.mean(np.abs(shap_values_cat.values[:,:,2]),axis=0)df = pd.DataFrame({'small':mean_0,'VoAGI':mean_1,'large':mean_2})# 绘制均值SHAP图fig,ax = plt.subplots(1,1,figsize=(20,10))df.plot.bar(ax=ax)ax.set_ylabel('均值SHAP',size = 30)ax.set_xticklabels(X.columns,rotation=45,size=20)ax.legend(fontsize=30)

我们可以在图5中看到输出。需要提到的一点是,每个柱子给出了所有预测的平均值。然而,实际预测的类别在每种情况下都会不同。因此,你可能会用不能解释预测类别的SHAP值来扭曲均值。这可能是为什么我们在VoAGI类别中看到较小均值的原因。

图5:多类别目标变量的均值SHAP图(来源:作者)

为了解决这个问题,我们可以专注于预测类别的SHAP值。我们首先获取每个实例的预测类别(第2行)。我们创建了一组新的SHAP值(new_shap_values)。这是通过循环原始值并仅选择与该实例的预测相对应的集合来完成的(第5-7行)。

# 获取模型预测结果preds = model_cat.predict(X)new_shap_values = []for i, pred in enumerate(preds):    # 获取预测类别的SHAP值    new_shap_values.append(shap_values_cat.values[i][:,pred])

然后,我们替换原始对象中的SHAP值(第2行)。现在,如果我们输出形状,我们得到(4177, 8)。换句话说,我们又回到了每个实例一个集合的SHAP值。

# 替换SHAP值shap_values_cat.values = np.array(new_shap_values)print(shap_values_cat.shape)

这种方法的好处是可以轻松使用内置的SHAP图。例如,图6中的平均SHAP图。我们可以将这些值解释为每个特征对预测类别的平均贡献。

shap.plots.bar(shap_values_cat)
图6:多类别目标变量的预测类别的平均SHAP(来源:作者)

我们还可以使用beeswarm图。然而,请注意,我们看不到SHAP值和特征值之间的明确关系。这是因为特征在不同的预测类别下具有不同的关系。年龄较大的鲍鱼会更大。因此,例如,较大的壳重量将导致对老(2)预测的概率更高。对于年轻(0)的预测相反。

shap.plots.beeswarm(shap_values_cat)
图6:多类别目标变量的beeswarm图(来源:作者)

因此,希望您清楚如何解释二进制和多类别目标变量的SHAP值。然而,您可能想知道为什么它们以对数几率和softmax的形式给出。这是因为SHAP值的计算方式。即通过线性模型同时进行。如果我们要用线性模型预测二进制或多类别变量,我们分别使用逻辑回归或softmax回归。这些链接函数是可微分的,并且它们允许我们将模型预测公式化为参数和特征的线性方程。同样,这些属性被用于高效地估计SHAP值。

了解更多关于shap的信息:

新的SHAP图:小提琴图和热力图

SHAP版本0.42.1中的图表可以告诉您有关您的模型的信息

towardsdatascience.com

SHAP的限制

SHAP如何受特征依赖性、因果推断和人为偏差的影响

towardsdatascience.com

使用SHAP调试PyTorch图像回归模型

使用DeepShap来理解和改进驱动自动驾驶汽车的模型

towardsdatascience.com

希望您喜欢这篇文章!您可以通过成为我的推荐会员来支持我 🙂

作为VoAGI会员,您会费的一部分将用于奖励您阅读的作家,并且您将获得对每个故事的完整访问权限…

conorosullyds.medium.com

| Twitter | YouTube | Newsletter — 免费注册以获得Python SHAP课程的访问权限

参考资料

Stackoverflow 如何解释使用SHAP时多类分类问题的基础值?https://stackoverflow.com/questions/65029216/how-to-interpret-base-value-of-multi-class-classification-problem-when-using-sha/65034362#65034362

Leave a Reply

Your email address will not be published. Required fields are marked *