SHAP TreeExplainer 用于随机森林多分类问题: shap_values[i] 是什么?

5

我想绘制SHAP图表。

这是我的代码。其中rnd_clf是一个RandomForestClassifier

import shap 
explainer = shap.TreeExplainer(rnd_clf) 
shap_values = explainer.shap_values(X) 
shap.summary_plot(shap_values[1], X) 

我知道shap_values[0]是负数,而shap_values[1]是正数。

但对于多类RandomForestClassifier呢? 我有一个rnd_clf分类以下之一:

['Gusto', 'Kestrel 200 SCI Older Road Bike','Vilano Aluminum Road Bike 21 Speed Shimano','Fixie']。

我如何确定shap_values[i]的哪个索引对应于输出的哪个类别?

2个回答

10
如何确定shap_values[i]的索引对应于输出的哪个类别?
shap_values[i]是第i个类别的SHAP值。第i个类别是根据您使用的编码模式(例如LabelEncoder、pd.factorize等)而定的。
您可以尝试以下提示:
from sklearn.preprocessing import LabelEncoder

labels = [
    "Gusto",
    "Kestrel 200 SCI Older Road Bike",
    "Vilano Aluminum Road Bike 21 Speed Shimano",
    "Fixie",
]
le = LabelEncoder()
y = le.fit_transform(labels)
encoding_scheme = dict(zip(y, labels))
pprint(encoding_scheme)

{0: 'Fixie',
 1: 'Gusto',
 2: 'Kestrel 200 SCI Older Road Bike',
 3: 'Vilano Aluminum Road Bike 21 Speed Shimano'}

所以,例如对于这个特定案例,shap_values[3] 是针对 'Vilano Aluminum Road Bike 21 Speed Shimano' 的。
为了进一步理解如何解释SHAP值,让我们准备一个包含100个特征和10个类别的多类分类的合成数据集。
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from shap import TreeExplainer
from shap import summary_plot

X, y = make_classification(1000, 100, n_informative=8, n_classes=10)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
print(X_train.shape)

(750, 100)

目前我们有一个包含750行、100个特征和10个类别的训练数据集。

让我们训练RandomForestClassifier并将其输入给TreeExplainer

clf = RandomForestClassifier(n_estimators=100, max_depth=3)
clf.fit(X_train, y_train)
explainer = TreeExplainer(clf)
shap_values = np.array(explainer.shap_values(X_train))
print(shap_values.shape)

(10, 750, 100)

10:班级数量。所有SHAP值被组织成10个数组,每个班级一个数组。 750:数据点数量。我们有每个数据点的本地SHAP值。 100:特征数量。我们有每个特征的SHAP值。
例如,对于“第3班级”,您将会有:
print(shap_values[3].shape)

(750, 100)

750:每个数据点的SHAP值 100:每个特征的SHAP值贡献
最后,您可以运行一次健全性检查,确保模型的真实预测与shap预测相同。
为此,我们将(1)交换shap_values的前两个维度,(2)对所有特征按类别求和SHAP值,(3)将SHAP值添加到基准值中。
shap_values_ = shap_values.transpose((1,0,2))

np.allclose(
    clf.predict_proba(X_train),
    shap_values_.sum(2) + explainer.expected_value
)

True

然后您可以继续查看summary_plot,它将根据每个类别的SHAP值显示特征排名。对于类别3,它将显示如下内容:
summary_plot(shap_values[3],X_train)
以下是解释:
- 基于SHAP贡献,对于第3类最具影响力的特征是44、64和17。 - 对于特征64和17,较低的值往往会导致较高的SHAP值(因此更高的类标签概率)。 - 在显示的20个特征中,特征92、6和53的影响最小。

3
最终的图表有误吗?它里面没有16、59等特征。 - dgg32
@dgg32 最终的图表是正确的。默认情况下,只显示前20个特征。但是你可以进行更改。 - Sergey Bushmanov
情节和解释不一致。你可以更新你的解释。44、64、17是最重要的特点。 - Hadij
@Hadij 谢谢,已纠正。对于这样一个古老的问题来说,你是第一个注意到差异的人,真奇怪。 - Sergey Bushmanov

0
根据此GitHub问题,shap_values列表的顺序对应于您模型的.classes_属性。最后,您可以绘制类似以下内容的图形:
shap.summary_plot(shap_values, X_test.toarray(), feature_names=model.get_feature_names_out(), class_names=model.classes_)

请注意,如果您使用管道,model 可能是两个不同的模型,可以通过 pipeline.named_steps 字典访问。比如,在自然语言处理中,您可能有一个用于分词的步骤 feature_names(即单词/ n-gram),以及一个用于分类的机器学习模型 class_names

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接