【问题标题】:How to interpret base_value of multi-class classification problem when using SHAP?使用 SHAP 时如何解释多类分类问题的 base_value?
【发布时间】:2021-03-09 18:10:09
【问题描述】:

我正在使用 shap 库来实现 ML 可解释性,以更好地理解 k-means 分割算法集群。简而言之,我制作了一些博客,使用 k-means 对它们进行聚类,然后将这些聚类作为标签并使用 xgboost 来尝试预测它们。我有 5 个集群,所以这是一个单标签多类分类问题。

import numpy as np
from sklearn.datasets import make_blobs
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans 
import xgboost as xgb
import shap

X, y = make_blobs(n_samples=500, centers=5, n_features=5, random_state=0)
data = pd.DataFrame(np.concatenate((X, y.reshape(500,1)), axis=1), columns=['var_1', 'var_2', 'var_3', 'var_4', 'var_5', 'cluster_id'])
data['cluster_id'] = data['cluster_id'].astype(int).astype(str)
scaler = StandardScaler()
scaled_features = scaler.fit_transform(data.iloc[:,:-1])
kmeans = KMeans(n_clusters=5, **kmeans_kwargs)
kmeans.fit(scaled_features)
data['predicted_cluster_id'] = kmeans.labels_.astype(int).astype(str)
clf = xgb.XGBClassifier()
clf.fit(scaled_data.iloc[:,:-1], scaled_data['predicted_cluster_id'])
shap.initjs()
explainer = shap.TreeExplainer(clf)
shap_values = explainer.shap_values(scaled_data.iloc[0,:-1].values.reshape(1,-1))
shap.force_plot(explainer.expected_value[0], shap_values[0], link='logit')  # repeat changing 0 for i in range(0, 5)

上面的图片很有意义,因为班级是“3”。但是为什么这个base_value,不应该是1/5吗?我刚才问自己一个similar question,但这次我已经设置了link='logit'。

【问题讨论】:

    标签: python machine-learning scikit-learn xgboost shap


    【解决方案1】:

    link="logit" 似乎不适用于多类,因为它只适用于二进制输出。这就是为什么您看不到总和为 1 的概率。

    让我们简化您的代码:

    import numpy as np
    from sklearn.datasets import make_blobs
    import pandas as pd
    from sklearn.preprocessing import StandardScaler
    from sklearn.cluster import KMeans 
    import xgboost as xgb
    import shap
    from scipy.special import softmax, logit, expit
    np.random.seed(42)
    
    X, y_true = make_blobs(n_samples=500, centers=5, n_features=3, random_state=0)
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
    kmeans = KMeans(n_clusters=5)
    y_predicted = kmeans.fit_predict(X_scaled, )
    
    clf = xgb.XGBClassifier()
    clf.fit(X_scaled, y_predicted)
    shap.initjs()
    

    然后,您在以下位置看到的预期值:

    explainer = shap.TreeExplainer(clf)
    explainer.expected_value
    array([0.67111245, 0.60223354, 0.53357694, 0.50821152, 0.50145331])
    

    是原始空间中的基本分数。

    可以使用softmax 将多类原始分数转换为概率:

    softmax(explainer.expected_value)
    array([0.22229282, 0.20749694, 0.19372895, 0.18887673, 0.18760457])
    

    shap.force_plot(..., link="logit") 对多类没有意义,而且似乎不可能从原始切换到概率并且仍然保持可加性(因为 softmax(x+y) ≠ softmax(x) + softmax(y))。

    如果您想在概率空间中分析您的数据,请尝试KernelExplainer

    from shap import KernelExplainer
    masker = shap.maskers.Independent(X_scaled, 100)
    ke = KernelExplainer(clf.predict_proba, data=masker.data)
    ke.expected_value
    # array([0.18976762, 0.1900516 , 0.20042894, 0.19995041, 0.21980143])
    shap_values=ke.shap_values(masker.data)
    shap.force_plot(ke.expected_value[0], shap_values[0][0])
    

    或总结图:

    from shap import Explanation
    shap.waterfall_plot(Explanation(shap_values[0][0],ke.expected_value[0]))
    

    它们现在是概率空间中形状值的附加值,并且与基本概率(见上文)和第 0 个数据点的预测概率很好地对齐:

    clf.predict_proba(masker.data[0].reshape(1,-1))
    array([[2.2844513e-04, 8.1287889e-04, 6.5225776e-04, 9.9737883e-01,
            9.2762709e-04]], dtype=float32)
    

    【讨论】:

    • 对我来说 shap.force_plot(ke.expected_value[0], shap_values[0][0]) 的输出还没有意义。 shap_values 长度为 5,与 num_clusters 一样。 y_predicted[0] 是'0',所以我希望这个图 f(x) 的输出是 1 而不是 0。对于 shap.waterfall_plot,我很难将图的输出与预测的类相匹配.
    • 如果对你来说仍然没有意义,我可以建议几件事。首先将 shap 值转换为 numpy 数组。数组的维度将是 [n_classes, n_samples, n_features]。为了更好地看到它,使类和功能的数量有所不同。然后shap_values_arr[:,i,:].sum(1) +expected_values 将是一个长度为 n_classes 的数组,其中包含第 i 个数据点的原始或概率预测。当然,预期的原始数据可以通过 softmax 与概率进行协调,并且可以通过 predict_proba 方法进行检查,正如我在瀑布图示例中向您展示的那样
    • 感谢您的帮助。我的意思是,如果您查看 y_predicted[:5],它将返回 [0, 0, 2, 3, 3]。所以第一行是“0”。这与您上面的情节不一致。
    • @G.Macia 感谢您与您的 cmets 一起回来。结果对我来说非常有意义。所有打印出来的结果都与图表一致。根据您的最后评论,第 0 个数据点的概率告诉该类是 4(来自 predict_proba),我严重怀疑 predict 方法会给出任何不同。
    • 另外,请注意y_predicted,如代码中所示,是基本事实,predict_proba 是模型学习预测的内容。因此,在许多情况下,它们可能会有所不同。
    猜你喜欢
    • 2021-12-19
    • 2020-07-02
    • 2023-02-13
    • 2022-11-05
    • 2020-10-19
    • 2019-12-08
    • 2023-03-22
    • 2020-09-20
    相关资源
    最近更新 更多